diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index c001898..8d4d81e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ *~ *#*# .*#* +.direnv +.ccad diff --git a/Cargo.lock b/Cargo.lock index 4c44c8f..e1e2885 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,43 +1,16 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" +name = "aho-corasick" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", + "memchr", ] -[[package]] -name = "aliasable" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" - -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -49,9 +22,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -64,118 +37,133 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell_polyfill", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "approx" -version = "0.3.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ "num-traits", ] [[package]] -name = "approx" -version = "0.5.1" +name = "arboard" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" dependencies = [ - "num-traits", + "clipboard-win", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "windows-sys 0.59.0", + "wl-clipboard-rs", + "x11rb", ] [[package]] -name = "arrayvec" -version = "0.7.4" +name = "ariadne" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "8454c8a44ce2cb9cc7e7fae67fc6128465b343b92c6631e94beca3c8d1524ea5" +dependencies = [ + "unicode-width 0.2.2", + "yansi", +] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "bezier-rs" -version = "0.4.0" +name = "beef" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde3aa314326e2f984f81adcb399c64b93eed3c0f2cd4258b711bf494c5741de" -dependencies = [ - "glam", -] +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +dependencies = [ + "serde_core", +] [[package]] -name = "bitflags" -version = "2.5.0" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] -name = "bumpalo" -version = "3.16.0" +name = "boolmesh" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "0b5e804fa333a4996316770fff780c7cd006371c58bc7824bd8798f17396eac2" +dependencies = [ + "glam 0.30.10", + "rayon", +] [[package]] -name = "bytecount" -version = "0.6.8" +name = "bumpalo" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytemuck" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" - -[[package]] -name = "byteorder" -version = "0.4.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -184,59 +172,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "casey" -version = "0.4.0" +name = "cc" +version = "1.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614586263949597dcc18675da12ef9b429135e13628d92eb8b8c6fa50ca5656b" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" dependencies = [ - "syn 1.0.109", + "find-msvc-tools", + "jobserver", + "libc", + "shlex", ] [[package]] -name = "castaway" -version = "0.2.2" +name = "cfg-if" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" -dependencies = [ - "rustversion", -] +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "cc" -version = "1.0.98" +name = "check_keyword" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "0dcba1e35fcf6c9350d9fb4863d87c66e5e37f1583f883560dca2c9320840bcc" dependencies = [ - "jobserver", - "libc", - "once_cell", + "phf", ] -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ - "android-tzdata", "iana-time-zone", - "js-sys", "num-traits", - "wasm-bindgen", - "windows-targets 0.52.5", + "serde", + "windows-link", ] [[package]] name = "clap" -version = "4.5.4" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -244,82 +222,72 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", - "terminal_size", ] [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" - -[[package]] -name = "colorchoice" -version = "1.0.1" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] -name = "command_cad" -version = "0.6.0" +name = "cli" +version = "0.1.0" dependencies = [ "anyhow", - "arrayvec", - "bezier-rs", - "casey", + "ariadne", "clap", - "common_data_types", - "compact_str", - "const-str", - "csv", - "enum_dispatch", - "enum_downcast", - "fj-core", - "fj-export", - "fj-math", - "fortuples", "git2", - "glam", - "imstr", - "lazy_static", - "log", - "macros", - "nalgebra", - "nom", - "nom_locate", - "ouroboros", - "paste", - "semver", - "serde", - "serde_json", - "serde_yaml", - "stderrlog", + "interpreter", + "nu-ansi-term", + "reedline", "tempfile", - "uneval", - "uom", - "whoami", + "termimad", + "tree-sitter", + "type-sitter", +] + +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", ] +[[package]] +name = "coalesce" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f42f93baa58655bd5b3db91dd9b2073dc8a0c887dc35bd1cfefbd43fc7cf07" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "common_data_types" version = "0.1.0" @@ -330,349 +298,783 @@ dependencies = [ ] [[package]] -name = "compact_str" -version = "0.7.1" +name = "convert_case" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ - "castaway", - "cfg-if", - "itoa", - "ryu", - "static_assertions", + "unicode-segmentation", ] [[package]] -name = "const-str" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3618cccc083bb987a415d85c02ca6c9994ea5b44731ec28b9ecf09658655fba9" - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" +name = "convert_case" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] [[package]] -name = "crc32fast" -version = "1.4.2" +name = "coolor" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "980c2afde4af43d6a05c5be738f9eae595cff86dce1f38f88b95058a98c027f3" dependencies = [ - "cfg-if", + "crossterm", ] [[package]] -name = "crossbeam-utils" -version = "0.8.20" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "csv" -version = "1.3.0" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", + "libc", ] [[package]] -name = "csv-core" -version = "0.1.11" +name = "crokey" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "51360853ebbeb3df20c76c82aecf43d387a62860f1a59ba65ab51f00eea85aad" dependencies = [ - "memchr", + "crokey-proc_macros", + "crossterm", + "once_cell", + "serde", + "strict", ] [[package]] -name = "decorum" -version = "0.3.1" +name = "crokey-proc_macros" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281759d3c8a14f5c3f0c49363be56810fcd7f910422f97f2db850c2920fde5cf" +checksum = "3bf1a727caeb5ee5e0a0826a97f205a9cf84ee964b0b48239fef5214a00ae439" dependencies = [ - "approx 0.3.2", - "num-traits", - "serde", - "serde_derive", + "crossterm", + "proc-macro2", + "quote", + "strict", + "syn", ] [[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - -[[package]] -name = "enum_dispatch" -version = "0.3.13" +name = "crossbeam" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.66", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", ] [[package]] -name = "enum_downcast" -version = "0.1.0" +name = "crossbeam-channel" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e532035fcb0d794ae56f74a0a39fea24bca14c7d47c428e3ab0667ebdccd8b4a" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ - "enum_downcast_derive", + "crossbeam-utils", ] [[package]] -name = "enum_downcast_derive" -version = "0.1.0" +name = "crossbeam-deque" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0e8987f91d86aa10313adf7c767a8cfd040364432d4e9f722b049b8ff7e634a" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] -name = "equivalent" -version = "1.0.1" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] [[package]] -name = "errno" -version = "0.3.9" +name = "crossbeam-queue" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ - "libc", - "windows-sys 0.52.0", + "crossbeam-utils", ] [[package]] -name = "fastrand" -version = "2.1.0" +name = "crossbeam-utils" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "fj-core" -version = "0.49.0" -source = "git+https://github.com/hannobraun/fornjot.git#613779934166dea13b7f87c0d56f3952f9658ff9" +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "fj-interop", - "fj-math", - "itertools 0.13.0", + "bitflags", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", "parking_lot", - "robust", - "spade", - "thiserror", - "type-map", + "rustix", + "serde", + "signal-hook", + "signal-hook-mio", + "winapi", ] [[package]] -name = "fj-export" -version = "0.49.0" -source = "git+https://github.com/hannobraun/fornjot.git#613779934166dea13b7f87c0d56f3952f9658ff9" +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ - "fj-interop", - "fj-math", - "stl", - "thiserror", - "threemf", - "wavefront_rs", + "winapi", ] [[package]] -name = "fj-interop" -version = "0.49.0" -source = "git+https://github.com/hannobraun/fornjot.git#613779934166dea13b7f87c0d56f3952f9658ff9" +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "fj-math", + "generic-array", + "typenum", ] [[package]] -name = "fj-math" -version = "0.49.0" -source = "git+https://github.com/hannobraun/fornjot.git#613779934166dea13b7f87c0d56f3952f9658ff9" +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ - "approx 0.5.1", - "decorum", - "nalgebra", - "num-traits", - "parry2d-f64", - "parry3d-f64", - "robust", + "csv-core", + "itoa", + "ryu", + "serde_core", ] [[package]] -name = "flate2" -version = "1.0.30" +name = "csv-core" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ - "crc32fast", - "miniz_oxide", + "memchr", ] [[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "derive_more" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ - "percent-encoding", + "derive_more-impl", ] [[package]] -name = "fortuples" -version = "0.9.1" +name = "derive_more-impl" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87630a8087e9cac4b7edfb6ee5e250ddca9112b57b6b17d8f5107375a3a8eace" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case 0.10.0", "proc-macro2", "quote", - "syn 1.0.109", + "rustc_version", + "syn", ] [[package]] -name = "git2" -version = "0.18.3" +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "bitflags 2.5.0", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", + "block-buffer", + "crypto-common", ] [[package]] -name = "glam" -version = "0.24.2" +name = "dispatch2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "serde", + "bitflags", + "objc2", ] [[package]] -name = "hashbrown" -version = "0.14.5" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "ahash", - "allocator-api2", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "heck" -version = "0.4.1" +name = "document-features" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] [[package]] -name = "heck" -version = "0.5.0" +name = "downcast-rs" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "dunce" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] -name = "iana-time-zone" -version = "0.1.60" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "enum-map" +version = "3.0.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "fb2a23ad36148a32085addb3ef1aa39805d044d4532ff258360d523a4eff38e5" dependencies = [ - "cc", + "enum-map-derive", ] [[package]] -name = "idna" -version = "0.5.0" +name = "enum-map-derive" +version = "1.0.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "44600091ce205df4f8b661e98617d49c37b2dd609e449ec82b0fb5d7b33e2eeb" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "imstr" +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enum_downcast" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e532035fcb0d794ae56f74a0a39fea24bca14c7d47c428e3ab0667ebdccd8b4a" +dependencies = [ + "enum_downcast_derive", +] + +[[package]] +name = "enum_downcast_derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0e8987f91d86aa10313adf7c767a8cfd040364432d4e9f722b049b8ff7e634a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "file-guard" version = "0.2.0" -source = "git+https://github.com/IamTheCarl/imstr#a679d58d06f06a8e87c8640922aa157a0f86693f" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ef72acf95ec3d7dbf61275be556299490a245f017cf084bd23b4f68cf9407c" dependencies = [ - "nom", + "libc", + "winapi", ] [[package]] -name = "indexmap" -version = "2.2.6" +name = "find-msvc-tools" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ - "equivalent", - "hashbrown", + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", ] [[package]] -name = "is-terminal" -version = "0.4.12" +name = "git2" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ - "hermit-abi", + "bitflags", "libc", - "windows-sys 0.52.0", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "glam" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da" + +[[package]] +name = "glam" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b" + +[[package]] +name = "glam" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16" + +[[package]] +name = "glam" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776" + +[[package]] +name = "glam" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34" + +[[package]] +name = "glam" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca" + +[[package]] +name = "glam" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f" + +[[package]] +name = "glam" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815" + +[[package]] +name = "glam" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" + +[[package]] +name = "glam" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c" + +[[package]] +name = "glam" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" + +[[package]] +name = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" + +[[package]] +name = "glam" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" + +[[package]] +name = "glam" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" + +[[package]] +name = "glam" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" + +[[package]] +name = "glam" +version = "0.30.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" + +[[package]] +name = "hashable-map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46ab77e35afc7a5a3087e72e8e93a90a226a78f62c5f4dceaeb3d7ef5decdccb" +dependencies = [ + "serde", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "imstr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3441deb04ea9c6b472f313be54d585428cd6a68cdb8dcf40cf43744bec78fe" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "interpreter" +version = "0.1.0" +dependencies = [ + "ariadne", + "boolmesh", + "common_data_types", + "enum_dispatch", + "enum_downcast", + "file-guard", + "hashable-map", + "hex", + "imstr", + "levenshtein", + "nalgebra", + "nom", + "num-traits", + "paste", + "pretty_assertions", + "rayon", + "selen", + "serde", + "sha2", + "stack", + "stl_io", + "tempfile", + "tree-sitter", + "tree-sitter-command-cad-model", + "type-sitter", + "type-sitter-gen", + "units", + "unwrap-enum", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" -version = "0.12.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] @@ -688,45 +1090,82 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ + "getrandom", "libc", ] +[[package]] +name = "join-lazy-fmt" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e90f66baf362a8a5ce2ca820290fbede0572a76fabf8408bc68c02f9ad1d03bf" + [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ + "once_cell", "wasm-bindgen", ] +[[package]] +name = "lazy-regex" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5c13b6857ade4c8ee05c3c3dc97d2ab5415d691213825b90d3211c425c1f907" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a95c68db5d41694cea563c86a4ba4dc02141c16ef64814108cb23def4d5438" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn", +] + [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "levenshtein" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libgit2-sys" -version = "0.16.2+1.7.2" +version = "0.18.3+1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" +checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" dependencies = [ "cc", "libc", @@ -737,16 +1176,20 @@ dependencies = [ ] [[package]] -name = "libm" -version = "0.2.8" +name = "libloading" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] [[package]] name = "libssh2-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" dependencies = [ "cc", "libc", @@ -758,9 +1201,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.18" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" dependencies = [ "cc", "libc", @@ -770,40 +1213,76 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.21" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] -name = "macros" -version = "0.1.0" +name = "logos" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff472f899b4ec2d99161c51f60ff7075eeb3097069a36050d8037a6325eb8154" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "192a3a2b90b0c05b27a0b2c43eecdb7c415e29243acc3f89cc8247a5b693045c" dependencies = [ + "beef", + "fnv", + "lazy_static", "proc-macro2", "quote", - "syn 2.0.66", + "regex-syntax", + "rustc_version", + "syn", +] + +[[package]] +name = "logos-derive" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "605d9697bcd5ef3a42d38efc51541aa3d6a4a25f7ab6d1ed0da5ac632a26b470" +dependencies = [ + "logos-codegen", ] [[package]] name = "matrixmultiply" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" dependencies = [ "autocfg", "rawpointer", @@ -811,32 +1290,54 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "minimad" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "df8b688969b16915f3ecadc7829d5b7779dee4977e503f767f34136803d5c06f" +dependencies = [ + "once_cell", +] [[package]] -name = "miniz_oxide" -version = "0.7.3" +name = "mio" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ - "adler", + "libc", + "log", + "wasi", + "windows-sys 0.61.2", ] [[package]] name = "nalgebra" -version = "0.32.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea4908d4f23254adda3daa60ffef0f1ac7b8c3e9a864cf3cc154b251908a2ef" -dependencies = [ - "approx 0.5.1", +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4d5b3eff5cd580f93da45e64715e8c20a3996342f1e466599cf7a267a0c2f5f" +dependencies = [ + "approx", + "glam 0.14.0", + "glam 0.15.2", + "glam 0.16.0", + "glam 0.17.3", + "glam 0.18.0", + "glam 0.19.0", + "glam 0.20.5", + "glam 0.21.3", + "glam 0.22.0", + "glam 0.23.0", + "glam 0.24.2", + "glam 0.25.0", + "glam 0.27.0", + "glam 0.28.0", + "glam 0.29.3", + "glam 0.30.10", "matrixmultiply", "nalgebra-macros", "num-complex", @@ -847,406 +1348,658 @@ dependencies = [ ] [[package]] -name = "nalgebra-macros" -version = "0.2.1" +name = "nalgebra-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "973e7178a678cfd059ccec50887658d482ce16b0aa9da3888ddeab5cd5eb4889" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-graphics", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "lock_api", + "parking_lot_core", ] [[package]] -name = "nom" -version = "7.1.3" +name = "parking_lot_core" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ - "memchr", - "minimal-lexical", + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", ] [[package]] -name = "nom_locate" -version = "4.2.0" +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" -dependencies = [ - "bytecount", - "memchr", - "nom", -] +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "num-complex" -version = "0.4.6" +name = "petgraph" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ - "num-traits", + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", ] [[package]] -name = "num-derive" -version = "0.4.2" +name = "phf" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", + "phf_macros", + "phf_shared", ] [[package]] -name = "num-integer" -version = "0.1.46" +name = "phf_generator" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "num-traits", + "phf_shared", + "rand", ] [[package]] -name = "num-rational" -version = "0.4.2" +name = "phf_macros" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "num-integer", - "num-traits", + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "num-traits" -version = "0.2.19" +name = "phf_shared" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "autocfg", - "libm", + "siphasher", ] [[package]] -name = "once_cell" -version = "1.19.0" +name = "pkg-config" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] -name = "openssl-probe" -version = "0.1.5" +name = "potential_utf" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] [[package]] -name = "openssl-sys" -version = "0.9.102" +name = "pretty_assertions" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "diff", + "yansi", ] [[package]] -name = "ordered-float" -version = "4.2.0" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "num-traits", + "proc-macro2", + "syn", ] [[package]] -name = "ouroboros" -version = "0.18.4" +name = "proc-macro2" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ - "aliasable", - "ouroboros_macro", - "static_assertions", + "unicode-ident", ] [[package]] -name = "ouroboros_macro" -version = "0.18.4" +name = "quasiquote" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" +checksum = "9d7f3e24436387bc9ee9963246739f2a851d9ca85ed73a2c5b878bf908e9b34d" dependencies = [ - "heck 0.4.1", - "itertools 0.12.1", "proc-macro2", - "proc-macro2-diagnostics", + "quasiquote-proc-macro", "quote", - "syn 2.0.66", ] [[package]] -name = "parking_lot" -version = "0.12.3" +name = "quasiquote-proc-macro" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "79bc0a2b2a185610156579070227676cf31d6282045d01273bbfc65804df6022" dependencies = [ - "lock_api", - "parking_lot_core", + "itertools 0.10.5", + "proc-macro2", + "quote", ] [[package]] -name = "parking_lot_core" -version = "0.9.10" +name = "quick-xml" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.1", - "smallvec", - "windows-targets 0.52.5", + "memchr", ] [[package]] -name = "parry2d-f64" -version = "0.15.1" +name = "quote" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416b765a3db5f3e219fcc878d649ea6dc6b93490694be846c39c7398074f90d1" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ - "approx 0.5.1", - "arrayvec", - "bitflags 1.3.2", - "downcast-rs", - "either", - "log", - "nalgebra", - "num-derive", - "num-traits", - "rustc-hash", - "simba", - "slab", - "smallvec", - "spade", + "proc-macro2", ] [[package]] -name = "parry3d-f64" -version = "0.15.1" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc07fa374273eca29066d59d3ded520bf845ed582e38c0d60109459ea4ed76e" -dependencies = [ - "approx 0.5.1", - "arrayvec", - "bitflags 1.3.2", - "downcast-rs", - "either", - "log", - "nalgebra", - "num-derive", - "num-traits", - "rustc-hash", - "simba", - "slab", - "smallvec", - "spade", -] +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "paste" -version = "1.0.15" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] [[package]] -name = "percent-encoding" -version = "2.3.1" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] -name = "pkg-config" -version = "0.3.30" +name = "rawpointer" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] -name = "proc-macro2" -version = "1.0.84" +name = "rayon" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ - "unicode-ident", + "either", + "rayon-core", ] [[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" +name = "rayon-core" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "version_check", - "yansi", + "crossbeam-deque", + "crossbeam-utils", ] [[package]] -name = "quick-xml" -version = "0.31.0" +name = "redox_syscall" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "memchr", - "serde", + "bitflags", ] [[package]] -name = "quote" -version = "1.0.36" +name = "reedline" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "67478e45862a0c29fd99658e382c07b1b80b9c1b7d946ce6bd2e4a679141554b" dependencies = [ - "proc-macro2", + "arboard", + "chrono", + "crossterm", + "fd-lock", + "itertools 0.13.0", + "nu-ansi-term", + "serde", + "strip-ansi-escapes", + "strum", + "strum_macros", + "thiserror 2.0.17", + "unicase", + "unicode-segmentation", + "unicode-width 0.2.2", ] [[package]] -name = "rawpointer" -version = "0.2.1" +name = "regex" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] [[package]] -name = "redox_syscall" -version = "0.4.1" +name = "regex-automata" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ - "bitflags 1.3.2", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] -name = "redox_syscall" -version = "0.5.1" +name = "regex-syntax" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" -dependencies = [ - "bitflags 2.5.0", -] +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] -name = "robust" -version = "1.1.0" +name = "rustc-stable-hash" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30" +checksum = "781442f29170c5c93b7185ad559492601acdc71d5bb0706f5868094f45cfcd08" [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] name = "rustix" -version = "0.38.34" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.5.0", + "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "safe_arch" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" dependencies = [ "bytemuck", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "selen" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069ccc9fbbb7f2de521452b5c96d7d898b0d2e5f49698adbcfc13914f218d3af" + [[package]] name = "semver" -version = "1.0.23" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ - "serde", + "serde_core", + "serde_derive", ] [[package]] -name = "serde" -version = "1.0.203" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ + "indexmap", "itoa", - "ryu", + "memchr", "serde", + "serde_core", + "zmij", ] [[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" +name = "sha2" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", ] [[package]] name = "simba" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95" dependencies = [ - "approx 0.5.1", + "approx", "num-complex", "num-traits", "paste", @@ -1254,190 +2007,297 @@ dependencies = [ ] [[package]] -name = "slab" -version = "0.4.9" +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stack" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c27dee130f751c0903be703c5355e9b333a17550d7d63d04cf0d3b1601edb8a3" +dependencies = [ + "coalesce", +] + +[[package]] +name = "stl_io" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "da63e75b86345156b191c021b3ce2a13b973941ecdb8c70d6f00cbbfe0076ed7" dependencies = [ - "autocfg", + "byteorder", + "float-cmp", ] [[package]] -name = "smallvec" -version = "1.13.2" +name = "streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" + +[[package]] +name = "strict" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f42444fea5b87a39db4218d9422087e66a85d0e7a0963a439b07bcdf91804006" + +[[package]] +name = "strip-ansi-escapes" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" +dependencies = [ + "vte", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] -name = "spade" -version = "2.8.0" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b20a809169ae442497e41a997fc5f14e2eea04e6ac590816a910d5d8068c8c0" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "hashbrown", - "num-traits", - "robust", - "smallvec", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "tempfile" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] [[package]] -name = "stderrlog" -version = "0.6.0" +name = "termimad" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c910772f992ab17d32d6760e167d2353f4130ed50e796752689556af07dc6b" +checksum = "889a9370996b74cf46016ce35b96c248a9ac36d69aab1d112b3e09bc33affa49" dependencies = [ - "chrono", - "is-terminal", - "log", - "termcolor", - "thread_local", + "coolor", + "crokey", + "crossbeam", + "lazy-regex", + "minimad", + "serde", + "thiserror 2.0.17", + "unicode-width 0.1.14", ] [[package]] -name = "stl" -version = "0.2.1" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "466e72b3a9258f51f0562a01f2aea3717fb71d9997f4050c65c251a623926e12" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "byteorder 0.4.2", + "thiserror-impl 1.0.69", ] [[package]] -name = "strsim" -version = "0.11.1" +name = "thiserror" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] [[package]] -name = "syn" -version = "1.0.109" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn", ] [[package]] -name = "syn" -version = "2.0.66" +name = "thiserror-impl" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn", ] [[package]] -name = "tempfile" -version = "3.10.1" +name = "tinystr" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", + "displaydoc", + "zerovec", ] [[package]] -name = "termcolor" -version = "1.1.3" +name = "tree-sitter" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "78f873475d258561b06f1c595d93308a7ed124d9977cb26b148c2084a4a3cc87" dependencies = [ - "winapi-util", + "cc", + "regex", + "regex-syntax", + "serde_json", + "streaming-iterator", + "tree-sitter-language", ] [[package]] -name = "terminal_size" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +name = "tree-sitter-command-cad-model" +version = "0.0.1" dependencies = [ - "rustix", - "windows-sys 0.48.0", + "cc", + "tree-sitter", ] [[package]] -name = "thiserror" -version = "1.0.61" +name = "tree-sitter-language" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" -dependencies = [ - "thiserror-impl", -] +checksum = "4ae62f7eae5eb549c71b76658648b72cc6111f2d87d24a1e31fa907f4943e3ce" [[package]] -name = "thiserror-impl" -version = "1.0.61" +name = "tree_magic_mini" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", + "memchr", + "nom", + "petgraph", ] [[package]] -name = "thread_local" -version = "1.1.8" +name = "type-sitter" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "41d7b6c3bc4c60ecee2147ecb3cd7820fe9ee1df4251e1b2b8b5a214fe0084ba" dependencies = [ - "cfg-if", - "once_cell", + "type-sitter-lib", + "type-sitter-proc", ] [[package]] -name = "threemf" -version = "0.5.0" +name = "type-sitter-gen" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "542e6ea27d8dc779b54b6325da4a52b97367f7a63a41f9c0ce67a3096e550123" +checksum = "95ad6a9e8aa527b2c037cd5016d6291e197c98eb6160ff09b25f2398374a3003" dependencies = [ - "quick-xml", + "cc", + "check_keyword", + "convert_case 0.8.0", + "dunce", + "enum-map", + "join-lazy-fmt", + "libc", + "libloading", + "logos", + "prettyplease", + "proc-macro2", + "quote", + "rustc-stable-hash", "serde", - "thiserror", - "zip", + "serde_json", + "slice-group-by", + "syn", + "tree-sitter", + "tree-sitter-language", + "walkdir", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "type-sitter-lib" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "092e2a9b5f4eb14bdd2246eb1557b2b99e67fe43be3e0411535d9483e73236b7" dependencies = [ - "tinyvec_macros", + "streaming-iterator", + "tree-sitter", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "type-map" -version = "0.5.0" +name = "type-sitter-proc" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +checksum = "4240aa23c38a18dc53f8422374fac3d7514654ec28258ef173b53ba9bee37a6e" dependencies = [ - "rustc-hash", + "syn", + "type-sitter-gen", ] [[package]] name = "typenum" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "uneval" @@ -1446,62 +2306,93 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63cc5d2fd8648d7e2be86098f60c9ece7045cc710b3c1e226910e2f37d11dc73" dependencies = [ "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] -name = "unicode-bidi" -version = "0.3.15" +name = "unicase" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] -name = "unicode-normalization" -version = "0.1.23" +name = "unicode-width" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "units" +version = "0.1.0" dependencies = [ - "tinyvec", + "common_data_types", + "csv", + "serde", + "uneval", ] [[package]] -name = "unsafe-libyaml" -version = "0.2.11" +name = "unwrap-enum" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +checksum = "e5639235db46432e23bff2caf3a02096d9a801c99560198f3edba4edf8d015a5" +dependencies = [ + "unwrap-enum-proc-macro", +] [[package]] -name = "uom" -version = "0.35.0" +name = "unwrap-enum-proc-macro" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8362194c7a9845a7a7f3562173d6e1da3f24f7132018cb78fe77a5b4474187b2" +checksum = "5771812808a3442129fa6c692ecb4ae77e690b530598854af2be0fda01657a94" dependencies = [ - "num-traits", - "typenum", + "proc-macro2", + "quasiquote", + "quote", + "syn", ] [[package]] name = "url" -version = "2.5.0" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "vcpkg" @@ -1511,46 +2402,62 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "wasite" -version = "0.1.0" +name = "vte" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" +dependencies = [ + "memchr", +] [[package]] -name = "wasm-bindgen" -version = "0.2.92" +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ - "cfg-if", - "wasm-bindgen-macro", + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" +name = "wasm-bindgen" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ - "bumpalo", - "log", + "cfg-if", "once_cell", - "proc-macro2", - "quote", - "syn 2.0.66", + "rustversion", + "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1558,216 +2465,324 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.66", - "wasm-bindgen-backend", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wayland-backend" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "smallvec", + "wayland-sys", +] [[package]] -name = "wavefront_rs" -version = "2.0.0-beta.1" +name = "wayland-client" +version = "0.31.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f237e2271c3f9ccc633ee16918789514fd5a823bf62ddce21f8a730a1d9930" +checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec" +dependencies = [ + "bitflags", + "rustix", + "wayland-backend", + "wayland-scanner", +] [[package]] -name = "web-sys" -version = "0.3.69" +name = "wayland-protocols" +version = "0.32.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" dependencies = [ - "js-sys", - "wasm-bindgen", + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", ] [[package]] -name = "whoami" -version = "1.5.1" +name = "wayland-sys" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd" dependencies = [ - "redox_syscall 0.4.1", - "wasite", - "web-sys", + "pkg-config", ] [[package]] name = "wide" -version = "0.7.21" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8dc749a1b03f3c255a3064a4f5c0ee5ed09b7c6bc6d4525d31f779cd74d7fc" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" dependencies = [ "bytemuck", "safe_arch", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" -version = "0.52.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-targets 0.52.5", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-implement" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "windows-targets 0.48.5", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-interface" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ - "windows-targets 0.52.5", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-link", ] [[package]] -name = "windows-targets" -version = "0.52.5" +name = "windows-strings" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows-link", ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +name = "windows-targets" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +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", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" +name = "windows_aarch64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] -name = "windows_i686_gnu" -version = "0.48.5" +name = "windows_aarch64_msvc" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] -name = "windows_i686_msvc" -version = "0.52.5" +name = "windows_x86_64_gnu" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +name = "windows_x86_64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" +name = "windows_x86_64_msvc" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.5" +name = "wl-clipboard-rs" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix", + "thiserror 2.0.17", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +name = "writeable" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "yansi" @@ -1776,33 +2791,84 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] -name = "zerocopy" -version = "0.7.34" +name = "yoke" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "zerocopy-derive", + "stable_deref_trait", + "yoke-derive", + "zerofrom", ] [[package]] -name = "zerocopy-derive" -version = "0.7.34" +name = "yoke-derive" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", + "synstructure", ] [[package]] -name = "zip" -version = "0.6.6" +name = "zerofrom" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ - "byteorder 1.5.0", - "crc32fast", - "crossbeam-utils", - "flate2", + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] + +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/Cargo.toml b/Cargo.toml index d8769de..c3e2959 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,48 +1,26 @@ -workspace = { members = ["crates/common_data_types", "crates/macros"] } -[package] -name = "command_cad" -version = "0.6.0" -edition = "2021" +[workspace] +resolver = "3" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +members = [ + "interpreter", + "common_data_types", + "tree-sitter-command-cad-model", + "units", + "cli" +] -[dependencies] -anyhow = "1.0" -casey = "0.4" -clap = { version = "4.4", features = [ "derive", "wrap_help" ] } -compact_str = "0.7" -const-str = "0.5" -enum_dispatch = "0.3" -enum_downcast = { version = "0.1", features = [ "derive" ] } -fj-core = { git = "https://github.com/hannobraun/fornjot.git" } -fj-math = { git = "https://github.com/hannobraun/fornjot.git" } -fj-export = { git = "https://github.com/hannobraun/fornjot.git" } -fortuples = "0.9" -git2 = "0.18" -imstr = { git = "https://github.com/IamTheCarl/imstr", version = "0.2", features = [ "std", "nom" ] } -lazy_static = "1.4" -log = "0.4" -nom = "7.1" -nom_locate = "4.2" -paste = "1.0" -semver = { version = "1.0", features = [ "serde" ] } -serde = { version = "1.0", features = [ "derive" ] } -serde_json = "1.0.113" -serde_yaml = "0.9" -uom = "0.35" -whoami = "1.4" -stderrlog = "0.6" -tempfile = "3.10" -common_data_types = { path = "crates/common_data_types" } -nalgebra = "0.32" -arrayvec = "0.7.4" -ouroboros = "0.18.3" -bezier-rs = "=0.4.0" -macros = { path = "crates/macros" } -glam = "0.24" +# We skip tree-sitter because it doesn't have actual tests and one of the generated documentation +# does not pass +default-members = [ + "interpreter", + "common_data_types", + "units", + "cli" +] -[build-dependencies] -csv = "1.3.0" -serde = { version = "1.0", features = [ "derive" ] } -uneval = "0.2.4" -common_data_types = { path = "crates/common_data_types" } +[workspace.dependencies] +ariadne = "0.6.0" +type-sitter = "0.8" +tree-sitter = "0.25" +tempfile = "3" +git2 = "0.20" diff --git a/README.md b/README.md index 2a57280..72c59e5 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,41 @@ # Command CAD -In my frustration with the state of CAD software as a whole, I lost my mind and wrote my own CAD program. Out of all of the ones I've used, the only ones that are really palatable are OpenSCAD and CAD Query. Command CAD takes most of its inspiration from these two. Command CAD has some critical differences though, like a stronger and convenient type safety for its scripting language and a more modular design. +Command CAD is the product of my frustrations with the current state of CAD software and my curiosity of what would happen if OpenSCAD was given heavy type safety, functional programming paradigms, and fully declarative. _Please note that Command CAD is in a highly experimental state, do not expect stability, even in the short term_ -Observe the following example: -``` -main_body = 15; -``` -This is valid syntax for both OpenSCAD and CAD Query. -The following critical information is missing: - * What is the dimension? Is this a length? An area? It could even be Power or Time. - * Even if we assume the dimension is length, what is the unit? Meters? Millimeters? +Command CAD is still highly experimental and therefore not well documented, but at least you can find examples in [the examples directory](./examples/README.md). -In the past I've worked around this issue with naming conventions: -``` -main_body_length_cm = 15; +The following is an fictional but practical example of a model: ``` -We now know the dimension and the unit, but there are problems: - * STL files are unaware of the units you use, and will just copy the numbers you input - * If you were to feed this into a slicer and it assumed that 15 is millimeters (this is the default behavior of most slicers), the body's length would be an entire magnitude too short. You would have to rewrite the script to produce millimeters for all final products. - * If you then use a different slicer that doesn't assume millimeters as the unit for STL files, you would have to again change the script to support that slicer. - * This naming convention is tedious to use, and many developers simply won't use it +let + nominal_angle_to_sun = 60deg; -Command CAD's solution to this problem is to make dimension and unit a part of the language: -``` -let main_body: Length = 15cm; -let nominal_angle_to_sun: Angle = 60deg; -let solar_panel_size: Area = main_body * 1m; // A length * by a length gives an area. -let nominal_power_output: Power = solar_panel_size * nominal_angle_to_sun.sin() * (30w/1m^2); // 30watts per square meter multiplied by square meters results in watts. -``` -Not only is the dimension of measurements tracked and verified for you (preventing common mathematical errors), but the units are automatically converted as well. -If I were to do all my calculations using meters and then specify that my STL file should use millimeters, Command CAD will convert all of the final results to millimeters when exporting the STL file. + # 30watts per square meter multiplied by square meters results in watts. + nominal_power_output = (main_body: std.scalar.Length) -> std.scalar.Power: + let + # A length * by a length gives an area. + solar_panel_size = main_body * 1m; + in + solar_panel_size * nominal_angle_to_sun::sin() * (30W/1 'm^2'); -Dimensional analysis also applies to linear algebra types. + # We need 3cm of cooling surface for each watt this produces + cooling_fin_size = nominal_power_output(main_body = 15cm) / 1W * 3cm; +in + std.mesh3d.cylinder( + diameter = cooling_fin_size, + height = 1cm, + sectors = 360u, + stacks = 1u + )::to_stl(name = "cooling_plate") ``` -vec3(1m, 2m, 3m) * 4 == vec3(4m, 8m, 12m) -vec3(1m, 2m, 3m) * 4m == vec3(4m^2, 8m^2, 12m^2) -``` - -## Design Goals - -* Scripting language for user to define models with - * Deterministic evaluation - * Convenient type safety - * Measurement units are automatically converted to the correct output when exporting - * Code Reuse - * Code is organized into projects for easy and robust inclusion into other projects as libraries - * Sketches, Solids, and Tasks can be imported from other projects - * A package manager automatically resolves project dependencies - * Self-Documentation - * Model designs usually have extensive mathematics behind them - * Typically this documentation is kept in separate files or even in a physical notebook. If these files or physical notebook are lost, so is the reasoning behind the model - * The model not only contains the math and reasoning, but is defined by it. Changing the math will automatically update the model, removing opportunities for human error - * If you use git, you will have a commentated history of how you've changed your thinking over time -* Modular - * Code Reuse isn't just for importing parts into your assembly. Other projects can add functionality, such as gcode generation techniques. - -## Current features - * CLI interfaces - * Functions to perform calculation and generate models can be called from the command line - * Arguments provided to functions are serialized from Json or Yaml (detection of format is automatic) - * Automatic unit conversion is applied to provided arguments - * The keyword `default` can be used to invoke a function's default arguments - * Values returned are serialized and printed to stdout or saved to a file - * Scripting Language - * Functions - * Default arguments can be specified. Use the keyword `default` when calling a function to indicate you wish to use the default value - * Constraints can be applied to arguments - * Structs - * Type safe - * Constraints can be applied to struct members - * While there is no polymorphic behavior, there is syntax to copy the values of one struct into another - * Basic types - * Strings - * A text format, UTF8 encoded. - * Can be formatted using similar syntax to Python's format method. - * Lists - * Lists of objects - * Not all values in the list must be the same type - * Measurements - * Guaranteed to never be NaN - * Dimensional analysis enforced through type safety - * Vectors - * Guaranteed to never be NaN - * Dimensional analysis enforced through type safety - * Automatic unit conversions - * Unit conversions provided by [a moderatly large CSV file](src/script/execution/types/measurement/units.csv) - * Boolean - * CAD Kernel - * Built off the impressive [Fornjot](https://fornjot.app/) kernel, which is just as experimental as Command CAD is - -## Alternatives +## Current Features -Other code based CAD programs exist, and with how experimental Command CAD currently is, you should probably prefer them. +The following features are currently available: +* Dimensional analysis + * Dimensional type safety enforced at method calls +* Mesh based modeling + * Note that there are plans to experiment with imperative modeling techniques -### CAD Query - * Technical overhead - * To install Cad Query you must first install Python and Pip, and then you can install CAD Query. A typical Mechanical Engineer on Windows is unwilling to go through this. - * Breaking Changes - * There is still vast numbers of outdated tutorials and advice on the internet for Python. There aren't just the breaking changes between Python 2 and 3, but the breaking changes between 3.5, 3.6, 3.7, etc. Coming back to a project after a few months can be very frustrating to beginner who does not know how to use Python environments. - * Does not provide measurement type safety -### OpenSCAD - * Limited language features - * Does not provide structs - * Lack of package management - * Does not provide measurement type safety diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 0000000..a3a5d02 --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cli" +version = "0.1.0" +edition = "2024" + +[dependencies] +clap = { version = "4.5.54", features = ["derive"] } +reedline = { version = "0.45.0", features = ["system_clipboard", "bashisms"] } +termimad = "0.34.1" +interpreter = { path = "../interpreter/" } +anyhow = "1.0.100" +ariadne = { workspace = true } +type-sitter = { workspace = true } +tree-sitter = { workspace = true } +nu-ansi-term = "0.50.3" +tempfile = { workspace = true } +git2 = { workspace = true } diff --git a/cli/src/arguments.rs b/cli/src/arguments.rs new file mode 100644 index 0000000..0527e14 --- /dev/null +++ b/cli/src/arguments.rs @@ -0,0 +1,18 @@ +use clap::{Parser, Subcommand}; +use std::path::PathBuf; + +#[derive(Parser)] +#[command(version, about, long_about = None)] +pub struct Arguments { + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + /// Enter read-eval-print loop + Repl, + + /// Evaluate a single file and whatever local dependencies it may reference + File { file: PathBuf }, +} diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 0000000..551df68 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,306 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + sync::{Arc, Mutex}, +}; + +mod arguments; +use anyhow::{Context, Result, anyhow, bail}; +use arguments::Arguments; +use ariadne::{Cache, Label, Report, ReportKind, Source}; +use clap::Parser as _; +use git2::Repository; +use reedline::{DefaultHinter, DefaultPrompt, Reedline, Signal}; +use tempfile::TempDir; +use type_sitter::Node as _; + +use crate::arguments::Commands; + +use interpreter::{ + ExecutionContext, ExecutionFileCache, ImString, LogMessage, Parser, RuntimeLog, + SourceReference, StackScope, StackTrace, Store, build_prelude, + compile::{compile, iter_raw_nodes}, + execute_expression, + execution::values::BuiltinCallableDatabase, + new_parser, run_file, + values::{Object, Style, Value}, +}; + +fn main() { + let arguments = Arguments::parse(); + + let result = match arguments.command { + Commands::Repl => repl(), + Commands::File { file } => process_file(file), + }; + + if let Err(error) = result { + println!("Fatal error: {error:?}"); + } +} + +#[derive(Debug)] +struct StderrLog; + +impl RuntimeLog for StderrLog { + fn push_message(&self, message: LogMessage) { + let level_char = match message.level { + interpreter::LogLevel::Info => 'I', + interpreter::LogLevel::Warning => 'W', + }; + + eprintln!("{}: {}: {}", level_char, message.origin, message); + } + + fn collect_syntax_errors<'t>( + &self, + input: &str, + tree: &'t interpreter::compile::RootTree, + file: &'t Arc, + span: SourceReference, + ) { + let Some(report) = build_syntax_errors(tree, file, span) else { + return; + }; + if let Err(error) = report.eprint(ReplFileCache(Source::from(input))) { + eprintln!("Failed to print syntax error message: {error}"); + } + } +} + +fn process_file(file: PathBuf) -> Result<()> { + if !file.exists() { + bail!("File does not exist"); + } + + if file.is_dir() { + bail!("File is a directory"); + } + + let database = BuiltinCallableDatabase::new(); + let prelude = build_prelude(&database).context("Failed to build prelude")?; + + let parent = file + .parent() + .context("Could not get parent directory of file")?; + + let store_directory = match Repository::discover(parent) { + Ok(repository) => { + let git_directory = repository.path(); + let project_directory = git_directory + .parent() + .context("Failed to get parent directory of .git")?; + project_directory.join(".ccad/store") + } + Err(error) => { + eprintln!( + "Failed to discover project directory (is this project in a git repository?): {error}" + ); + eprintln!("Current directory will be used for the store."); + PathBuf::from("./.ccad/store") + } + }; + std::fs::create_dir_all(&store_directory).context("Failed to create store directory")?; + + let store = Store::new(store_directory); + let log = StderrLog; + let files = Mutex::new(HashMap::new()); + + let context = ExecutionContext { + log: &log as &dyn RuntimeLog, + stack_trace: &StackTrace::bootstrap(), + stack: &StackScope::top(&prelude), + database: &database, + store: &store, + file_cache: &files, + working_directory: Path::new("."), + import_limit: 100, + }; + + let result = run_file(&context, file); + match result { + Ok(result) => { + let mut output = String::new(); + result + .format(&context, &mut output, Style::Default, None) + .context("Failed to write output to display")?; + + println!("{output}"); + } + Err(error) => { + let file_cache = context.file_cache.lock().expect("File cache was poisoned"); + + let report = error.report(); + report + .eprint(ExecutionFileCache(&file_cache)) + .context("Failed to format error message")?; + } + } + + Ok(()) +} + +struct ReplFileCache<'i>(Source<&'i str>); + +impl<'i> Cache> for ReplFileCache<'i> { + type Storage = &'i str; + + fn fetch( + &mut self, + _id: &Arc, + ) -> Result<&Source, impl std::fmt::Debug> { + self.0.fetch(&()) + } + + fn display<'a>(&self, _id: &'a Arc) -> Option { + self.0.display(&()) + } +} + +fn repl() -> Result<()> { + let mut line_editor = Reedline::create().with_hinter(Box::new( + DefaultHinter::default().with_style( + nu_ansi_term::Style::new() + .italic() + .fg(nu_ansi_term::Color::DarkGray), + ), + )); + let prompt = DefaultPrompt::default(); + println!("Welcome to REPL mode. Press Ctrl-C or Ctrl-D to exit"); + + let repl_file = Arc::new(PathBuf::from("repl.ccm")); + let mut parser = new_parser(); + + let database = BuiltinCallableDatabase::new(); + let prelude = build_prelude(&database).context("Failed to build prelude")?; + + let store_directory = TempDir::new().unwrap(); + let store = Store::new(store_directory.path()); + + println!("Store is located at {:?}", store_directory.path()); + println!("Store will be deleted on exit."); + + loop { + let sig = line_editor.read_line(&prompt); + match sig { + Ok(Signal::Success(input)) => { + if let Err(error) = run_line( + &mut parser, + &prelude, + &database, + &store, + &repl_file, + input.as_str(), + ) { + eprintln!("Failed to run line: {error}"); + } + } + Ok(Signal::CtrlD) | Ok(Signal::CtrlC) => { + println!("\nAborted!"); + break Ok(()); + } + x => { + println!("Event: {:?}", x); + } + } + } +} + +fn build_syntax_errors<'t>( + tree: &'t interpreter::compile::RootTree, + repl_file: &'t Arc, + span: SourceReference, +) -> Option> { + let mut report_builder = Report::build(ReportKind::Error, span); + report_builder.set_message("Syntax issues found while parsing"); + + let mut has_syntax_issues = false; + + for node in iter_raw_nodes(tree) { + if node.is_missing() { + let kind = node.raw().kind(); + report_builder.add_label( + Label::new(SourceReference { + file: repl_file.clone(), + range: node.range(), + }) + .with_message(format!("Missing expected node `{kind}`")), + ); + + has_syntax_issues = true; + } + + if node.is_error() { + report_builder.add_label( + Label::new(SourceReference { + file: repl_file.clone(), + range: node.range(), + }) + .with_message("Could not parse node"), + ); + + has_syntax_issues = true; + } + } + + if has_syntax_issues { + Some(report_builder.finish()) + } else { + Option::None + } +} + +fn run_line( + parser: &mut Parser, + prelude: &HashMap, + database: &BuiltinCallableDatabase, + store: &Store, + repl_file: &Arc, + input: &str, +) -> Result<()> { + let tree = parser + .parse(input, None) + .map_err(|error| anyhow!("Failed to parse input: {error:?}"))?; + let root = + compile(repl_file, input, &tree).map_err(|error| anyhow!("Failed to compile: {error}"))?; + + let log = StderrLog; + let files = Mutex::new(HashMap::new()); + + let context = ExecutionContext { + log: &log as &dyn RuntimeLog, + stack_trace: &StackTrace::top(root.reference.clone()), + stack: &StackScope::top(prelude), + database, + store, + file_cache: &files, + working_directory: Path::new("."), + import_limit: 100, + }; + + if let Some(report) = build_syntax_errors(&tree, repl_file, root.reference.clone()) { + report + .eprint(ReplFileCache(Source::from(input))) + .context("Failed to format syntax error message")?; + } + + let result = execute_expression(&context, &root); + match result { + Ok(result) => { + let mut output = String::new(); + result + .format(&context, &mut output, Style::Default, None) + .context("Failed to write output to display")?; + + println!("{output}"); + } + Err(error) => { + let report = error.report(); + report + .eprint(ReplFileCache(Source::from(input))) + .context("Failed to format error message")?; + } + } + + Ok(()) +} diff --git a/crates/common_data_types/Cargo.toml b/common_data_types/Cargo.toml similarity index 100% rename from crates/common_data_types/Cargo.toml rename to common_data_types/Cargo.toml diff --git a/crates/common_data_types/src/lib.rs b/common_data_types/src/lib.rs similarity index 84% rename from crates/common_data_types/src/lib.rs rename to common_data_types/src/lib.rs index 7945510..57079a9 100644 --- a/crates/common_data_types/src/lib.rs +++ b/common_data_types/src/lib.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::HashMap}; +use std::collections::HashMap; use ordered_float::NotNan; use paste::paste; @@ -9,10 +9,10 @@ pub type Float = NotNan; pub use ordered_float::{FloatIsNan, ParseNotNanError}; pub use std::f64::consts; -pub type ConversionFactorDatabase = HashMap, ConversionFactor>; -pub type DimensionNameDatabase = HashMap>; +pub type ConversionFactorDatabase = HashMap; +pub type DimensionNameDatabase = HashMap; pub type UnitList = Vec<(String, Vec)>; -pub type BaseUnits = HashMap>; +pub type BaseUnits = HashMap; #[derive(Debug, Serialize)] pub struct UnitDescription { @@ -62,6 +62,36 @@ impl RatioTypeHint { bit_getter_setter!(Self::PIXEL_MASK, pixel); } +impl std::fmt::Display for RatioTypeHint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_angle() { + write!(f, "-ANGLE")?; + } + + if self.is_constituent_concentration() { + write!(f, "-CONSTITUENT_CONCENTRATION")?; + } + + if self.is_information() { + write!(f, "-INFORMATION")?; + } + + if self.is_solid_angle() { + write!(f, "-SOLID_ANGLE")?; + } + + if self.is_temperature() { + write!(f, "-TEMPRATURE")?; + } + + if self.is_pixel() { + write!(f, "-PIXEL")?; + } + + Ok(()) + } +} + impl std::ops::BitOr for RatioTypeHint { type Output = Self; @@ -238,6 +268,19 @@ impl Dimension { } } + pub const fn area() -> Self { + Self { + length: 2, + mass: 0, + time: 0, + electric_current: 0, + thermodynamic_temprature: 0, + amount_of_substance: 0, + luminous_intensity: 0, + ratio_type_hint: RatioTypeHint(0), + } + } + pub fn is_zero_dimension(&self) -> bool { self.length == 0 && self.mass == 0 @@ -256,6 +299,16 @@ pub struct ConversionFactor { pub dimension: Dimension, } +impl ConversionFactor { + pub fn convert_to_base_unit(&self, input: Float) -> Float { + input * self.coefficient + self.constant + } + + pub fn convert_from_base_unit(&self, input: Float) -> Float { + (input - self.constant) / self.coefficient + } +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml deleted file mode 100644 index 6520800..0000000 --- a/crates/macros/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "macros" -version = "0.1.0" -edition = "2021" - - -[lib] -proc-macro = true - -[dependencies] -quote = "1.0" -syn = { version = "2.0", features = ["derive"] } -proc-macro2 = "1.0" diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs deleted file mode 100644 index 7d0f4a9..0000000 --- a/crates/macros/src/lib.rs +++ /dev/null @@ -1,112 +0,0 @@ -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, DeriveInput, Fields, Meta}; - -#[proc_macro_derive(Struct, attributes(default))] -pub fn derive_struct(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - let struct_name = input.ident; - - let contains_span = input.generics.type_params().any(|param| param.ident == "S"); - - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let struct_name_string = struct_name.to_string(); - let struct_definition_name = struct_name.to_string(); - let struct_definition_name2 = struct_definition_name.clone(); - - let function_generics = if !contains_span { - quote! { } - } else { - quote! {} - }; - - let fields = match input.data { - syn::Data::Struct(data) => match data.fields { - Fields::Named(fields) => fields.named, - _ => panic!("Struct must have named fields."), - }, - _ => { - panic!("Only structs can be made available to scripts."); - } - }; - let define_fields = fields.clone().into_iter().map(|field| { - let name = field.ident.unwrap().to_string(); - let ty = field.ty; - - let mut default = quote! { None }; - - for attribute in field.attrs.iter() { - if let Meta::NameValue(name_value) = &attribute.meta { - if name_value.path.is_ident("default") { - let value = &name_value.value; - default = quote! { Some(crate::script::parsing::Litteral::parse(S::from_str(#value)).unwrap().1) }; - } - } - } - - quote! { - MemberVariable { - name: S::from_str(#name), - ty: MemberVariableType { - ty: <#ty as crate::script::execution::types::TypedObject>::get_type(), - constraints: None, - default_value: #default, - } - } - } - }); - - let extract_fields = fields.clone().into_iter().map(|field| { - let name = field.ident.unwrap(); - let name_string = name.to_string(); - let ty = field.ty; - - quote! { - let #name = members.remove(#name_string).unwrap(); - let #name = #name.downcast::<#ty>(span)?; - } - }); - - let import_fields = fields.clone().into_iter().map(|field| { - let name = field.ident.unwrap(); - quote! { #name } - }); - - quote! { - impl #impl_generics #struct_name #ty_generics #where_clause { - pub fn unpack_struct #function_generics (span: &S, structure: crate::script::execution::types::Structure) -> std::result::Result> { - // Check that it's the correct type. - if structure.name() == #struct_name_string { - let mut members = Rc::unwrap_or_clone(structure.members); - - // Extract fields. - #(#extract_fields)* - - // Import fields into the struct. - Ok(#struct_name { - #(#import_fields),* - }) - } else { - Err(crate::script::execution::Failure::ExpectedGot(span.clone(), #struct_name_string.into(), structure.name().to_string().into())) - } - } - } - impl #impl_generics #struct_name #ty_generics #where_clause { - fn define_struct #function_generics (context: &mut crate::script::execution::ExecutionContext<'_, S>) { - context.stack.new_variable_str( - #struct_definition_name, - StructDefinition { - definition: Rc::new(parsing::StructDefinition { - name: S::from_str(#struct_definition_name2), - members: vec![ - #(#define_fields),* - ], - }), - } - .into(), - ); - } - } - }.into() -} diff --git a/default.nix b/default.nix deleted file mode 100644 index 86d234d..0000000 --- a/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -# `nixpkgs` by default if no argument is passed to it. -{ pkgs ? import {} }: - -# This avoids typing `pkgs.` before each package name. -with pkgs; - -# Defines a shell. -mkShell { - # Sets the build inputs, i.e. what will be available in our - # local environment. - buildInputs = [ rustup pkg-config openssl_3_1 ]; -} - diff --git a/examples/README.md b/examples/README.md index e68ec32..9ee7b24 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,115 +1,10 @@ # Examples -These are examples of what can currently be done with Command CAD. -Please note that most files contain multiple examples you can run. - -## Basic Math - -Examples of mathmatical operations, and some control flow demos. Really demonstraights the CLI serialization and deserialization features. - -### Addition - -Performs addition of two numbers. - -``` -cargo run -- run examples/basic_math.ccm addition -- 2 3 -5.0 -``` - -### Recursive Addition - -A recursive function that will count down from the specified number of cycles, and add the remaning number of cycles to the total sum. -For example, if I were to input 3, the output would be the sum of 3 + 2 + 1, resulting in 6. - -``` -cargo run -- run examples/basic_math.ccm recursive_addition -- 3 -6.0 -``` - -### Convert to Meters - -Converts lengths to meters. - -``` -cargo run -- run examples/basic_math.ccm convert_to_meters -- 3yd -2.7432 -``` - -### Default value - -Functions can have default values provided, but you can also override the default value from the CLI. - -``` -cargo run -- run examples/basic_math.ccm default_value -- default -42.0 -``` - -``` -cargo run -- run examples/basic_math.ccm default_value -- 3yd -2.7432 -``` - -## Bearing - -The file `bearing.ccm` is actually pseudo code that was used to drive the design of Command CAD. Eventually it will be a working example but for the moment it is not. - -## Fornjot Demos - -Copies the demos from the Fornjot repository (Fornjot being the CAD kernel this project is built on) - -### Cuboid - -Create a box structure by providing its measurements. -``` -cargo run -- form examples/fornjot_demos.ccm cuboid --output ~/cuboid.stl -- 1cm 1cm 1cm -``` - -### Holes - -Craete a box and show off some holes! -``` -cargo run -- form examples/fornjot_demos.ccm holes --output ~/output.stl -- 5mm -``` - -### Spacer - -Create a simple ring spacer. -``` -cargo run -- form examples/fornjot_demos.ccm spacer --output ~/output.stl -- 10mm 5mm 5mm -``` - -### Star - -Create a star. -``` -cargo run -- form examples/fornjot_demos.ccm star --output ~/output.stl -- 5 10mm 5mm 5mm -``` - -## Sketches - -Demonstrates the creation of 2D objects. -Please note that export of 2D objects is not yet supported, so the output of these is not very exciting. - -### Circle - -Creates a circle with the provided diameter. - -``` -cargo run -- sketch examples/sketches.ccm circle -- 1cm -``` - -### Square - -Creates a square with the provided side length. - -``` -cargo run -- sketch examples/sketches.ccm square -- 1cm -``` - -### Square with Circle - -Creates a sketch with both a square and a circle with the provided dimensions. - -``` -cargo run -- sketch examples/sketches.ccm square_with_circle -- 2cm 1cm -``` +Examples are sorted into the following categories: +* Language + * Demonstrate features of the Command CAD language +* Modeling + * Mesh 3D + * Demonstrate 3D meshing features +* Other + * Demonstrate features that do not fit into the other categories. diff --git a/examples/advance_sketches.ccm b/examples/advance_sketches.ccm deleted file mode 100644 index 761b0d1..0000000 --- a/examples/advance_sketches.ccm +++ /dev/null @@ -1,146 +0,0 @@ - -sketch square(width: Length, height: Length) { - let half_width = width / 2; - let half_height = height / 2; - - let corner_1 = vec2(-half_width, -half_height); - let corner_2 = vec2(half_width, -half_height); - let corner_3 = vec2(half_width, half_height); - let corner_4 = vec2(-half_width, half_height); - - new_sketch(sketch_plane(), [ - new_region(Segments { - start = corner_1, - surface = sketch_plane(), - distance_between_verticies = default, - segments = [ - Line { - end = corner_2 - }, - Line { - end = corner_3 - }, - Line { - end = corner_4 - }, - Line { - end = corner_1 - }, - ] - }), - ]) -} - -solid square_sweep(depth: Length = 2mm, width: Length = 2cm, height: Length = 3cm) { - let square_sketch = square(width, height); - - square_sketch.sweep(global_xy_plane(), vec3(0m, 0m, depth)) -} - -sketch rounded_square(width: Length, height: Length, corner_radius: Length) { - let half_width = width / 2; - let half_height = height / 2; - - print("Half Width: {} Half Height: {}".format(half_width, half_height)); - - let corner_1 = vec2(-half_width, -half_height); - let corner_2 = vec2(half_width, -half_height); - let corner_3 = vec2(half_width, half_height); - let corner_4 = vec2(-half_width, half_height); - - print("Corner 1: ({})x({})".format(corner_1.x, corner_1.y)); - print("Corner 2: ({})x({})".format(corner_2.x, corner_2.y)); - print("Corner 3: ({})x({})".format(corner_3.x, corner_3.y)); - print("Corner 4: ({})x({})".format(corner_4.x, corner_4.y)); - - new_sketch(sketch_plane(), [ - new_region(Segments { - start = corner_1 + vec2(0m, corner_radius), - surface = sketch_plane(), - distance_between_verticies = default, - segments = [ - CenterArc { - center = corner_1 + vec2(corner_radius, corner_radius), - angle = 90deg, - clockwise = false, - }, - Line { - end = corner_2 + vec2(-corner_radius, 0m), - }, - CenterArc { - center = corner_2 + vec2(-corner_radius, corner_radius), - angle = 90deg, - clockwise = false, - }, - Line { - end = corner_3 + vec2(0m, -corner_radius), - }, - CenterArc { - center = corner_3 + vec2(-corner_radius, -corner_radius), - angle = 90deg, - clockwise = false, - }, - Line { - end = corner_4 + vec2(corner_radius, 0m), - }, - CenterArc { - center = corner_4 + vec2(corner_radius, -corner_radius), - angle = 90deg, - clockwise = false, - }, - Line { - end = corner_1 + vec2(0m, corner_radius), - }, - ] - }), - ]) -} - -solid rounded_square_sweep(depth: Length = 2mm, width: Length = 2cm, height: Length = 3cm, corner_radius: Length = 5mm) { - let square_sketch = rounded_square(width, height, corner_radius); - - square_sketch.sweep(global_xy_plane(), vec3(0m, 0m, depth)) -} - -sketch beziers(width: Length, height: Length) { - let half_width = width / 2; - let half_height = height / 2; - - print("Half Width: {} Half Height: {}".format(half_width, half_height)); - - let corner_1 = vec2(-half_width, -half_height); - let corner_2 = vec2(half_width, -half_height); - let corner_3 = vec2(half_width, half_height); - let corner_4 = vec2(-half_width, half_height); - - new_sketch(sketch_plane(), [ - new_region(Segments { - start = corner_1, - surface = sketch_plane(), - distance_between_verticies = default, - segments = [ - Line { - end = corner_2, - }, - QuadraticBezier { - end = corner_3, - handle = vec2(half_width * 2, 0m), - }, - Line { - end = corner_4, - }, - CubicBezier { - end = corner_1, - start_handle = corner_4 + vec2(half_width, 0m), - end_handle = corner_1 + vec2(-half_width, 0m), - }, - ] - }), - ]) -} - -solid beziers_sweep(depth: Length = 2mm, width: Length = 2cm, height: Length = 3cm) { - let beziers_sketch = beziers(width, height); - - beziers_sketch.sweep(global_xy_plane(), vec3(0m, 0m, depth)) -} \ No newline at end of file diff --git a/examples/basic_math.ccm b/examples/basic_math.ccm deleted file mode 100644 index 26f01e1..0000000 --- a/examples/basic_math.ccm +++ /dev/null @@ -1,22 +0,0 @@ - -task addition(a: Number, b: Number) -> Number { - a + b -} - -task recursive_addition(number_of_cycles: Number) -> Number { - if number_of_cycles > 0 { - number_of_cycles + recursive_addition(number_of_cycles - 1) - } else { - 0 - } -} - -task convert_to_meters(input: Length) -> Number { - // We can't return measurements to the CLI. - // External programs won't know how to interpret those, so we have to convert it to a number. - input.to_number("m") -} - -task default_value(input: Length = 42m) -> Number { - input.to_number("m") -} \ No newline at end of file diff --git a/examples/bearing.ccm b/examples/bearing.ccm deleted file mode 100644 index b6298bb..0000000 --- a/examples/bearing.ccm +++ /dev/null @@ -1,166 +0,0 @@ - -struct Parameters { - drum_bottom_radius: Length = 50mm; - drum_top_radius: Length = 51mm; - drum_height: Length = 50mm; - - thickness: Length = 10mm; - depth: Length = 10mm; - - support_ring_thickness_scalar: Number = 0.2; - ring_position_on_bucket: Length = 25mm; - - printer_preferred_slope: Angle = 60degrees; - - #[min(1), integer] - number_of_segments: Number = 4; - - - #[min(0)] - bearing_gap: Length = 0.5mm; - - groove_depth: Length = 1mm; -} - -struct CommonDerived { - drum_slope: Length, - outer_starting_radius: Length, - outer_ending_radius: Length, - mid_radius: Length, - support_ring_thickness: Length, - bearing_space: Length, - bearing_radius: Length, -} - -function common_derived(parameters: Parameters) -> CommonDerived { - CommonDerived { - drum_slope: (parameters.drum_top_radius - parameters.drum_bottom_radius) / parameters.drum_height, - outer_starting_radius: parameters.drum_bottom_radius + parameters.ring_position_on_bucket * parameters.drum_slope, - outer_ending_radius: parameters.outer_starting_radius - parameters.thickness * parameters.drum_slope, - mid_radius: parameters.outer_starting_radius - parameters.thickness * 0.5, - support_ring_thickness: parameters.support_ring_thickness_scalar * parameters.thickness, - bearing_space: parameters.thickness - parameters.support_ring_thickness * 2, - bearing_radius: parameters.bearing_space - parameters.bearing_gap, - groove_width: tan(parameters.printer_preferred_slope) * parameters.groove_depth, - } -} - -// sketch RingPolygon() {} - -widget OuterRing(parameters: Parameters) { - let derived = common_derived(parameters); - - let ring_points = - [V2(outer_starting_radius, 0), V2(outer_ending_radius, depth), - V2(derived.mid_radius + derived.bearing_space * 0.5 + parameters.groove_depth, parameters.depth), - V2(derived.mid_radius + derived.bearing_space * 0.5, parameters.depth - tan(parameters.printer_preferred_slope) * parameters.groove_depth), - V2(derived.mid_radius + derived.bearing_space * 0.5, tan(parameters.printer_preferred_slope) * parameters.groove_depth), - V2(derived.mid_radius + derived.bearing_space * 0.5 + parameters.groove_depth, 0)]; - - let sketch = polygon(ring_points); - let main_rail = sketch.rotate_extrude(360degrees / parameteres.number_of_segments); - - let mid_point = V2(outer_starting_radius - groove_depth * 0.8, depth/2); - - let male_tongue_points = [ - mid_point, - ring_points[2], - ring_points[3], - ring_points[4], - ring_points[5]]; - - let sketch = polygon(male_tongue_points); - let male_tongue = sketch.rotate_extrude(-10degrees); - - let female_tonge_points = [ - outer_ring_points[2], - mid_point, - outer_ring_points[5], - outer_ring_points[0], - outer_ring_points[1]]; - - let sketch = polygon(female_tonge_points); - let female_tonge = sketch.rotate_extrude(10degrees); - let female_tonge.rotate(V3(0mm, 0mm, 1mm), 360degrees / parameters.number_of_segments); - - female_tonge -} - - -// module InnerRing() { -// inner_starting_radius_mm = OUTER_STARTING__MM - THICKNESS_MM; -// inner_ending_radius_mm = OUTER_ENDING_RADIUS_MM - THICKNESS_MM; -// -// outer_ring_points = -// [[inner_starting_radius_mm, 0], [inner_ending_radius_mm, DEPTH_MM], -// [MID_RADIUS_MM - BEARING_SPACE_MM * 0.5 - GROOVE_DEPTH_MM, DEPTH_MM], -// [MID_RADIUS_MM - BEARING_SPACE_MM * 0.5, DEPTH_MM - GROOVE_WIDTH_MM], -// [MID_RADIUS_MM - BEARING_SPACE_MM * 0.5, GROOVE_WIDTH_MM], -// [MID_RADIUS_MM - BEARING_SPACE_MM * 0.5 - GROOVE_DEPTH_MM, 0]]; -// -// rotate_extrude(angle = 360 / NUMBER_OF_SEGMENTS, $fn=360) { -// polygon(outer_ring_points); -// } -// -// mid_point = [inner_starting_radius_mm + GROOVE_DEPTH_MM * 0.8, DEPTH_MM/2]; -// -// male_tongue_points = [ -// mid_point, -// outer_ring_points[2], -// outer_ring_points[3], -// outer_ring_points[4], -// outer_ring_points[5]]; -// -// rotate_extrude(angle = -10, $fn=360) { -// polygon(male_tongue_points); -// } -// -// female_tonge_points = [ -// outer_ring_points[2], -// mid_point, -// outer_ring_points[5], -// outer_ring_points[0], -// outer_ring_points[1]]; -// -// rotate([0, 0, 360 / NUMBER_OF_SEGMENTS]) { -// rotate_extrude(angle = 10, $fn=360) { -// polygon(female_tonge_points); -// } -// } -// } -// -// module Bearing() { -// roller_radius_mm = BEARING_RADIUS_MM * 0.5; -// flange_radius_mm = roller_radius_mm + GROOVE_DEPTH_MM; -// -// points = [ -// [0.0, 0.0], -// [flange_radius_mm, 0.0], -// [roller_radius_mm, GROOVE_WIDTH_MM], -// [roller_radius_mm, DEPTH_MM - GROOVE_WIDTH_MM], -// [flange_radius_mm, DEPTH_MM], -// [0.0, DEPTH_MM]]; -// -// rotate_extrude($fn=360) { -// polygon(points); -// } -// } -// -// if (EXPORT == "PREVIEW") { -// for (i = [0:NUMBER_OF_SEGMENTS]) { -// rotate([0, 0, i*(360/NUMBER_OF_SEGMENTS)]) { -// OuterRing(); -// InnerRing(); -// } -// } -// -// number_of_bearings = floor((MID_RADIUS_MM * PI * 2) / (BEARING_SPACE_MM + GROOVE_DEPTH_MM * 2)); -// -// for (i = [0:number_of_bearings]) { -// rotate([0, 0, i*(360/number_of_bearings)]) { -// translate([MID_RADIUS_MM, 0, 0]) { -// Bearing(); -// } -// } -// } -// } diff --git a/examples/cooling_plate.ccm b/examples/cooling_plate.ccm new file mode 100644 index 0000000..a0110b9 --- /dev/null +++ b/examples/cooling_plate.ccm @@ -0,0 +1,20 @@ +let + nominal_angle_to_sun = 60deg; + + # 30watts per square meter multiplied by square meters results in watts. + nominal_power_output = (main_body: std.scalar.Length) -> std.scalar.Power: + let + # A length * by a length gives an area. + solar_panel_size = main_body * 1m; + in + solar_panel_size * nominal_angle_to_sun::sin() * (30W/1 'm^2'); + + # We need 3cm of cooling surface for each watt this produces + cooling_fin_size = nominal_power_output(main_body = 15cm) / 1W * 3cm; +in + std.mesh3d.cylinder( + diameter = cooling_fin_size, + height = 1cm, + sectors = 360u, + stacks = 1u + )::to_stl(name = "cooling_plate") diff --git a/examples/fornjot_demos.ccm b/examples/fornjot_demos.ccm deleted file mode 100644 index 2b84248..0000000 --- a/examples/fornjot_demos.ccm +++ /dev/null @@ -1,83 +0,0 @@ - -solid cuboid(x: Length, y: Length, z: Length) { - let base_sketch = new_sketch(Polygon { points = [ - vec2(-x / 2., -y / 2.), - vec2(x / 2., -y / 2.), - vec2(x / 2., y / 2.), - vec2(-x / 2., y / 2.) - ], - surface = global_xy_plane() - }); - - base_sketch.sweep(global_xy_plane(), vec3(0cm, 0cm, z)) -} - -solid holes(hole_radius: Length) { - let size = hole_radius * 4; - let cube = cuboid(size * 2, size, size); - - let cube = cube.update_shell(cube.shells[0], [hole_radius, size](shell: Shell) -> Shell { - let offset = size / 2; - let depth = size / 2; - - let bottom_face = shell.faces[0]; - let shell = shell.add_blind_hole(bottom_face, vec2(-offset, 0mm), hole_radius, vec3(0mm, 0mm, depth)); - - let bottom_face = shell.faces[0]; - let top_face = shell.faces[5]; - let shell = shell.add_through_hole(bottom_face, vec2(offset, 0mm), top_face, vec2(offset, 0mm), hole_radius); - - [shell] - }); - - cube -} - -solid spacer(outer_radius: Length, inner_radius: Length, height: Length) { - let bottom_surface = global_xy_plane(); - let sweep_path = vec3(0mm, 0mm, height); - - let base_sketch = new_sketch(global_xy_plane(), [ - new_region(RawRegion { - exterior = new_cycle(Circle { center = vec2(0mm, 0mm), radius = outer_radius, surface = global_xy_plane() }), - interiors = [new_cycle(Circle { center = vec2(0mm, 0mm), radius = inner_radius, surface = global_xy_plane() }).reverse()], - }) - ]); - - base_sketch.sweep(bottom_surface, sweep_path) -} - -solid star(num_points: Number, r1: Length, r2: Length, h: Length) { - let num_verticies = num_points * 2; - - let outer_points = []; - let inner_points = []; - - for index in 0..num_verticies { - let angle = 2rad / num_verticies * index; - - // TODO We need to add support for the modulous operator. - let radius = if (index / 2).fract() == 0 { r1 } else { r2 }; - - let normal_vector = angle.sin_cos(); - let point = normal_vector * radius; - - outer_points.append([point]); - inner_points.append([point / 2]); - } - - let bottom_surface = global_xy_plane(); - let sweep_path = vec3(0mm, 0mm, h); - - let exterior = new_cycle(Polygon { points = outer_points, surface = global_xy_plane() }); - let interiors = [ new_cycle(Polygon { points = inner_points, surface = global_xy_plane() }).reverse() ]; - - let base = new_sketch(global_xy_plane(), [new_region(RawRegion { - exterior = exterior, - interiors = interiors, - })]); - - base.sweep(bottom_surface, sweep_path) -} - -// Further demos are not yet possible with the current limitations of the language. \ No newline at end of file diff --git a/examples/language/arithmatic.ccm b/examples/language/arithmatic.ccm new file mode 100644 index 0000000..30cabfc --- /dev/null +++ b/examples/language/arithmatic.ccm @@ -0,0 +1,9 @@ +# Perform some basic arithmatic with units. +[ + 1 + 1, + 1m + 1m, + 1m * 1m, + 1m * 2, + 1ft + 1m, + (32768Mb - 8Gb) / 1Mb +] diff --git a/examples/language/closures.ccm b/examples/language/closures.ccm new file mode 100644 index 0000000..7979d67 --- /dev/null +++ b/examples/language/closures.ccm @@ -0,0 +1,6 @@ +# Show the feature set of closures. +[ + let fn = () -> std.scalar.Length: 5m; in fn(), + let fn = (input: std.scalar.Length) -> std.scalar.Length: input * 2; in fn(input = 2m), + let x = 5; fn = (input: std.scalar.Length) -> std.scalar.Length: input * x; in fn(input = 2m), # Closure capture +] diff --git a/examples/language/lists.ccm b/examples/language/lists.ccm new file mode 100644 index 0000000..dabd845 --- /dev/null +++ b/examples/language/lists.ccm @@ -0,0 +1,5 @@ +[ + [1, 2, 3, 4] * 1m, # An arithmetic operation applied to a list is applied to all its members + [1, 2, 3, 4]::map(f = (c: std.scalar.Number) -> std.scalar.Length: c * 2m + 1ft), # Map values in a list, producing a new list of the mapped values + [1, 2, 3, 4]::fold(init = 0, f = (previous: std.scalar.Number, c: std.scalar.Number) -> std.scalar.Number: previous + c) # Fold all values of the list into a single value +] diff --git a/examples/language/methods.ccm b/examples/language/methods.ccm new file mode 100644 index 0000000..33cef97 --- /dev/null +++ b/examples/language/methods.ccm @@ -0,0 +1,7 @@ +# Call methods on objects +[ + 5deg::sin(), + 1rad::sin(), + 9::cbrt(), + 9 'm^3'::cbrt() +] diff --git a/examples/language/vectors.ccm b/examples/language/vectors.ccm new file mode 100644 index 0000000..ce0ae6a --- /dev/null +++ b/examples/language/vectors.ccm @@ -0,0 +1,6 @@ +[ + <(1, 2)>, <(1, 2, 3)>, <(1, 2, 3, 4)>, # Three different sizes of vector are available + <(1m, 2m, 3m)>, # Vectors can have units + <(1, 2, 3)> * 1m, # Arithmetic operations on the vector are applied to the vector's components. + [<(1, 2, 3)>, <(4, 5, 6)>, <(7, 8, 9)>, <(10, 11, 12)>] * 1mm # Use this with a list to mass-apply a unit +] diff --git a/examples/modeling/mesh3d.ccm b/examples/modeling/mesh3d.ccm new file mode 100644 index 0000000..b0cbb9c --- /dev/null +++ b/examples/modeling/mesh3d.ccm @@ -0,0 +1,6 @@ +[ + std.mesh3d.icosphere(subdivions = 5u, diameter = 2cm)::to_stl(name="my_sphere"), + (std.mesh3d.icosphere(subdivions = 5u, diameter = 2cm) | std.mesh3d.cube(size = <(2cm, 2cm, 2cm)>) + <(1cm, 0cm, 0cm)>)::to_stl(name="my_cube_sphere_or"), + (std.mesh3d.icosphere(subdivions = 5u, diameter = 2cm) & std.mesh3d.cube(size = <(2cm, 2cm, 2cm)>) + <(1cm, 0cm, 0cm)>)::to_stl(name="my_cube_sphere_and"), + (std.mesh3d.icosphere(subdivions = 5u, diameter = 2cm) ^ std.mesh3d.cube(size = <(2cm, 2cm, 2cm)>) + <(1cm, 0cm, 0cm)>)::to_stl(name="my_cube_sphere_xor") +] diff --git a/examples/other/importing.ccm b/examples/other/importing.ccm new file mode 100644 index 0000000..adc1317 --- /dev/null +++ b/examples/other/importing.ccm @@ -0,0 +1,7 @@ +[ + std.import(path = "../language/arithmatic.ccm"), + std.import(path = "../language/closures.ccm"), + std.import(path = "../language/lists.ccm"), + std.import(path = "../language/methods.ccm"), + std.import(path = "../language/vectors.ccm") +] diff --git a/examples/other/string_formatting.ccm b/examples/other/string_formatting.ccm new file mode 100644 index 0000000..64bc136 --- /dev/null +++ b/examples/other/string_formatting.ccm @@ -0,0 +1,7 @@ +let + a = 1m; + b = 1ft; + c = b / 1ft; # Units of measurement are always presented with their internal representation. You can use this trick to print them as a specific unit. + d = "hello"; +in + "{a} {b} {c}ft {d} {e}"::format(e = "world") diff --git a/examples/sketches.ccm b/examples/sketches.ccm deleted file mode 100644 index ed98a8b..0000000 --- a/examples/sketches.ccm +++ /dev/null @@ -1,17 +0,0 @@ - -sketch circle(diameter: Length) { - new_sketch(Circle { center = vec2(0mm, 0mm), radius = diameter / 2, surface = sketch_plane() }) -} - -sketch square(width: Length) { - new_sketch(Polygon { points = [vec2(width, 0m), vec2(width, width), vec2(0m, width), vec2(0m, 0m)], - surface = sketch_plane() }) -} - -sketch square_with_circle(square_width: Length, hole_diameter: Length) { - new_sketch(sketch_plane(), [ - new_region(Circle { center = vec2(0m, 0m), radius = hole_diameter / 2, surface = sketch_plane() }), - new_region(Polygon { points = [vec2(hole_diameter, 0m), vec2(0m, square_width), vec2(square_width, square_width), vec2(square_width, 0m)], surface = sketch_plane() }) - ]) -} - diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..fd396b7 --- /dev/null +++ b/flake.lock @@ -0,0 +1,116 @@ +{ + "nodes": { + "crane": { + "locked": { + "lastModified": 1767461147, + "narHash": "sha256-TH/xTeq/RI+DOzo+c+4F431eVuBpYVwQwBxzURe7kcI=", + "owner": "ipetkov", + "repo": "crane", + "rev": "7d59256814085fd9666a2ae3e774dc5ee216b630", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1767596244, + "narHash": "sha256-P4NRZUjYbeuzv4hGrXxfdg0QpdGVoeNn0CMmzIyr398=", + "owner": "nix-community", + "repo": "fenix", + "rev": "eedfb5a27900e82ec0390acc83d4d226ce86e714", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1767379071, + "narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "fb7944c166a3b630f177938e478f0378e64ce108", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "fenix": "fenix", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1767551763, + "narHash": "sha256-lcA/e3++3aZQSj6xCsBi2VpYyC3Q+oO/oukgfHiL+Ts=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "6a1246b69ca761480b9278df019f717b549cface", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..2a8c3ec --- /dev/null +++ b/flake.nix @@ -0,0 +1,78 @@ +{ + description = "A data driven CAD program"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + crane.url = "github:ipetkov/crane"; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + crane, + fenix, + ... + }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + }; + fenix-pkgs = fenix.packages.${system}; + fenix-channel = (fenix-pkgs.stable); + + craneLib = (crane.mkLib pkgs).overrideScope (final: prev: { + cargo = fenix-channel.cargo; + rustc = fenix-channel.rustc; + }); + in rec + { + packages.default = with pkgs; craneLib.buildPackage { + nativeBuildInputs = [ + openssl + pkg-config + ]; + + buildInputs = [ + openssl + ]; + + src = craneLib.cleanCargoSource ./.; + strictDeps = true; + }; + + devShells.default = with pkgs; pkgs.mkShell { + inputsFrom = [ packages.default ]; + buildInputs = [ + bashInteractive + nodejs_24 + tree-sitter + fenix-pkgs.rust-analyzer + fenix-channel.rustfmt + fenix-channel.rustc + fenix-channel.cargo + fenix-channel.clippy + cargo-expand + openssl + pkg-config + ]; + + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ + openssl + ]; + + shellHook = '' + export SHELL=${pkgs.bashInteractive}/bin/bash + export NIX_HARDENING_ENABLE="" + ''; + }; + + } + ); +} diff --git a/formatter/Cargo.toml b/formatter/Cargo.toml new file mode 100644 index 0000000..957d038 --- /dev/null +++ b/formatter/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "formatter" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +tree-sitter-command-cad-model= { path = "../tree-sitter-command-cad-model" } +# topiary-core = "0.5.1" +clap = { version = "4.4", features = [ "derive", "wrap_help" ] } \ No newline at end of file diff --git a/formatter/src/arguments.rs b/formatter/src/arguments.rs new file mode 100644 index 0000000..5a1ded5 --- /dev/null +++ b/formatter/src/arguments.rs @@ -0,0 +1,15 @@ +use std::path::PathBuf; + +#[derive(clap::Parser)] +#[command(name = "formatter")] +#[command(bin_name = "formatter")] +/// Format a Command CAD script +pub struct RunArgs { + #[arg(long)] + /// Path to script to format. Leave unspecified to use standard input. + pub input: Option, + + #[arg(long)] + /// Path to output file. Leave unspecified to use standard output. + pub output: Option, +} diff --git a/formatter/src/main.rs b/formatter/src/main.rs new file mode 100644 index 0000000..843657c --- /dev/null +++ b/formatter/src/main.rs @@ -0,0 +1,33 @@ +use std::{ + fs, + io::{self, Read, Write}, +}; + +use anyhow::{Context, Result}; +use clap::Parser as _; + +mod arguments; + +fn main() { + if let Err(error) = trampoline() { + eprintln!("Fatal error: {error:?}") + } +} + +fn trampoline() -> Result<()> { + let args = arguments::RunArgs::parse(); + + let mut input: Box = if let Some(input) = args.input { + Box::new(fs::File::open(input).context("Failed to open input file")?) + } else { + Box::new(std::io::stdin()) + }; + + let mut input_str = String::new(); + input + .read_to_string(&mut input_str) + .context("Failed to read input file")?; + let input = input_str; + + todo!() +} diff --git a/formatter/test_files/empty_closure.ccm b/formatter/test_files/empty_closure.ccm new file mode 100644 index 0000000..0655b8c --- /dev/null +++ b/formatter/test_files/empty_closure.ccm @@ -0,0 +1,2 @@ +# An empty closure that takes no arguments, and returns no arguments. +() -> {} \ No newline at end of file diff --git a/interpreter/Cargo.lock b/interpreter/Cargo.lock new file mode 100644 index 0000000..a838a2b --- /dev/null +++ b/interpreter/Cargo.lock @@ -0,0 +1,1244 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + +[[package]] +name = "cc" +version = "1.2.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "check_keyword" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dcba1e35fcf6c9350d9fb4863d87c66e5e37f1583f883560dca2c9320840bcc" +dependencies = [ + "phf", +] + +[[package]] +name = "coalesce" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f42f93baa58655bd5b3db91dd9b2073dc8a0c887dc35bd1cfefbd43fc7cf07" + +[[package]] +name = "common_data_types" +version = "0.1.0" +dependencies = [ + "ordered-float", + "paste", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "enum-map" +version = "3.0.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb2a23ad36148a32085addb3ef1aa39805d044d4532ff258360d523a4eff38e5" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "1.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44600091ce205df4f8b661e98617d49c37b2dd609e449ec82b0fb5d7b33e2eeb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enum_downcast" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e532035fcb0d794ae56f74a0a39fea24bca14c7d47c428e3ab0667ebdccd8b4a" +dependencies = [ + "enum_downcast_derive", +] + +[[package]] +name = "enum_downcast_derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0e8987f91d86aa10313adf7c767a8cfd040364432d4e9f722b049b8ff7e634a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "file-guard" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ef72acf95ec3d7dbf61275be556299490a245f017cf084bd23b4f68cf9407c" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "glam" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da" + +[[package]] +name = "glam" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b" + +[[package]] +name = "glam" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16" + +[[package]] +name = "glam" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776" + +[[package]] +name = "glam" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34" + +[[package]] +name = "glam" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca" + +[[package]] +name = "glam" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f" + +[[package]] +name = "glam" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815" + +[[package]] +name = "glam" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" + +[[package]] +name = "glam" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c" + +[[package]] +name = "glam" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" + +[[package]] +name = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" + +[[package]] +name = "glam" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" + +[[package]] +name = "glam" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" + +[[package]] +name = "glam" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" + +[[package]] +name = "glam" +version = "0.30.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" + +[[package]] +name = "hashable-map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46ab77e35afc7a5a3087e72e8e93a90a226a78f62c5f4dceaeb3d7ef5decdccb" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "imstr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3441deb04ea9c6b472f313be54d585428cd6a68cdb8dcf40cf43744bec78fe" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "interpreter" +version = "0.1.0" +dependencies = [ + "common_data_types", + "enum_dispatch", + "enum_downcast", + "file-guard", + "hashable-map", + "imstr", + "nalgebra", + "nom", + "num-traits", + "paste", + "pretty_assertions", + "rayon", + "stack", + "tempfile", + "tree-sitter", + "tree-sitter-command-cad-model", + "type-sitter", + "type-sitter-gen", + "units", + "unwrap-enum", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "join-lazy-fmt" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e90f66baf362a8a5ce2ca820290fbede0572a76fabf8408bc68c02f9ad1d03bf" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "logos" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff472f899b4ec2d99161c51f60ff7075eeb3097069a36050d8037a6325eb8154" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "192a3a2b90b0c05b27a0b2c43eecdb7c415e29243acc3f89cc8247a5b693045c" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax", + "rustc_version", + "syn", +] + +[[package]] +name = "logos-derive" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "605d9697bcd5ef3a42d38efc51541aa3d6a4a25f7ab6d1ed0da5ac632a26b470" +dependencies = [ + "logos-codegen", +] + +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "nalgebra" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4d5b3eff5cd580f93da45e64715e8c20a3996342f1e466599cf7a267a0c2f5f" +dependencies = [ + "approx", + "glam 0.14.0", + "glam 0.15.2", + "glam 0.16.0", + "glam 0.17.3", + "glam 0.18.0", + "glam 0.19.0", + "glam 0.20.5", + "glam 0.21.3", + "glam 0.22.0", + "glam 0.23.0", + "glam 0.24.2", + "glam 0.25.0", + "glam 0.27.0", + "glam 0.28.0", + "glam 0.29.3", + "glam 0.30.10", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "973e7178a678cfd059ccec50887658d482ce16b0aa9da3888ddeab5cd5eb4889" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quasiquote" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7f3e24436387bc9ee9963246739f2a851d9ca85ed73a2c5b878bf908e9b34d" +dependencies = [ + "proc-macro2", + "quasiquote-proc-macro", + "quote", +] + +[[package]] +name = "quasiquote-proc-macro" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79bc0a2b2a185610156579070227676cf31d6282045d01273bbfc65804df6022" +dependencies = [ + "itertools", + "proc-macro2", + "quote", +] + +[[package]] +name = "quote" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustc-stable-hash" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781442f29170c5c93b7185ad559492601acdc71d5bb0706f5868094f45cfcd08" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" + +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simba" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "stack" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c27dee130f751c0903be703c5355e9b333a17550d7d63d04cf0d3b1601edb8a3" +dependencies = [ + "coalesce", +] + +[[package]] +name = "streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tree-sitter" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f873475d258561b06f1c595d93308a7ed124d9977cb26b148c2084a4a3cc87" +dependencies = [ + "cc", + "regex", + "regex-syntax", + "serde_json", + "streaming-iterator", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-command-cad-model" +version = "0.0.1" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-language" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae62f7eae5eb549c71b76658648b72cc6111f2d87d24a1e31fa907f4943e3ce" + +[[package]] +name = "type-sitter" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d7b6c3bc4c60ecee2147ecb3cd7820fe9ee1df4251e1b2b8b5a214fe0084ba" +dependencies = [ + "type-sitter-lib", + "type-sitter-proc", +] + +[[package]] +name = "type-sitter-gen" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ad6a9e8aa527b2c037cd5016d6291e197c98eb6160ff09b25f2398374a3003" +dependencies = [ + "cc", + "check_keyword", + "convert_case", + "dunce", + "enum-map", + "join-lazy-fmt", + "libc", + "libloading", + "logos", + "prettyplease", + "proc-macro2", + "quote", + "rustc-stable-hash", + "serde", + "serde_json", + "slice-group-by", + "syn", + "tree-sitter", + "tree-sitter-language", + "walkdir", +] + +[[package]] +name = "type-sitter-lib" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092e2a9b5f4eb14bdd2246eb1557b2b99e67fe43be3e0411535d9483e73236b7" +dependencies = [ + "streaming-iterator", + "tree-sitter", +] + +[[package]] +name = "type-sitter-proc" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4240aa23c38a18dc53f8422374fac3d7514654ec28258ef173b53ba9bee37a6e" +dependencies = [ + "syn", + "type-sitter-gen", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "uneval" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63cc5d2fd8648d7e2be86098f60c9ece7045cc710b3c1e226910e2f37d11dc73" +dependencies = [ + "serde", + "thiserror", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "units" +version = "0.1.0" +dependencies = [ + "common_data_types", + "csv", + "serde", + "uneval", +] + +[[package]] +name = "unwrap-enum" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5639235db46432e23bff2caf3a02096d9a801c99560198f3edba4edf8d015a5" +dependencies = [ + "unwrap-enum-proc-macro", +] + +[[package]] +name = "unwrap-enum-proc-macro" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5771812808a3442129fa6c692ecb4ae77e690b530598854af2be0fda01657a94" +dependencies = [ + "proc-macro2", + "quasiquote", + "quote", + "syn", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml new file mode 100644 index 0000000..c2ed92b --- /dev/null +++ b/interpreter/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "interpreter" +version = "0.1.0" +edition = "2021" + +[dependencies] +common_data_types = { path = "../common_data_types" } +units = { path = "../units" } +tree-sitter = { workspace = true } +tree-sitter-command-cad-model = { path = "../tree-sitter-command-cad-model" } +type-sitter = { workspace = true } +unwrap-enum = "0.1.0" +file-guard = "0.2.0" +enum_dispatch = "0.3" +enum_downcast = { version = "0.1", features = [ "derive" ] } +num-traits = "0.2" +hashable-map = { version = "0.4.0", features = ["serde"] } +imstr = "0.2.0" +nalgebra = "0.34.1" +stack = "0.4.0" +paste = "1.0.15" +tempfile = { workspace = true } +rayon = "1.11.0" +nom = "8.0.0" +ariadne = { workspace = true } +levenshtein = "1.0.5" +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"] } +stl_io = "0.10.0" + +[build-dependencies] +type-sitter-gen = "0.8" +tree-sitter-command-cad-model = { path = "../tree-sitter-command-cad-model" } + +[dev-dependencies] +pretty_assertions = "1.4" + diff --git a/interpreter/build.rs b/interpreter/build.rs new file mode 100644 index 0000000..59aab22 --- /dev/null +++ b/interpreter/build.rs @@ -0,0 +1,63 @@ +// use std::path::{Path, PathBuf}; +// use std::{env, fs}; +// use type_sitter_gen::{generate_nodes, generate_queries, super_nodes}; +// +// fn main() { +// // Common setup +// let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); +// println!("cargo::rerun-if-changed=build.rs"); +// +// let parser_path = "../tree-sitter-command-cad-model"; +// +// // Obligatory: in this and future lines, replace `vendor/path/to/tree-sitter-foobar-lang` +// // with the path to your grammar's folder, relative to the folder containing `Cargo.toml` +// println!("cargo::rerun-if-changed={parser_path}"); +// +// let parser_path = Path::new(parser_path); +// +// // To generate nodes +// let path = parser_path.join("src/node-types.json"); +// fs::write( +// out_dir.join("nodes.rs"), +// generate_nodes(path).unwrap().into_string(), +// ) +// .unwrap(); +// +// // To generate queries +// fs::write( +// out_dir.join("queries.rs"), +// generate_queries( +// parser_path.join("queries"), +// parser_path, +// // Replace with a different `syn::Path` if the nodes don't exist in a subling to `dest_path` named `nodes` +// &super_nodes(), +// // Replace with `true` if you are using the `yak-sitter` feature (by default, no) +// false, +// ) +// .unwrap() +// .into_string(), +// ) +// .unwrap(); +// } +use std::path::PathBuf; +use std::{env, fs}; +use type_sitter_gen::generate_nodes; + +fn main() { + // Common setup. Same as before + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + println!("cargo::rerun-if-changed=build.rs"); + + // Obligatory: in this and future lines, replace `vendor/path/to/tree-sitter-foobar-lang` + // with the path to your grammar's folder, relative to the folder containing `Cargo.toml` + println!("cargo::rerun-if-changed=../tree-sitter-command-cad-model"); + + // To generate nodes + fs::write( + out_dir.join("nodes.rs"), + generate_nodes(tree_sitter_command_cad_model::NODE_TYPES) + .unwrap() + .into_string(), + ) + .unwrap(); +} diff --git a/interpreter/src/compile/constraint_set.rs b/interpreter/src/compile/constraint_set.rs new file mode 100644 index 0000000..7a82640 --- /dev/null +++ b/interpreter/src/compile/constraint_set.rs @@ -0,0 +1,665 @@ +/* + * Copyright 2026 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::{path::PathBuf, sync::Arc}; + +use crate::compile::{nodes, unwrap_missing, Error, Parse}; + +use super::{AstNode, Scalar}; +use imstr::ImString; +use type_sitter::{HasChild, Node}; +use unwrap_enum::EnumAs; + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct ConstraintSet { + pub variables: Vec>, + pub left: AstNode, + pub right: AstNode, + pub relation: Relation, +} + +impl<'t> Parse<'t, nodes::ConstraintSet<'t>> for ConstraintSet { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSet<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + let variables = { + let variables_node = value.variables()?; + let mut cursor = variables_node.walk(); + + let mut variables = Vec::new(); + for field in variables_node.identifiers(&mut cursor) { + variables.push(ImString::parse(file, input, field)?); + } + + variables + }; + + let left = ConstraintSetExpression::parse(file, input, value.lhs()?)?; + let right = ConstraintSetExpression::parse(file, input, value.rhs()?)?; + + type Operator<'t> = nodes::anon_unions::NotEq_Lt_LtEq_EqEq_Gt_GtEq<'t>; + let relation = match value.relation()? { + Operator::Lt(_) => Relation::Less, + Operator::LtEq(_) => Relation::LessEqual, + Operator::EqEq(_) => Relation::Equal, + Operator::GtEq(_) => Relation::GreaterEqual, + Operator::Gt(_) => Relation::Greater, + Operator::NotEq(_) => Relation::NotEqual, + }; + + Ok(AstNode::new( + file, + &value, + Self { + variables, + left, + right, + relation, + }, + )) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub enum Relation { + Less, + LessEqual, + Equal, + GreaterEqual, + Greater, + NotEqual, +} + +#[derive(Debug, Hash, Eq, PartialEq, EnumAs)] +pub enum ConstraintSetExpression { + Parenthesis(Box>), + Scalar(AstNode), + Identifier(AstNode), + UnaryExpression(Box>), + BinaryExpression(Box>), + MethodCall(Box>), +} + +impl<'t> Parse<'t, nodes::ConstraintSetExpression<'t>> for ConstraintSetExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSetExpression<'t>, + ) -> Result, Error<'t, 'i>> { + type ChildType<'t> = as HasChild<'t>>::Child; + + unwrap_missing(&value)?; + + match value.child()? { + ChildType::ConstraintSetBinaryExpression(binary_expression) => { + Self::parse(file, input, binary_expression) + } + ChildType::ConstraintSetParenthesis(parenthesis) => { + Self::parse(file, input, parenthesis) + } + ChildType::Identifier(ident) => Ok(AstNode::new( + file, + &value, + Self::Identifier(ImString::parse(file, input, ident)?), + )), + ChildType::Scalar(scalar) => Self::parse(file, input, scalar), + ChildType::ConstraintSetUnaryExpression(unary_expression) => { + Self::parse(file, input, unary_expression) + } + ChildType::ConstraintSetMethodCall(method_call) => { + Self::parse(file, input, method_call) + } + } + } +} + +impl<'t> Parse<'t, nodes::ConstraintSetBinaryExpression<'t>> for ConstraintSetExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSetBinaryExpression<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::BinaryExpression(Box::new(BinaryExpression::parse(file, input, value)?)), + )) + } +} + +impl<'t> Parse<'t, nodes::ConstraintSetParenthesis<'t>> for ConstraintSetExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSetParenthesis<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + let expression = value.constraint_set_expression()?; + Ok(AstNode::new( + file, + &value, + Self::Parenthesis(Box::new(Self::parse(file, input, expression)?)), + )) + } +} + +impl<'t> Parse<'t, nodes::Scalar<'t>> for ConstraintSetExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Scalar<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::Scalar(Scalar::parse(file, input, value)?), + )) + } +} + +impl<'t> Parse<'t, nodes::ConstraintSetUnaryExpression<'t>> for ConstraintSetExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSetUnaryExpression<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::UnaryExpression(Box::new(UnaryExpression::parse(file, input, value)?)), + )) + } +} + +impl<'t> Parse<'t, nodes::ConstraintSetMethodCall<'t>> for ConstraintSetExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSetMethodCall<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::MethodCall(Box::new(MethodCall::parse(file, input, value)?)), + )) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub enum UnaryExpressionOperation { + Add, + Sub, +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct UnaryExpression { + pub operation: AstNode, + pub expression: AstNode, +} + +impl<'t> Parse<'t, nodes::ConstraintSetUnaryExpression<'t>> for UnaryExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSetUnaryExpression<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + let operation = value.op()?; + + let operation = match operation { + nodes::anon_unions::Add_Sub::Add(add) => { + AstNode::new(file, &add, UnaryExpressionOperation::Add) + } + nodes::anon_unions::Add_Sub::Sub(sub) => { + AstNode::new(file, &sub, UnaryExpressionOperation::Sub) + } + }; + + let expression = + ConstraintSetExpression::parse(file, input, value.constraint_set_expression()?)?; + + Ok(AstNode::new( + file, + &value, + Self { + operation, + expression, + }, + )) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub enum BinaryExpressionOperation { + Mul, + Add, + Sub, + Div, +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct BinaryExpression { + pub operation: AstNode, + pub a: AstNode, + pub b: AstNode, +} + +impl<'t> Parse<'t, nodes::ConstraintSetBinaryExpression<'t>> for BinaryExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSetBinaryExpression<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + type Operation<'t> = nodes::anon_unions::Mul_Add_Sub_Div<'t>; + + let operation = value.op()?; + + let operation = match operation { + Operation::Mul(mul) => AstNode::new(file, &mul, BinaryExpressionOperation::Mul), + Operation::Add(add) => AstNode::new(file, &add, BinaryExpressionOperation::Add), + Operation::Sub(sub) => AstNode::new(file, &sub, BinaryExpressionOperation::Sub), + Operation::Div(div) => AstNode::new(file, &div, BinaryExpressionOperation::Div), + }; + + let a = value.a()?; + let a = ConstraintSetExpression::parse(file, input, a)?; + + let b = value.b()?; + let b = ConstraintSetExpression::parse(file, input, b)?; + + Ok(AstNode::new(file, &value, Self { operation, a, b })) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct Vector2 { + pub x: AstNode, + pub y: AstNode, +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct Vector3 { + pub x: AstNode, + pub y: AstNode, + pub z: AstNode, +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct MethodCall { + pub self_dictionary: AstNode, + pub to_call: AstNode, + pub argument: AstNode, +} + +impl<'t> Parse<'t, nodes::ConstraintSetMethodCall<'t>> for MethodCall { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSetMethodCall<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + let self_dictionary = + ConstraintSetExpression::parse(file, input, value.self_dictionary()?)?; + let to_call = ImString::parse(file, input, value.to_call()?)?; + let argument = ConstraintSetExpression::parse(file, input, value.argument()?)?; + + Ok(AstNode::new( + file, + &value, + Self { + self_dictionary, + to_call, + argument, + }, + )) + } +} + +#[cfg(test)] +mod test { + use common_data_types::{Dimension, Float}; + use pretty_assertions::assert_eq; + + use crate::compile::{full_compile, Expression}; + + use super::*; + + fn comparison_test(input: &str, relation: Relation) { + let root = full_compile(input); + let formula = root.node.as_constraintset().unwrap(); + let a = &formula.node.variables[0]; + let left = &formula.node.left; + let left_ident = left.node.as_identifier().unwrap(); + let right = &formula.node.right; + let right_ident = right.node.as_identifier().unwrap(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::ConstraintSet(AstNode { + reference: formula.reference.clone(), + node: Arc::new(ConstraintSet { + variables: vec![AstNode { + reference: a.reference.clone(), + node: ImString::from("a") + }], + left: AstNode { + reference: left.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: left_ident.reference.clone(), + node: ImString::from("a") + }) + }, + right: AstNode { + reference: right.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: right_ident.reference.clone(), + node: ImString::from("a") + }) + }, + relation + }) + }) + } + ); + } + + #[test] + fn less_than() { + comparison_test("<<>>", Relation::Less); + } + #[test] + fn less_than_equel() { + comparison_test("<<>>", Relation::LessEqual); + } + #[test] + fn equel() { + comparison_test("<<>>", Relation::Equal); + } + #[test] + fn greater_than_equel() { + comparison_test("<<= a>>>", Relation::GreaterEqual); + } + #[test] + fn greater_than() { + comparison_test("<< a>>>", Relation::Greater); + } + #[test] + fn not_equel() { + comparison_test("<<>>", Relation::NotEqual); + } + + fn expression_test( + input: &str, + expression_builder: impl FnOnce(&ConstraintSetExpression) -> ConstraintSetExpression, + ) { + let root = full_compile(input); + let formula = root.node.as_constraintset().unwrap(); + let a = &formula.node.variables[0]; + let left = &formula.node.left; + let right = &formula.node.right; + let right_ident = right.node.as_identifier().unwrap(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::ConstraintSet(AstNode { + reference: formula.reference.clone(), + node: Arc::new(ConstraintSet { + variables: vec![AstNode { + reference: a.reference.clone(), + node: ImString::from("a") + }], + left: AstNode { + reference: left.reference.clone(), + node: expression_builder(&left.node) + }, + right: AstNode { + reference: right.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: right_ident.reference.clone(), + node: ImString::from("a") + }) + }, + relation: Relation::Equal + }) + }) + } + ); + } + + #[test] + fn parenthesis() { + expression_test("<<>>", |expression| { + let paren = expression.as_parenthesis().unwrap(); + let ident = paren.node.as_identifier().unwrap(); + ConstraintSetExpression::Parenthesis(Box::new(AstNode { + reference: paren.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: ident.reference.clone(), + node: ImString::from("a"), + }), + })) + }); + } + + #[test] + fn scalar() { + expression_test("<<>>", |expression| { + let value = expression.as_scalar().unwrap(); + ConstraintSetExpression::Scalar(AstNode { + reference: value.reference.clone(), + node: Scalar { + dimension: Dimension::length(), + value: Float::new(5.0).unwrap(), + }, + }) + }); + } + + fn unary_test(input: &str, operation: UnaryExpressionOperation) { + expression_test(input, |expression| { + let unary = expression.as_unaryexpression().unwrap(); + let operation_ref = &unary.node.operation.reference; + let ident = unary.node.expression.node.as_identifier().unwrap(); + ConstraintSetExpression::UnaryExpression(Box::new(AstNode { + reference: unary.reference.clone(), + node: UnaryExpression { + operation: AstNode { + reference: operation_ref.clone(), + node: operation, + }, + expression: AstNode { + reference: unary.node.expression.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: ident.reference.clone(), + node: ImString::from("a"), + }), + }, + }, + })) + }); + } + + #[test] + fn unary_plus() { + unary_test("<<>>", UnaryExpressionOperation::Add); + } + + #[test] + fn unary_minus() { + unary_test("<<>>", UnaryExpressionOperation::Sub); + } + + fn binary_test(input: &str, operation: BinaryExpressionOperation) { + expression_test(input, |expression| { + let binary = expression.as_binaryexpression().unwrap(); + let operation_ref = &binary.node.operation.reference; + let ident_a = binary.node.a.node.as_identifier().unwrap(); + let ident_b = binary.node.b.node.as_identifier().unwrap(); + ConstraintSetExpression::BinaryExpression(Box::new(AstNode { + reference: binary.reference.clone(), + node: BinaryExpression { + operation: AstNode { + reference: operation_ref.clone(), + node: operation, + }, + a: AstNode { + reference: binary.node.a.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: ident_a.reference.clone(), + node: ImString::from("c"), + }), + }, + b: AstNode { + reference: binary.node.b.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: ident_b.reference.clone(), + node: ImString::from("b"), + }), + }, + }, + })) + }); + } + + #[test] + fn multiply() { + binary_test("<<>>", BinaryExpressionOperation::Mul); + } + + #[test] + fn add() { + binary_test("<<>>", BinaryExpressionOperation::Add); + } + + #[test] + fn sub() { + binary_test("<<>>", BinaryExpressionOperation::Sub); + } + + #[test] + fn divide() { + binary_test("<<>>", BinaryExpressionOperation::Div); + } + + #[test] + fn method_call() { + expression_test("<<>>", |expression| { + let method_call = expression.as_methodcall().unwrap(); + let self_dictionary = &method_call + .node + .self_dictionary + .node + .as_identifier() + .unwrap(); + let to_call = &method_call.node.to_call; + let argument = method_call.node.argument.node.as_identifier().unwrap(); + ConstraintSetExpression::MethodCall(Box::new(AstNode { + reference: method_call.reference.clone(), + node: MethodCall { + self_dictionary: AstNode { + reference: method_call.node.self_dictionary.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: self_dictionary.reference.clone(), + node: ImString::from("b"), + }), + }, + to_call: AstNode { + reference: to_call.reference.clone(), + node: ImString::from("method"), + }, + argument: AstNode { + reference: method_call.node.argument.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: argument.reference.clone(), + node: ImString::from("c"), + }), + }, + }, + })) + }); + } + + #[test] + fn multiple_variables() { + let root = full_compile("<<>>"); + let formula = root.node.as_constraintset().unwrap(); + let variables = &formula.node.variables; + let left = &formula.node.left; + let left_ident = left.node.as_identifier().unwrap(); + let right = &formula.node.right; + let right_ident = right.node.as_identifier().unwrap(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::ConstraintSet(AstNode { + reference: formula.reference.clone(), + node: Arc::new(ConstraintSet { + variables: vec![ + AstNode { + reference: variables[0].reference.clone(), + node: ImString::from("a") + }, + AstNode { + reference: variables[1].reference.clone(), + node: ImString::from("b") + } + ], + left: AstNode { + reference: left.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: left_ident.reference.clone(), + node: ImString::from("a") + }) + }, + right: AstNode { + reference: right.reference.clone(), + node: ConstraintSetExpression::Identifier(AstNode { + reference: right_ident.reference.clone(), + node: ImString::from("b") + }) + }, + relation: Relation::Equal + }) + }) + } + ); + } +} diff --git a/interpreter/src/compile/expressions.rs b/interpreter/src/compile/expressions.rs new file mode 100644 index 0000000..a56ded2 --- /dev/null +++ b/interpreter/src/compile/expressions.rs @@ -0,0 +1,2530 @@ +use std::{cmp::Ordering, collections::HashSet, path::PathBuf, sync::Arc}; + +use hashable_map::HashableSet; +use imstr::ImString; +use type_sitter::{HasChild, Node}; +use unwrap_enum::EnumAs; + +use crate::{ + compile::{constraint_set::ConstraintSet, unwrap_missing, Scalar}, + execution::find_all_variable_accesses_in_expression, +}; + +use super::{nodes, AstNode, Error, Parse}; + +/// Used for sorting operations that have dependencies on other operations for parallel execution. +trait DependentOperation { + fn original_index(&self) -> usize; + fn name(&self) -> &ImString; + fn dependencies(&self) -> &HashableSet; +} + +fn sort_and_group_dependencies(deps: &mut [D]) -> Vec> +where + D: DependentOperation, +{ + if !deps.is_empty() { + deps.sort_by(|a, b| { + let a_name = a.name(); + let b_name = b.name(); + let a_index = a.original_index(); + let b_index = b.original_index(); + let a = a.dependencies(); + let b = b.dependencies(); + + // Dependency takes president. + if a.contains(b_name) && a_index > b_index { + Ordering::Greater + } else if b.contains(a_name) && b_index > a_index { + Ordering::Less + } else { + // If they have no dependency on each other, put the ones with fewer dependencies + // as a higher priority. + a.len().cmp(&b.len()) + } + }); + + let mut compute_groups = Vec::new(); + let mut start = 0; + let mut iterator = deps.iter().enumerate().peekable(); + + while let (Some((a_index, a)), Some((b_index, b))) = (iterator.next(), iterator.peek()) { + // Every transition indicates the end of a group. + if a.dependencies() != b.dependencies() { + compute_groups.push(start..a_index + 1); + start = *b_index; + } + } + + // Whatever remains is its own group + compute_groups.push(start..deps.len()); + + compute_groups + } else { + // Nothing to compute. + Vec::new() + } +} + +#[derive(Debug, Eq, PartialEq)] +pub struct InvalidUnitError<'t, 'i> { + pub name: &'i str, + pub node: nodes::anon_unions::Identifier_UnitQuote<'t>, +} + +#[derive(Debug, Eq, PartialEq)] +pub struct ParseIntError<'t> { + pub error: std::num::ParseIntError, + pub node: nodes::Integer<'t>, +} + +#[derive(Debug, Eq, PartialEq)] +pub struct ParseNumberError<'t> { + pub error: std::num::ParseIntError, + pub node: nodes::Number<'t>, +} + +#[derive(Debug, Hash, Eq, PartialEq, EnumAs)] +pub enum Expression { + BinaryExpression(AstNode>), + Boolean(AstNode), + ClosureDefinition(AstNode>), + DictionaryConstruction(AstNode), + If(AstNode>), + List(AstNode>>), + Parenthesis(Box>), + MemberAccess(Box>), + Self_(AstNode), + Identifier(AstNode), + Scalar(AstNode), + Vector2(AstNode>), + Vector3(AstNode>), + Vector4(AstNode>), + SignedInteger(AstNode), + String(AstNode), + StructDefinition(AstNode), + UnaryExpression(AstNode>), + UnsignedInteger(AstNode), + FunctionCall(AstNode>), + MethodCall(AstNode>), + LetIn(AstNode>), + ConstraintSet(AstNode>), + Malformed(ImString), +} + +impl<'t> Parse<'t, nodes::BinaryExpression<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::BinaryExpression<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::BinaryExpression( + BinaryExpression::parse(file, input, value).map(|o| o.into_box())?, + ), + )) + } +} + +impl<'t> Parse<'t, nodes::ConstraintSet<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ConstraintSet<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::ConstraintSet(ConstraintSet::parse(file, input, value)?.into_arc()), + )) + } +} + +impl<'t> Parse<'t, nodes::Boolean<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Boolean<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::Boolean(bool::parse(file, input, value)?), + )) + } +} + +impl<'t> Parse<'t, nodes::ClosureDefinition<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ClosureDefinition<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::ClosureDefinition(ClosureDefinition::parse(file, input, value)?.into_box()), + )) + } +} + +impl<'t> Parse<'t, nodes::DictionaryConstruction<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::DictionaryConstruction<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::DictionaryConstruction(DictionaryConstruction::parse(file, input, value)?), + )) + } +} + +impl<'t> Parse<'t, nodes::If<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::If<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::If(IfExpression::parse(file, input, value)?.into_box()), + )) + } +} + +impl<'t> Parse<'t, nodes::List<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::List<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + let mut cursor = value.walk(); + + let mut list = Vec::new(); + for expression in value.expressions(&mut cursor) { + let expression = expression?; + let expression = Self::parse(file, input, expression)?; + list.push(expression); + } + + Ok(AstNode::new( + file, + &value, + Self::List(AstNode::new(file, &value, list)), + )) + } +} + +impl<'t> Parse<'t, nodes::Parenthesis<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Parenthesis<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + let expression = value.expression()?; + Ok(AstNode::new( + file, + &value, + Self::Parenthesis(Box::new(Self::parse(file, input, expression)?)), + )) + } +} + +impl<'t> Parse<'t, nodes::MemberAccess<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::MemberAccess<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::MemberAccess(Box::new(MemberAccess::parse(file, input, value)?)), + )) + } +} + +impl<'t> Parse<'t, nodes::Self_<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Self_<'t>, + ) -> Result, Error<'t, 'i>> { + Ok(AstNode::new( + file, + &value, + Self::Self_(Self_::parse(file, input, value)?), + )) + } +} + +impl<'t> Parse<'t, nodes::Scalar<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Scalar<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::Scalar(Scalar::parse(file, input, value)?), + )) + } +} + +impl<'t> Parse<'t, nodes::Vector2<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Vector2<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::Vector2(Vector2::parse(file, input, value)?.into_box()), + )) + } +} + +impl<'t> Parse<'t, nodes::Vector3<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Vector3<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::Vector3(Vector3::parse(file, input, value)?.into_box()), + )) + } +} + +impl<'t> Parse<'t, nodes::Vector4<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Vector4<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::Vector4(Vector4::parse(file, input, value)?.into_box()), + )) + } +} + +impl<'t> Parse<'t, nodes::SignedInteger<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::SignedInteger<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::SignedInteger(i64::parse(file, input, value)?), + )) + } +} + +impl<'t> Parse<'t, nodes::UnsignedInteger<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::UnsignedInteger<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::UnsignedInteger(u64::parse(file, input, value)?), + )) + } +} + +impl<'t> Parse<'t, nodes::String<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::String<'t>, + ) -> Result, Error<'t, 'i>> { + static ESCAPE_SEQUENCES: &[(&str, &str); 3] = + &[("\"", "\\\""), ("\n", "\\n"), ("\\", "\\\\")]; + unwrap_missing(&value)?; + + let raw_text = &input[value.byte_range()]; + let raw_text = &raw_text[1..raw_text.len() - 1]; + + let mut sequence_iter = ESCAPE_SEQUENCES.iter(); + let (replace, find) = sequence_iter.next().unwrap(); // Should never fail since we static initalized that array. + + let mut processed_text = raw_text.replace(find, replace); + + for (replace, find) in sequence_iter { + processed_text = processed_text.replace(find, replace); + } + + Ok(AstNode::new( + file, + &value, + Self::String(AstNode::new(file, &value, processed_text)), + )) + } +} + +impl<'t> Parse<'t, nodes::StructDefinition<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::StructDefinition<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::StructDefinition(StructDefinition::parse(file, input, value)?), + )) + } +} + +impl<'t> Parse<'t, nodes::UnaryExpression<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::UnaryExpression<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::UnaryExpression( + UnaryExpression::parse(file, input, value).map(|n| n.into_box())?, + ), + )) + } +} + +impl<'t> Parse<'t, nodes::FunctionCall<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::FunctionCall<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::FunctionCall(FunctionCall::parse(file, input, value)?.into_box()), + )) + } +} + +impl<'t> Parse<'t, nodes::MethodCall<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::MethodCall<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + + Ok(AstNode::new( + file, + &value, + Self::MethodCall(MethodCall::parse(file, input, value)?.into_box()), + )) + } +} + +impl<'t> Parse<'t, nodes::LetIn<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + node: nodes::LetIn<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&node)?; + + Ok(AstNode::new( + file, + &node, + Self::LetIn(LetIn::parse(file, input, node)?.into_box()), + )) + } +} + +impl<'t> Parse<'t, nodes::Expression<'t>> for Expression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Expression<'t>, + ) -> Result, Error<'t, 'i>> { + type ChildType<'t> = as HasChild<'t>>::Child; + + let result = match value.child()? { + ChildType::BinaryExpression(binary_expression) => { + Self::parse(file, input, binary_expression) + } + ChildType::Boolean(value) => Self::parse(file, input, value), + ChildType::ClosureDefinition(closure_definition) => { + Self::parse(file, input, closure_definition) + } + ChildType::DictionaryConstruction(dictionary_construction) => { + Self::parse(file, input, dictionary_construction) + } + ChildType::If(if_expression) => Self::parse(file, input, if_expression), + ChildType::List(list) => Self::parse(file, input, list), + ChildType::Parenthesis(parenthesis) => Self::parse(file, input, parenthesis), + ChildType::MemberAccess(path) => Self::parse(file, input, path), + ChildType::Self_(path) => Self::parse(file, input, path), + ChildType::Identifier(ident) => Ok(AstNode::new( + file, + &value, + Self::Identifier(ImString::parse(file, input, ident)?), + )), + ChildType::Scalar(scalar) => Self::parse(file, input, scalar), + ChildType::Vector2(vector) => Self::parse(file, input, vector), + ChildType::Vector3(vector) => Self::parse(file, input, vector), + ChildType::Vector4(vector) => Self::parse(file, input, vector), + ChildType::SignedInteger(signed_integer) => Self::parse(file, input, signed_integer), + ChildType::String(string) => Self::parse(file, input, string), + ChildType::StructDefinition(struct_definition) => { + Self::parse(file, input, struct_definition) + } + ChildType::UnaryExpression(unary_expression) => { + Self::parse(file, input, unary_expression) + } + ChildType::UnsignedInteger(unsigned_integer) => { + Self::parse(file, input, unsigned_integer) + } + ChildType::FunctionCall(function_call) => Self::parse(file, input, function_call), + ChildType::MethodCall(method_call) => Self::parse(file, input, method_call), + ChildType::ConstraintSet(constraint_set) => Self::parse(file, input, constraint_set), + ChildType::LetIn(let_in) => Self::parse(file, input, let_in), + }; + + match result { + Ok(expression) => Ok(expression), + Err(Error::Malformed(kind)) => Ok(AstNode::new( + file, + &value, + Self::Malformed(ImString::from(kind)), + )), + Err(error) => Err(error), + } + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub enum UnaryExpressionOperation { + Add, + Sub, + Not, +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct UnaryExpression { + pub operation: AstNode, + pub expression: AstNode, +} + +impl<'t> Parse<'t, nodes::UnaryExpression<'t>> for UnaryExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::UnaryExpression<'t>, + ) -> Result, Error<'t, 'i>> { + let operation = value.op()?; + + let operation = match operation { + nodes::anon_unions::Not_Add_Sub::Not(not) => { + AstNode::new(file, ¬, UnaryExpressionOperation::Not) + } + nodes::anon_unions::Not_Add_Sub::Add(add) => { + AstNode::new(file, &add, UnaryExpressionOperation::Add) + } + nodes::anon_unions::Not_Add_Sub::Sub(sub) => { + AstNode::new(file, &sub, UnaryExpressionOperation::Sub) + } + }; + + let expression = Expression::parse(file, input, value.expression()?)?; + + Ok(AstNode::new( + file, + &value, + Self { + operation, + expression, + }, + )) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub enum BinaryExpressionOperation { + NotEq, + And, + AndAnd, + Mul, + MulMul, + Add, + Sub, + Div, + Lt, + LtLt, + LtEq, + EqEq, + Gt, + GtEq, + GtGt, + BitXor, + Xor, + Or, + OrOr, +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct BinaryExpression { + pub operation: AstNode, + pub a: AstNode, + pub b: AstNode, +} + +impl<'t> Parse<'t, nodes::BinaryExpression<'t>> for BinaryExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::BinaryExpression<'t>, + ) -> Result, Error<'t, 'i>> { + type Operation<'t> = nodes::anon_unions::NotEq_And_AndAnd_Mul_MulMul_Add_Sub_Div_Lt_LtLt_LtEq_EqEq_Gt_GtEq_GtGt_BitXor_BitXorBitXor_Or_OrOr<'t>; + + let operation = value.op()?; + + let operation = match operation { + Operation::NotEq(not_eq) => { + AstNode::new(file, ¬_eq, BinaryExpressionOperation::NotEq) + } + Operation::And(and) => AstNode::new(file, &and, BinaryExpressionOperation::And), + Operation::AndAnd(and_and) => { + AstNode::new(file, &and_and, BinaryExpressionOperation::AndAnd) + } + Operation::Mul(mul) => AstNode::new(file, &mul, BinaryExpressionOperation::Mul), + Operation::MulMul(mul_mul) => { + AstNode::new(file, &mul_mul, BinaryExpressionOperation::MulMul) + } + Operation::Add(add) => AstNode::new(file, &add, BinaryExpressionOperation::Add), + Operation::Sub(sub) => AstNode::new(file, &sub, BinaryExpressionOperation::Sub), + Operation::Div(div) => AstNode::new(file, &div, BinaryExpressionOperation::Div), + Operation::Lt(lt) => AstNode::new(file, <, BinaryExpressionOperation::Lt), + Operation::LtLt(lt_lt) => AstNode::new(file, <_lt, BinaryExpressionOperation::LtLt), + Operation::LtEq(lt_eq) => AstNode::new(file, <_eq, BinaryExpressionOperation::LtEq), + Operation::EqEq(eq_eq) => AstNode::new(file, &eq_eq, BinaryExpressionOperation::EqEq), + Operation::Gt(gt) => AstNode::new(file, >, BinaryExpressionOperation::Gt), + Operation::GtEq(gt_eq) => AstNode::new(file, >_eq, BinaryExpressionOperation::GtEq), + Operation::GtGt(gt_gt) => AstNode::new(file, >_gt, BinaryExpressionOperation::GtGt), + Operation::BitXor(bit_xor) => { + AstNode::new(file, &bit_xor, BinaryExpressionOperation::BitXor) + } + Operation::BitXorBitXor(xor_xor) => { + AstNode::new(file, &xor_xor, BinaryExpressionOperation::Xor) + } + Operation::Or(or) => AstNode::new(file, &or, BinaryExpressionOperation::Or), + Operation::OrOr(or_or) => AstNode::new(file, &or_or, BinaryExpressionOperation::OrOr), + }; + + let a = value.a()?; + let a = Expression::parse(file, input, a)?; + + let b = value.b()?; + let b = Expression::parse(file, input, b)?; + + Ok(AstNode::new(file, &value, Self { operation, a, b })) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct MemberAccess { + pub base: AstNode, + pub member: AstNode, +} + +impl<'t> Parse<'t, nodes::MemberAccess<'t>> for MemberAccess { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::MemberAccess<'t>, + ) -> Result, Error<'t, 'i>> { + let base = Expression::parse(file, input, value.base()?)?; + let member = ImString::parse(file, input, value.member()?)?; + + Ok(AstNode::new(file, &value, Self { base, member })) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct Self_; + +impl<'t> Parse<'t, nodes::Self_<'t>> for Self_ { + fn parse<'i>( + file: &Arc, + _input: &'i str, + value: nodes::Self_<'t>, + ) -> Result, Error<'t, 'i>> { + Ok(AstNode::new(file, &value, Self)) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct Vector2 { + pub x: AstNode, + pub y: AstNode, +} + +impl<'t> Parse<'t, nodes::Vector2<'t>> for Vector2 { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Vector2<'t>, + ) -> Result, Error<'t, 'i>> { + let x = value.x()?; + let x = Expression::parse(file, input, x)?; + + let y = value.y()?; + let y = Expression::parse(file, input, y)?; + + Ok(AstNode::new(file, &value, Self { x, y })) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct Vector3 { + pub x: AstNode, + pub y: AstNode, + pub z: AstNode, +} + +impl<'t> Parse<'t, nodes::Vector3<'t>> for Vector3 { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Vector3<'t>, + ) -> Result, Error<'t, 'i>> { + let x = value.x()?; + let x = Expression::parse(file, input, x)?; + + let y = value.y()?; + let y = Expression::parse(file, input, y)?; + + let z = value.z()?; + let z = Expression::parse(file, input, z)?; + + Ok(AstNode::new(file, &value, Self { x, y, z })) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct Vector4 { + pub x: AstNode, + pub y: AstNode, + pub z: AstNode, + pub w: AstNode, +} + +impl<'t> Parse<'t, nodes::Vector4<'t>> for Vector4 { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Vector4<'t>, + ) -> Result, Error<'t, 'i>> { + let x = value.x()?; + let x = Expression::parse(file, input, x)?; + + let y = value.y()?; + let y = Expression::parse(file, input, y)?; + + let z = value.z()?; + let z = Expression::parse(file, input, z)?; + + let w = value.w()?; + let w = Expression::parse(file, input, w)?; + + Ok(AstNode::new(file, &value, Self { x, y, z, w })) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct IfExpression { + pub condition: AstNode, + pub on_true: AstNode, + pub on_false: AstNode, +} + +impl<'t> Parse<'t, nodes::If<'t>> for IfExpression { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::If<'t>, + ) -> Result, Error<'t, 'i>> { + let condition = value.condition()?; + let condition = Expression::parse(file, input, condition)?; + + let on_true = Expression::parse(file, input, value.on_true()?)?; + let on_false = Expression::parse(file, input, value.on_false()?)?; + + Ok(AstNode::new( + file, + &value, + IfExpression { + condition, + on_true, + on_false, + }, + )) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct DictionaryMemberAssignment { + pub index: usize, + pub dependencies: HashableSet, + pub name: AstNode, + pub assignment: AstNode, +} + +impl<'t> Parse<'t, nodes::DictionaryMemberAssignment<'t>> for DictionaryMemberAssignment { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::DictionaryMemberAssignment<'t>, + ) -> Result, Error<'t, 'i>> { + let name = value.name()?; + let name = ImString::parse(file, input, name)?; + let assignment = Expression::parse(file, input, value.assignment()?)?; + + let mut dependencies = HashableSet::new(); + + // Because our access collector never returns errors, this will never fail. + find_all_variable_accesses_in_expression(&assignment.node, &mut |name| { + dependencies.insert(name.node.clone()); + Ok(()) + }) + .ok(); + + Ok(AstNode::new( + file, + &value, + Self { + index: 0, + dependencies, + name, + assignment, + }, + )) + } +} + +impl DependentOperation for AstNode { + fn original_index(&self) -> usize { + self.node.index + } + + fn name(&self) -> &ImString { + &self.node.name.node + } + + fn dependencies(&self) -> &HashableSet { + &self.node.dependencies + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct DictionaryConstruction { + pub assignments: Vec>, + pub compute_groups: Vec>, +} + +impl DictionaryConstruction { + pub fn compute_groups(&self) -> impl Iterator]> { + self.compute_groups + .iter() + .map(|range| &self.assignments[range.clone()]) + } +} + +impl<'t> Parse<'t, nodes::DictionaryConstruction<'t>> for DictionaryConstruction { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::DictionaryConstruction<'t>, + ) -> Result, Error<'t, 'i>> { + let mut assignments = Vec::new(); + let mut cursor = value.walk(); + let assignments_iter = value.assignmentss(&mut cursor); + + let mut variable_names = HashSet::new(); + let mut index = 0; + + for assignment in assignments_iter { + let assignment = assignment?; + + // Skip the commas. + if let Some(assignment) = assignment.as_dictionary_member_assignment() { + let mut assignment = DictionaryMemberAssignment::parse(file, input, assignment)?; + assignment + .node + .dependencies + .retain(|name| variable_names.contains(name)); + assignment.node.index = index; + index += 1; + + variable_names.insert(assignment.node.name.node.clone()); + assignments.push(assignment); + } + } + + let compute_groups = sort_and_group_dependencies(&mut assignments); + + let node = Self { + assignments, + compute_groups, + }; + Ok(AstNode::new(file, &value, node)) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct ClosureDefinition { + pub argument_type: AstNode, + pub return_type: AstNode, + pub expression: Arc>, +} + +impl<'t> Parse<'t, nodes::ClosureDefinition<'t>> for ClosureDefinition { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::ClosureDefinition<'t>, + ) -> Result, Error<'t, 'i>> { + let argument = value.argument()?; + let argument = StructDefinition::parse(file, input, argument)?; + + let returns = value.result()?; + let returns = Expression::parse(file, input, returns)?; + + let expression = value.expression()?; + let expression = Expression::parse(file, input, expression)?; + let expression = Arc::new(AstNode { + reference: expression.reference, + node: expression.node, + }); + + Ok(AstNode::new( + file, + &value, + Self { + argument_type: argument, + return_type: returns, + expression, + }, + )) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct StructMember { + pub name: AstNode, + pub ty: AstNode, + pub default: Option>, +} + +impl<'t> Parse<'t, nodes::StructMember<'t>> for StructMember { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::StructMember<'t>, + ) -> Result, Error<'t, 'i>> { + let name = value.name()?; + let name = ImString::parse(file, input, name)?; + + let ty = value.declaration_type()?.expression()?; + let ty = Expression::parse(file, input, ty)?; + + let default = if let Some(default) = value.default() { + let default = default?; + Some(Expression::parse(file, input, default)?) + } else { + None + }; + + Ok(AstNode::new(file, &value, Self { name, ty, default })) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct StructDefinition { + pub members: Vec>, + pub variadic: bool, +} + +impl<'t> Parse<'t, nodes::StructDefinition<'t>> for StructDefinition { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::StructDefinition<'t>, + ) -> Result, Error<'t, 'i>> { + let mut cursor = value.walk(); + + let mut members = Vec::new(); + for member in value.memberss(&mut cursor) { + let member = member?; + if let nodes::anon_unions::Comma_StructMember::StructMember(member) = member { + let member = StructMember::parse(file, input, member)?; + members.push(member); + } + } + + let mut cursor = value.walk(); + + // There can only ever be one. + let final_member = value.final_elements(&mut cursor).next(); + let variadic = match final_member { + Option::Some(member) => { + let member = member?; + match member { + nodes::anon_unions::Comma_StructMember_VaradicDots::Comma(_comma) => false, + nodes::anon_unions::Comma_StructMember_VaradicDots::StructMember(member) => { + let member = StructMember::parse(file, input, member)?; + members.push(member); + false + } + nodes::anon_unions::Comma_StructMember_VaradicDots::VaradicDots(_) => true, + } + } + Option::None => false, + }; + + Ok(AstNode::new(file, &value, Self { members, variadic })) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct FunctionCall { + pub to_call: AstNode, + pub argument: AstNode, +} + +impl<'t> Parse<'t, nodes::FunctionCall<'t>> for FunctionCall { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::FunctionCall<'t>, + ) -> Result, Error<'t, 'i>> { + let to_call = Expression::parse(file, input, value.to_call()?)?; + let argument = DictionaryConstruction::parse(file, input, value.argument()?)?; + + Ok(AstNode::new(file, &value, Self { to_call, argument })) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct MethodCall { + pub self_dictionary: AstNode, + pub to_call: AstNode, + pub argument: AstNode, +} + +impl<'t> Parse<'t, nodes::MethodCall<'t>> for MethodCall { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::MethodCall<'t>, + ) -> Result, Error<'t, 'i>> { + let self_dictionary = Expression::parse(file, input, value.self_dictionary()?)?; + let to_call = ImString::parse(file, input, value.to_call()?)?; + let argument = DictionaryConstruction::parse(file, input, value.argument()?)?; + + Ok(AstNode::new( + file, + &value, + Self { + self_dictionary, + to_call, + argument, + }, + )) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct LetInAssignment { + pub index: usize, + pub ident: AstNode, + pub dependencies: HashableSet, + pub value: AstNode, +} + +impl DependentOperation for AstNode { + fn original_index(&self) -> usize { + self.node.index + } + + fn name(&self) -> &ImString { + &self.node.ident.node + } + + fn dependencies(&self) -> &HashableSet { + &self.node.dependencies + } +} + +impl<'t> Parse<'t, nodes::LetInAssignment<'t>> for LetInAssignment { + fn parse<'i>( + file: &Arc, + input: &'i str, + node: nodes::LetInAssignment<'t>, + ) -> Result, Error<'t, 'i>> { + let ident = ImString::parse(file, input, node.ident()?)?; + + let value = Expression::parse(file, input, node.value()?)?; + + let mut dependencies = HashableSet::new(); + + // Because our access collector never returns errors, this will never fail. + find_all_variable_accesses_in_expression(&value.node, &mut |name| { + dependencies.insert(name.node.clone()); + Ok(()) + }) + .ok(); + + Ok(AstNode::new( + file, + &node, + Self { + index: 0, + ident, + dependencies, + value, + }, + )) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct LetIn { + pub assignments: Vec>, + pub compute_groups: Vec>, + pub expression: AstNode, +} + +impl LetIn { + pub fn compute_groups(&self) -> impl Iterator]> { + self.compute_groups + .iter() + .map(|range| &self.assignments[range.clone()]) + } +} + +impl<'t> Parse<'t, nodes::LetIn<'t>> for LetIn { + fn parse<'i>( + file: &Arc, + input: &'i str, + node: nodes::LetIn<'t>, + ) -> Result, Error<'t, 'i>> { + let mut cursor = node.walk(); + let mut assignments = Vec::new(); + + let mut variable_names = HashSet::new(); + + for (index, assignment) in node.assignments(&mut cursor).enumerate() { + let assignment = assignment?; + let mut assignment = LetInAssignment::parse(file, input, assignment)?; + // Restrict all dependencies to names within our dependency set, that we already know + // of. + assignment + .node + .dependencies + .retain(|name| variable_names.contains(name)); + assignment.node.index = index; + + variable_names.insert(assignment.node.ident.node.clone()); + assignments.push(assignment); + } + + let compute_groups = sort_and_group_dependencies(&mut assignments); + + let expression = Expression::parse(file, input, node.expression()?)?; + + Ok(AstNode::new( + file, + &node, + Self { + assignments, + compute_groups, + expression, + }, + )) + } +} + +pub type Parser = type_sitter::Parser>; +pub fn new_parser() -> Parser { + type_sitter::Parser::new(&tree_sitter_command_cad_model::language()) + .expect("Error loading CommandCadModel grammar") +} + +#[cfg(test)] +mod test { + use crate::compile::full_compile; + use common_data_types::{Dimension, Float}; + use pretty_assertions::assert_eq; + + use super::*; + + #[test] + fn binary_expression() { + let root = full_compile("2i + 3i"); + + let binary_expression = root.node.as_binaryexpression().unwrap(); + let operation_reference = binary_expression.node.operation.reference.clone(); + + let a = &binary_expression.node.a; + let a_reference = a.reference.clone(); + + let b = &binary_expression.node.b; + let b_reference = b.reference.clone(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::BinaryExpression(AstNode { + reference: binary_expression.reference.clone(), + node: Box::new(BinaryExpression { + operation: AstNode { + reference: operation_reference, + node: BinaryExpressionOperation::Add + }, + a: AstNode { + reference: a_reference, + node: Expression::SignedInteger(AstNode { + reference: a.node.as_signedinteger().unwrap().reference.clone(), + node: 2 + },) + }, + b: AstNode { + reference: b_reference, + node: Expression::SignedInteger(AstNode { + reference: b.node.as_signedinteger().unwrap().reference.clone(), + node: 3 + }) + } + }) + }) + } + ); + } + + #[test] + fn boolean_true() { + let root = full_compile("true"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Boolean(AstNode { + reference: root.node.as_boolean().unwrap().reference.clone(), + node: true + }) + } + ); + } + + #[test] + fn boolean_false() { + let root = full_compile("false"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Boolean(AstNode { + reference: root.node.as_boolean().unwrap().reference.clone(), + node: false + }) + } + ); + } + + #[test] + fn closure_definition() { + let root = full_compile("() -> thing: \"\""); + let closure = root.node.as_closuredefinition().unwrap(); + let closure_reference = closure.reference.clone(); + let argument = &closure.node.argument_type; + let argument_reference = argument.reference.clone(); + + let returns = &closure.node.return_type; + let returns_reference = returns.reference.clone(); + let returns_path = returns.node.as_identifier().unwrap(); + let returns_path_reference = returns_path.reference.clone(); + + let expression = &closure.node.expression; + let expression_reference = expression.reference.clone(); + let string_reference = expression.node.as_string().unwrap().reference.clone(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::ClosureDefinition(AstNode { + reference: closure_reference, + node: Box::new(ClosureDefinition { + argument_type: AstNode { + reference: argument_reference, + node: StructDefinition { + members: vec![], + variadic: false + } + }, + return_type: AstNode { + reference: returns_reference, + node: Expression::Identifier(AstNode { + reference: returns_path_reference, + node: ImString::from("thing") + }) + }, + expression: Arc::new(AstNode { + reference: expression_reference, + node: Expression::String(AstNode { + reference: string_reference, + node: String::new() + }) + }) + }) + }) + } + ); + } + + #[test] + fn dictionary_construction() { + let root = full_compile("(a = true, b = false)"); + let construction_expression = root.node.as_dictionaryconstruction().unwrap(); + assert_eq!(construction_expression.node.assignments.len(), 2); + let a = &construction_expression.node.assignments[0]; + let b = &construction_expression.node.assignments[1]; + + assert_eq!( + a.node.name, + AstNode { + reference: a.node.name.reference.clone(), + node: "a".into() + } + ); + assert_eq!( + a.node.assignment, + AstNode { + reference: a.node.assignment.reference.clone(), + node: Expression::Boolean(AstNode { + reference: a.node.assignment.reference.clone(), + node: true + }) + } + ); + + assert_eq!( + b.node.name, + AstNode { + reference: b.node.name.reference.clone(), + node: "b".into() + } + ); + assert_eq!( + b.node.assignment, + AstNode { + reference: b.node.assignment.reference.clone(), + node: Expression::Boolean(AstNode { + reference: b.node.assignment.reference.clone(), + node: false + }) + } + ); + } + + #[test] + fn if_else_expression() { + let root = full_compile("if true then \"true\" else \"false\""); + let if_expression = root.node.as_if().unwrap(); + let if_reference = if_expression.reference.clone(); + let condition = &if_expression.node.condition; + let condition_reference = condition.reference.clone(); + let boolean_reference = condition.node.as_boolean().unwrap().reference.clone(); + let true_expression_reference = if_expression.node.on_true.reference.clone(); + let true_string_reference = if_expression + .node + .on_true + .node + .as_string() + .unwrap() + .reference + .clone(); + let false_expression_reference = if_expression.node.on_false.reference.clone(); + let false_string_reference = if_expression + .node + .on_false + .node + .as_string() + .unwrap() + .reference + .clone(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::If(AstNode { + reference: if_reference, + node: Box::new(IfExpression { + condition: AstNode { + reference: condition_reference, + node: Expression::Boolean(AstNode { + reference: boolean_reference, + node: true + }) + }, + on_true: AstNode { + reference: true_expression_reference, + node: Expression::String(AstNode { + reference: true_string_reference, + node: String::from("true") + }) + }, + on_false: AstNode { + reference: false_expression_reference, + node: Expression::String(AstNode { + reference: false_string_reference, + node: String::from("false") + }) + }, + }) + }) + } + ); + } + + #[test] + fn list() { + let root = full_compile("[1i, 2i, 3i, 4i, 5i]"); + + let list = root.node.as_list().unwrap(); + let list_reference = list.reference.clone(); + let element_1_reference = list.node[0].reference.clone(); + let integer_1_reference = list.node[0] + .node + .as_signedinteger() + .unwrap() + .reference + .clone(); + let element_2_reference = list.node[1].reference.clone(); + let integer_2_reference = list.node[1] + .node + .as_signedinteger() + .unwrap() + .reference + .clone(); + let element_3_reference = list.node[2].reference.clone(); + let integer_3_reference = list.node[2] + .node + .as_signedinteger() + .unwrap() + .reference + .clone(); + let element_4_reference = list.node[3].reference.clone(); + let integer_4_reference = list.node[3] + .node + .as_signedinteger() + .unwrap() + .reference + .clone(); + let element_5_reference = list.node[4].reference.clone(); + let integer_5_reference = list.node[4] + .node + .as_signedinteger() + .unwrap() + .reference + .clone(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::List(AstNode { + reference: list_reference, + node: vec![ + AstNode { + reference: element_1_reference, + node: Expression::SignedInteger(AstNode { + reference: integer_1_reference, + node: 1, + }) + }, + AstNode { + reference: element_2_reference, + node: Expression::SignedInteger(AstNode { + reference: integer_2_reference, + node: 2, + }) + }, + AstNode { + reference: element_3_reference, + node: Expression::SignedInteger(AstNode { + reference: integer_3_reference, + node: 3, + }) + }, + AstNode { + reference: element_4_reference, + node: Expression::SignedInteger(AstNode { + reference: integer_4_reference, + node: 4, + }) + }, + AstNode { + reference: element_5_reference, + node: Expression::SignedInteger(AstNode { + reference: integer_5_reference, + node: 5, + }) + }, + ] + }) + } + ); + } + + #[test] + fn parenthesis() { + let root = full_compile("(5i)"); + let parenthesis = root.node.as_parenthesis().unwrap(); + let parenthesis_reference = parenthesis.reference.clone(); + let integer = parenthesis.node.as_signedinteger().unwrap(); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Parenthesis(Box::new(AstNode { + reference: parenthesis_reference, + node: Expression::SignedInteger(AstNode { + reference: integer.reference.clone(), + node: 5 + }) + })) + } + ); + } + + #[test] + fn member_access() { + let root = full_compile("this.thang"); + let path = root.node.as_memberaccess().unwrap(); + let base = &path.node.base; + let this = base.node.as_identifier().unwrap(); + let member = &path.node.member; + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::MemberAccess(Box::new(AstNode { + reference: path.reference.clone(), + node: MemberAccess { + base: AstNode { + reference: base.reference.clone(), + node: Expression::Identifier(AstNode { + reference: this.reference.clone(), + node: ImString::from("this") + }) + }, + member: AstNode { + reference: member.reference.clone(), + node: ImString::from("thang") + } + } + })) + } + ); + } + + #[test] + fn self_path() { + let root = full_compile("self.this.thang"); + let thang = root.node.as_memberaccess().unwrap(); + let this_expr = &thang.node.base; + let this = this_expr.node.as_memberaccess().unwrap(); + let self_expr = &this.node.base; + let self_ = self_expr.node.as_self_().unwrap(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::MemberAccess(Box::new(AstNode { + reference: thang.reference.clone(), + node: MemberAccess { + base: AstNode { + reference: this.reference.clone(), + node: Expression::MemberAccess(Box::new(AstNode { + reference: this.reference.clone(), + node: MemberAccess { + base: AstNode { + reference: self_expr.reference.clone(), + node: Expression::Self_(AstNode { + reference: self_.reference.clone(), + node: Self_ + }) + }, + member: AstNode { + reference: this.node.member.reference.clone(), + node: ImString::from("this") + } + } + })) + }, + member: AstNode { + reference: thang.node.member.reference.clone(), + node: ImString::from("thang") + } + } + })) + } + ); + } + + #[test] + fn scalar_no_decimal() { + let root = full_compile("0"); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Scalar(AstNode { + reference: root.node.as_scalar().unwrap().reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(0.0).unwrap() + } + }) + } + ); + } + + #[test] + fn scalar_no_unit() { + let root = full_compile("0.0"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Scalar(AstNode { + reference: root.node.as_scalar().unwrap().reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(0.0).unwrap() + } + }) + } + ); + } + + #[test] + fn scalar_with_unit() { + let root = full_compile("0.0mm"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Scalar(AstNode { + reference: root.node.as_scalar().unwrap().reference.clone(), + node: Scalar { + dimension: Dimension::length(), + value: Float::new(0.0).unwrap() + } + }) + } + ); + } + + #[test] + fn scalar_unit_conversion() { + // Test conversion factor + let root = full_compile("1cm"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Scalar(AstNode { + reference: root.node.as_scalar().unwrap().reference.clone(), + node: Scalar { + dimension: Dimension::length(), + value: Float::new(0.01).unwrap() + } + }) + } + ); + } + + #[test] + fn scalar_quoted_unit() { + // Test conversion factor + let root = full_compile("1'm^2'"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Scalar(AstNode { + reference: root.node.as_scalar().unwrap().reference.clone(), + node: Scalar { + dimension: Dimension::area(), + value: Float::new(1.0).unwrap() + } + }) + } + ); + } + + #[test] + fn signed_integer() { + let root = full_compile("5i"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::SignedInteger(AstNode { + reference: root.node.as_signedinteger().unwrap().reference.clone(), + node: 5 + }) + } + ); + } + + #[test] + fn signed_integer_hex() { + let root = full_compile("0x5i"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::SignedInteger(AstNode { + reference: root.node.as_signedinteger().unwrap().reference.clone(), + node: 0x5 + }) + } + ); + } + + #[test] + fn signed_integer_octal() { + let root = full_compile("0o5i"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::SignedInteger(AstNode { + reference: root.node.as_signedinteger().unwrap().reference.clone(), + node: 0o5 + }) + } + ); + } + + #[test] + fn signed_integer_binary() { + let root = full_compile("0b1010i"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::SignedInteger(AstNode { + reference: root.node.as_signedinteger().unwrap().reference.clone(), + node: 0b1010 + }) + } + ); + } + + #[test] + fn string() { + let root = full_compile("\"Some text\\n\""); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::String(AstNode { + reference: root.node.as_string().unwrap().reference.clone(), + node: "Some text\n".into() + }) + } + ); + } + + #[test] + fn struct_definition() { + let root = full_compile("( one: One, two: Two = a, ... )"); + let struct_definition = root.node.as_structdefinition().unwrap(); + let members = &struct_definition.node.members; + let one = &members[0]; + let one_ty = one.node.ty.node.as_identifier().unwrap(); + let two = &members[1]; + let two_ty = two.node.ty.node.as_identifier().unwrap(); + let two_default = two.node.default.as_ref().unwrap(); + let two_default_path = two_default.node.as_identifier().unwrap(); + + assert!(struct_definition.node.variadic); + assert_eq!(members.len(), 2); + + assert_eq!( + *one, + AstNode { + reference: one.reference.clone(), + node: StructMember { + name: AstNode { + reference: one.node.name.reference.clone(), + node: "one".into() + }, + ty: AstNode { + reference: one.node.ty.reference.clone(), + node: Expression::Identifier(AstNode { + reference: one_ty.reference.clone(), + node: "One".into() + }) + }, + default: None + } + } + ); + assert_eq!( + *two, + AstNode { + reference: two.reference.clone(), + node: StructMember { + name: AstNode { + reference: two.node.name.reference.clone(), + node: "two".into() + }, + ty: AstNode { + reference: two.node.ty.reference.clone(), + node: Expression::Identifier(AstNode { + reference: two_ty.reference.clone(), + node: "Two".into() + }) + }, + default: Some(AstNode { + reference: two_default.reference.clone(), + node: Expression::Identifier(AstNode { + reference: two_default_path.reference.clone(), + node: "a".into() + }) + }) + } + } + ); + } + + #[test] + fn unary_expression() { + let root = full_compile("-5i"); + let unary_expression = root.node.as_unaryexpression().unwrap(); + let unary_expression_reference = unary_expression.reference.clone(); + let expression_reference = unary_expression.node.expression.reference.clone(); + let integer_reference = unary_expression + .node + .expression + .node + .as_signedinteger() + .unwrap() + .reference + .clone(); + let operation_reference = unary_expression.node.operation.reference.clone(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::UnaryExpression(AstNode { + reference: unary_expression_reference, + node: Box::new(UnaryExpression { + expression: AstNode { + reference: expression_reference, + node: Expression::SignedInteger(AstNode { + reference: integer_reference, + node: 5 + }) + }, + operation: AstNode { + reference: operation_reference, + node: UnaryExpressionOperation::Sub + } + }) + }) + } + ); + } + + #[test] + fn unsigned_integer() { + let root = full_compile("5u"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::UnsignedInteger(AstNode { + reference: root.node.as_unsignedinteger().unwrap().reference.clone(), + node: 5 + }) + } + ); + } + + #[test] + fn unsigned_integer_hex() { + let root = full_compile("0x5u"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::UnsignedInteger(AstNode { + reference: root.node.as_unsignedinteger().unwrap().reference.clone(), + node: 0x5 + }) + } + ); + } + + #[test] + fn unsigned_integer_octal() { + let root = full_compile("0o5u"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::UnsignedInteger(AstNode { + reference: root.node.as_unsignedinteger().unwrap().reference.clone(), + node: 0o5 + }) + } + ); + } + + #[test] + fn unsigned_integer_binary() { + let root = full_compile("0b1010u"); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::UnsignedInteger(AstNode { + reference: root.node.as_unsignedinteger().unwrap().reference.clone(), + node: 0b1010 + }) + } + ); + } + + #[test] + fn function_call_no_arguments() { + let root = full_compile("a()"); + let call = root.node.as_functioncall().unwrap(); + let to_call = call.node.to_call.node.as_identifier().unwrap(); + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::FunctionCall(AstNode { + reference: call.reference.clone(), + node: Box::new(FunctionCall { + to_call: AstNode { + reference: call.node.to_call.reference.clone(), + node: Expression::Identifier(AstNode { + reference: to_call.reference.clone(), + node: "a".into() + }) + }, + argument: AstNode { + reference: call.node.argument.reference.clone(), + node: DictionaryConstruction { + assignments: vec![], + compute_groups: vec![] + } + } + }) + }) + } + ); + } + + #[test] + fn function_call_with_arguments() { + let root = full_compile("a(value = 24u)"); + let call = root.node.as_functioncall().unwrap(); + let to_call = call.node.to_call.node.as_identifier().unwrap(); + let dict = &call.node.argument.node; + let dict_assignment = &dict.assignments[0]; + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::FunctionCall(AstNode { + reference: call.reference.clone(), + node: Box::new(FunctionCall { + to_call: AstNode { + reference: call.node.to_call.reference.clone(), + node: Expression::Identifier(AstNode { + reference: to_call.reference.clone(), + node: "a".into() + }) + }, + argument: AstNode { + reference: call.node.argument.reference.clone(), + node: DictionaryConstruction { + assignments: vec![AstNode { + reference: dict_assignment.reference.clone(), + node: DictionaryMemberAssignment { + index: 0, + name: AstNode { + reference: dict_assignment.node.name.reference.clone(), + node: "value".into() + }, + dependencies: HashableSet::from(HashSet::from_iter([])), + assignment: AstNode { + reference: dict_assignment + .node + .assignment + .reference + .clone(), + node: Expression::UnsignedInteger(AstNode { + reference: dict_assignment + .node + .assignment + .node + .as_unsignedinteger() + .unwrap() + .reference + .clone(), + node: 24u64 + }) + } + } + }], + compute_groups: vec![0..1] + } + } + }) + }) + } + ); + } + + #[test] + fn method_call_no_arguments() { + let root = full_compile("5u::c()"); + let call = root.node.as_methodcall().unwrap(); + let to_call = &call.node.to_call; + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::MethodCall(AstNode { + reference: call.reference.clone(), + node: Box::new(MethodCall { + self_dictionary: AstNode { + reference: call.node.self_dictionary.reference.clone(), + node: Expression::UnsignedInteger(AstNode { + reference: call + .node + .self_dictionary + .node + .as_unsignedinteger() + .unwrap() + .reference + .clone(), + node: 5u64 + }) + }, + to_call: AstNode { + reference: to_call.reference.clone(), + node: "c".into() + }, + argument: AstNode { + reference: call.node.argument.reference.clone(), + node: DictionaryConstruction { + assignments: vec![], + compute_groups: vec![], + } + } + }) + }) + } + ); + } + + #[test] + fn method_call_with_arguments() { + let root = full_compile("83u::c(value = 95u)"); + let call = root.node.as_methodcall().unwrap(); + let to_call = &call.node.to_call; + let dict = &call.node.argument.node; + let dict_assignment = &dict.assignments[0]; + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::MethodCall(AstNode { + reference: call.reference.clone(), + node: Box::new(MethodCall { + self_dictionary: AstNode { + reference: call.node.self_dictionary.reference.clone(), + node: Expression::UnsignedInteger(AstNode { + reference: call + .node + .self_dictionary + .node + .as_unsignedinteger() + .unwrap() + .reference + .clone(), + node: 83u64 + }) + }, + to_call: AstNode { + reference: to_call.reference.clone(), + node: "c".into() + }, + argument: AstNode { + reference: call.node.argument.reference.clone(), + node: DictionaryConstruction { + assignments: vec![AstNode { + reference: dict_assignment.reference.clone(), + node: DictionaryMemberAssignment { + index: 0, + dependencies: HashableSet::from(HashSet::from_iter([])), + name: AstNode { + reference: dict_assignment.node.name.reference.clone(), + node: "value".into() + }, + assignment: AstNode { + reference: dict_assignment + .node + .assignment + .reference + .clone(), + node: Expression::UnsignedInteger(AstNode { + reference: dict_assignment + .node + .assignment + .node + .as_unsignedinteger() + .unwrap() + .reference + .clone(), + node: 95u64 + }) + } + } + }], + compute_groups: vec![0..1] + } + } + }) + }) + } + ); + } + + #[test] + fn let_in() { + let root = full_compile("let value1 = 1u; value2 = 2u; in 3u"); + + let let_in = root.node.as_letin().unwrap(); + + let value1 = &let_in.node.assignments[0]; + let value1_ident = &value1.node.ident; + let value1_value = &value1.node.value; + let value1_value_uint = &value1.node.value.node.as_unsignedinteger().unwrap(); + + let value2 = &let_in.node.assignments[1]; + let value2_ident = &value2.node.ident; + let value2_value = &value2.node.value; + let value2_value_uint = &value2.node.value.node.as_unsignedinteger().unwrap(); + + let expression = &let_in.node.expression; + let expression_uint = &let_in.node.expression.node.as_unsignedinteger().unwrap(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::LetIn( + AstNode { + reference: let_in.reference.clone(), + node: LetIn { + assignments: vec![ + AstNode { + reference: value1.reference.clone(), + node: LetInAssignment { + ident: AstNode { + reference: value1_ident.reference.clone(), + node: "value1".into(), + }, + index: 0, + dependencies: HashableSet::new(), + value: AstNode { + reference: value1_value.reference.clone(), + node: Expression::UnsignedInteger(AstNode { + reference: value1_value_uint.reference.clone(), + node: 1 + }), + } + } + }, + AstNode { + reference: value2.reference.clone(), + node: LetInAssignment { + ident: AstNode { + reference: value2_ident.reference.clone(), + node: "value2".into(), + }, + index: 1, + dependencies: HashableSet::new(), + value: AstNode { + reference: value2_value.reference.clone(), + node: Expression::UnsignedInteger(AstNode { + reference: value2_value_uint.reference.clone(), + node: 2 + }), + } + } + } + ], + compute_groups: vec![0..2], + expression: AstNode { + reference: expression.reference.clone(), + node: Expression::UnsignedInteger(AstNode { + reference: expression_uint.reference.clone(), + node: 3 + }) + } + } + } + .into_box() + ) + } + ); + } + + #[derive(Debug, PartialEq, Eq)] + struct TestDependency { + name: ImString, + index: usize, + dependencies: HashableSet, + } + + impl TestDependency { + fn new( + index: usize, + name: impl Into, + dependencies: impl IntoIterator, + ) -> Self { + Self { + index, + name: name.into(), + dependencies: HashableSet::from(HashSet::from_iter( + dependencies.into_iter().map(ImString::from), + )), + } + } + } + + impl DependentOperation for TestDependency { + fn original_index(&self) -> usize { + self.index + } + + fn name(&self) -> &ImString { + &self.name + } + + fn dependencies(&self) -> &HashableSet { + &self.dependencies + } + } + + #[test] + fn let_in_ordering() { + let mut dependencies = vec![ + TestDependency::new(0, "a", []), + TestDependency::new(1, "c", ["a"]), + TestDependency::new(2, "d", ["c"]), + TestDependency::new(3, "b", []), + ]; + + let groups = sort_and_group_dependencies(&mut dependencies); + + assert_eq!( + dependencies, + vec![ + TestDependency::new(0, "a", []), + TestDependency::new(3, "b", []), + TestDependency::new(1, "c", ["a"]), + TestDependency::new(2, "d", ["c"]), + ] + ); + + assert_eq!(groups, vec![0..2, 2..3, 3..4]); + } + + #[test] + fn let_in_broken_ordering() { + // This would be a case of user error, but we need to keep the user's mistakes in the new + // ordering to make the error messages make sense. + let mut dependencies = vec![ + TestDependency::new(0, "a", []), + TestDependency::new(1, "d", ["c"]), + TestDependency::new(2, "c", ["a"]), + TestDependency::new(3, "b", []), + ]; + + let groups = sort_and_group_dependencies(&mut dependencies); + + assert_eq!( + dependencies, + vec![ + TestDependency::new(0, "a", []), + TestDependency::new(3, "b", []), + TestDependency::new(1, "d", ["c"]), + TestDependency::new(2, "c", ["a"]), + ] + ); + + assert_eq!(groups, vec![0..2, 2..3, 3..4]); + } + + #[test] + fn let_in_circular_dependency() { + let mut dependencies = vec![ + TestDependency::new(0, "a", ["b"]), + TestDependency::new(1, "b", ["a"]), + ]; + + let groups = sort_and_group_dependencies(&mut dependencies); + + assert_eq!( + dependencies, + vec![ + TestDependency::new(0, "a", ["b"]), + TestDependency::new(1, "b", ["a"]), + ] + ); + + assert_eq!(groups, vec![0..1, 1..2]); + } + + #[test] + fn vector2() { + let root = full_compile("<(1.0, 2.0)>"); + let vector = root.node.as_vector2().unwrap(); + let x = &vector.node.x; + let x_scalar = x.node.as_scalar().unwrap(); + let y = &vector.node.y; + let y_scalar = y.node.as_scalar().unwrap(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Vector2(AstNode { + reference: vector.reference.clone(), + node: Box::new(Vector2 { + x: AstNode { + reference: x.reference.clone(), + node: Expression::Scalar(AstNode { + reference: x_scalar.reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(1.0).unwrap() + } + }) + }, + y: AstNode { + reference: y.reference.clone(), + node: Expression::Scalar(AstNode { + reference: y_scalar.reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(2.0).unwrap() + } + }) + } + }) + }) + } + ); + } + + #[test] + fn vector3() { + let root = full_compile("<(1.0, 2.0, 3.0)>"); + let vector = root.node.as_vector3().unwrap(); + let x = &vector.node.x; + let x_scalar = x.node.as_scalar().unwrap(); + let y = &vector.node.y; + let y_scalar = y.node.as_scalar().unwrap(); + let z = &vector.node.z; + let z_scalar = z.node.as_scalar().unwrap(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Vector3(AstNode { + reference: vector.reference.clone(), + node: Box::new(Vector3 { + x: AstNode { + reference: x.reference.clone(), + node: Expression::Scalar(AstNode { + reference: x_scalar.reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(1.0).unwrap() + } + }) + }, + y: AstNode { + reference: y.reference.clone(), + node: Expression::Scalar(AstNode { + reference: y_scalar.reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(2.0).unwrap() + } + }) + }, + z: AstNode { + reference: z.reference.clone(), + node: Expression::Scalar(AstNode { + reference: z_scalar.reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(3.0).unwrap() + } + }) + } + }) + }) + } + ); + } + + #[test] + fn vector4() { + let root = full_compile("<(1.0, 2.0, 3.0, 4.0)>"); + let vector = root.node.as_vector4().unwrap(); + let x = &vector.node.x; + let x_scalar = x.node.as_scalar().unwrap(); + let y = &vector.node.y; + let y_scalar = y.node.as_scalar().unwrap(); + let z = &vector.node.z; + let z_scalar = z.node.as_scalar().unwrap(); + let w = &vector.node.w; + let w_scalar = w.node.as_scalar().unwrap(); + + assert_eq!( + root, + AstNode { + reference: root.reference.clone(), + node: Expression::Vector4(AstNode { + reference: vector.reference.clone(), + node: Box::new(Vector4 { + x: AstNode { + reference: x.reference.clone(), + node: Expression::Scalar(AstNode { + reference: x_scalar.reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(1.0).unwrap() + } + }) + }, + y: AstNode { + reference: y.reference.clone(), + node: Expression::Scalar(AstNode { + reference: y_scalar.reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(2.0).unwrap() + } + }) + }, + z: AstNode { + reference: z.reference.clone(), + node: Expression::Scalar(AstNode { + reference: z_scalar.reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(3.0).unwrap() + } + }) + }, + w: AstNode { + reference: w.reference.clone(), + node: Expression::Scalar(AstNode { + reference: w_scalar.reference.clone(), + node: Scalar { + dimension: Dimension::zero(), + value: Float::new(4.0).unwrap() + } + }) + } + }) + }) + } + ); + } +} diff --git a/interpreter/src/compile/mod.rs b/interpreter/src/compile/mod.rs new file mode 100644 index 0000000..2f03781 --- /dev/null +++ b/interpreter/src/compile/mod.rs @@ -0,0 +1,394 @@ +pub mod constraint_set; +mod expressions; + +use ariadne::Span; +use common_data_types::{ConversionFactor, Dimension, Float, RawFloat}; +use imstr::ImString; +use std::{path::PathBuf, sync::Arc}; +use tree_sitter::Range; +use type_sitter::{HasChild, IncorrectKind, Node, UntypedNode}; + +pub use expressions::*; + +// This thing is packed with generated clippy violations. +#[allow(clippy::all)] +pub mod nodes { + include!(concat!(env!("OUT_DIR"), "/nodes.rs")); +} + +#[derive(Debug, Hash, Eq, PartialEq, Clone)] +pub struct SourceReference { + pub file: Arc, + pub range: Range, +} + +impl Span for SourceReference { + type SourceId = Arc; + + fn source(&self) -> &Self::SourceId { + &self.file + } + + fn start(&self) -> usize { + self.range.start_byte + } + + fn end(&self) -> usize { + self.range.end_byte + } +} + +#[derive(Debug, Eq, PartialEq)] +pub struct AstNode { + pub reference: SourceReference, + pub node: N, +} + +impl AstNode { + fn new<'n>(file: &Arc, value: &impl Node<'n>, node: N) -> Self { + Self { + reference: SourceReference { + file: file.clone(), + range: value.range(), + }, + node, + } + } + + fn into_box(self) -> AstNode> { + AstNode { + reference: self.reference, + node: Box::new(self.node), + } + } + + fn into_arc(self) -> AstNode> { + AstNode { + reference: self.reference, + node: Arc::new(self.node), + } + } +} + +impl std::hash::Hash for AstNode +where + N: std::hash::Hash, +{ + fn hash(&self, hash: &mut H) + where + H: std::hash::Hasher, + { + // We only hash the node, not the location of it. + self.node.hash(hash) + } +} + +fn unwrap_missing<'t, 'i, N: Node<'t> + 't>(value: &N) -> Result<(), Error<'t, 'i>> { + if !value.has_error() { + Ok(()) + } else { + Err(Error::Malformed(value.raw().kind())) + } +} + +trait Parse<'t, N: 't>: Sized { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: N, + ) -> Result, Error<'t, 'i>>; +} + +impl<'t, N: Node<'t> + 't> Parse<'t, N> for ImString { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: N, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + let text = &input[value.byte_range()]; + Ok(AstNode::new(file, &value, ImString::from(text.to_string()))) + } +} + +impl<'t> Parse<'t, nodes::Boolean<'t>> for bool { + fn parse<'i>( + file: &Arc, + _input: &'i str, + value: nodes::Boolean<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + type ChildType<'t> = as HasChild<'t>>::Child; + let child = value.child()?; + + Ok(AstNode::new( + file, + &child, + matches!(child, ChildType::True(_)), + )) + } +} + +impl<'t> Parse<'t, nodes::SignedInteger<'t>> for i64 { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::SignedInteger<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + let number = value.value()?; + let integer = match number.child()? { + nodes::anon_unions::BaseTen_Binary_Hex_Octal::Hex(value) => { + let text = &input[value.byte_range()][2..]; + i64::from_str_radix(text, 16) + } + nodes::anon_unions::BaseTen_Binary_Hex_Octal::BaseTen(value) => { + let text = &input[value.byte_range()]; + text.parse::() + } + nodes::anon_unions::BaseTen_Binary_Hex_Octal::Octal(value) => { + let text = &input[value.byte_range()][2..]; + i64::from_str_radix(text, 8) + } + nodes::anon_unions::BaseTen_Binary_Hex_Octal::Binary(value) => { + let text = &input[value.byte_range()][2..]; + i64::from_str_radix(text, 2) + } + } + .map_err(|error| ParseIntError { + error, + node: number, + })?; + + Ok(AstNode::new(file, &value, integer)) + } +} + +impl<'t> Parse<'t, nodes::UnsignedInteger<'t>> for u64 { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::UnsignedInteger<'t>, + ) -> Result, Error<'t, 'i>> { + unwrap_missing(&value)?; + let number = value.value()?; + let integer = match number.child()? { + nodes::anon_unions::BaseTen_Binary_Hex_Octal::Hex(value) => { + let text = &input[value.byte_range()][2..]; + u64::from_str_radix(text, 16) + } + nodes::anon_unions::BaseTen_Binary_Hex_Octal::BaseTen(value) => { + let text = &input[value.byte_range()]; + text.parse::() + } + nodes::anon_unions::BaseTen_Binary_Hex_Octal::Octal(value) => { + let text = &input[value.byte_range()][2..]; + u64::from_str_radix(text, 8) + } + nodes::anon_unions::BaseTen_Binary_Hex_Octal::Binary(value) => { + let text = &input[value.byte_range()][2..]; + u64::from_str_radix(text, 2) + } + } + .map_err(|error| ParseIntError { + error, + node: number, + })?; + + Ok(AstNode::new(file, &value, integer)) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct Scalar { + pub dimension: Dimension, + pub value: Float, +} + +impl<'t> Parse<'t, nodes::Scalar<'t>> for Scalar { + fn parse<'i>( + file: &Arc, + input: &'i str, + value: nodes::Scalar<'t>, + ) -> Result, Error<'t, 'i>> { + const IDENTITY_CONVERSION_FACTOR: ConversionFactor = ConversionFactor { + constant: 0.0, + coefficient: 1.0, + dimension: Dimension::zero(), + }; + + unwrap_missing(&value)?; + + // Get the conversion factor using the unit name. + let conversion_factor = if let Some(unit_name) = value.unit() { + let unit_name = unit_name?; + + let unit_name_str = match unit_name { + nodes::anon_unions::Identifier_UnitQuote::Identifier(_identifier) => { + &input[unit_name.byte_range()] + } + nodes::anon_unions::Identifier_UnitQuote::UnitQuote(_unit_quote) => { + let original = &input[unit_name.byte_range()]; + &original[1..original.len() - 1] + } + }; + + if let Some(conversion_factor) = units::get_conversion_factor(unit_name_str) { + conversion_factor + } else { + // TODO try and list similar names to what they user may have meant. + // We don't know what this is. + return Err(Error::InvalidUnit(InvalidUnitError { + name: unit_name_str, + node: unit_name, + })); + } + } else { + // If a unit is not specified, we assume the zero dimension and no conversion. + &IDENTITY_CONVERSION_FACTOR + }; + + let whole_node = value.whole()?; + let whole = &input[whole_node.byte_range()]; + let whole: u64 = whole.parse().map_err(|error| ParseNumberError { + error, + node: whole_node, + })?; + + let (fraction, fraction_len): (u64, _) = if let Some(fractional_node) = value.fractional() { + let fraction_str = &input[fractional_node.byte_range()]; + let fraction = fraction_str.parse().map_err(|error| ParseNumberError { + error, + node: whole_node, + })?; + + (fraction, fraction_str.len()) + } else { + // Faction is not present. We assume zero. + (0, 1) + }; + + let scale_value: f64 = + whole as RawFloat + (fraction as RawFloat / (10.0 as RawFloat).powi(fraction_len as _)); + let scale_value = Float::new(scale_value).expect("Float was NaN"); + let scale_value = conversion_factor.convert_to_base_unit(scale_value); + + let dimension = conversion_factor.dimension; + + Ok(AstNode::new( + file, + &value, + Self { + dimension, + value: scale_value, + }, + )) + } +} + +/// Represents an error while compiling. +/// I would have preferred to use the `thiserror` crate, but +/// the non static lifetime of IncorrectKind ruined any opportunity of that. +#[derive(Debug, Eq, PartialEq)] +pub enum Error<'t, 'i> { + IncorrectKind(IncorrectKind<'t>), + InvalidUnit(InvalidUnitError<'t, 'i>), + ParseInt(ParseIntError<'t>), + ParseNumber(ParseNumberError<'t>), + Malformed(&'static str), +} + +impl<'t> std::error::Error for Error<'t, '_> {} + +impl<'t> std::fmt::Display for Error<'t, '_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::IncorrectKind(incorrect_kind) => { + write!( + f, + "incorrect node type, got {}, expected {}", + incorrect_kind.node.kind(), + incorrect_kind.kind + ) + } + Error::InvalidUnit(invalid_unit_error) => { + write!(f, "invalid scalar unit: {}", invalid_unit_error.name) + } + Error::ParseInt(parse_int_error) => { + write!(f, "failed to parse integer: {}", parse_int_error.error) + } + Error::ParseNumber(parse_number_error) => { + write!(f, "failed to parse scalar: {}", parse_number_error.error) + } + Error::Malformed(kind) => { + write!(f, "Missing `{kind}`") + } + } + } +} + +impl<'t> From> for Error<'t, '_> { + fn from(value: IncorrectKind<'t>) -> Self { + Self::IncorrectKind(value) + } +} + +impl<'t> From> for Error<'t, '_> { + fn from(value: ParseIntError<'t>) -> Self { + Self::ParseInt(value) + } +} + +impl<'t> From> for Error<'t, '_> { + fn from(value: ParseNumberError<'t>) -> Self { + Self::ParseNumber(value) + } +} + +pub fn compile<'t, 'i>( + file: &Arc, + input: &'i str, + tree: &'t RootTree, +) -> Result, Error<'t, 'i>> { + let root = tree.root_node()?; + + Expression::parse(file, input, root.expression()?) +} + +pub type RootTree = type_sitter::Tree>; + +pub fn iter_raw_nodes<'t>(tree: &'t RootTree) -> impl Iterator> { + let mut cursor: type_sitter::TreeCursor = tree.walk(); + + std::iter::from_fn(move || { + let node = cursor.node(); + + // Go to the first child, or the next sibling. + if cursor.goto_first_child() || cursor.goto_next_sibling() { + Some(node) + } else { + // No more children. + // Work your way back up the tree until you can reach another sibling, or you reach the + // root. + loop { + if !cursor.goto_parent() { + // We have reached the root. We are done iterating. + return Option::None; + } + if cursor.goto_next_sibling() { + return Some(node); + } + } + } + }) +} + +/// Compiles a full document. Panics if there are any issues. +/// This is for testing only. +#[cfg(test)] +pub(crate) fn full_compile(input: &str) -> AstNode { + let test_file = Arc::new(PathBuf::from("test.ccm")); + let mut tree = new_parser(); + let root = tree.parse(input, None).unwrap(); + crate::compile(&test_file, input, &root).unwrap() +} diff --git a/interpreter/src/execution/errors.rs b/interpreter/src/execution/errors.rs new file mode 100644 index 0000000..bef29b4 --- /dev/null +++ b/interpreter/src/execution/errors.rs @@ -0,0 +1,84 @@ +/* + * Copyright 2024 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::{any::Any, borrow::Cow, fmt::Display}; + +use ariadne::{Label, Report, ReportKind}; + +use crate::compile::SourceReference; + +pub type ExpressionResult = std::result::Result; + +#[derive(Debug)] +pub struct Error { + pub ty: Box, + pub trace: Vec, +} + +impl Error { + pub fn report(&self) -> Report<'_, SourceReference> { + let bottom = self.trace.first().expect("Error has no trace").clone(); + + let mut builder = Report::build(ReportKind::Error, bottom.clone()); + builder.set_message("Failed to evaluate"); + builder.add_label(Label::new(bottom).with_message(format!("{}", self.ty))); + + builder.finish() + } +} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{}", self.ty)?; + writeln!(f, "Backtrace:")?; + for layer in self.trace.iter() { + writeln!(f, "\t{}", layer)?; + } + + Ok(()) + } +} + +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>); + +impl ErrorType for GenericFailure {} + +impl Display for GenericFailure { + 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 {} + +pub trait Raise { + fn to_error<'s>(self, stack_trace: impl IntoIterator) -> Error; +} + +impl Raise for E { + fn to_error<'s>(self, stack_trace: impl IntoIterator) -> Error { + Error { + ty: Box::new(self), + trace: stack_trace.into_iter().cloned().collect(), + } + } +} diff --git a/interpreter/src/execution/logging.rs b/interpreter/src/execution/logging.rs new file mode 100644 index 0000000..b925b36 --- /dev/null +++ b/interpreter/src/execution/logging.rs @@ -0,0 +1,270 @@ +/* + * Copyright 2025 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use ariadne::{Cache, Source}; +use imstr::ImString; +use std::{ + borrow::Cow, + collections::HashMap, + fmt::Display, + ops::{Deref, DerefMut}, + path::PathBuf, + sync::{Arc, Mutex}, +}; +use tree_sitter::{Point, Range}; + +use crate::compile::SourceReference; + +pub trait RuntimeLog: std::fmt::Debug + Send + Sync { + fn push_message(&self, message: LogMessage); + fn collect_syntax_errors<'t>( + &self, + input: &str, + tree: &'t crate::compile::RootTree, + file: &'t Arc, + span: SourceReference, + ); +} + +impl RuntimeLog for Mutex> { + fn push_message(&self, message: LogMessage) { + self.lock().expect("Log was poisoned").push(message); + } + fn collect_syntax_errors<'t>( + &self, + _input: &str, + _tree: &'t crate::compile::RootTree, + _file: &'t Arc, + _span: SourceReference, + ) { + } +} + +#[derive(Debug, Clone)] +pub struct StackTrace<'p> { + parent: Option<&'p StackTrace<'p>>, + reference: SourceReference, +} + +impl<'p> Display for StackTrace<'p> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(parent) = self.parent { + write!(f, "{parent}")?; + } + + writeln!(f, "{}", self.reference) + } +} + +impl StackTrace<'static> { + pub fn top(reference: SourceReference) -> Self { + Self { + parent: None, + reference, + } + } + + /// A special stack trace for building the prelude + pub fn bootstrap() -> Self { + Self { + parent: None, + reference: SourceReference { + file: Arc::new(PathBuf::from("bootstrap.ccm")), + range: Range { + start_byte: 0, + end_byte: 0, + start_point: Point { row: 0, column: 0 }, + end_point: Point { row: 0, column: 0 }, + }, + }, + } + } + + #[cfg(test)] + pub(crate) fn test() -> Self { + Self { + parent: None, + reference: SourceReference { + file: Arc::new(PathBuf::from("test.ccm")), + range: Range { + start_byte: 0, + end_byte: 0, + start_point: Point { row: 0, column: 0 }, + end_point: Point { row: 0, column: 0 }, + }, + }, + } + } +} + +impl<'p> StackTrace<'p> { + pub fn trace_scope(&'p self, reference: impl Into, code: F) -> R + where + F: FnOnce(StackTrace<'p>) -> R, + { + let scope = Self { + parent: Some(self), + reference: reference.into(), + }; + + code(scope) + } + + pub fn bottom(&self) -> &SourceReference { + &self.reference + } + + pub fn iter(&'p self) -> StackTraceIter<'p> { + StackTraceIter { + current: Some(self), + } + } +} + +impl<'p> IntoIterator for &'p StackTrace<'p> { + type Item = &'p SourceReference; + type IntoIter = StackTraceIter<'p>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +pub struct StackTraceIter<'p> { + current: Option<&'p StackTrace<'p>>, +} + +impl<'p> Iterator for StackTraceIter<'p> { + type Item = &'p SourceReference; + + fn next(&mut self) -> Option { + let next = self.current.take(); + if let Some(next) = next { + self.current = next.parent; + } + next.map(|next| &next.reference) + } +} + +impl Display for SourceReference { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}:{}:{}", + self.file.to_string_lossy(), + // Most IDEs expect the first line to be 1. + self.range.start_point.row + 1, + self.range.start_point.column + 1 + ) + } +} + +/// A string that knows what file it came from. +pub struct LocatedString { + pub location: SourceReference, + pub string: String, +} + +impl Deref for LocatedString { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.string + } +} + +impl DerefMut for LocatedString { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.string + } +} + +impl AsRef for LocatedString { + fn as_ref(&self) -> &str { + self.string.as_ref() + } +} + +/// A str that knows what file it came from. +pub struct LocatedStr<'s> { + pub location: SourceReference, + pub string: &'s str, +} + +impl<'s> Deref for LocatedStr<'s> { + type Target = &'s str; + + fn deref(&self) -> &Self::Target { + &self.string + } +} + +impl<'s> From<&'s LocatedString> for LocatedStr<'s> { + fn from(value: &'s LocatedString) -> Self { + Self { + location: value.location.clone(), + string: &value.string, + } + } +} + +impl<'s> From<&'s LocatedStr<'s>> for LocatedStr<'s> { + fn from(value: &'s LocatedStr<'s>) -> Self { + Self { + location: value.location.clone(), + string: value.string, + } + } +} + +#[derive(Debug)] +pub struct LogMessage { + pub origin: SourceReference, + pub level: LogLevel, + pub message: Cow<'static, str>, +} + +impl std::fmt::Display for LogMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.origin, self.message) + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum LogLevel { + Info, + Warning, +} + +pub struct ExecutionFileCache<'i>(pub &'i HashMap, Source>); + +impl<'i> Cache> for ExecutionFileCache<'i> { + type Storage = ImString; + + fn fetch(&mut self, id: &Arc) -> Result<&Source, impl std::fmt::Debug> { + if let Some(source) = self.0.get(id) { + Ok(source) + } else { + Err("File was never loaded during execution") + } + } + + fn display<'a>(&self, id: &'a Arc) -> Option { + Some(id.as_path().display().to_string()) + } +} diff --git a/interpreter/src/execution/mod.rs b/interpreter/src/execution/mod.rs new file mode 100644 index 0000000..ee8543f --- /dev/null +++ b/interpreter/src/execution/mod.rs @@ -0,0 +1,784 @@ +/* + * Copyright 2025 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::{ + cmp::Ordering, + collections::HashMap, + path::{Path, PathBuf}, + sync::{Arc, Mutex}, +}; + +use crate::{ + build_function, + compile::{self, AstNode, BinaryExpressionOperation, Expression, UnaryExpressionOperation}, + execution::{ + errors::{GenericFailure, Raise}, + stack::ScopeType, + values::BuiltinCallableDatabase, + }, + new_parser, + values::{ + constraint_set::find_all_captured_variables_in_constraint_set, ConstraintSet, IString, + }, + SourceReference, +}; + +use ariadne::Source; +use rayon::{join, prelude::*}; + +mod errors; +pub use errors::Error; +mod logging; +mod stack; +mod standard_environment; +pub mod values; +pub use errors::ExpressionResult; +use imstr::ImString; +use logging::LocatedStr; +pub use logging::{ExecutionFileCache, LogLevel, LogMessage, RuntimeLog, StackTrace}; +pub use stack::StackScope; +mod store; +pub use store::Store; + +use values::{ + closure::find_all_variable_accesses_in_closure_capture, + dictionary::find_all_variable_accesses_in_dictionary_construction, Object, Value, ValueType, +}; + +pub use standard_environment::build_prelude; + +pub fn find_all_variable_accesses_in_expression( + expression: &Expression, + access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, +) -> ExpressionResult<()> { + match expression { + Expression::BinaryExpression(ast_node) => { + find_all_variable_accesses_in_expression(&ast_node.node.a.node, access_collector)?; + find_all_variable_accesses_in_expression(&ast_node.node.b.node, access_collector)?; + + Ok(()) + } + Expression::ClosureDefinition(ast_node) => { + find_all_variable_accesses_in_expression( + &ast_node.node.return_type.node, + access_collector, + )?; + find_all_variable_accesses_in_closure_capture(&ast_node.node, access_collector)?; + + Ok(()) + } + Expression::DictionaryConstruction(ast_node) => { + find_all_variable_accesses_in_dictionary_construction(&ast_node.node, access_collector) + } + Expression::If(ast_node) => { + find_all_variable_accesses_in_expression( + &ast_node.node.condition.node, + access_collector, + )?; + find_all_variable_accesses_in_expression( + &ast_node.node.on_true.node, + access_collector, + )?; + find_all_variable_accesses_in_expression( + &ast_node.node.on_false.node, + access_collector, + )?; + + Ok(()) + } + Expression::List(ast_node) => { + for expression in ast_node.node.iter() { + find_all_variable_accesses_in_expression(&expression.node, access_collector)?; + } + + Ok(()) + } + Expression::Parenthesis(ast_node) => { + find_all_variable_accesses_in_expression(&ast_node.node, access_collector) + } + Expression::MemberAccess(ast_node) => { + find_all_variable_accesses_in_expression(&ast_node.node.base.node, access_collector) + } + Expression::StructDefinition(ast_node) => { + for member in ast_node.node.members.iter() { + find_all_variable_accesses_in_expression(&member.node.ty.node, access_collector)?; + if let Some(default) = member.node.default.as_ref() { + find_all_variable_accesses_in_expression(&default.node, access_collector)?; + } + } + + Ok(()) + } + Expression::UnaryExpression(ast_node) => find_all_variable_accesses_in_expression( + &ast_node.node.expression.node, + access_collector, + ), + Expression::FunctionCall(ast_node) => { + find_all_variable_accesses_in_expression( + &ast_node.node.to_call.node, + access_collector, + )?; + find_all_variable_accesses_in_dictionary_construction( + &ast_node.node.argument.node, + access_collector, + )?; + + Ok(()) + } + Expression::MethodCall(ast_node) => { + find_all_variable_accesses_in_expression( + &ast_node.node.self_dictionary.node, + access_collector, + )?; + find_all_variable_accesses_in_dictionary_construction( + &ast_node.node.argument.node, + access_collector, + )?; + + Ok(()) + } + Expression::LetIn(ast_node) => { + for assignment in ast_node.node.assignments.iter() { + find_all_variable_accesses_in_expression( + &assignment.node.value.node, + access_collector, + )?; + } + + let variable_names: Vec<&ImString> = { + let mut variable_names = Vec::with_capacity(ast_node.node.assignments.len()); + + for argument in ast_node.node.assignments.iter() { + variable_names.push(&argument.node.ident.node); + } + + // We typically won't have more than 6 arguments, so a binary search will typically + // outperform a hashset. + variable_names.sort(); + + variable_names + }; + + find_all_variable_accesses_in_expression( + &ast_node.node.expression.node, + &mut move |variable_name| { + if variable_names.binary_search(&&variable_name.node).is_err() { + // This is not an argument, which means it must be captured from the environment. + access_collector(variable_name)?; + } + + Ok(()) + }, + )?; + + Ok(()) + } + Expression::Identifier(ast_node) => access_collector(ast_node), + Expression::ConstraintSet(constraint_set) => { + find_all_captured_variables_in_constraint_set(&constraint_set.node, access_collector) + } + Expression::Boolean(_) + | Expression::Scalar(_) + | Expression::Vector2(_) + | Expression::Vector3(_) + | Expression::Vector4(_) + | Expression::SignedInteger(_) + | Expression::String(_) + | Expression::UnsignedInteger(_) + | Expression::Self_(_) + | Expression::Malformed(_) => Ok(()), + } +} + +#[derive(Debug, Clone)] +pub struct ExecutionContext<'c> { + pub log: &'c dyn RuntimeLog, + pub stack_trace: &'c StackTrace<'c>, + pub stack: &'c StackScope<'c>, + pub database: &'c BuiltinCallableDatabase, + pub store: &'c Store, + pub file_cache: &'c Mutex, Source>>, + pub working_directory: &'c Path, + pub import_limit: usize, +} + +impl<'c> ExecutionContext<'c> { + pub fn trace_scope(&'c self, 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 + }; + + code(&context) + }) + } + + pub fn get_variable<'s, S: Into>>(&self, name: S) -> ExpressionResult<&Value> { + self.stack.get_variable(self.stack_trace, [], name) + } + + pub fn get_variable_for_closure<'s, S: Into>>( + &self, + local_variables: impl IntoIterator, + name: S, + ) -> ExpressionResult<&Value> { + self.stack + .get_variable(self.stack_trace, local_variables, name) + } + + pub fn stack_scope( + &self, + mode: ScopeType, + variables: HashMap, + block: B, + ) -> ExpressionResult + where + B: FnOnce(&ExecutionContext) -> R, + { + self.stack + .scope(self.stack_trace, mode, variables, |stack, stack_trace| { + let context = ExecutionContext { + stack_trace, + stack, + ..*self + }; + + block(&context) + }) + } +} + +pub fn execute_expression( + context: &ExecutionContext, + expression: &compile::AstNode, +) -> ExpressionResult { + context.trace_scope(expression.reference.clone(), |context| { + match &expression.node { + compile::Expression::BinaryExpression(ast_node) => { + execute_binary_expression(context, ast_node) + } + compile::Expression::Boolean(ast_node) => Ok(values::Boolean(ast_node.node).into()), + compile::Expression::ClosureDefinition(ast_node) => { + Ok(values::UserClosure::from_ast(context, ast_node)?.into()) + } + compile::Expression::DictionaryConstruction(ast_node) => { + Ok(values::Dictionary::from_ast(context, ast_node)?.into()) + } + compile::Expression::If(ast_node) => execute_if_expression(context, ast_node), + compile::Expression::List(ast_node) => { + Ok(values::List::from_ast(context, ast_node)?.into()) + } + compile::Expression::Parenthesis(ast_node) => execute_expression(context, ast_node), + compile::Expression::MemberAccess(ast_node) => { + let base = execute_expression(context, &ast_node.node.base)?; + + context.trace_scope(ast_node.node.member.reference.clone(), |context| { + base.get_attribute(context, &ast_node.node.member.node) + }) + } + compile::Expression::Self_(ast_node) => context + .get_variable(LocatedStr { + location: ast_node.reference.clone(), + string: "self", + }) + .cloned(), + compile::Expression::Identifier(ast_node) => context + .get_variable(LocatedStr { + location: ast_node.reference.clone(), + string: ast_node.node.as_str(), + }) + .cloned(), + compile::Expression::Scalar(ast_node) => Ok(values::Scalar { + dimension: ast_node.node.dimension, + value: ast_node.node.value, + } + .into()), + compile::Expression::Vector2(vector) => { + Ok(values::Vector2::from_ast(context, vector)?.into()) + } + compile::Expression::Vector3(vector) => { + Ok(values::Vector3::from_ast(context, vector)?.into()) + } + compile::Expression::Vector4(vector) => { + Ok(values::Vector4::from_ast(context, vector)?.into()) + } + compile::Expression::SignedInteger(ast_node) => { + Ok(values::SignedInteger::from(ast_node.node).into()) + } + compile::Expression::String(ast_node) => { + Ok(values::IString::from(ast_node.node.clone()).into()) + } + compile::Expression::StructDefinition(ast_node) => { + Ok(ValueType::from(values::StructDefinition::new(context, ast_node)?).into()) + } + compile::Expression::UnaryExpression(ast_node) => { + execute_unary_expression(context, ast_node) + } + compile::Expression::UnsignedInteger(ast_node) => { + Ok(values::UnsignedInteger::from(ast_node.node).into()) + } + compile::Expression::FunctionCall(ast_node) => execute_function_call(context, ast_node), + compile::Expression::MethodCall(ast_node) => execute_method_call(context, ast_node), + compile::Expression::LetIn(ast_node) => execute_let_in(context, ast_node), + 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)), + } + }) +} + +fn execute_unary_expression( + context: &ExecutionContext, + expression: &compile::AstNode>, +) -> ExpressionResult { + context.trace_scope(expression.reference.clone(), |context| { + let node = &expression.node; + let value = execute_expression(context, &node.expression)?; + match node.operation.node { + UnaryExpressionOperation::Add => value.unary_plus(context), + UnaryExpressionOperation::Sub => value.unary_minus(context), + UnaryExpressionOperation::Not => value.unary_not(context), + } + }) +} + +fn execute_function_call( + context: &ExecutionContext, + call: &compile::AstNode>, +) -> ExpressionResult { + let to_call = execute_expression(context, &call.node.to_call)?; + let argument = values::Dictionary::from_ast(context, &call.node.argument)?; + + context.stack_scope( + to_call.call_scope_type(context), + HashMap::new(), + |context| to_call.call(context, argument), + )? +} + +fn execute_method_call( + context: &ExecutionContext, + call: &compile::AstNode>, +) -> ExpressionResult { + let self_dictionary = execute_expression(context, &call.node.self_dictionary)?; + let to_call = self_dictionary + .get_attribute(context, &call.node.to_call.node)? + .clone(); + let argument = values::Dictionary::from_ast(context, &call.node.argument)?; + + context.stack_scope( + to_call.call_scope_type(context), + HashMap::from_iter([(ImString::from("self"), self_dictionary)]), + |context| to_call.call(context, argument), + )? +} + +fn execute_let_in( + context: &ExecutionContext, + expression: &compile::AstNode>, +) -> ExpressionResult { + context.trace_scope(expression.reference.clone(), |context| { + context.stack.scope_mut( + context.stack_trace, + ScopeType::Inherited, + HashMap::with_capacity(expression.node.assignments.len()), + |stack, stack_trace| { + let mut buffer = Vec::new(); + for group in expression.node.compute_groups() { + let context = ExecutionContext { + stack_trace, + stack, + ..*context + }; + + buffer.par_extend(group.par_iter().map(|assignment| { + ( + assignment.node.ident.node.clone(), + execute_expression(&context, &assignment.node.value), + ) + })); + + for (name, result) in buffer.drain(..) { + let value = result?; + stack.insert_value(name, value); + } + } + + let context = ExecutionContext { + stack_trace, + stack, + ..*context + }; + + let node = &expression.node; + execute_expression(&context, &node.expression) + }, + )? + }) +} + +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), + ); + + 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 { + let condition = execute_expression(context, &expression.node.condition)? + .downcast::(context.stack_trace)? + .0; + + let expression = if condition { + &expression.node.on_true + } else { + &expression.node.on_false + }; + + execute_expression(context, expression) +} + +#[cfg(test)] +pub(crate) fn test_run(input: &str) -> ExpressionResult { + let root = compile::full_compile(input); + + test_context([], |context| execute_expression(context, &root)) +} + +#[cfg(test)] +pub(crate) fn test_context( + extra_prelude: impl IntoIterator, + f: impl FnOnce(&ExecutionContext) -> R, +) -> R { + let database = BuiltinCallableDatabase::new(); + test_context_custom_database(database, extra_prelude, f) +} + +#[cfg(test)] +pub(crate) fn test_context_custom_database( + database: BuiltinCallableDatabase, + extra_prelude: impl IntoIterator, + f: impl FnOnce(&ExecutionContext) -> R, +) -> R { + use standard_environment::build_prelude; + use std::sync::Mutex; + use tempfile::TempDir; + + let mut prelude = build_prelude(&database).unwrap(); + + for (name, value) in extra_prelude.into_iter() { + prelude.insert(name, value); + } + + let store_directory = TempDir::new().unwrap(); + let store = Store::new(store_directory.path()); + + let file_cache = Mutex::new(HashMap::new()); + let working_directory = Path::new("."); + + let context = ExecutionContext { + log: &Mutex::new(Vec::new()), + stack_trace: &StackTrace::test(), + stack: &StackScope::top(&prelude), + database: &database, + store: &store, + file_cache: &file_cache, + working_directory, + import_limit: 100, + }; + + f(&context) +} + +pub fn run_file(context: &ExecutionContext, file: impl Into) -> ExpressionResult { + // 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); + + vacency.insert_entry(Source::from(input)) + } + }; + 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 root = crate::compile(&file, input, &tree).map_err(|error| { + GenericFailure(format!("Failed to compile: {error}").into()) + .to_error(context.stack_trace) + })?; + + context + .log + .collect_syntax_errors(input, &tree, &file, root.reference.clone()); + + root + }; + + let context = ExecutionContext { + working_directory: parent_dir, + import_limit: context.import_limit - 1, + ..context.clone() + }; + + execute_expression(&context, &root) +} + +pub mod functions { + pub struct Import; +} + +pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { + build_function!( + database, + functions::Import, "std::import", ( + context: &ExecutionContext, + path: IString + ) -> Value + { + let file = PathBuf::from(path.0.as_str()); + run_file(context, file) + } + ); +} + +#[cfg(test)] +mod test { + use hashable_map::HashableMap; + use std::{collections::HashMap, sync::Arc}; + + use super::*; + + #[test] + fn boolean_type() { + let product = test_run("true").unwrap(); + assert_eq!(product, values::Boolean(true).into()); + } + + #[test] + fn signed_integer_type() { + let product = test_run("5i").unwrap(); + assert_eq!(product, values::SignedInteger::from(5).into()); + } + + #[test] + fn unsigned_integer_type() { + let product = test_run("5u").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(5).into()); + } + + #[test] + fn parenthesis() { + // Fails because of a type mismatch. + let product = test_run("(1i + 2i) * 3i").unwrap(); + assert_eq!(product, values::SignedInteger::from(9).into()); + } + + #[test] + fn struct_definition() { + let product = test_run("(name: std.types.None = std.consts.None, ...)").unwrap(); + assert_eq!( + product, + values::ValueType::Dictionary(values::StructDefinition { + members: Arc::new(HashableMap::from(HashMap::from([( + "name".into(), + values::StructMember { + ty: ValueType::TypeNone, + default: Some(Value::ValueNone(values::ValueNone)) + } + )]))), + variadic: true + }) + .into() + ); + } + + #[test] + fn nested_value_access() { + let product = test_run("let dictionary = (a = (b = 23u)); in dictionary.a.b").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(23).into()); + } + + #[test] + fn let_in() { + let product = test_run("let value = 23u; in value").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(23).into()); + } + + #[test] + fn let_in_self_ref() { + let product = test_run("let value = 23u; value2 = value + 2u; in value2").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(25).into()); + } + + #[test] + fn variable_access_collection_for_let_in() { + let product = + test_run("let fn = () -> std.types.UInt: let value = 5u; in value; in fn()").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(5).into()); + } + + #[test] + fn string() { + let product = test_run("\"a simple string of text\"").unwrap(); + assert_eq!( + product, + values::IString::from("a simple string of text").into() + ); + } + + #[test] + fn if_expression() { + let product = test_run("if true then 1u else 2u").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(1).into()); + + let product = test_run("if false then 1u else 2u").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(2).into()); + } + + #[test] + fn import() { + let product = test_run("std.import(path = \"./test_assets/import_me.ccm\")").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(5).into()); + } + + #[test] + fn recursive_import() { + let product = + test_run("std.import(path = \"./test_assets/recursive_import.ccm\")").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(5).into()); + } + + #[test] + fn infinite_recursion_import() { + test_run("std.import(path = \"./test_assets/infinite_recursion_import.ccm\")").unwrap_err(); + } +} diff --git a/interpreter/src/execution/stack.rs b/interpreter/src/execution/stack.rs new file mode 100644 index 0000000..e517d0f --- /dev/null +++ b/interpreter/src/execution/stack.rs @@ -0,0 +1,357 @@ +/* + * Copyright 2025 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use crate::execution::logging::StackTrace; + +use super::{ + errors::{ErrorType, ExpressionResult, Raise}, + logging::LocatedStr, + values::Value, +}; +use imstr::ImString; +use levenshtein::levenshtein; +use std::{cmp::Ordering, collections::HashMap, fmt::Display}; + +#[derive(Debug, Clone, Copy)] +pub enum ScopeType { + Isolated, + Inherited, +} + +#[derive(Debug)] +pub struct StackScope<'p> { + prelude: &'p HashMap, + parent: Option<&'p StackScope<'p>>, + ty: ScopeType, + variables: HashMap, +} + +impl<'p> StackScope<'p> { + pub fn top(prelude: &'p HashMap) -> Self { + Self { + prelude, + parent: None, + ty: ScopeType::Isolated, + variables: HashMap::new(), + } + } + + pub fn scope( + &'p self, + stack_trace: &StackTrace, + mode: ScopeType, + variables: HashMap, + block: B, + ) -> ExpressionResult + where + B: FnOnce(&Self, &StackTrace) -> R, + { + let scope = Self { + prelude: self.prelude, + parent: Some(self), + ty: mode, + variables, + }; + + let result = block(&scope, stack_trace); + + Ok(result) + } + + pub fn scope_mut( + &'p self, + stack_trace: &StackTrace, + mode: ScopeType, + variables: HashMap, + block: B, + ) -> ExpressionResult + where + B: FnOnce(&mut Self, &StackTrace) -> R, + { + let mut scope = Self { + prelude: self.prelude, + parent: Some(self), + ty: mode, + variables, + }; + + let result = block(&mut scope, stack_trace); + + Ok(result) + } + + pub fn insert_value(&mut self, name: ImString, value: Value) { + self.variables.insert(name, value); + } + + fn iter_visible_scopes(&'p self) -> StackScopeIter<'p> { + StackScopeIter { + only_visible: true, + next: Some(self), + } + } + + pub fn iter_visible_variables(&'p self) -> impl Iterator { + self.iter_visible_scopes() + .flat_map(|scope| scope.variables.iter()) + .chain(self.prelude.iter()) + } + + pub fn suggest_similar_names( + &self, + local_variables: impl IntoIterator, + name: &str, + ) -> Vec { + let mut names: Vec<_> = self + .iter_visible_variables() + .map(|(name, _value)| name.clone()) + .chain(local_variables) + .collect(); + names.sort_by(|name_a, name_b| { + match (name_a.starts_with(name), name_b.starts_with(name)) { + (true, false) => Ordering::Less, + _ => match levenshtein(name_a, name).cmp(&levenshtein(name_b, name)) { + Ordering::Equal => name_b.cmp(name_a), // They're equel, so just alphabetize + // them to insure consistent test + // results. + ord => ord, + }, + } + }); + + names + } + + /// Gets a reference to a variable on the stack. + // TODO Recommending similar named variables would help users to notice typos. + // https://crates.io/crates/levenshtein + pub fn get_variable<'s, S: Into>>( + &self, + stack_trace: &StackTrace, + local_variables: impl IntoIterator, + name: S, + ) -> ExpressionResult<&Value> { + let name = name.into(); + + let mut scope_iterator = self.iter_visible_scopes(); + let mut value = None; + + // Search the stack for the thing. + for scope in &mut scope_iterator { + if let Some(local_value) = scope.variables.get(name.string) { + value = Some(local_value); + break; + } + } + + if let Some(value) = value { + Ok(value) + } else { + // See if we can find it in the prelude. + if let Some(value) = self.prelude.get(name.string) { + return Ok(value); + } + + // We couldn't find it. + Err(NotInScopeError { + variable_name: ImString::from(name.string), + suggestions: self.suggest_similar_names(local_variables, name.string), + } + .to_error(stack_trace.iter().chain([&name.location]))) + } + } +} + +pub struct StackScopeIter<'p> { + only_visible: bool, + next: Option<&'p StackScope<'p>>, +} + +impl<'p> Iterator for StackScopeIter<'p> { + type Item = &'p StackScope<'p>; + + fn next(&mut self) -> Option { + let current = self.next.take(); + if let Some(current) = current { + if !self.only_visible || matches!(current.ty, ScopeType::Inherited) { + self.next = current.parent; + } else { + self.next = None; + } + } + current + } +} + +#[derive(Debug, Eq, PartialEq)] +struct NotInScopeError { + variable_name: ImString, + suggestions: Vec, +} + +impl ErrorType for NotInScopeError {} + +impl Display for NotInScopeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "`{}` could not be found in the current scope. Possible alternatives: ", + self.variable_name + )?; + + let mut names = self.suggestions.iter().take(3).peekable(); + + while let Some(name) = names.next() { + if names.peek().is_some() { + write!(f, "{name}, ")?; + } else { + write!(f, "{name}")?; + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use pretty_assertions::assert_eq; + + use crate::values::{UnsignedInteger, ValueNone}; + + use super::*; + + #[test] + fn iter_variables() { + let stack_trace = StackTrace::test(); + let prelude = HashMap::from_iter([("a".into(), UnsignedInteger::from(1).into())]); + let stack = StackScope::top(&prelude); + + stack + .scope( + &stack_trace, + ScopeType::Inherited, + HashMap::from_iter([("b".into(), UnsignedInteger::from(2).into())]), + |stack, stack_trace| { + stack + .scope( + stack_trace, + ScopeType::Inherited, + HashMap::from_iter([("c".into(), UnsignedInteger::from(3).into())]), + |stack, _stack_trace| { + let mut variables = + stack.iter_visible_variables().collect::>(); + variables.sort_by(|(name_a, _value_a), (name_b, _value_b)| { + name_a.cmp(name_b) + }); + assert_eq!( + variables, + vec![ + (&"a".into(), &UnsignedInteger::from(1).into()), + (&"b".into(), &UnsignedInteger::from(2).into()), + (&"c".into(), &UnsignedInteger::from(3).into()) + ] + ); + }, + ) + .unwrap(); + }, + ) + .unwrap(); + } + + #[test] + fn iter_isolated_variables() { + let stack_trace = StackTrace::test(); + let prelude = HashMap::from_iter([("a".into(), UnsignedInteger::from(1).into())]); + let stack = StackScope::top(&prelude); + + stack + .scope( + &stack_trace, + ScopeType::Inherited, + HashMap::from_iter([("b".into(), UnsignedInteger::from(2).into())]), + |stack, stack_trace| { + stack + .scope( + stack_trace, + ScopeType::Isolated, + HashMap::from_iter([("c".into(), UnsignedInteger::from(3).into())]), + |stack, _stack_trace| { + let mut variables = + stack.iter_visible_variables().collect::>(); + variables.sort_by(|(name_a, _value_a), (name_b, _value_b)| { + name_a.cmp(name_b) + }); + + assert_eq!( + variables, + vec![ + (&"a".into(), &UnsignedInteger::from(1).into()), + (&"c".into(), &UnsignedInteger::from(3).into()) + ] + ); + }, + ) + .unwrap(); + }, + ) + .unwrap(); + } + + #[test] + fn suggest_similar_names() { + let prelude = HashMap::from_iter([ + ("abc".into(), ValueNone.into()), + ("abcdef".into(), ValueNone.into()), + ("123".into(), ValueNone.into()), + ("12345".into(), ValueNone.into()), + ]); + let stack = StackScope::top(&prelude); + + assert_eq!( + stack.suggest_similar_names([], "abc"), + vec![ + ImString::from("abc"), + ImString::from("abcdef"), + ImString::from("123"), + ImString::from("12345") + ] + ); + + assert_eq!( + stack.suggest_similar_names([], "abcde"), + vec![ + ImString::from("abcdef"), + ImString::from("abc"), + ImString::from("12345"), + ImString::from("123"), + ] + ); + + assert_eq!( + stack.suggest_similar_names([], "123"), + vec![ + ImString::from("123"), + ImString::from("12345"), + ImString::from("abc"), + ImString::from("abcdef") + ] + ); + } +} diff --git a/interpreter/src/execution/standard_environment.rs b/interpreter/src/execution/standard_environment.rs new file mode 100644 index 0000000..cc97745 --- /dev/null +++ b/interpreter/src/execution/standard_environment.rs @@ -0,0 +1,179 @@ +/* + * Copyright 2026 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::{collections::HashMap, path::Path, sync::Mutex}; + +use common_data_types::{Dimension, Float}; +use imstr::ImString; +use tempfile::TempDir; + +use crate::{ + execution::{ + functions::Import, + logging::StackTrace, + stack::StackScope, + store::Store, + values::{BuiltinCallableDatabase, Scalar, SignedInteger, UnsignedInteger, ValueNone}, + ExecutionContext, + }, + values::BuiltinFunction, +}; + +use super::values::{Dictionary, Value, ValueType}; + +/// Builds standard library. +pub fn build_prelude( + database: &BuiltinCallableDatabase, +) -> std::io::Result> { + // Build an incomplete context for bootstrapping. + let prelude = HashMap::new(); + let store_directory = TempDir::new()?; + let store = Store::new(store_directory.path()); + let file_cache = Mutex::new(HashMap::new()); + + let working_directory = Path::new("."); + + let context = ExecutionContext { + log: &Mutex::new(Vec::new()), + stack_trace: &StackTrace::bootstrap(), + stack: &StackScope::top(&prelude), + database, + store: &store, + file_cache: &file_cache, + working_directory, + import_limit: 100, + }; + + let global = HashMap::from([("std".into(), build_std(&context).into())]); + + Ok(global) +} + +fn build_std(context: &ExecutionContext) -> Dictionary { + let std = HashMap::from([ + ("types".into(), build_types(context).into()), + ( + "scalar".into(), + build_dimension_types(context, ValueType::Scalar).into(), + ), + ( + "vector2".into(), + build_dimension_types(context, ValueType::Vector2).into(), + ), + ( + "vector3".into(), + build_dimension_types(context, ValueType::Vector3).into(), + ), + ( + "vector4".into(), + build_dimension_types(context, ValueType::Vector4).into(), + ), + ("consts".into(), build_consts(context).into()), + ("mesh3d".into(), build_mesh_3d(context).into()), + ("import".into(), BuiltinFunction::new::().into()), + ]); + Dictionary::new(context, std) +} + +/// Adds library for constants. +fn build_consts(context: &ExecutionContext) -> Dictionary { + let types: HashMap = HashMap::from_iter([ + ("None".into(), ValueNone.into()), + ( + "Infinity".into(), + Scalar { + dimension: Dimension::zero(), + value: Float::new(common_data_types::RawFloat::INFINITY).expect("Infinity was NaN"), + } + .into(), + ), + ("UIntMax".into(), UnsignedInteger::from(u64::MAX).into()), + ("UIntMin".into(), UnsignedInteger::from(u64::MIN).into()), + ( + "UIntBits".into(), + UnsignedInteger::from(u64::BITS as u64).into(), + ), + ("SIntMax".into(), SignedInteger::from(i64::MAX).into()), + ("SIntMin".into(), SignedInteger::from(i64::MIN).into()), + ( + "SIntBits".into(), + UnsignedInteger::from(i64::BITS as u64).into(), + ), + ]); + Dictionary::new(context, types) +} + +/// Adds library for type safety. +fn build_types(context: &ExecutionContext) -> Dictionary { + let types: HashMap = HashMap::from_iter([ + ("None".into(), ValueType::TypeNone.into()), + ("Any".into(), ValueType::Any.into()), + ("Bool".into(), ValueType::Boolean.into()), + ("SInt".into(), ValueType::SignedInteger.into()), + ("UInt".into(), ValueType::UnsignedInteger.into()), + ("String".into(), ValueType::String.into()), + ("ValueType".into(), ValueType::ValueType.into()), + ("ManifoldMesh".into(), ValueType::ManifoldMesh3D.into()), + // TODO we need File types. + // TODO we'll need a function to build custom function signature types. + // ("Function".into(), ValueType::Closure(Arc)), + + // TODO add a function to build custom unit types. + ]); + Dictionary::new(context, types) +} + +fn build_dimension_types( + context: &ExecutionContext, + type_builder: impl Fn(Option) -> ValueType, +) -> Dictionary { + let types: HashMap = HashMap::from_iter( + units::list_named_dimensions() + .map(|(name, dimension)| (name, Some(dimension))) + .chain([("Any", Option::None)]) + .map(move |(name, dimension)| (name.into(), type_builder(dimension).into())), + ); + + Dictionary::new(context, types) +} + +fn build_mesh_3d(context: &ExecutionContext) -> Dictionary { + use crate::values::manifold_mesh::methods::*; + + let types: HashMap = HashMap::from_iter([ + ("cone".into(), BuiltinFunction::new::().into()), + ("cube".into(), BuiltinFunction::new::().into()), + ( + "cylinder".into(), + BuiltinFunction::new::().into(), + ), + ( + "icosphere".into(), + BuiltinFunction::new::().into(), + ), + ( + "torus".into(), + BuiltinFunction::new::().into(), + ), + ( + "uv_sphere".into(), + BuiltinFunction::new::().into(), + ), + ]); + Dictionary::new(context, types) +} diff --git a/interpreter/src/execution/store.rs b/interpreter/src/execution/store.rs new file mode 100644 index 0000000..0b760e4 --- /dev/null +++ b/interpreter/src/execution/store.rs @@ -0,0 +1,208 @@ +/* + * Copyright 2026 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::{ + io::{ErrorKind, Write}, + path::{Path, PathBuf}, +}; + +use sha2::{Digest, Sha256}; +use tempfile::{NamedTempFile, TempDir}; + +use crate::{ + execution::errors::{ErrorType, ExpressionResult, Raise}, + ExecutionContext, +}; + +#[derive(Debug)] +pub struct Store { + path: PathBuf, +} + +impl Store { + pub fn new(path: impl Into) -> Self { + Self { path: path.into() } + } + + pub fn get_or_init_file( + &self, + 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) + } + } + + pub fn get_or_init_directory( + &self, + 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) + } + } + + fn generate_store_path( + &self, + hashable: &impl std::hash::Hash, + name: impl AsRef, + ) -> PathBuf { + let mut hasher = StoreHasher(Sha256::new()); + hashable.hash(&mut hasher); + let hash = hasher.0.finalize(); + let file_name = { + let mut file_name = hex::encode(&hash[..]); + file_name += "-"; + file_name += name.as_ref(); + file_name + }; + self.path.join(file_name) + } + + fn move_path_into_store( + &self, + context: &ExecutionContext, + temp_path: &Path, + store_path: &Path, + ) -> ExpressionResult<()> { + // Move the file into the store. + match std::fs::rename(temp_path, store_path) { + Ok(_) => Ok(store_path), + Err(error) => { + let result = match error.kind() { + // Seems another thread created the object while we were working. + // That's unfortunate, but we can just use their result. + ErrorKind::AlreadyExists => Ok(store_path), + _ => Err(error), + }; + + // Either way, we need to remove that old directory. + if let Err(error) = std::fs::remove_dir_all(temp_path) { + // This just really isn't our day... + context.log.push_message(super::LogMessage { + origin: context.stack_trace.bottom().clone(), + level: super::LogLevel::Warning, + message: format!( + "Failed to remove temporary file at {temp_path:?}: {error}" + ) + .into(), + }); + } + + result + } + } + .map_err(|error| IoError(error).to_error(context.stack_trace))?; + + Ok(()) + } +} + +#[derive(Debug)] +pub struct PendingAsset { + store_path: PathBuf, + asset: A, +} + +impl std::ops::Deref for PendingAsset { + type Target = A; + + fn deref(&self) -> &Self::Target { + &self.asset + } +} + +impl std::ops::DerefMut for PendingAsset { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.asset + } +} + +#[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 { + fn finish(&self) -> u64 { + // Use the Sha256 built in to get the real hash. + 0 + } + + fn write(&mut self, bytes: &[u8]) { + Digest::update(&mut self.0, bytes); + } +} diff --git a/interpreter/src/execution/values/boolean.rs b/interpreter/src/execution/values/boolean.rs new file mode 100644 index 0000000..2bb9fec --- /dev/null +++ b/interpreter/src/execution/values/boolean.rs @@ -0,0 +1,111 @@ +/* + * Copyright 2024 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use crate::execution::{ + logging::{LogLevel, LogMessage}, + values::{string::formatting::Style, StaticType}, + ExecutionContext, +}; + +use super::{value_type::ValueType, ExpressionResult, Object, StaticTypeName, Value}; + +use std::borrow::Cow; + +#[derive(Debug, Hash, Clone, Copy, Eq, PartialEq)] +pub struct Boolean(pub bool); + +impl Object for Boolean { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::Boolean + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + if !matches!(style, Style::Default) { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Boolean values only support default formatting".into(), + }); + } + + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Boolean values cannot be formatted with precision".into(), + }); + } + + write!(f, "{}", self.0) + } + + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + 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)?; + 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)?; + 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)?; + Ok(Self((self.0 && rhs.0) || (!self.0 && !rhs.0)).into()) + } + + fn unary_not(self, _context: &ExecutionContext) -> ExpressionResult { + Ok(Self(!self.0).into()) + } +} + +impl StaticTypeName for Boolean { + fn static_type_name() -> Cow<'static, str> { + "Boolean".into() + } +} + +impl StaticType for Boolean { + fn static_type() -> ValueType { + ValueType::Boolean + } +} + +#[cfg(test)] +mod test { + use crate::execution::{test_run, values}; + + #[test] + fn format() { + let product = test_run("\"{value}\"::format(value = true) == \"true\"").unwrap(); + assert_eq!(product, values::Boolean(true).into()); + + let product = test_run("\"{value}\"::format(value = false) == \"false\"").unwrap(); + assert_eq!(product, values::Boolean(true).into()); + } +} diff --git a/interpreter/src/execution/values/closure.rs b/interpreter/src/execution/values/closure.rs new file mode 100644 index 0000000..5124c32 --- /dev/null +++ b/interpreter/src/execution/values/closure.rs @@ -0,0 +1,959 @@ +/* + * Copyright 2024 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::{any::TypeId, borrow::Cow, collections::HashMap, fmt::Display, sync::Arc}; + +use hashable_map::HashableMap; +use imstr::ImString; + +use crate::{ + compile::{AstNode, ClosureDefinition, Expression}, + execute_expression, + execution::{ + errors::{ExpressionResult, Raise}, + find_all_variable_accesses_in_expression, + logging::{LocatedStr, LogLevel, LogMessage}, + stack::ScopeType, + values::{string::formatting::Style, Dictionary, Value}, + ExecutionContext, + }, +}; + +use super::{Object, StaticTypeName, StructDefinition, ValueType}; + +#[derive(Debug, Default)] +pub struct BuiltinCallableDatabase { + callables: HashMap, + names: HashMap, +} + +impl BuiltinCallableDatabase { + pub fn new() -> Self { + let mut database = Self::default(); + + super::integer::register_methods(&mut database); + super::scalar::register_methods(&mut database); + super::vector::register_methods(&mut database); + super::value_type::register_methods(&mut database); + super::list::register_methods(&mut database); + super::file::register_methods(&mut database); + super::string::register_methods(&mut database); + super::constraint_set::register_methods(&mut database); + super::manifold_mesh::register_methods_and_functions(&mut database); + crate::execution::register_methods_and_functions(&mut database); + + database + } + + pub fn register(&mut self, callable: Box) { + if self + .names + .insert(callable.name().to_string(), TypeId::of::()) + .is_some() + { + panic!("Duplicate bultin function name: {}", callable.name()); + } + + if self + .callables + .insert(TypeId::of::(), CallableStorage { callable }) + .is_some() + { + panic!("Duplicate bultin function tag: {:?}", TypeId::of::()); + } + } + + fn get_callable(&self, id: TypeId) -> &CallableStorage { + self.callables + .get(&id) + .expect("Forward callable was not present") + } +} + +#[derive(Debug)] +struct CallableStorage { + callable: Box, +} + +impl std::ops::Deref for CallableStorage { + type Target = dyn BuiltinCallable; + + fn deref(&self) -> &Self::Target { + self.callable.as_ref() + } +} + +/// Signature of a closure, used for type comparison. +#[derive(Debug, Eq, PartialEq)] +pub struct Signature { + pub argument_type: StructDefinition, + pub return_type: ValueType, +} + +impl Display for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} -> {}", self.argument_type, self.return_type) + } +} + +pub fn find_all_variable_accesses_in_closure_capture( + closure: &crate::compile::ClosureDefinition, + mut access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, +) -> ExpressionResult<()> { + let argument_names: Vec<&ImString> = { + let mut argument_names = Vec::with_capacity(closure.argument_type.node.members.len()); + + for argument in closure.argument_type.node.members.iter() { + argument_names.push(&argument.node.name.node); + } + + // We typically won't have more than 6 arguments, so a binary search will typically + // outperform a hashset. + argument_names.sort(); + + argument_names + }; + + find_all_variable_accesses_in_expression( + &closure.expression.node, + &mut move |variable_name| { + if argument_names.binary_search(&&variable_name.node).is_err() { + // This is not an argument, which means it must be captured from the environment. + access_collector(variable_name)?; + } + + Ok(()) + }, + )?; + + Ok(()) +} + +/// Closures are immutable, meaning that all copies can reference the same data. +/// This is that common data. +#[derive(Debug, Eq, PartialEq)] +struct UserClosureInternals { + signature: Arc, + captured_values: HashableMap, + expression: Arc>, +} + +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct UserClosure { + data: Arc, +} + +impl UserClosure { + pub fn from_ast( + context: &ExecutionContext, + source: &AstNode>, + ) -> ExpressionResult { + let argument_type = + context.trace_scope(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| { + execute_expression(context, &source.node.return_type)? + .downcast::(context.stack_trace) + })?; + + let signature = Arc::new(Signature { + argument_type, + return_type, + }); + + let expression = source.node.expression.clone(); + + let mut captured_values = HashableMap::new(); + find_all_variable_accesses_in_closure_capture(&source.node, &mut |field_name| { + let local_variables = signature.argument_type.members.keys().cloned(); + + let value = context + .get_variable_for_closure( + local_variables, + LocatedStr { + location: field_name.reference.clone(), + string: field_name.node.as_str(), + }, + )? + .clone(); + + captured_values.insert(field_name.node.clone(), value); + + Ok(()) + })?; + + Ok(Self { + data: Arc::new(UserClosureInternals { + signature, + captured_values, + expression, + }), + }) + } +} + +impl Object for UserClosure { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::Closure(self.data.signature.clone()) + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + if !matches!(style, Style::Default) { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Boolean values only support default formatting".into(), + }); + } + + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Boolean values cannot be formatted with precision".into(), + }); + } + + write!(f, "{}", self.get_type(context)) + } + + fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExpressionResult { + self.data + .signature + .argument_type + .check_other_qualifies(argument.struct_def()) + .map_err(|error| error.to_error(context.stack_trace))?; + + let argument = self.data.signature.argument_type.fill_defaults(argument); + + let variables: HashMap = argument + .iter() + .chain(self.data.captured_values.iter()) + .map(|(name, value)| (name.clone(), value.clone())) + .collect(); + + context.stack_scope(ScopeType::Inherited, variables, |context| { + let result = execute_expression(context, &self.data.expression)?; + + self.data + .signature + .return_type + .check_other_qualifies(&result.get_type(context)) + .map_err(|error| error.to_error(context.stack_trace))?; + + Ok(result) + })? + } +} + +impl StaticTypeName for UserClosure { + fn static_type_name() -> Cow<'static, str> { + "Closure".into() + } +} + +pub trait BuiltinCallable: Sync + Send { + fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExpressionResult; + + fn name(&self) -> &str; + + fn signature(&self) -> &Arc; + + fn scope_type(&self) -> ScopeType { + ScopeType::Isolated + } +} + +impl std::fmt::Debug for dyn BuiltinCallable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Builtin") + .field("name", &self.name()) + .field("signature", self.signature()) + .finish() + } +} + +#[macro_export] +macro_rules! build_member_from_sig { + ($name:ident: $ty:ty) => { + ( + imstr::ImString::from(stringify!($name)), + $crate::execution::values::StructMember { + ty: <$ty as $crate::execution::values::StaticType>::static_type(), + default: None, + }, + ) + }; + ($name:ident: $ty:ty = $default:expr) => { + ( + imstr::ImString::from(stringify!($name)), + $crate::execution::values::StructMember { + ty: <$ty as $crate::execution::values::StaticType>::static_type(), + default: Some($default), + }, + ) + }; +} + +#[macro_export] +macro_rules! build_argument_signature_list { + ($($arg:ident: $ty:path $(= $default:expr)?),*) => {{ + let list: [(imstr::ImString, $crate::execution::values::StructMember); _] = [$($crate::build_member_from_sig!($arg: $ty $(= $default)?),)*]; + list + }}; +} + +#[macro_export] +macro_rules! build_struct_definition { + (variadic: $variadic:literal, ($($arg:ident: $ty:path $(= $default:expr)?),*)) => {{ + $crate::execution::values::StructDefinition { + members: std::sync::Arc::new(hashable_map::HashableMap::from(std::collections::HashMap::from($crate::build_argument_signature_list!($($arg: $ty $(= $default)?),*)))), + variadic: $variadic, + } + }}; +} + +#[macro_export] +macro_rules! build_closure_signature { + (($($arg:ident: $ty:path $(= $default:expr)?),*) -> $return_type:ty) => {{ + std::sync::Arc::new($crate::execution::values::closure::Signature { + argument_type: $crate::build_struct_definition!(variadic: false, ($($arg: $ty $(= $default)?),*)), + return_type: <$return_type as $crate::execution::values::StaticType>::static_type(), + }) + }}; +} + +#[macro_export] +macro_rules! build_closure_type { + ($name:ident($($arg:ident: $ty:path $(= $default:expr)?),*) -> $return_type:ty) => { + struct $name(pub $crate::execution::values::UserClosure); + + impl $crate::execution::values::StaticType for $name { + fn static_type() -> $crate::execution::values::ValueType { + static TYPE: std::sync::OnceLock< + std::sync::Arc<$crate::execution::values::closure::Signature>, + > = std::sync::OnceLock::new(); + let signature = TYPE.get_or_init(|| $crate::build_closure_signature!(($($arg: $ty $(= $default)?),*) -> $return_type)); + + $crate::execution::values::ValueType::Closure(signature.clone()) + } + } + + impl $crate::execution::values::StaticTypeName for $name { + fn static_type_name() -> std::borrow::Cow<'static, str> { + "Closure".into() + } + } + + impl enum_downcast::IntoVariant<$name> for $crate::execution::values::Value { + fn into_variant(self) -> Result<$name, $crate::execution::values::Value> { + Ok($name(self.into_variant()?)) + } + } + + impl From<$name> for $crate::execution::values::UserClosure { + fn from(value: $name) -> Self { + value.0 + } + } + + impl std::ops::Deref for $name { + type Target = $crate::execution::values::UserClosure; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + }; +} + +#[macro_export] +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> + { + function: F, + signature: std::sync::Arc<$crate::execution::values::closure::Signature>, + name: String, + } + + 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, + { + fn call( + &self, + context: &$crate::execution::ExecutionContext, + argument: $crate::execution::values::Dictionary, + ) -> $crate::execution::errors::ExpressionResult<$crate::execution::values::Value> { + (self.function)(context, &self.signature, argument) + } + + fn name(&self) -> &str { + &self.name + } + + fn signature(&self) -> &std::sync::Arc<$crate::execution::values::closure::Signature> { + &self.signature + } + } + + BuiltFunction { + function: move | + $context: &$crate::execution::ExecutionContext, + signature: &$crate::execution::values::closure::Signature, + argument: $crate::execution::values::Dictionary + | -> $crate::execution::ExpressionResult<$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()))?; + + // 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)?;)*)? + + let result: $return_type = { + $code? + }; + Ok(result.into()) + }, + signature: $crate::build_closure_signature!(($($($arg: $ty $(= $default)?),*)*) -> $return_type), + name: $name.into(), + } + }}; +} + +#[macro_export] +macro_rules! build_function { + ($database:ident, + $ident:ty, $name:literal, ($context:ident: &ExecutionContext $(, $($arg:ident: $ty:path $(= $default:expr)?),+)?) -> $return_type:ty $code:block + ) => {{ + let callable = $crate::build_function_callable!($name ($context: &ExecutionContext $(, $($arg: $ty $(= $default)?),+)?) -> $return_type $code); + + $database.register::<$ident>(Box::new(callable)) + }}; +} + +#[macro_export] +macro_rules! build_method_callable { + ($name:expr, + ($context:ident: &ExecutionContext, $this:ident: $this_type:ty $(, $($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> + { + function: F, + signature: std::sync::Arc<$crate::execution::values::closure::Signature>, + name: String, + } + + 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, + { + fn call( + &self, + context: &$crate::execution::ExecutionContext, + argument: $crate::execution::values::Dictionary, + ) -> $crate::execution::errors::ExpressionResult<$crate::execution::values::Value> { + (self.function)(context, &self.signature, argument) + } + + fn name(&self) -> &str { + &self.name + } + + fn signature(&self) -> &std::sync::Arc<$crate::execution::values::closure::Signature> { + &self.signature + } + } + + BuiltFunction { + function: move | + $context: &$crate::execution::ExecutionContext, + signature: &$crate::execution::values::closure::Signature, + argument: $crate::execution::values::Dictionary + | -> $crate::execution::ExpressionResult<$crate::execution::values::Value> { + use $crate::execution::errors::Raise as _; + + let $this = $context.get_variable( + $crate::execution::logging::LocatedStr { + location: $context.stack_trace.bottom().clone(), + string: "self", + }, + )?.downcast_ref::<$this_type>($context.stack_trace)?.clone(); + + signature + .argument_type + .check_other_qualifies(argument.struct_def()) + .map_err(|error| error.to_error($context.stack_trace.iter()))?; + + // 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)?;)*)? + + let result: $return_type = { + $code? + }; + Ok(result.into()) + }, + signature: $crate::build_closure_signature!(($($($arg: $ty $(= $default)?),*)*) -> $return_type), + name: $name.into(), + } + }}; +} + +#[macro_export] +macro_rules! build_method { + ($database:ident, + $ident:ty, $name:expr, ($context:ident: &ExecutionContext, $this:ident: $this_type:ty $(, $($arg:ident: $ty:path $(= $default:expr)?),+)?) -> $return_type:path $code:block + ) => {{ + let callable = $crate::build_method_callable!($name, + ($context: &ExecutionContext, $this: $this_type $(, $($arg: $ty $(= $default)?),+)?) -> $return_type $code + ); + + $database.register::<$ident>(Box::new(callable)) + }}; +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BuiltinFunction(pub TypeId); + +impl BuiltinFunction { + pub fn new() -> Self { + Self(TypeId::of::()) + } +} + +impl Object for BuiltinFunction { + fn get_type(&self, context: &ExecutionContext) -> ValueType { + ValueType::Closure(context.database.get_callable(self.0).signature().clone()) + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + if !matches!(style, Style::Default) { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Closures only support default formatting".into(), + }); + } + + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Closures cannot be formatted with precision".into(), + }); + } + + write!(f, "{}", self.get_type(context)) + } + + fn call_scope_type(&self, context: &ExecutionContext) -> ScopeType { + context.database.get_callable(self.0).callable.scope_type() + } + + fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExpressionResult { + context + .database + .get_callable(self.0) + .call(context, argument) + } +} + +impl StaticTypeName for BuiltinFunction { + fn static_type_name() -> Cow<'static, str> { + "Builtin Function".into() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::execution::{ + test_context_custom_database, test_run, + values::{self, SignedInteger, StructMember, UnsignedInteger}, + }; + use hashable_map::HashableMap; + use pretty_assertions::assert_eq; + + #[test] + fn define_closure() { + let product = test_run("() -> std.types.UInt: 1u").unwrap(); + + let expression = product.as_userclosure().unwrap().data.expression.clone(); + + assert_eq!( + product, + UserClosure { + data: Arc::new(UserClosureInternals { + signature: Arc::new(Signature { + argument_type: StructDefinition { + members: HashableMap::new().into(), + variadic: false, + }, + return_type: ValueType::UnsignedInteger, + }), + captured_values: HashableMap::new(), + expression + }) + } + .into() + ); + } + + #[test] + fn call_closure() { + let product = test_run( + "let my_function = (a: std.types.UInt) -> std.types.UInt: a + 2u; in my_function(a = 3u)", + ) + .unwrap(); + assert_eq!(product, values::UnsignedInteger::from(5).into()); + } + + #[test] + fn call_closure_bad_args() { + test_run( + "let my_function = (a: std.types.UInt) -> std.types.UInt: a + 2u; in my_function(a = 3i)", + ) + .unwrap_err(); + } + + #[test] + fn call_closure_bad_result() { + test_run( + "let my_function = (a: std.types.UInt) -> std.types.UInt: \"test\"; in my_function(a = 3u)", + ) + .unwrap_err(); + } + + #[test] + fn call_closure_default_value() { + let product = test_run( + "let my_function = (a: std.types.UInt = 3u) -> std.types.UInt: a + 2u; in my_function()", + ) + .unwrap(); + assert_eq!(product, values::UnsignedInteger::from(5).into()); + } + + #[test] + fn call_closure_captured_variable() { + let product = + test_run("let value = 3u; my_function = (input: std.types.UInt) -> std.types.UInt: value + input; in my_function(input = 4u)") + .unwrap(); + assert_eq!(product, values::UnsignedInteger::from(7).into()); + } + + #[test] + fn call_custom_method() { + let product = test_run( + "let object = (value = 5u, method = () -> std.types.UInt: self.value); in object::method()", + ) + .unwrap(); + assert_eq!(product, values::UnsignedInteger::from(5).into()); + } + + #[test] + fn build_argument_signature() { + assert_eq!(build_argument_signature_list!(), []); + assert_eq!( + build_argument_signature_list!(value: SignedInteger), + [( + ImString::from("value"), + StructMember { + ty: ValueType::SignedInteger, + default: None + } + )] + ); + assert_eq!( + build_argument_signature_list!(value: UnsignedInteger), + [( + ImString::from("value"), + StructMember { + ty: ValueType::UnsignedInteger, + default: None + } + )] + ); + assert_eq!( + build_argument_signature_list!(value: UnsignedInteger = UnsignedInteger::from(23).into()), + [( + ImString::from("value"), + StructMember { + ty: ValueType::UnsignedInteger, + default: Some(Value::UnsignedInteger(23.into())) + } + )] + ); + + assert_eq!( + build_argument_signature_list!(value: UnsignedInteger, value1: SignedInteger), + [ + ( + ImString::from("value"), + StructMember { + ty: ValueType::UnsignedInteger, + default: None + } + ), + ( + ImString::from("value1"), + StructMember { + ty: ValueType::SignedInteger, + default: None + } + ) + ] + ); + + assert_eq!( + build_argument_signature_list!(value: UnsignedInteger = UnsignedInteger::from(32).into(), value1: SignedInteger), + [ + ( + ImString::from("value"), + StructMember { + ty: ValueType::UnsignedInteger, + default: Some(UnsignedInteger::from(32).into()) + } + ), + ( + ImString::from("value1"), + StructMember { + ty: ValueType::SignedInteger, + default: None + } + ) + ] + ); + } + + #[test] + fn builtin_function_no_args() { + let mut database = BuiltinCallableDatabase::new(); + + struct TestFunction; + build_function!( + database, + TestFunction, "test_function", ( + _context: &ExecutionContext + ) -> UnsignedInteger { + Ok(values::UnsignedInteger::from(846)) + } + ); + + let root = crate::compile::full_compile("test_function()"); + test_context_custom_database( + database, + [( + "test_function".into(), + BuiltinFunction::new::().into(), + )], + |context| { + let product = execute_expression(context, &root).unwrap(); + + assert_eq!(product, values::UnsignedInteger::from(846).into()); + }, + ) + } + + #[test] + fn builtin_function_with_args() { + let mut database = BuiltinCallableDatabase::new(); + + struct TestFunction; + build_function!( + database, + TestFunction, "test_function", ( + _context: &ExecutionContext, + a: UnsignedInteger, + b: UnsignedInteger + ) -> UnsignedInteger { + Ok(values::UnsignedInteger::from(a.0 + b.0)) + } + ); + + let root = crate::compile::full_compile("test_function(a = 1u, b = 2u)"); + test_context_custom_database( + database, + [( + "test_function".into(), + BuiltinFunction::new::().into(), + )], + |context| { + let product = execute_expression(context, &root).unwrap(); + + assert_eq!(product, values::UnsignedInteger::from(3).into()); + }, + ) + } + + #[test] + fn builtin_function_with_default_value() { + let mut database = BuiltinCallableDatabase::new(); + struct TestFunction; + build_function!( + database, + TestFunction, "test_function", ( + _context: &ExecutionContext, + a: UnsignedInteger, + b: UnsignedInteger = UnsignedInteger::from(2).into() + ) -> UnsignedInteger { + Ok(values::UnsignedInteger::from(a.0 + b.0)) + } + ); + + let root = crate::compile::full_compile("test_function(a = 1u)"); + test_context_custom_database( + database, + [( + "test_function".into(), + BuiltinFunction::new::().into(), + )], + |context| { + let product = execute_expression(context, &root).unwrap(); + + assert_eq!(product, values::UnsignedInteger::from(3).into()); + }, + ) + } + + #[test] + fn builtin_function_captured_value() { + let mut database = BuiltinCallableDatabase::new(); + let b = 2; + + struct TestFunction; + build_function!( + database, + TestFunction, "test_function", ( + _context: &ExecutionContext, + a: UnsignedInteger + ) -> UnsignedInteger { + Ok(values::UnsignedInteger::from(a.0 + b)) + } + ); + + let root = crate::compile::full_compile("test_function(a = 1u)"); + test_context_custom_database( + database, + [( + "test_function".into(), + BuiltinFunction::new::().into(), + )], + |context| { + let product = execute_expression(context, &root).unwrap(); + + assert_eq!(product, values::UnsignedInteger::from(3).into()); + }, + ) + } + + #[test] + fn builtin_method() { + let mut database = BuiltinCallableDatabase::new(); + struct TestMethod; + build_method!( + database, + TestMethod, "test_method", (context: &ExecutionContext, this: Dictionary) -> Value { + this.get_attribute(context, "value") + } + ); + + let root = crate::compile::full_compile( + "let object = (value = 5u, test_method = provided_test_method); in object::test_method()", + ); + test_context_custom_database( + database, + [( + "provided_test_method".into(), + BuiltinFunction::new::().into(), + )], + |context| { + let product = execute_expression(context, &root).unwrap(); + + assert_eq!(product, values::UnsignedInteger::from(5).into()); + }, + ) + } + + #[test] + fn builtin_method_with_argument() { + let mut database = BuiltinCallableDatabase::new(); + struct TestMethod; + + build_method!( + database, + TestMethod, "test_method", ( + context: &ExecutionContext, + this: Dictionary, + to_add: UnsignedInteger + ) -> UnsignedInteger { + let value: UnsignedInteger = this.get_attribute(context, "value")?.downcast(context.stack_trace)?; + + Ok(values::UnsignedInteger::from(value.0 + to_add.0)) + } + ); + + let root = crate::compile::full_compile( + "let object = (value = 5u, test_method = provided_test_method); in object::test_method(to_add = 10u)", + ); + test_context_custom_database( + database, + [( + "provided_test_method".into(), + BuiltinFunction::new::().into(), + )], + |context| { + let product = execute_expression(context, &root).unwrap(); + + assert_eq!(product, values::UnsignedInteger::from(15).into()); + }, + ) + } +} diff --git a/interpreter/src/execution/values/constraint_set.rs b/interpreter/src/execution/values/constraint_set.rs new file mode 100644 index 0000000..303f9bd --- /dev/null +++ b/interpreter/src/execution/values/constraint_set.rs @@ -0,0 +1,662 @@ +/* + * Copyright 2026 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use common_data_types::{Dimension, Float}; +use hashable_map::{HashableMap, HashableSet}; +use imstr::ImString; +use selen::api::{add, div, eq, float, ge, gt, le, lt, mul, ne, sub, ExprBuilder, Model, VarId}; + +use crate::{ + compile::{ + constraint_set::{ + BinaryExpressionOperation, ConstraintSet as AstConstraintSet, ConstraintSetExpression, + Relation, UnaryExpressionOperation, + }, + AstNode, + }, + execution::{ + errors::{ErrorType, ExpressionResult, GenericFailure, Raise}, + logging::LocatedStr, + }, + values::{ + self, + closure::{BuiltinCallable, Signature}, + scalar::UnwrapNotNan, + BuiltinCallableDatabase, BuiltinFunction, Dictionary, MissingAttributeError, Object, + Scalar, StaticTypeName, StructDefinition, Style, Value, ValueType, + }, + ExecutionContext, +}; +use std::{ + borrow::Cow, + collections::{HashMap, HashSet}, + sync::Arc, +}; + +pub fn find_all_captured_variables_in_constraint_set( + constraint_set: &AstConstraintSet, + access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, +) -> ExpressionResult<()> { + use crate::compile::constraint_set; + + fn search_expression( + variables: &Vec>, + expression: &constraint_set::ConstraintSetExpression, + access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, + ) -> ExpressionResult<()> { + match expression { + ConstraintSetExpression::Parenthesis(expression) => { + search_expression(variables, &expression.node, access_collector) + } + ConstraintSetExpression::Identifier(ast_node) => { + if !variables + .iter() + .any(|variable| variable.node == ast_node.node) + { + access_collector(ast_node) + } else { + // Is not a captured variable. + Ok(()) + } + } + ConstraintSetExpression::UnaryExpression(ast_node) => { + search_expression(variables, &ast_node.node.expression.node, access_collector) + } + ConstraintSetExpression::BinaryExpression(ast_node) => { + search_expression(variables, &ast_node.node.a.node, access_collector)?; + search_expression(variables, &ast_node.node.b.node, access_collector)?; + Ok(()) + } + ConstraintSetExpression::MethodCall(ast_node) => { + search_expression( + variables, + &ast_node.node.self_dictionary.node, + access_collector, + )?; + search_expression(variables, &ast_node.node.argument.node, access_collector)?; + + Ok(()) + } + ConstraintSetExpression::Scalar(_) => Ok(()), + } + } + + search_expression( + &constraint_set.variables, + &constraint_set.left.node, + access_collector, + )?; + search_expression( + &constraint_set.variables, + &constraint_set.right.node, + access_collector, + )?; + + Ok(()) +} + +fn display_expression( + context: &ExecutionContext, + captured_values: &HashableMap, + expression: &ConstraintSetExpression, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, +) -> std::fmt::Result { + match expression { + ConstraintSetExpression::Parenthesis(ast_node) => { + write!(f, "(")?; + display_expression( + context, + captured_values, + &ast_node.node, + f, + style, + precision, + )?; + write!(f, ")")?; + + Ok(()) + } + ConstraintSetExpression::Scalar(ast_node) => values::Scalar { + dimension: ast_node.node.dimension, + value: ast_node.node.value, + } + .format(context, f, style, precision), + ConstraintSetExpression::Identifier(ast_node) => { + if let Some(value) = captured_values.get(&ast_node.node) { + value.format(context, f, style, precision) + } else { + write!(f, "{}", ast_node.node) + } + } + ConstraintSetExpression::UnaryExpression(ast_node) => { + let operation = match ast_node.node.operation.node { + UnaryExpressionOperation::Add => "+", + UnaryExpressionOperation::Sub => "-", + }; + + write!(f, "{operation}")?; + display_expression( + context, + captured_values, + &ast_node.node.expression.node, + f, + style, + precision, + ) + } + ConstraintSetExpression::BinaryExpression(ast_node) => { + let operation = match ast_node.node.operation.node { + BinaryExpressionOperation::Mul => "*", + BinaryExpressionOperation::Add => "+", + BinaryExpressionOperation::Sub => "-", + BinaryExpressionOperation::Div => "/", + }; + + display_expression( + context, + captured_values, + &ast_node.node.a.node, + f, + style, + precision, + )?; + write!(f, " {operation} ")?; + display_expression( + context, + captured_values, + &ast_node.node.b.node, + f, + style, + precision, + ) + } + ConstraintSetExpression::MethodCall(ast_node) => { + display_expression( + context, + captured_values, + &ast_node.node.self_dictionary.node, + f, + style, + precision, + )?; + write!(f, "::")?; + display_expression( + context, + captured_values, + &ast_node.node.argument.node, + f, + style, + precision, + ) + } + } +} + +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct ConstraintSet { + variables: Arc>, + captured_values: Arc>, + source: Arc, +} + +impl ConstraintSet { + pub fn from_ast( + context: &ExecutionContext, + source: &AstNode>, + ) -> ExpressionResult { + let mut variables = HashSet::new(); + let mut duplicate_variables = Vec::new(); + for variable in source.node.variables.iter().map(|field| &field.node) { + if !variables.insert(variable.clone()) { + duplicate_variables.push(variable.clone()); + } + } + + if !duplicate_variables.is_empty() { + return Err(DuplicateVariablesError { + variables: duplicate_variables, + } + .to_error(context.stack_trace)); + } + + let mut captured_values = HashMap::new(); + find_all_captured_variables_in_constraint_set(&source.node, &mut |name| { + let value = context + .get_variable_for_closure( + [], + LocatedStr { + location: name.reference.clone(), + string: name.node.as_str(), + }, + )? + .clone(); + + captured_values.insert(name.node.clone(), value); + + Ok(()) + })?; + + Ok(Self { + variables: Arc::new(HashableSet::from(HashSet::from_iter(variables))), + captured_values: Arc::new(HashableMap::from(captured_values)), + source: source.node.clone(), + }) + } +} + +impl Object for ConstraintSet { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::ConstraintSet(self.variables.clone()) + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + write!(f, "<<<")?; + + let mut variables: Vec<_> = self.variables.iter().collect(); + variables.sort_unstable(); + + let mut variables = self.variables.iter().peekable(); + + while let Some(variable) = variables.next() { + if variables.peek().is_some() { + write!(f, "{variable}, ")?; + } else { + write!(f, "{variable}")?; + } + } + + write!(f, ": ")?; + + display_expression( + context, + &self.captured_values, + &self.source.left.node, + f, + style, + precision, + )?; + + let relation = match self.source.relation { + Relation::Less => "<", + Relation::LessEqual => "<=", + Relation::Equal => "==", + Relation::GreaterEqual => ">=", + Relation::Greater => ">", + Relation::NotEqual => "!=", + }; + write!(f, " {} ", relation)?; + + display_expression( + context, + &self.captured_values, + &self.source.right.node, + f, + style, + precision, + )?; + + write!(f, ">>>")?; + + Ok(()) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + match attribute { + "solve" => Ok(BuiltinFunction::new::().into()), + _ => Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)), + } + } +} + +struct ValueProvider<'l> { + provided: &'l Dictionary, + captured: &'l HashableMap, +} + +impl<'l> ValueProvider<'l> { + fn get(&self, name: &ImString) -> Option<&Value> { + self.provided + .get(name.as_str()) + .or_else(|| self.captured.get(name)) + } +} + +impl ConstraintSet { + fn solve( + &self, + context: &ExecutionContext, + provided: Dictionary, + ) -> ExpressionResult { + let value_provider = ValueProvider { + provided: &provided, + captured: &self.captured_values, + }; + + let mut m = Model::default(); + let mut dimension = Option::None; + + let mut variables = HashMap::new(); + + let left = Self::model_expression( + context, + &value_provider, + &mut m, + &mut variables, + &mut dimension, + &self.source.left.node, + )?; + let right = Self::model_expression( + context, + &value_provider, + &mut m, + &mut variables, + &mut dimension, + &self.source.right.node, + )?; + + Self::build_relation(&mut m, &self.source.relation, left, right); + let solution = m + .solve() + .map_err(|error| SolverError(error).to_error(context.stack_trace))?; + + let dimension = dimension.ok_or_else(|| { + GenericFailure("Could not determine dimension of constraint set".into()) + .to_error(context.stack_trace) + })?; + + let mut members = HashMap::new(); + for (variable_name, variable_id) in variables { + // Values that do not get solved are our inputs. + if let Some(value) = solution.as_float(variable_id) { + members.insert( + variable_name, + Scalar { + dimension, + value: Float::new(value).unwrap_not_nan(context.stack_trace)?, + } + .into(), + ); + } + } + + // Include the provided values. + for (name, value) in provided.data.members.iter() { + members.entry(name.clone()).or_insert(value.clone()); + } + + Ok(Dictionary::new(context, members)) + } + + fn build_relation( + m: &mut Model, + relation: &Relation, + left: impl Into, + right: impl Into, + ) { + match relation { + Relation::Less => lt(m, left, right), + Relation::LessEqual => le(m, left, right), + Relation::Equal => eq(m, left, right), + Relation::GreaterEqual => ge(m, left, right), + Relation::Greater => gt(m, left, right), + Relation::NotEqual => ne(m, left, right), + } + } + + fn model_expression( + context: &ExecutionContext, + value_provider: &ValueProvider<'_>, + m: &mut Model, + variables: &mut HashMap, + dimension: &mut Option, + expression: &ConstraintSetExpression, + ) -> ExpressionResult { + match expression { + ConstraintSetExpression::Parenthesis(ast_node) => Self::model_expression( + context, + value_provider, + m, + variables, + dimension, + &ast_node.node, + ), + ConstraintSetExpression::Scalar(ast_node) => { + let value = Scalar { + dimension: ast_node.node.dimension, + value: ast_node.node.value, + }; + + context.trace_scope(ast_node.reference.clone(), |context| { + Self::build_scalar(context, dimension, value) + }) + } + ConstraintSetExpression::Identifier(ast_node) => { + context.trace_scope(ast_node.reference.clone(), |context| { + let name = &ast_node.node; + if let Some(value) = value_provider + .get(name) + .filter(|value| !matches!(value, Value::ValueNone(_))) + { + match value { + 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)), + } + } else { + let variable = variables + .entry(name.clone()) + .or_insert_with(|| m.float(f64::MIN, f64::MAX)); + Ok(ExprBuilder::Var(*variable)) + } + }) + } + ConstraintSetExpression::UnaryExpression(ast_node) => { + let expression = Self::model_expression( + context, + value_provider, + m, + variables, + dimension, + &ast_node.node.expression.node, + )?; + + match ast_node.node.operation.node { + UnaryExpressionOperation::Add => Ok(expression), + UnaryExpressionOperation::Sub => Ok(expression.mul(-1)), + } + } + ConstraintSetExpression::BinaryExpression(ast_node) => { + let a = Self::model_expression( + context, + value_provider, + m, + variables, + dimension, + &ast_node.node.a.node, + )?; + let b = Self::model_expression( + context, + value_provider, + m, + variables, + dimension, + &ast_node.node.b.node, + )?; + + match ast_node.node.operation.node { + BinaryExpressionOperation::Mul => Ok(mul(a, b)), + BinaryExpressionOperation::Add => Ok(add(a, b)), + BinaryExpressionOperation::Sub => Ok(sub(a, b)), + BinaryExpressionOperation::Div => Ok(div(a, b)), + } + } + 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), + ) + }) + } + } + } + + fn build_scalar( + context: &ExecutionContext, + dimension: &mut Option, + value: Scalar, + ) -> ExpressionResult { + 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(), + ) + .to_error(context.stack_trace)); + } + } else if value.dimension != Dimension::zero() { + *dimension = Some(value.dimension); + } + + Ok(ExprBuilder::Val(float(*value.value))) + } +} + +impl StaticTypeName for ConstraintSet { + fn static_type_name() -> Cow<'static, str> { + "ConstraintSet".into() + } +} + +#[derive(Debug, Eq, PartialEq)] +struct DuplicateVariablesError { + pub variables: Vec, +} + +impl ErrorType for DuplicateVariablesError {} + +impl std::fmt::Display for DuplicateVariablesError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Constraint set contains the following duplicate fields: " + )?; + + let mut variables = self.variables.iter().peekable(); + + while let Some(variable) = variables.next() { + if variables.peek().is_some() { + write!(f, "{variable}, ")?; + } else { + write!(f, "{variable}")?; + } + } + + Ok(()) + } +} + +#[derive(Debug)] +struct SolverError(selen::api::SolverError); + +impl ErrorType for SolverError {} + +impl std::fmt::Display for SolverError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +mod methods { + pub struct Solve; +} + +pub fn register_methods(database: &mut BuiltinCallableDatabase) { + struct BuiltFunction { + signature: Arc, + } + + impl BuiltinCallable for BuiltFunction { + fn call( + &self, + context: &ExecutionContext, + argument: Dictionary, + ) -> ExpressionResult { + let this = context + .get_variable(LocatedStr { + location: context.stack_trace.bottom().clone(), + string: "self", + })? + .downcast_ref::(context.stack_trace)? + .clone(); + + let solution = this.solve(context, argument)?; + Ok(solution.into()) + } + + fn name(&self) -> &str { + "ConstraintSet::solve" + } + + fn signature(&self) -> &Arc { + &self.signature + } + } + + let callable = BuiltFunction { + signature: Arc::new(Signature { + argument_type: StructDefinition { + members: Arc::new(HashableMap::from(HashMap::new())), + variadic: true, + }, + return_type: ValueType::Dictionary(StructDefinition { + members: Arc::new(HashableMap::new()), + variadic: true, + }), + }), + }; + + database.register::(Box::new(callable)) +} + +// TODO test variable capturing. +// TODO test formatting +// TODO test explicitly passing none. +// TODO test all 6 constraint types. diff --git a/interpreter/src/execution/values/dictionary.rs b/interpreter/src/execution/values/dictionary.rs new file mode 100644 index 0000000..1a8decc --- /dev/null +++ b/interpreter/src/execution/values/dictionary.rs @@ -0,0 +1,300 @@ +/* + * Copyright 2024 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::{borrow::Cow, collections::HashMap, fmt::Display, sync::Arc}; + +use hashable_map::HashableMap; +use imstr::ImString; +use rayon::prelude::*; + +use crate::{ + compile::{AstNode, DictionaryConstruction}, + execute_expression, + execution::{ + errors::{ErrorType, ExpressionResult, Raise as _}, + find_all_variable_accesses_in_expression, + stack::ScopeType, + values::string::formatting::Style, + ExecutionContext, + }, + values::StaticType, +}; + +use super::{ + MissingAttributeError, Object, StaticTypeName, StructDefinition, StructMember, Value, ValueType, +}; + +#[derive(Clone, Debug, Eq)] +pub(crate) struct DictionaryData { + pub members: HashableMap, + pub struct_def: StructDefinition, +} + +impl PartialEq for DictionaryData { + fn eq(&self, other: &Self) -> bool { + self.members == other.members + } +} + +pub fn find_all_variable_accesses_in_dictionary_construction( + dictionary_construction: &crate::compile::DictionaryConstruction, + access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, +) -> ExpressionResult<()> { + for assignment in dictionary_construction.assignments.iter() { + find_all_variable_accesses_in_expression( + &assignment.node.assignment.node, + access_collector, + )?; + } + + Ok(()) +} + +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct Dictionary { + pub(crate) data: Arc, +} + +impl Object for Dictionary { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + self.data.struct_def.clone().into() + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + write!(f, "(")?; + + let mut items = self.data.members.iter().peekable(); + + while let Some((name, value)) = items.next() { + write!(f, "{name} = ")?; + value.format(context, f, style, precision)?; + if items.peek().is_some() { + write!(f, ", ")?; + } + } + + write!(f, ")")?; + + Ok(()) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + if let Some(member) = self.data.members.get(attribute) { + Ok(member.clone()) + } else { + Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)) + } + } +} + +impl StaticTypeName for Dictionary { + fn static_type_name() -> Cow<'static, str> { + "Dictionary".into() + } +} + +impl StaticType for Dictionary { + fn static_type() -> ValueType { + static TYPE: std::sync::OnceLock>> = + std::sync::OnceLock::new(); + let signature = TYPE.get_or_init(|| Arc::new(HashableMap::new())); + ValueType::Dictionary(StructDefinition { + members: signature.clone(), + variadic: true, + }) + } +} + +impl Dictionary { + pub fn struct_def(&self) -> &StructDefinition { + &self.data.struct_def + } + + pub fn from_ast( + context: &ExecutionContext, + ast_node: &AstNode, + ) -> ExpressionResult { + let mut members = HashMap::with_capacity(ast_node.node.assignments.len()); + + context.stack.scope_mut( + context.stack_trace, + ScopeType::Inherited, + HashMap::new(), + |stack, stack_trace| { + let mut buffer = Vec::new(); + for group in ast_node.node.compute_groups() { + { + let context = ExecutionContext { + stack_trace, + stack, + ..context.clone() + }; + + buffer.par_extend(group.par_iter().map(|assignment| { + ( + assignment.node.name.node.clone(), + execute_expression(&context, &assignment.node.assignment), + ) + })); + } + + for (name, result) in buffer.drain(..) { + let value = result?; + + 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]), + )); + } + + stack.insert_value(name, value); + } + } + + Ok(()) + }, + )??; + + Ok(Self::new(context, members)) + } + + pub fn new(context: &ExecutionContext, map: HashMap) -> Self { + let mut struct_members = HashMap::with_capacity(map.len()); + + for (name, value) in map.iter() { + let member = StructMember { + ty: value.get_type(context), + default: None, + }; + + struct_members.insert(name.clone(), member); + } + + // HashableMap is just a wrapper around HashMap, so this has no additional cost. + let data = Arc::new(DictionaryData { + members: HashableMap::from(map), + struct_def: StructDefinition { + members: Arc::new(HashableMap::from(struct_members)), + variadic: false, + }, + }); + + Self { data } + } + + pub fn iter(&self) -> impl Iterator { + self.data.members.iter() + } + + pub fn get(&self, name: &str) -> Option<&Value> { + self.data.members.get(name) + } +} + +#[derive(Debug, Eq, PartialEq)] +pub struct DuplicateMemberError { + pub name: ImString, +} + +impl ErrorType for DuplicateMemberError {} + +impl Display for DuplicateMemberError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Dictionary was created with duplicate member: {}", + self.name + ) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::execution::{test_run, values}; + + #[test] + fn build_dictionary() { + let product = test_run("(none = std.consts.None)").unwrap(); + let expected = Arc::new(DictionaryData { + members: HashableMap::from(HashMap::from_iter([( + "none".into(), + values::ValueNone.into(), + )])), + struct_def: StructDefinition { + members: Arc::new(HashableMap::from(HashMap::from([( + "none".into(), + StructMember { + ty: ValueType::TypeNone, + default: None, + }, + )]))), + variadic: false, + }, + }); + + assert_eq!(product.as_dictionary().unwrap().data, expected); + } + + #[test] + fn duplicate_entries() { + // Two values of the same name is not allowed. + test_run("(void = (), void = ())").unwrap_err(); + } + + #[test] + fn non_existant_member() { + test_run("(void = ()).does_not_exist").unwrap_err(); + } + + #[test] + fn reference_own_member() { + let product = test_run("let d = (one = 1u, two = one + 1u); in d.two").unwrap(); + assert_eq!(product, values::UnsignedInteger::from(2).into()); + } + + #[test] + fn format() { + let product = test_run("\"{value}\"::format(value = (a = 1u)) == \"(a = 1)\"").unwrap(); + assert_eq!(product, values::Boolean(true).into()); + + let product = + test_run("let result = \"{value}\"::format(value = (a = 1u, b = 2u)); in result == \"(a = 1, b = 2)\" || result == \"(b = 2, a = 1)\"") + .unwrap(); + assert_eq!(product, values::Boolean(true).into()); + + let product = + test_run("\"{value:X}\"::format(value = (a = 0xDEADBEEFu)) == \"(a = DEADBEEF)\"") + .unwrap(); + assert_eq!(product, values::Boolean(true).into()); + } +} diff --git a/interpreter/src/execution/values/file.rs b/interpreter/src/execution/values/file.rs new file mode 100644 index 0000000..771e5c7 --- /dev/null +++ b/interpreter/src/execution/values/file.rs @@ -0,0 +1,111 @@ +/* + * Copyright 2026 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::{borrow::Cow, path::PathBuf, sync::Arc}; + +use imstr::ImString; + +use crate::{ + build_method, + execution::{ + errors::GenericFailure, + logging::{LogLevel, LogMessage}, + values::{ + string::formatting::Style, BuiltinCallableDatabase, IString, Object, StaticTypeName, + ValueType, + }, + ExecutionContext, + }, + values::StaticType, +}; + +#[derive(Debug, Clone)] +pub struct File { + pub path: Arc, +} + +impl Eq for File {} + +impl PartialEq for File { + fn eq(&self, other: &Self) -> bool { + self.path == other.path + } +} + +impl Object for File { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::File + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + if !matches!(style, Style::Debug) { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Files only support debug formatting".into(), + }); + } + + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Files cannot be formatted with precision".into(), + }); + } + + write!(f, "{:?}", self.path) + } +} + +impl StaticTypeName for File { + fn static_type_name() -> Cow<'static, str> { + "File".into() + } +} + +impl StaticType for File { + fn static_type() -> ValueType { + ValueType::File + } +} + +mod methods { + pub struct ToString; +} + +pub fn register_methods(database: &mut BuiltinCallableDatabase) { + build_method!( + database, + methods::ToString, "File::to_string", ( + 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))?; + + Ok(IString(ImString::from(content))) + } + ); +} diff --git a/interpreter/src/execution/values/integer.rs b/interpreter/src/execution/values/integer.rs new file mode 100644 index 0000000..508f109 --- /dev/null +++ b/interpreter/src/execution/values/integer.rs @@ -0,0 +1,1263 @@ +/* + * Copyright 2025 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use enum_downcast::{AsVariant, IntoVariant}; +use num_traits::{ + pow::checked_pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One, ToPrimitive, +}; +use std::{ + borrow::Cow, + cmp::Ordering, + hash::Hash, + ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}, +}; + +use crate::execution::{ + errors::{ExpressionResult, GenericFailure, Raise}, + logging::{LogLevel, LogMessage, StackTrace}, + values::{ + closure::BuiltinCallableDatabase, integer::methods::MethodSet, string::formatting::Style, + BuiltinFunction, MissingAttributeError, StaticType, + }, + ExecutionContext, +}; + +use super::{value_type::ValueType, Object, StaticTypeName, Value}; + +#[derive(Debug, Hash, Clone, Copy, Eq, PartialEq)] +pub struct Integer(pub I); + +impl From for Integer { + fn from(value: I) -> Self { + Self(value) + } +} + +const INTEGER_FORMAT_WARNING_MESSAGE: &str = + "Integer formats such as Octal and Hex ignore precision"; + +impl Object for Integer +where + I: IntOps, + Self: StaticTypeName + Into, + Value: AsVariant + IntoVariant, +{ + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + I::static_type() + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + match style { + Style::Default => write!(f, "{}", self.0), + Style::Debug => write!(f, "{}", self.0), + Style::Octal => { + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: INTEGER_FORMAT_WARNING_MESSAGE.into(), + }); + } + write!(f, "{:o}", self.0) + } + Style::Hex => { + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: INTEGER_FORMAT_WARNING_MESSAGE.into(), + }); + } + write!(f, "{:x}", self.0) + } + Style::CapitalizedHex => { + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: INTEGER_FORMAT_WARNING_MESSAGE.into(), + }); + } + write!(f, "{:X}", self.0) + } + Style::Exponent => { + write!(f, "{:e}", self.0) + } + Style::CapitalizedExponent => { + write!(f, "{:E}", self.0) + } + } + } + + fn bit_and(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + 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)?; + 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)?; + 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)?; + 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)?; + 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) + })?) + .into()) + } + fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + 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) + })?) + .into()) + } + fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + 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) + })?) + .into()) + } + fn divide(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + 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))?, + ) + .into()) + } + fn exponent(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + + // 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) + })?; + + 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) + })?) + .into()) + } + fn left_shift(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + 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)?; + Ok(Self(self.0 >> rhs.0).into()) + } + fn unary_plus(self, _context: &ExecutionContext) -> ExpressionResult { + Ok(self.into()) + } + fn unary_minus(self, context: &ExecutionContext) -> ExpressionResult { + self.0.neg(context.stack_trace) + } + fn unary_not(self, _context: &ExecutionContext) -> ExpressionResult { + Ok(Self(!self.0).into()) + } + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + match attribute { + "count_ones" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::CountOnes, + >() + .into()), + "count_zeros" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::CountZeros, + >() + .into()), + "leading_zeros" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::LeadingZeros, + >() + .into()), + "trailing_zeros" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::TrailingZeros, + >() + .into()), + "leading_ones" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::LeadingOnes, + >() + .into()), + "trailing_ones" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::TrailingOnes, + >() + .into()), + "rotate_left" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::RotateLeft, + >() + .into()), + "rotate_right" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::RotateRight, + >() + .into()), + "reverse_bits" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::ReverseBits, + >() + .into()), + "abs" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Abs, + >() + .into()), + "sqrt" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Sqrt, + >() + .into()), + "abs_diff" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::AbsDiff, + >() + .into()), + "signum" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Signum, + >() + .into()), + "is_positive" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::IsPositive, + >() + .into()), + "is_negative" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::IsNegative, + >() + .into()), + "midpoint" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Midpoint, + >() + .into()), + _ => Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)), + } + } +} + +impl StaticType for Integer +where + I: StaticType, +{ + fn static_type() -> ValueType { + I::static_type() + } +} + +trait IntOps: + CheckedAdd + + CheckedDiv + + CheckedMul + + CheckedSub + + CheckedAdd + + Ord + + Hash + + Copy + + Eq + + BitAnd + + BitOr + + BitXor + + Shl + + Shr + + Not + + One + + ToPrimitive + + StaticType + + std::fmt::Display + + std::fmt::Debug + + std::fmt::Binary + + std::fmt::LowerExp + + std::fmt::UpperExp + + std::fmt::LowerHex + + std::fmt::UpperHex + + std::fmt::Octal +{ + type MethodSet: MethodSet + 'static; + + fn neg(&self, stack_trace: &StackTrace) -> ExpressionResult; + + fn count_ones(&self) -> u64; + fn count_zeros(&self) -> u64; + + fn leading_zeros(&self) -> u64; + fn trailing_zeros(&self) -> u64; + + fn leading_ones(&self) -> u64; + fn trailing_ones(&self) -> u64; + + fn rotate_left(&self, by: u64) -> Self; + fn rotate_right(&self, by: u64) -> Self; + + fn reverse_bits(&self) -> Self; + + fn abs(&self) -> Self; + fn sqrt(&self) -> Self; + + fn abs_diff(&self, rhs: Self) -> u64; + fn signum(&self) -> i64; + fn is_positive(&self) -> bool; + fn is_negative(&self) -> bool; + fn midpoint(&self, rhs: Self) -> Self; +} + +// TODO cast_signed and cast_unsigned + +impl IntOps for i64 { + type MethodSet = methods::SignedIntegerMethodSet; + + fn neg(&self, _stack_trace: &StackTrace) -> ExpressionResult { + Ok(SignedInteger::from(-self).into()) + } + + fn count_ones(&self) -> u64 { + i64::count_ones(*self) as u64 + } + + fn count_zeros(&self) -> u64 { + i64::count_zeros(*self) as u64 + } + + fn leading_zeros(&self) -> u64 { + i64::leading_zeros(*self) as u64 + } + + fn trailing_zeros(&self) -> u64 { + i64::trailing_zeros(*self) as u64 + } + + fn leading_ones(&self) -> u64 { + i64::leading_ones(*self) as u64 + } + + fn trailing_ones(&self) -> u64 { + i64::trailing_ones(*self) as u64 + } + + fn rotate_left(&self, by: u64) -> Self { + i64::rotate_left(*self, by.min(u32::MAX as u64) as u32) + } + + fn rotate_right(&self, by: u64) -> Self { + i64::rotate_right(*self, by.min(u32::MAX as u64) as u32) + } + + fn reverse_bits(&self) -> Self { + i64::reverse_bits(*self) + } + + fn abs(&self) -> Self { + i64::abs(*self) + } + + fn sqrt(&self) -> Self { + i64::isqrt(*self) + } + + fn abs_diff(&self, rhs: Self) -> u64 { + i64::abs_diff(*self, rhs) + } + + fn signum(&self) -> i64 { + i64::signum(*self) + } + + fn is_positive(&self) -> bool { + i64::is_positive(*self) + } + + fn is_negative(&self) -> bool { + i64::is_negative(*self) + } + + fn midpoint(&self, rhs: Self) -> Self { + i64::midpoint(*self, rhs) + } +} + +impl StaticTypeName for Integer { + fn static_type_name() -> Cow<'static, str> { + "Signed Integer".into() + } +} + +impl StaticType for i64 { + fn static_type() -> ValueType { + ValueType::SignedInteger + } +} + +impl IntOps for u64 { + type MethodSet = methods::UnsignedIntegerMethodSet; + + fn neg(&self, stack_trace: &StackTrace) -> ExpressionResult { + Err(super::UnsupportedOperationError { + type_name: UnsignedInteger::static_type_name(), + operation_name: "negate", + } + .to_error(stack_trace)) + } + + fn count_ones(&self) -> u64 { + u64::count_ones(*self) as u64 + } + + fn count_zeros(&self) -> u64 { + u64::count_zeros(*self) as u64 + } + + fn leading_zeros(&self) -> u64 { + u64::leading_zeros(*self) as u64 + } + + fn trailing_zeros(&self) -> u64 { + u64::trailing_zeros(*self) as u64 + } + + fn leading_ones(&self) -> u64 { + u64::leading_ones(*self) as u64 + } + + fn trailing_ones(&self) -> u64 { + u64::trailing_ones(*self) as u64 + } + + fn rotate_left(&self, by: u64) -> Self { + u64::rotate_left(*self, by.min(u32::MAX as u64) as u32) + } + + fn rotate_right(&self, by: u64) -> Self { + u64::rotate_right(*self, by.min(u32::MAX as u64) as u32) + } + + fn reverse_bits(&self) -> Self { + u64::reverse_bits(*self) + } + + fn abs(&self) -> Self { + *self + } + + fn sqrt(&self) -> Self { + u64::isqrt(*self) + } + + fn abs_diff(&self, rhs: Self) -> u64 { + u64::abs_diff(*self, rhs) + } + + fn signum(&self) -> i64 { + if *self > 0 { + 1 + } else { + 0 + } + } + + fn is_positive(&self) -> bool { + *self > 0 + } + + fn is_negative(&self) -> bool { + false + } + + fn midpoint(&self, rhs: Self) -> Self { + u64::midpoint(*self, rhs) + } +} + +impl StaticTypeName for Integer { + fn static_type_name() -> Cow<'static, str> { + "Unsigned Integer".into() + } +} + +impl StaticType for u64 { + fn static_type() -> ValueType { + ValueType::UnsignedInteger + } +} + +mod methods { + use enum_downcast::IntoVariant; + + use super::*; + use crate::{build_method, execution::values::Boolean}; + + pub trait MethodSet { + type CountOnes; + type CountZeros; + + type LeadingZeros; + type TrailingZeros; + + type LeadingOnes; + type TrailingOnes; + + type RotateLeft; + type RotateRight; + + type ReverseBits; + + type Abs; + type Sqrt; + + type AbsDiff; + type Signum; + type IsPositive; + type IsNegative; + type Midpoint; + } + + macro_rules! build_method_set { + ($name:ident) => { + paste::paste! { + pub struct [<$name CountOnes>]; + pub struct [<$name CountZeros>]; + pub struct [<$name LeadingZeros>]; + pub struct [<$name TrailingZeros>]; + pub struct [<$name LeadingOnes>]; + pub struct [<$name TrailingOnes>]; + pub struct [<$name RotateLeft>]; + pub struct [<$name RotateRight>]; + pub struct [<$name ReverseBitsf>]; + pub struct [<$name Abs>]; + pub struct [<$name Sqrt>]; + pub struct [<$name AbsDiff>]; + pub struct [<$name Signum>]; + pub struct [<$name IsPositive>]; + pub struct [<$name IsNegative>]; + pub struct [<$name Midpoint>]; + + pub struct [<$name MethodSet>]; + impl MethodSet for [<$name MethodSet>] { + type CountOnes = [<$name CountOnes>]; + type CountZeros = [<$name CountZeros>]; + type LeadingZeros = [<$name LeadingZeros>]; + type TrailingZeros = [<$name TrailingZeros>]; + type LeadingOnes = [<$name LeadingOnes>]; + type TrailingOnes = [<$name TrailingOnes>]; + type RotateLeft = [<$name RotateLeft>]; + type RotateRight = [<$name RotateRight>]; + type ReverseBits = [<$name ReverseBitsf>]; + type Abs = [<$name Abs>]; + type Sqrt = [<$name Sqrt>]; + type AbsDiff = [<$name AbsDiff>]; + type Signum = [<$name Signum>]; + type IsPositive = [<$name IsPositive>]; + type IsNegative = [<$name IsNegative>]; + type Midpoint = [<$name Midpoint>]; + } + } + }; + } + + build_method_set!(SignedInteger); + build_method_set!(UnsignedInteger); + + pub fn register_methods(database: &mut BuiltinCallableDatabase) + where + I: IntOps + Send + Sync + std::fmt::Debug + std::fmt::LowerHex, + Integer: StaticTypeName + 'static + Into, + Value: IntoVariant> + AsVariant>, + { + build_method!( + database, + ::CountOnes, format!("{}::count_ones", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> UnsignedInteger { + Ok(UnsignedInteger::from(this.0.count_ones())) + } + ); + build_method!( + database, + ::CountZeros, format!("{}::count_zeros", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> UnsignedInteger { + Ok(UnsignedInteger::from(this.0.count_zeros())) + } + ); + build_method!( + database, + ::LeadingZeros, format!("{}::leading_zeros", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> UnsignedInteger { + Ok(UnsignedInteger::from(this.0.leading_zeros())) + } + ); + build_method!( + database, + ::TrailingZeros, format!("{}::trailing_zeros", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> UnsignedInteger { + Ok(UnsignedInteger::from(this.0.trailing_zeros())) + } + ); + build_method!( + database, + ::LeadingOnes, format!("{}::leading_ones", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> UnsignedInteger { + Ok(UnsignedInteger::from(this.0.leading_ones())) + } + ); + build_method!( + database, + ::TrailingOnes, format!("{}::trailing_ones", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> UnsignedInteger { + Ok(UnsignedInteger::from(this.0.trailing_ones())) + } + ); + build_method!( + database, + ::RotateLeft, format!("{}::rotate_left", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer, + n: UnsignedInteger + ) -> Integer { + Ok(Integer::::from(this.0.rotate_left(n.0))) + } + ); + build_method!( + database, + ::RotateRight, format!("{}::rotate_right", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer, + n: UnsignedInteger + ) -> Integer { + Ok(Integer::::from(this.0.rotate_right(n.0))) + } + ); + build_method!( + database, + ::ReverseBits, format!("{}::reverse_bits", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> Integer { + Ok(Integer::::from(this.0.reverse_bits())) + } + ); + build_method!( + database, + ::Abs, format!("{}::abs", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> Integer { + Ok(Integer::::from(this.0.abs())) + } + ); + build_method!( + database, + ::Sqrt, format!("{}::sqrt", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> Integer { + Ok(Integer::::from(this.0.sqrt())) + } + ); + build_method!( + database, + ::AbsDiff, format!("{}::abs_diff", Integer::::static_type_name()), ( + context: &ExecutionContext, + this: Integer, + other: Integer + ) -> UnsignedInteger { + Ok(UnsignedInteger::from(this.0.abs_diff(other.0))) + } + ); + build_method!( + database, + ::Signum, format!("{}::signum", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> SignedInteger { + Ok(SignedInteger::from(this.0.signum())) + } + ); + build_method!( + database, + ::IsPositive, format!("{}::is_positive", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> Boolean { + Ok(Boolean(this.0.is_positive())) + } + ); + build_method!( + database, + ::IsNegative, format!("{}::is_negative", Integer::::static_type_name()), ( + _context: &ExecutionContext, + this: Integer + ) -> Boolean { + Ok(Boolean(this.0.is_negative())) + } + ); + build_method!( + database, + ::Midpoint, format!("{}::midpoint", Integer::::static_type_name()), ( + context: &ExecutionContext, + this: Integer, + rhs: Integer + ) -> Integer { + Ok(Integer::::from(this.0.midpoint(rhs.0))) + } + ); + } +} + +pub type SignedInteger = Integer; +pub type UnsignedInteger = Integer; + +pub fn register_methods(database: &mut BuiltinCallableDatabase) { + methods::register_methods::(database); + methods::register_methods::(database); +} + +#[cfg(test)] +mod test { + use crate::execution::{test_run, values::Boolean}; + + use super::*; + + #[test] + fn signed_bit_or() { + let product = test_run("0xAAi | 0x55i").unwrap(); + assert_eq!(product, SignedInteger::from(0xFF).into()); + } + + #[test] + fn signed_bit_and() { + let product = test_run("0xFFi & 0x0Fi").unwrap(); + assert_eq!(product, SignedInteger::from(0x0F).into()); + } + + #[test] + fn signed_bit_xor() { + let product = test_run("0xF0i ^ 0xFFi").unwrap(); + assert_eq!(product, SignedInteger::from(0x0F).into()); + } + + #[test] + fn signed_cmp_greater_than() { + let product = test_run("3i > 2i").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("2i > 3i").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn signed_cmp_greater_than_eq() { + let product = test_run("3i >= 2i").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("3i >= 3i").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("2i >= 3i").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn signed_cmp_eq() { + let product = test_run("3i == 3i").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("3i == 2i").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("3i != 3i").unwrap(); + assert_eq!(product, Boolean(false).into()); + let product = test_run("3i != 2i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_cmp_less_than_eq() { + let product = test_run("3i <= 2i").unwrap(); + assert_eq!(product, Boolean(false).into()); + let product = test_run("3i <= 3i").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("2i <= 3i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_cmp_less_than() { + let product = test_run("3i < 2i").unwrap(); + assert_eq!(product, Boolean(false).into()); + let product = test_run("2i < 3i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_addition() { + let product = test_run("3i + 2i").unwrap(); + assert_eq!(product, SignedInteger::from(5).into()); + } + + #[test] + fn signed_subtraction() { + let product = test_run("3i - 2i").unwrap(); + assert_eq!(product, SignedInteger::from(1).into()); + } + + #[test] + fn signed_multiply() { + let product = test_run("3i * 2i").unwrap(); + assert_eq!(product, SignedInteger::from(6).into()); + } + + #[test] + fn signed_divide() { + let product = test_run("6i / 2i").unwrap(); + assert_eq!(product, SignedInteger::from(3).into()); + } + + #[test] + fn signed_exponent() { + let product = test_run("6i ** 3i").unwrap(); + assert_eq!(product, SignedInteger::from(216).into()); + } + + #[test] + fn signed_shift_left() { + let product = test_run("0x0Fi << 4i").unwrap(); + assert_eq!(product, SignedInteger::from(0xF0).into()); + } + + #[test] + fn signed_shift_right() { + let product = test_run("0xF0i >> 4i").unwrap(); + assert_eq!(product, SignedInteger::from(0x0F).into()); + } + + #[test] + fn signed_unary_plus() { + let product = test_run("+3i").unwrap(); + assert_eq!(product, SignedInteger::from(3).into()); + } + + #[test] + fn signed_unary_minus() { + let product = test_run("-3i").unwrap(); + assert_eq!(product, SignedInteger::from(-3).into()); + } + + #[test] + fn signed_unary_bit_not() { + let product = test_run("!0xAAi").unwrap(); + assert_eq!(product, SignedInteger::from(!0xAA).into()); + } + + #[test] + fn unsigned_bit_or() { + let product = test_run("0xAAu | 0x55u").unwrap(); + assert_eq!(product, UnsignedInteger::from(0xFF).into()); + } + + #[test] + fn unsigned_bit_and() { + let product = test_run("0xFFu & 0x0Fu").unwrap(); + assert_eq!(product, UnsignedInteger::from(0x0F).into()); + } + + #[test] + fn unsigned_bit_xor() { + let product = test_run("0xF0u ^ 0xFFu").unwrap(); + assert_eq!(product, UnsignedInteger::from(0x0F).into()); + } + + #[test] + fn unsigned_cmp_greater_than() { + let product = test_run("3u > 2u").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("2u > 3u").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn unsigned_cmp_greater_than_eq() { + let product = test_run("3u >= 2u").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("3u >= 3u").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("2u >= 3u").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn unsigned_cmp_eq() { + let product = test_run("3u == 3u").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("3u == 2u").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("3u != 3u").unwrap(); + assert_eq!(product, Boolean(false).into()); + let product = test_run("3u != 2u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_cmp_less_than_eq() { + let product = test_run("3u <= 2u").unwrap(); + assert_eq!(product, Boolean(false).into()); + let product = test_run("3u <= 3u").unwrap(); + assert_eq!(product, Boolean(true).into()); + let product = test_run("2u <= 3u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_cmp_less_than() { + let product = test_run("3u < 2u").unwrap(); + assert_eq!(product, Boolean(false).into()); + let product = test_run("2u < 3u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_addition() { + let product = test_run("3u + 2u").unwrap(); + assert_eq!(product, UnsignedInteger::from(5).into()); + } + + #[test] + fn unsigned_subtraction() { + let product = test_run("3u - 2u").unwrap(); + assert_eq!(product, UnsignedInteger::from(1).into()); + } + + #[test] + fn unsigned_multiply() { + let product = test_run("3u * 2u").unwrap(); + assert_eq!(product, UnsignedInteger::from(6).into()); + } + + #[test] + fn unsigned_divide() { + let product = test_run("6u / 2u").unwrap(); + assert_eq!(product, UnsignedInteger::from(3).into()); + } + + #[test] + fn unsigned_exponent() { + let product = test_run("6u ** 3u").unwrap(); + assert_eq!(product, UnsignedInteger::from(216).into()); + } + + #[test] + fn unsigned_shift_left() { + let product = test_run("0x0Fu << 4u").unwrap(); + assert_eq!(product, UnsignedInteger::from(0xF0).into()); + } + + #[test] + fn unsigned_shift_right() { + let product = test_run("0xF0u >> 4u").unwrap(); + assert_eq!(product, UnsignedInteger::from(0x0F).into()); + } + + #[test] + fn unsigned_unary_plus() { + let product = test_run("+3u").unwrap(); + assert_eq!(product, UnsignedInteger::from(3).into()); + } + + #[test] + fn unsigned_unary_minus() { + test_run("-3u").unwrap_err(); + } + + #[test] + fn unsigned_unary_bit_not() { + let product = test_run("!0xAAu").unwrap(); + assert_eq!(product, UnsignedInteger::from(!0xAA).into()); + } + + #[test] + fn unsigned_count_ones() { + let product = test_run("0x00FF00FF00FF00FFu::count_ones() == 32u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_count_ones() { + let product = test_run("0x00FF00FF00FF00FFi::count_ones() == 32u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_count_zeros() { + let product = test_run("0x00FF00FF00FF00FFu::count_zeros() == 32u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_count_zeros() { + let product = test_run("0x00FF00FF00FF00FFi::count_zeros() == 32u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_leading_zeros() { + let product = test_run("0x00FF00FF00FF00FFu::leading_zeros() == 8u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_leading_zeros() { + let product = test_run("0x00FF00FF00FF00FFi::leading_zeros() == 8u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_trailing_zeros() { + let product = test_run("0xFF00FF00FF00FF00u::trailing_zeros() == 8u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_trailing_zeros() { + let product = test_run("0x0000FF00FF00FF00i::trailing_zeros() == 8u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_leading_ones() { + let product = test_run("0xFF00FF00FF00FF00u::leading_ones() == 8u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_leading_ones() { + let product = test_run("(-1i)::leading_ones() == 64u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_trailing_ones() { + let product = test_run("0x00FF00FF00FF00FFu::trailing_ones() == 8u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_trailing_ones() { + let product = test_run("0x00FF00FF00FF00FFi::trailing_ones() == 8u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_rotate_left() { + let product = + test_run("0x00FF00FF00FF00FFu::rotate_left(n = 4u) == 0x0FF00FF00FF00FF0u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_rotate_left() { + let product = + test_run("0x00FF00FF00FF00FFi::rotate_left(n = 4u) == 0x0FF00FF00FF00FF0i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_rotate_right() { + let product = + test_run("0x00FF00FF00FF00FFu::rotate_right(n = 4u) == 0xF00FF00FF00FF00Fu").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_rotate_right() { + let product = test_run("0xAAi::rotate_right(n = 12u) == 0x0AA0000000000000i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_reverse_bits() { + let product = + test_run("0x5555555555555555u::reverse_bits() == 0xAAAAAAAAAAAAAAAAu").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_reverse_bits() { + let product = + test_run("0x00000000FFFF0000i::reverse_bits() == 0x0000FFFF00000000i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_abs() { + let product = test_run("10u::abs() == 10u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_abs() { + let product = test_run("(-10i)::abs() == 10i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_sqrt() { + let product = test_run("64u::sqrt() == 8u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_sqrt() { + let product = test_run("64i::sqrt() == 8i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_abs_diff() { + let product = test_run("10u::abs_diff(other = 5u) == 5u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_abs_diff() { + let product = test_run("(-10i)::abs_diff(other = 5i) == 15u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_signum() { + let product = test_run("10u::signum() == 1i").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("0u::signum() == 0i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_signum() { + let product = test_run("10i::signum() == 1i").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("0i::signum() == 0i").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(-1i)::signum() == -1i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_is_positive() { + let product = test_run("10u::is_positive()").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("0u::is_positive()").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn signed_is_positive() { + let product = test_run("10i::is_positive()").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("0i::is_positive()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("(-1i)::is_positive()").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn unsigned_is_negative() { + let product = test_run("10u::is_negative()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("0u::is_negative()").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn signed_is_negative() { + let product = test_run("10i::is_negative()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("0i::is_negative()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("(-1i)::is_negative()").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + // type Midpoint = [<$name Midpoint>]; + #[test] + fn unsigned_midpoint() { + let product = test_run("10u::midpoint(rhs = 10u) == 10u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_midpoint() { + let product = test_run("10i::midpoint(rhs = 10i) == 10i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unsigned_format() { + let product = test_run("\"{a} {b:?} {c:o} {d:x} {e:X} {f:e} {g:E}\"::format(a = 10u, b = 32u, c = 0o123u, d = 0xDEADBEEFu, e = 0xDEADBEEFu, f = 1000u, g = 1000u) == \"10 32 123 deadbeef DEADBEEF 1e3 1E3\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signed_format() { + let product = test_run("\"{a} {b:?} {c:o} {d:x} {e:X} {f:e} {g:E}\"::format(a = 10i, b = 32i, c = 0o123i, d = 0xDEADBEEFi, e = 0xDEADBEEFi, f = 1000i, g = 1000i) == \"10 32 123 deadbeef DEADBEEF 1e3 1E3\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + } +} diff --git a/interpreter/src/execution/values/list.rs b/interpreter/src/execution/values/list.rs new file mode 100644 index 0000000..dae696f --- /dev/null +++ b/interpreter/src/execution/values/list.rs @@ -0,0 +1,978 @@ +/* + * Copyright 2024 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use crate::{ + build_closure_type, build_method, + compile::{AstNode, Expression}, + execute_expression, + execution::{ + errors::{Error, ErrorType, GenericFailure, Raise as _}, + values::{ + closure::BuiltinCallableDatabase, string::formatting::Style, Boolean, BuiltinFunction, + Dictionary, MissingAttributeError, StaticType, UnsignedInteger, ValueNone, + }, + ExecutionContext, + }, +}; + +use super::{value_type::ValueType, ExpressionResult, Object, StaticTypeName, Value}; + +use std::{borrow::Cow, cmp::Ordering, collections::HashMap, sync::Arc}; + +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct List { + // In theory, we could use a lot less memory by dynamically sizing everything to fit + // our smallest type, but we aren't going to implement that today. + + // None means that the list is empty and can be used anywhere. + internal_type: Option, + values: Arc>, +} + +impl List { + pub fn from_ast( + context: &ExecutionContext, + ast_node: &AstNode>>, + ) -> ExpressionResult { + let values: ExpressionResult> = ast_node + .node + .par_iter() + .map(|expression| execute_expression(context, expression)) + .collect(); + + Ok(List::from_iter(context, values?)) + } + + pub fn from_iter(context: &ExecutionContext, iterator: I) -> Self + where + I: IntoIterator, + { + let values: Vec<_> = iterator.into_iter().collect(); + + let internal_type = + values + .first() + .map(|first| first.get_type(context)) + .map(|initial_type| { + values.iter().fold(initial_type, |accumulated, next| { + accumulated.merge(next.get_type(context)) + }) + }); + + Self { + internal_type, + values: Arc::new(values), + } + } + + fn map_raw( + &self, + context: &ExecutionContext, + operation_name: &'static str, + mut operation: impl FnMut(&Value) -> ExpressionResult, + ) -> ExpressionResult { + let values: Vec = self + .values + .iter() + .enumerate() + .map(|(index, value)| { + operation(value).map_err(|error| OperationMappingError { + operation_name, + index, + error, + }) + }) + .collect::>() + .map_err(|error| error.to_error(context.stack_trace))?; + + Ok(Self::from_iter(context, values)) + } + + fn map_operation( + &self, + context: &ExecutionContext, + operation_name: &'static str, + operation: impl FnMut(&Value) -> ExpressionResult, + ) -> ExpressionResult { + self.map_raw(context, operation_name, operation) + .map(|value| value.into()) + } +} + +impl Object for List { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::List(self.internal_type.clone().map(Box::new)) + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + write!(f, "[")?; + + let mut items = self.values.iter().peekable(); + + while let Some(value) = items.next() { + value.format(context, f, style, precision)?; + if items.peek().is_some() { + write!(f, ", ")?; + } + } + + write!(f, "]")?; + + Ok(()) + } + + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + Ok(self.values == rhs.values) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + match attribute { + "append" => Ok(BuiltinFunction::new::().into()), + "slice" => Ok(BuiltinFunction::new::().into()), + "get" => Ok(BuiltinFunction::new::().into()), + "chunks" => Ok(BuiltinFunction::new::().into()), + + "map" => Ok(BuiltinFunction::new::().into()), + "fold" => Ok(BuiltinFunction::new::().into()), + "retain" => Ok(BuiltinFunction::new::().into()), + + "sort" => Ok(BuiltinFunction::new::().into()), + "reverse" => Ok(BuiltinFunction::new::().into()), + "truncate" => Ok(BuiltinFunction::new::().into()), + + "deduplicate" => Ok(BuiltinFunction::new::().into()), + "union" => Ok(BuiltinFunction::new::().into()), + "intersection" => Ok(BuiltinFunction::new::().into()), + "difference" => Ok(BuiltinFunction::new::().into()), + "symmetric_difference" => { + Ok(BuiltinFunction::new::().into()) + } + "cartesian_product" => Ok(BuiltinFunction::new::().into()), + _ => Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)), + } + } + + fn and(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "and", move |value| { + value.clone().and(context, rhs.clone()) + }) + } + fn or(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "or", move |value| { + value.clone().or(context, rhs.clone()) + }) + } + fn xor(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "xor", move |value| { + value.clone().xor(context, rhs.clone()) + }) + } + fn bit_and(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "bit and", move |value| { + value.clone().bit_and(context, rhs.clone()) + }) + } + fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "bit or", move |value| { + value.clone().bit_or(context, rhs.clone()) + }) + } + fn bit_xor(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "bit xor", move |value| { + value.clone().bit_xor(context, rhs.clone()) + }) + } + fn addition(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "addition", move |value| { + value.clone().addition(context, rhs.clone()) + }) + } + fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "subtraction", move |value| { + value.clone().subtraction(context, rhs.clone()) + }) + } + fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "multiply", move |value| { + value.clone().multiply(context, rhs.clone()) + }) + } + fn divide(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "divide", move |value| { + value.clone().divide(context, rhs.clone()) + }) + } + fn exponent(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "exponent", move |value| { + value.clone().exponent(context, rhs.clone()) + }) + } + fn left_shift(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "left shift", move |value| { + value.clone().left_shift(context, rhs.clone()) + }) + } + fn right_shift(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + self.map_operation(context, "right shift", move |value| { + value.clone().right_shift(context, rhs.clone()) + }) + } + fn unary_plus(self, context: &ExecutionContext) -> ExpressionResult { + self.map_operation(context, "unary plus", move |value| { + value.clone().unary_plus(context) + }) + } + fn unary_minus(self, context: &ExecutionContext) -> ExpressionResult { + self.map_operation(context, "unary minus", move |value| { + value.clone().unary_minus(context) + }) + } + fn unary_not(self, context: &ExecutionContext) -> ExpressionResult { + self.map_operation(context, "unary not", move |value| { + value.clone().unary_not(context) + }) + } +} + +impl StaticTypeName for List { + fn static_type_name() -> Cow<'static, str> { + "List".into() + } +} + +impl StaticType for List { + fn static_type() -> ValueType { + ValueType::List(Option::None) + } +} + +#[derive(Debug)] +struct OperationMappingError { + pub operation_name: &'static str, + pub index: usize, + pub error: Error, +} + +impl ErrorType for OperationMappingError {} + +impl std::fmt::Display for OperationMappingError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Failed to map operation `{}` to index {}: {}", + self.operation_name, self.index, self.error + ) + } +} + +#[derive(Debug)] +struct SortingError { + pub errors: Vec, +} + +impl ErrorType for SortingError {} + +impl std::fmt::Display for SortingError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.errors.len() == 1 { + write!( + f, + "Failed to sort. An element of the list could not be compared: {}", + self.errors[0] + ) + } else { + writeln!( + f, + "Failed to sort. Multiple elements could not be compared:", + )?; + + for error in self.errors.iter() { + writeln!(f, "\t{}", error)?; + } + + Ok(()) + } + } +} + +mod methods { + pub struct Append; + pub struct Slice; + pub struct Get; + pub struct Chunks; + + pub struct Map; + pub struct Fold; + pub struct Retain; + + pub struct Sort; + pub struct Reverse; + pub struct Truncate; + + pub struct Deduplicate; + pub struct Union; + pub struct Intersection; + pub struct Difference; + pub struct SymmetricDifference; + pub struct CartesianProduct; +} + +pub fn register_methods(database: &mut BuiltinCallableDatabase) { + build_closure_type!(MapClosure(c: Value) -> Value); + build_closure_type!(FoldClosure(previous: Value, c: Value) -> Value); + build_closure_type!(RetainClosure(c: Value) -> Boolean); + + build_method!( + database, + methods::Append, "List::append", ( + context: &ExecutionContext, + this: List, + rhs: List + ) -> List { + let mut content = Arc::unwrap_or_clone(this.values); + content.extend_from_slice(&rhs.values); + + Ok(List::from_iter(context, content)) + } + ); + build_method!( + database, + methods::Slice, "List::slice", ( + context: &ExecutionContext, + this: List, + start: Option = ValueNone.into(), + end: Option = ValueNone.into() + ) -> List { + + let start = if let Some(start) = start { + start.0 as usize + } else { + 0usize + }; + + let end = if let Some(end) = end { + end.0 as usize + } else { + this.values.len() + }; + + let slice = this.values.get(start..end); + + 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)) + } + } + ); + build_method!( + database, + methods::Get, "List::get", ( + context: &ExecutionContext, + this: List, + i: UnsignedInteger + ) -> Value { + let slice = this.values.get(i.0 as usize); + + if let Some(slice) = slice { + Ok(slice.clone()) + } else { + Err(GenericFailure("Index out of range".into()).to_error(context.stack_trace)) + } + } + ); + build_method!( + database, + methods::Chunks, "List::chunks", ( + context: &ExecutionContext, + this: List, + size: UnsignedInteger, + strict: Boolean = Boolean(true).into() + ) -> List { + fn wrap_chunks<'i, I: Iterator>(context: &ExecutionContext, chunks: I) -> List { + let mut list = Vec::new(); + for chunk in chunks { + list.push(List::from_iter(context, chunk.iter().cloned()).into()); + } + List::from_iter(context, list.into_iter()) + } + + let chunks = if strict.0 { + wrap_chunks(context, this.values.chunks_exact(size.0 as usize)) + } else { + wrap_chunks(context, this.values.chunks(size.0 as usize)) + }; + + Ok(chunks) + + } + ); + build_method!( + database, + methods::Map, "List::map",( + context: &ExecutionContext, + this: List, + f: MapClosure + ) -> List { + let values: Vec = this.values.iter().map(|c| f.call(context, Dictionary::new(context, HashMap::from_iter([ + ( + "c".into(), + c.clone() + ) + ])))).collect::>()?; + + Ok(List::from_iter(context, values.into_iter())) + } + ); + build_method!( + database, + methods::Fold, "List::fold",( + context: &ExecutionContext, + this: List, + init: Value, + f: FoldClosure + ) -> Value { + let mut accumulator = init; + for component in this.values.iter() { + accumulator = f.call(context, Dictionary::new(context, HashMap::from_iter([ + ( + "c".into(), + component.clone() + ), + ( + "previous".into(), + accumulator + ) + ])))?; + } + + Ok(accumulator) + } + ); + build_method!( + database, + methods::Retain, "List::retain",( + context: &ExecutionContext, + this: List, + f: RetainClosure + ) -> List { + let mut values: Vec = Vec::with_capacity(this.values.len()); + + for value in this.values.iter() { + let retain = f.call(context, Dictionary::new(context, HashMap::from_iter([ + ( + "c".into(), + value.clone() + ) + ])))?.downcast::(context.stack_trace)?; + + if retain.0 { + values.push(value.clone()); + } + } + + Ok(List::from_iter(context, values.into_iter())) + } + ); + build_method!( + database, + methods::Sort, "List::sort",( + context: &ExecutionContext, + this: List + ) -> List { + let mut errors: Vec = vec![]; + let mut values: Vec = Arc::unwrap_or_clone(this.values); + values.sort_unstable_by(|left: &Value, right: &Value| { + let result = left.clone().cmp(context, right.clone()); + match result { + Ok(cmp) => cmp, + Err(error) => { + errors.push(error); + Ordering::Less + } + } + }); + + if errors.is_empty() { + Ok(List::from_iter(context, values.into_iter())) + } else { + Err(SortingError { errors }.to_error(context.stack_trace)) + } + } + ); + build_method!( + database, + methods::Reverse, "List::reverse",( + context: &ExecutionContext, + this: List + ) -> List { + let mut values: Vec = Arc::unwrap_or_clone(this.values); + values.reverse(); + + Ok(List::from_iter(context, values.into_iter())) + } + ); + build_method!( + database, + methods::Truncate, "List::truncate",( + context: &ExecutionContext, + this: List, + length: UnsignedInteger + ) -> List { + let mut values: Vec = Arc::unwrap_or_clone(this.values); + values.truncate(length.0 as usize); + + Ok(List::from_iter(context, values.into_iter())) + } + ); + build_method!( + database, + methods::Deduplicate, "List::deduplicate",( + context: &ExecutionContext, + this: List + ) -> List { + let mut values: Vec = Arc::unwrap_or_clone(this.values); + values.dedup_by(|left, right| left.clone().eq(context, right.clone()).unwrap_or(false)); + + Ok(List::from_iter(context, values.into_iter())) + } + ); + build_method!( + database, + methods::Union, "List::union",( + context: &ExecutionContext, + this: List, + other: List + ) -> List { + let mut values: Vec = Arc::unwrap_or_clone(this.values); + + for new in other.values.iter() { + if !values.iter().any(|old| old.clone().eq(context, new.clone()).unwrap_or(false)) { + values.push(new.clone()); + } + } + + Ok(List::from_iter(context, values.into_iter())) + } + ); + build_method!( + database, + methods::Intersection, "List::intersection",( + context: &ExecutionContext, + this: List, + other: List + ) -> List { + let mut values: Vec = Vec::new(); + + for new in other.values.iter() { + if this.values.iter().any(|old| old.clone().eq(context, new.clone()).unwrap_or(false)) { + values.push(new.clone()); + } + } + + Ok(List::from_iter(context, values.into_iter())) + } + ); + build_method!( + database, + methods::Difference, "List::difference",( + context: &ExecutionContext, + this: List, + other: List + ) -> List { + let mut values: Vec = Vec::new(); + + // Add values from ourselves that the other list does not have. + for new in this.values.iter() { + if !other.values.iter().any(|old| old.clone().eq(context, new.clone()).unwrap_or(false)) { + values.push(new.clone()); + } + } + + Ok(List::from_iter(context, values.into_iter())) + } + ); + build_method!( + database, + methods::SymmetricDifference, "List::symmetric_difference",( + context: &ExecutionContext, + this: List, + other: List + ) -> List { + let mut values: Vec = Vec::new(); + + // Add values from ourselves that the other list does not have. + for new in this.values.iter() { + if !other.values.iter().any(|old| old.clone().eq(context, new.clone()).unwrap_or(false)) { + values.push(new.clone()); + } + } + + // Add values from the other list that are not already in the new list. + for new in other.values.iter() { + if !this.values.iter().any(|old| old.clone().eq(context, new.clone()).unwrap_or(false)) { + values.push(new.clone()); + } + } + + Ok(List::from_iter(context, values.into_iter())) + } + ); + build_method!( + database, + methods::CartesianProduct, "List::cartesian_product",( + context: &ExecutionContext, + this: List, + other: List + ) -> List { + let mut values: Vec = Vec::with_capacity(this.values.len() * other.values.len()); + + for a in this.values.iter() { + for b in other.values.iter() { + let list = List::from_iter(context, [a.clone(), b.clone()].into_iter()); + values.push(list.into()); + } + } + + Ok(List::from_iter(context, values.into_iter())) + } + ); +} + +#[cfg(test)] +mod test { + use super::*; + use crate::execution::{ + test_context, test_run, + values::{Boolean, SignedInteger, UnsignedInteger}, + }; + + #[test] + fn create_empty() { + test_context([], |context| { + let product = test_run("[]").unwrap(); + assert_eq!(product, List::from_iter(context, []).into()); + }) + } + + #[test] + fn create() { + test_context([], |context| { + let product = test_run("[1u, 2u, 3u]").unwrap(); + assert_eq!( + product, + List::from_iter( + context, + [ + UnsignedInteger::from(1).into(), + UnsignedInteger::from(2).into(), + UnsignedInteger::from(3).into() + ] + ) + .into() + ); + }) + } + + #[test] + fn create_multi_type() { + test_context([], |context| { + let product = test_run("[1u, 2i, 3u]").unwrap(); + assert_eq!( + product, + List::from_iter( + context, + [ + UnsignedInteger::from(1).into(), + SignedInteger::from(2).into(), + UnsignedInteger::from(3).into() + ] + ) + .into() + ); + }) + } + + #[test] + fn type_detection() { + test_context([], |context| { + assert_eq!( + List::from_iter( + context, + [ + UnsignedInteger::from(1).into(), + SignedInteger::from(2).into(), + UnsignedInteger::from(3).into() + ] + ) + .internal_type, + Some(ValueType::MultiType( + Box::new(ValueType::UnsignedInteger), + Box::new(ValueType::SignedInteger) + )) + ); + }) + } + + #[test] + fn and() { + let product = test_run("([true, false] && true) == [true, false]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn or() { + let product = test_run("([true, false] || true) == [true, true]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn xor() { + let product = test_run("([true, false] ^^ false) == [false, true]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn bit_and() { + let product = test_run("([0xFFFFFFFFFFFFFFFFu, 0x0000000000000000u] & 0x0F0F0F0F0F0F0F0Fu) == [0x0F0F0F0F0F0F0F0Fu, 0x0000000000000000u]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn bit_or() { + let product = test_run("([0xFFFFFFFFFFFFFFFFu, 0x0000000000000000u] | 0x0F0F0F0F0F0F0F0Fu) == [0xFFFFFFFFFFFFFFFFu, 0x0F0F0F0F0F0F0F0Fu]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn bit_xor() { + let product = test_run("([0xFFFFFFFFFFFFFFFFu, 0x0000000000000000u] ^ 0x0F0F0F0F0F0F0F0Fu) == [0xF0F0F0F0F0F0F0F0u, 0x0F0F0F0F0F0F0F0Fu]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn addition() { + let product = test_run("([1, 2] + 10) == [11, 12]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn subtraction() { + let product = test_run("([1, 2] - 10) == [-9, -8]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn multiply() { + let product = test_run("([1, 2] * 10) == [10, 20]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn divide() { + let product = test_run("([5, 10] / 2) == [2.5, 5]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn exponent() { + let product = test_run("([2, 10] ** 3) == [8, 1000]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn left_shift() { + let product = test_run("([0x0Au, 0x0Fu] << 4u) == [0xA0u, 0xF0u]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn right_shift() { + let product = test_run("([0xA0u, 0xF0u] >> 4u) == [0x0Au, 0x0Fu]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unary_plus() { + let product = test_run("(+[1, 2]) == [1, 2]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unary_minus() { + let product = test_run("(-[1, 2]) == [-1, -2]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn unary_not() { + let product = test_run("(![true, false]) == [false, true]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_append() { + let product = test_run("[1, 2]::append(rhs = [3, 4]) == [1, 2, 3, 4]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_slice() { + let product = test_run("[1, 2, 3, 4]::slice(start = 1u, end = 3u) == [2, 3]").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("[1, 2, 3, 4]::slice(end = 3u) == [1, 2, 3]").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("[1, 2, 3, 4]::slice(start = 1u) == [2, 3, 4]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_get() { + let product = test_run("let list = [1, 2, 3, 4]; in list::get(i = 0u) == 1 && list::get(i = 1u) == 2 && list::get(i = 2u) == 3 && list::get(i = 3u) == 4").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_chunks() { + let product = + test_run("[1, 2, 3, 4, 5, 6, 7]::chunks(size = 2u) == [[1, 2], [3, 4], [5, 6]]") + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "[1, 2, 3, 4, 5, 6, 7]::chunks(size = 2u, strict = true) == [[1, 2], [3, 4], [5, 6]]", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("[1, 2, 3, 4, 5, 6, 7]::chunks(size = 2u, strict = false) == [[1, 2], [3, 4], [5, 6], [7]]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_map() { + let product = test_run( + "[1u, 2u, 3u, 4u]::map(f= (c: std.types.UInt) -> std.types.UInt: c + 1u) == [2u, 3u, 4u, 5u]", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_fold() { + let product = + test_run("[1u, 2u, 3u, 4u]::fold(init = 0u, f = (previous: std.types.UInt, c: std.types.UInt) -> std.types.UInt: previous + c) == 10u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_retain() { + let product = test_run( + "[1u, 2u, 3u, 4u]::retain(f = (c: std.types.UInt) -> std.types.Bool: c == 1u || c == 3u) == [1u, 3u]", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_sort() { + let product = test_run("[3, 2, 4, 1]::sort() == [1, 2, 3, 4]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_reverse() { + let product = test_run("[1, 2, 3, 4]::reverse() == [4, 3, 2, 1]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_truncate() { + let product = test_run("[1, 2, 3, 4]::truncate(length = 2u) == [1, 2]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_deduplicate() { + let product = + test_run("[1, 2, 1, 4, 3, 4, 3]::sort()::deduplicate() == [1, 2, 3, 4]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_union() { + let product = test_run("[1, 2, 3]::union(other = [3, 4, 5]) == [1, 2, 3, 4, 5]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_intersection() { + let product = test_run("[1, 2, 3]::intersection(other = [3, 4, 5]) == [3]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_difference() { + let product = test_run("[1, 2, 3]::difference(other = [3, 4, 5]) == [1, 2]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_symmetric_difference() { + let product = + test_run("[1, 2, 3]::symmetric_difference(other = [3, 4, 5]) == [1, 2, 4, 5]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_cartesian_product() { + let product = test_run("[1, 2, 3]::cartesian_product(other = [3, 4, 5]) == [[1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 3], [3, 4], [3, 5]]").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn format() { + let product = test_run("\"{value}\"::format(value = [1u]) == \"[1]\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"{value}\"::format(value = [1u, 2u]) == \"[1, 2]\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = + test_run("\"{value:X}\"::format(value = [0xDEADBEEFu]) == \"[DEADBEEF]\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + } +} diff --git a/interpreter/src/execution/values/manifold_mesh.rs b/interpreter/src/execution/values/manifold_mesh.rs new file mode 100644 index 0000000..c941d1f --- /dev/null +++ b/interpreter/src/execution/values/manifold_mesh.rs @@ -0,0 +1,464 @@ +/* + * Copyright 2026 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use boolmesh::prelude::*; +use common_data_types::{Dimension, Float, RawFloat}; +use std::{ + borrow::Cow, + io::{BufWriter, Write as _}, + sync::Arc, +}; + +use crate::{ + build_function, build_method, + execution::{ + errors::{ExpressionResult, GenericFailure, Raise}, + store::IoError, + }, + values::{ + scalar::{Length, UnwrapNotNan}, + vector::{Length3, Zero3}, + Boolean, BuiltinCallableDatabase, BuiltinFunction, DowncastError, File, IString, + MissingAttributeError, Object, Scalar, StaticType, StaticTypeName, Style, UnsignedInteger, + Value, ValueNone, ValueType, Vector3, + }, + ExecutionContext, +}; + +#[derive(Debug, Clone)] +pub struct ManifoldMesh3D(Arc); + +impl Eq for ManifoldMesh3D {} + +impl PartialEq for ManifoldMesh3D { + fn eq(&self, other: &Self) -> bool { + // FIXME this is skipping a lot of information. + self.0.ps == other.0.ps + } +} + +impl std::hash::Hash for ManifoldMesh3D { + fn hash(&self, state: &mut H) { + // FIXME this is skipping a lot of information. + self.0.ps.iter().for_each(|v| { + v.x.to_le_bytes().hash(state); + v.y.to_le_bytes().hash(state); + v.z.to_le_bytes().hash(state); + }); + } +} + +impl Object for ManifoldMesh3D { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::ManifoldMesh3D + } + + fn format( + &self, + _context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + _style: Style, + _precision: Option, + ) -> std::fmt::Result { + write!( + f, + "Manifold Mesh with {} verticies, {} faces, and {} half-edges", + self.0.nv, self.0.nf, self.0.nh + ) + } + + fn addition(mut self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let input = Self::unpack_arithmetic_input(context, 0.0, rhs)?; + match input { + ArethmeticInput::Vector(vector) => { + let vector = vector.raw_value(); + let manifold = Arc::make_mut(&mut self.0); + manifold.translate(vector.x, vector.y, vector.z); + 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) + })?; + Ok(Self(Arc::new(manifold)).into()) + } + } + } + + fn subtraction(mut self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let input = Self::unpack_arithmetic_input(context, 0.0, rhs)?; + match input { + ArethmeticInput::Vector(vector) => { + let vector = vector.raw_value(); + let manifold = Arc::make_mut(&mut self.0); + manifold.translate(-vector.x, -vector.y, -vector.z); + 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) + })?; + Ok(Self(Arc::new(manifold)).into()) + } + } + } + + fn multiply(mut self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let input = rhs.downcast::(context.stack_trace)?; + 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)?; + let manifold = compute_boolean(&self.0, &rhs.0, OpType::Add) + .map_err(|message| GenericFailure(message.into()).to_error(context.stack_trace))?; + 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)?; + + // 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))?; + + let union = compute_boolean(&self.0, &rhs.0, OpType::Add) + .map_err(|message| GenericFailure(message.into()).to_error(context.stack_trace))?; + + let difference = compute_boolean(&union, &intersection, OpType::Subtract) + .map_err(|message| GenericFailure(message.into()).to_error(context.stack_trace))?; + + 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)?; + let manifold = compute_boolean(&self.0, &rhs.0, OpType::Intersect) + .map_err(|message| GenericFailure(message.into()).to_error(context.stack_trace))?; + Ok(Self(Arc::new(manifold)).into()) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + match attribute { + "to_stl" => Ok(BuiltinFunction::new::().into()), + _ => Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)), + } + } +} + +enum ArethmeticInput { + Vector(Vector3), + Manifold(ManifoldMesh3D), +} + +impl ManifoldMesh3D { + fn unpack_arithmetic_input( + context: &ExecutionContext, + default: RawFloat, + input: Value, + ) -> ExpressionResult { + let value = match input { + Value::Vector2(v) => { + let raw = v.raw_value(); + Ok(ArethmeticInput::Vector(Vector3::new( + context, + v.dimension(), + [raw.x, raw.y, default], + )?)) + } + Value::Vector3(v) => Ok(ArethmeticInput::Vector(v)), + Value::ManifoldMesh3D(manifold) => Ok(ArethmeticInput::Manifold(manifold)), + value => Err(DowncastError { + expected: "Vector2 or Vector3 of lengths, or another ManifoldMesh3D".into(), + got: value.get_type(context).name(), + } + .to_error(context.stack_trace)), + }?; + + match value { + // We need to validate the value of the dimension. + ArethmeticInput::Vector(vector) => { + if vector.dimension() != Dimension::length() { + // Wrong dimension type. + Err(DowncastError { + expected: "Vector2 or Vector3 of lengths, or another ManifoldMesh3D".into(), + got: vector.get_type(context).name(), + } + .to_error(context.stack_trace)) + } else { + Ok(ArethmeticInput::Vector(vector)) + } + } + // Everything else can be passed as is. + value => Ok(value), + } + } +} + +impl StaticTypeName for ManifoldMesh3D { + fn static_type_name() -> Cow<'static, str> { + "ManifoldMesh3D".into() + } +} + +impl StaticType for ManifoldMesh3D { + fn static_type() -> ValueType { + ValueType::ManifoldMesh3D + } +} + +pub mod methods { + pub struct GenerateCone; + pub struct GenerateCube; + pub struct GenerateCylinder; + pub struct GenerateIcosphere; + pub struct GenerateTorus; + pub struct GenerateUvSphere; + + pub struct ToStl; +} + +fn unpack_radius( + context: &ExecutionContext, + radius: Option, + diameter: Option, +) -> ExpressionResult { + match (radius, diameter) { + (Some(radius), Option::None) => Ok(radius.into()), + (Option::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)), + (Option::None, Option::None) => Err(GenericFailure( + "You must provide the radius or the diameter, neither were provided".into(), + ) + .to_error(context.stack_trace)), + } +} + +pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { + build_function!( + database, + methods::GenerateCone, "ManifoldMesh3D::cone", ( + context: &ExecutionContext, + apex: Length3, + center: Length3, + radius: Option = ValueNone.into(), + diameter: Option = ValueNone.into(), + divide: UnsignedInteger) -> ManifoldMesh3D + { + 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))?; + Ok(ManifoldMesh3D(Arc::new(manifold))) + } + ); + build_function!( + database, + methods::GenerateCube, "ManifoldMesh3D::cube", ( + context: &ExecutionContext, + size: Length3 + ) -> ManifoldMesh3D { + 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))?; + manifold.scale(size.x, size.y, size.z); + + Ok(ManifoldMesh3D(Arc::new(manifold))) + } + ); + build_function!( + database, + methods::GenerateCylinder, "ManifoldMesh3D::cylinder", ( + context: &ExecutionContext, + radius: Option = ValueNone.into(), + diameter: Option = ValueNone.into(), + height: Length, + sectors: UnsignedInteger, + stacks: UnsignedInteger + ) -> ManifoldMesh3D { + 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))?; + Ok(ManifoldMesh3D(Arc::new(manifold))) + } + ); + build_function!( + database, + methods::GenerateIcosphere, "ManifoldMesh3D::icosphere", ( + context: &ExecutionContext, + subdivions: UnsignedInteger, + radius: Option = ValueNone.into(), + diameter: Option = ValueNone.into() + ) -> ManifoldMesh3D { + 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))?; + manifold.scale(scale, scale, scale); + + Ok(ManifoldMesh3D(Arc::new(manifold))) + } + ); + build_function!( + database, + methods::GenerateTorus, "ManifoldMesh3D::torus", ( + context: &ExecutionContext, + major_radius: Length, + minor_raidus: Length, + rings: UnsignedInteger, + 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))?; + Ok(ManifoldMesh3D(Arc::new(manifold))) + } + ); + build_function!( + database, + methods::GenerateUvSphere, "ManifoldMesh3D::uv_sphere", ( + context: &ExecutionContext, + sectors: UnsignedInteger, + stacks: UnsignedInteger, + radius: Option = ValueNone.into(), + diameter: Option = ValueNone.into() + ) -> ManifoldMesh3D { + 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))?; + manifold.scale(scale, scale, scale); + + Ok(ManifoldMesh3D(Arc::new(manifold))) + } + ); + + build_method!( + database, + methods::ToStl, "ManifoldMesh3D::to_stl", ( + context: &ExecutionContext, + this: ManifoldMesh3D, + name: IString, + scale: Length = Scalar { + dimension: Dimension::length(), + value: Float::new(1.0/1000.0).expect("Default stl scale was NaN") + }.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}; + + 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 scale = 1.0 / *scale.value; + + 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])] + }; + + mesh.push(triangle); + } + + // 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))?; + + 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))?; + + Ok(()) + })?; + + 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)?; + + // 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)?; + + 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, "\tfacet normal {} {} {}", normal.x, normal.y, normal.z)?; + + { + 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, "\tendfacet")?; + } + + writeln!(file, "endsolid {}", name.0)?; + + Ok(()) + }; + + trampoline().map_err(|error| IoError(error).to_error(context.stack_trace))?; + + Ok(()) + })?; + + Ok(File { path: Arc::new(path) }) + } + } + ); +} diff --git a/interpreter/src/execution/values/mod.rs b/interpreter/src/execution/values/mod.rs new file mode 100644 index 0000000..6eedf9a --- /dev/null +++ b/interpreter/src/execution/values/mod.rs @@ -0,0 +1,420 @@ +/* + * Copyright 2025 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::{borrow::Cow, cmp::Ordering, fmt::Display}; + +use enum_dispatch::enum_dispatch; +use enum_downcast::{AsVariant, EnumDowncast, IntoVariant}; +use unwrap_enum::EnumAs; + +use crate::{ + execution::{logging::StackTrace, stack::ScopeType, ExecutionContext}, + values::manifold_mesh::ManifoldMesh3D, +}; + +use super::errors::{ErrorType, ExpressionResult, Raise as _}; + +mod void; +pub use void::ValueNone; + +mod boolean; +pub use boolean::Boolean; + +mod integer; +pub use integer::{SignedInteger, UnsignedInteger}; + +mod scalar; +pub use scalar::Scalar; + +mod vector; +pub use vector::{Vector2, Vector3, Vector4}; + +pub mod closure; +pub use closure::{BuiltinCallableDatabase, BuiltinFunction, UserClosure}; + +pub mod dictionary; +pub use dictionary::Dictionary; + +mod list; +pub use list::List; + +mod string; +pub use string::{formatting::Style, IString}; + +mod file; +pub use file::File; + +pub mod constraint_set; +pub use constraint_set::ConstraintSet; + +pub mod manifold_mesh; + +mod value_type; +pub use value_type::{StructDefinition, StructMember, ValueType}; + +pub trait StaticTypeName { + /// Provides the type name without having an instance of the object. + /// This is used for formatting error messages when failing to cast to an expected type. + fn static_type_name() -> Cow<'static, str>; +} + +impl StaticTypeName for Option +where + V: StaticTypeName, +{ + fn static_type_name() -> Cow<'static, str> { + format!("None | {}", V::static_type_name()).into() + } +} + +impl IntoVariant> for Value +where + Value: IntoVariant + IntoVariant, +{ + fn into_variant(self) -> Result, Self> + where + Self: Sized, + { + let value: Result = self.into_variant(); + + match value { + Ok(value) => Ok(Some(value)), + Err(original) => { + let value: Result = original.into_variant(); + + match value { + Ok(_none) => Ok(Option::None), + Err(original) => Err(original), + } + } + } + } +} + +pub trait StaticType { + // Provites the TypeValue without having an instance of the object. + // This is used for type checking built-in functions and methods. + // Not all types provide this, and thus, not all types can be used with + // built in functions. + fn static_type() -> ValueType; +} + +impl StaticType for Option +where + V: StaticType, +{ + fn static_type() -> ValueType { + ValueType::MultiType(Box::new(ValueType::TypeNone), Box::new(V::static_type())) + } +} + +#[derive(Debug, Eq, PartialEq)] +struct UnsupportedOperationError { + pub type_name: Cow<'static, str>, + pub operation_name: &'static str, +} + +impl ErrorType for UnsupportedOperationError {} + +impl Display for UnsupportedOperationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Type `{}` does not support {} operation", + self.type_name, self.operation_name + ) + } +} + +impl UnsupportedOperationError { + fn raise( + object: &O, + stack_trace: &StackTrace, + operation_name: &'static str, + ) -> ExpressionResult { + Err(Self { + type_name: object.type_name(), + operation_name, + } + .to_error(stack_trace)) + } +} + +#[derive(Debug, Eq, PartialEq)] +struct MissingAttributeError { + pub name: String, +} + +impl ErrorType for MissingAttributeError {} + +impl Display for MissingAttributeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Object does not contain the attribute `{}`", self.name) + } +} + +#[enum_dispatch] +pub trait Object: StaticTypeName + Sized + Eq + PartialEq + Clone { + fn get_type(&self, context: &ExecutionContext) -> ValueType; + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result; + + fn type_name(&self) -> Cow<'static, str> { + Self::static_type_name() + } + + fn and(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "logical and") + } + fn or(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "logical or") + } + fn xor(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "logical xor") + } + fn bit_and(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "binary and") + } + fn bit_or(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "binary or") + } + fn bit_xor(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "binary xor") + } + fn cmp(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "compare") + } + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + Ok(matches!(self.cmp(context, rhs)?, Ordering::Equal)) + } + fn addition(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "addition") + } + fn subtraction(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "subtraction") + } + fn multiply(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "multiply") + } + fn divide(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "divide") + } + fn exponent(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "exponent") + } + fn left_shift(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "left shift") + } + fn right_shift(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "right shift") + } + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)) + } + 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 formula_call(&self, context: &ExecutionContext, _value: Value) -> ExpressionResult { + UnsupportedOperationError::raise(self, context.stack_trace, "inverse call") + } + fn formula_inverse_call( + &self, + context: &ExecutionContext, + _value: Value, + ) -> ExpressionResult { + UnsupportedOperationError::raise(self, context.stack_trace, "inverse call") + } + fn unary_plus(self, context: &ExecutionContext) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "unary plus") + } + fn unary_minus(self, context: &ExecutionContext) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "unary minus") + } + fn unary_not(self, context: &ExecutionContext) -> ExpressionResult { + UnsupportedOperationError::raise(&self, context.stack_trace, "unary not") + } + + // fn export( + // &self, + // _log: &dyn RuntimeLog, + // stack_trace: &StackScope, + // ) -> OperatorResult { + // UnsupportedOperationError::raise(self, stack_trace, "export") + // } +} + +#[enum_dispatch(Object, ObjectCopy)] +#[derive(Debug, Eq, PartialEq, EnumDowncast, EnumAs, Clone)] +pub enum Value { + ValueNone, + SignedInteger, + UnsignedInteger, + Boolean, + Scalar, + UserClosure, + BuiltinFunction, + ValueType, + Dictionary(Dictionary), + List(List), + String(IString), + Vector2(Vector2), + Vector3(Vector3), + Vector4(Vector4), + File, + ConstraintSet, + ManifoldMesh3D, + // Quaternion, +} + +impl StaticTypeName for Value { + fn static_type_name() -> Cow<'static, str> { + "Value".into() + } +} + +impl StaticType for Value { + fn static_type() -> ValueType { + ValueType::Any + } +} + +#[derive(Debug, Eq, PartialEq)] +pub struct DowncastError { + pub expected: Cow<'static, str>, + pub got: Cow<'static, str>, +} + +impl ErrorType for DowncastError {} + +impl Display for DowncastError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Expected `{}`, got `{}`", self.expected, self.got) + } +} + +#[derive(Debug, Eq, PartialEq)] +pub struct DowncastForBinaryOpError { + pub expected: Cow<'static, str>, + pub got: Cow<'static, str>, +} + +impl ErrorType for DowncastForBinaryOpError {} + +impl Display for DowncastForBinaryOpError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Left side is `{}`, right side is `{}`, these must be the same", + self.expected, self.got + ) + } +} + +impl From for DowncastForBinaryOpError { + fn from(error: DowncastError) -> Self { + Self { + expected: error.expected, + got: error.got, + } + } +} + +impl IntoVariant for Value { + fn into_variant(self) -> Result + where + Self: Sized, + { + Ok(self) + } +} + +impl Value { + pub fn downcast_for_binary_op_ref(&self, stack_trace: &StackTrace) -> ExpressionResult<&T> + where + T: StaticTypeName, + Self: AsVariant, + { + self.downcast_ref(stack_trace) + } + + pub fn downcast_for_binary_op(self, stack_trace: &StackTrace) -> ExpressionResult + where + T: StaticTypeName, + Self: IntoVariant, + { + self.downcast(stack_trace) + } + + pub fn downcast_ref(&self, stack_trace: &StackTrace) -> ExpressionResult<&T> + where + T: StaticTypeName, + Self: AsVariant, + { + if let Some(value) = self.enum_downcast_ref() { + Ok(value) + } else { + Err(DowncastForBinaryOpError { + expected: T::static_type_name(), + got: self.type_name(), + } + .to_error(stack_trace)) + } + } + + pub fn downcast(self, stack_trace: &StackTrace) -> ExpressionResult + where + T: StaticTypeName, + Self: IntoVariant, + { + match self.into_variant() { + Ok(value) => Ok(value), + Err(original) => Err(DowncastForBinaryOpError { + expected: T::static_type_name(), + got: original.type_name(), + } + .to_error(stack_trace)), + } + } + + pub fn downcast_optional(self, stack_trace: &StackTrace) -> ExpressionResult> + where + T: StaticTypeName, + Self: IntoVariant, + { + match self { + Self::ValueNone(_) => Ok(None), + this => Ok(Some(this.downcast_for_binary_op::(stack_trace)?)), + } + } +} diff --git a/interpreter/src/execution/values/scalar.rs b/interpreter/src/execution/values/scalar.rs new file mode 100644 index 0000000..c367758 --- /dev/null +++ b/interpreter/src/execution/values/scalar.rs @@ -0,0 +1,1318 @@ +/* + * Copyright 2025 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ +use std::{borrow::Cow, cmp::Ordering, f64::consts::PI}; + +use common_data_types::{Dimension, Float, FloatIsNan}; + +use crate::{ + build_method, + execution::{ + errors::{ExpressionResult, GenericFailure, Raise}, + logging::{LogLevel, LogMessage, StackTrace}, + values::{ + self, closure::BuiltinCallableDatabase, string::formatting::Style, Boolean, + BuiltinFunction, MissingAttributeError, SignedInteger, StaticType, UnsignedInteger, + Vector2, + }, + ExecutionContext, + }, + values::DowncastError, +}; + +use super::{value_type::ValueType, DowncastForBinaryOpError, Object, StaticTypeName, Value}; + +pub trait UnwrapNotNan: Sized { + fn unwrap_not_nan(self, stack_trace: &StackTrace) -> ExpressionResult; +} + +impl UnwrapNotNan for std::result::Result { + fn unwrap_not_nan(self, stack_trace: &StackTrace) -> ExpressionResult { + match self { + Ok(number) => Ok(number), + Err(_float_is_nan) => Err(GenericFailure( + "Result of arithmetic operation is NaN".into(), + ) + .to_error(stack_trace)), + } + } +} + +#[derive(Debug, Hash, Clone, Copy, Eq, PartialEq)] +pub struct Scalar { + pub dimension: Dimension, + pub value: Float, +} + +impl Object for Scalar { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::Scalar(Some(self.dimension)) + } + + fn type_name(&self) -> Cow<'static, str> { + units::get_dimension_name(&self.dimension) + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + // This just takes a reference to the unit name, so it's pretty cheap. I don't mind if it's not always used. + // In the rare case that a unit name is generated and memory is allocated on the heap, well we're clearly + // not about to format a number, so it's clear that we're going to use this. + let unit_name = units::get_base_unit_name(&self.dimension); + + match (style, precision, unit_name) { + (Style::Default, Option::None, Option::None) => { + write!(f, "{}", self.value) + } + (Style::Default, Option::None, Some(unit_name)) => { + write!(f, "{}{unit_name}", self.value) + } + (Style::Default, Some(precision), Option::None) => { + write!(f, "{:.1$}", self.value, precision as usize) + } + (Style::Default, Some(precision), Some(unit_name)) => { + write!(f, "{:.1$}{unit_name}", self.value, precision as usize) + } + (Style::Debug, Option::None, Option::None) => { + write!(f, "{}", self.value) + } + (Style::Debug, Option::None, Some(unit_name)) => { + write!(f, "{}{unit_name}", self.value) + } + (Style::Debug, Some(precision), Option::None) => { + write!(f, "{:.1$}", self.value, precision as usize) + } + (Style::Debug, Some(precision), Some(unit_name)) => { + write!(f, "{:.1$}{unit_name}", self.value, precision as usize) + } + (Style::Octal | Style::Hex | Style::CapitalizedHex, _, _) => { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Scalar values cannot be formatted as integers".into(), + }); + + // Don't stop execution. Just use default formatting. + self.format(context, f, Style::Default, precision) + } + (Style::Exponent, Option::None, Option::None) => { + write!(f, "{:e}", self.value.into_inner() as usize) + } + (Style::Exponent, Option::None, Some(unit_name)) => { + write!(f, "{:e}{unit_name}", self.value.into_inner() as usize) + } + (Style::Exponent, Some(precision), Option::None) => { + write!(f, "{:.1$e}", self.value.into_inner(), precision as usize) + } + (Style::Exponent, Some(precision), Some(unit_name)) => write!( + f, + "{:.1$e}{unit_name}", + self.value.into_inner(), + precision as usize + ), + (Style::CapitalizedExponent, Option::None, Option::None) => { + write!(f, "{:E}", self.value.into_inner()) + } + (Style::CapitalizedExponent, Option::None, Some(unit_name)) => { + write!(f, "{:E}{unit_name}", self.value.into_inner()) + } + (Style::CapitalizedExponent, Some(precision), Option::None) => { + write!(f, "{:.1$E}", self.value.into_inner(), precision as usize) + } + (Style::CapitalizedExponent, Some(precision), Some(unit_name)) => write!( + f, + "{:.1$E}{unit_name}", + self.value.into_inner(), + precision as usize + ), + } + } + + fn addition(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs = self.unpack_same_dimension(context.stack_trace, rhs)?; + + let value = Float::new(*self.value + *rhs.value).unwrap_not_nan(context.stack_trace)?; + + Ok(Self { value, ..self }.into()) + } + fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs = self.unpack_same_dimension(context.stack_trace, rhs)?; + + let value = Float::new(*self.value - *rhs.value).unwrap_not_nan(context.stack_trace)?; + + 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 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) + .map(|rhs| rhs.into()) + } + fn exponent(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs = rhs.downcast::(context.stack_trace)?; + + 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)?, + } + .into()) + } else { + Err(DowncastError { + expected: ValueType::Scalar(Some(Dimension::zero())).name(), + got: rhs.get_type(context).name(), + } + .to_error(context.stack_trace)) + } + } + fn unary_plus(self, _context: &ExecutionContext) -> ExpressionResult { + Ok(self.into()) + } + fn unary_minus(self, _context: &ExecutionContext) -> ExpressionResult { + 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)?; + if self.dimension == rhs.dimension { + Ok(std::cmp::Ord::cmp(&self.value, &rhs.value)) + } else { + Err(DowncastForBinaryOpError { + expected: self.type_name(), + got: rhs.type_name(), + } + .to_error(context.stack_trace)) + } + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + match attribute { + "to_signed_integer" => Ok(BuiltinFunction::new::().into()), + "to_unsigned_integer" => { + Ok(BuiltinFunction::new::().into()) + } + "abs" => Ok(BuiltinFunction::new::().into()), + "clamp" => Ok(BuiltinFunction::new::().into()), + "copysign" => Ok(BuiltinFunction::new::().into()), + "hypot" => Ok(BuiltinFunction::new::().into()), + "is_finite" => Ok(BuiltinFunction::new::().into()), + "is_infinite" => Ok(BuiltinFunction::new::().into()), + "is_normal" => Ok(BuiltinFunction::new::().into()), + "cbrt" => Ok(BuiltinFunction::new::().into()), + "pow" => Ok(BuiltinFunction::new::().into()), + "sqrt" => Ok(BuiltinFunction::new::().into()), + "is_sign_negative" => Ok(BuiltinFunction::new::().into()), + "is_sign_positive" => Ok(BuiltinFunction::new::().into()), + "recip" => Ok(BuiltinFunction::new::().into()), + "round" => Ok(BuiltinFunction::new::().into()), + "trunc" => Ok(BuiltinFunction::new::().into()), + "fract" => Ok(BuiltinFunction::new::().into()), + "floor" => Ok(BuiltinFunction::new::().into()), + "ceil" => Ok(BuiltinFunction::new::().into()), + "max" => Ok(BuiltinFunction::new::().into()), + "min" => Ok(BuiltinFunction::new::().into()), + "signum" => Ok(BuiltinFunction::new::().into()), + "acos" => Ok(BuiltinFunction::new::().into()), + "acosh" => Ok(BuiltinFunction::new::().into()), + "cos" => Ok(BuiltinFunction::new::().into()), + "cosh" => Ok(BuiltinFunction::new::().into()), + "asin" => Ok(BuiltinFunction::new::().into()), + "asinh" => Ok(BuiltinFunction::new::().into()), + "sin" => Ok(BuiltinFunction::new::().into()), + "sinh" => Ok(BuiltinFunction::new::().into()), + "cossin" => Ok(BuiltinFunction::new::().into()), + "atan" => Ok(BuiltinFunction::new::().into()), + "atanh" => Ok(BuiltinFunction::new::().into()), + "tan" => Ok(BuiltinFunction::new::().into()), + "tanh" => Ok(BuiltinFunction::new::().into()), + _ => Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)), + } + } + + // fn export( + // &self, + // _log: &dyn RuntimeLog, + // _stack_trace: &StackTrace, + // ) -> ExpressionResult { + // Ok(SerializableValue::Scalar(self.clone())) + // } +} + +impl StaticTypeName for Scalar { + fn static_type_name() -> Cow<'static, str> { + "Scalar".into() + } +} + +impl StaticType for Scalar { + fn static_type() -> ValueType { + ValueType::Scalar(None) + } +} + +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)?; + let dimension = self.dimension + rhs.dimension; + + Ok(Self { dimension, value }) + } + + fn divide_by_measurement( + &self, + stack_trace: &StackTrace, + rhs: &Self, + ) -> ExpressionResult { + let value = Float::new(*self.value / *rhs.value).unwrap_not_nan(stack_trace)?; + let dimension = self.dimension - rhs.dimension; + + Ok(Self { dimension, value }) + } + + fn check_inverse_trig_compatible(&self, stack_trace: &StackTrace) -> ExpressionResult<()> { + 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)) + } + } + + fn check_trig_compatible(&self, stack_trace: &StackTrace) -> ExpressionResult<()> { + 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), + ) + } + } + + fn unpack_same_dimension(self, stack_trace: &StackTrace, rhs: Value) -> ExpressionResult { + if let Value::Scalar(rhs) = rhs { + if self.dimension == rhs.dimension { + Ok(rhs) + } else { + Err(DowncastForBinaryOpError { + expected: self.type_name(), + got: rhs.type_name(), + } + .to_error(stack_trace)) + } + } else { + Err(DowncastForBinaryOpError { + expected: self.type_name(), + got: rhs.type_name(), + } + .to_error(stack_trace)) + } + } +} + +mod methods { + pub struct ToSignedInteger; + pub struct ToUnsignedInteger; + pub struct Abs; + pub struct Clamp; + pub struct Copysign; + pub struct Hypot; + pub struct IsFinite; + pub struct IsInfinite; + pub struct IsNormal; + pub struct Cbrt; + pub struct Pow; + pub struct Sqrt; + pub struct IsSignNegative; + pub struct IsSignPositive; + pub struct Recip; + pub struct Round; + pub struct Trunc; + pub struct Fract; + pub struct Floor; + pub struct Ceil; + pub struct Max; + pub struct Min; + pub struct Signum; + pub struct Acos; + pub struct Acosh; + pub struct Cos; + pub struct Cosh; + pub struct Asin; + pub struct Asinh; + pub struct Sin; + pub struct Sinh; + pub struct CosSin; + pub struct Atan; + pub struct Atanh; + pub struct Tan; + pub struct Tanh; +} + +pub fn register_methods(database: &mut BuiltinCallableDatabase) { + build_method!( + database, + methods::ToSignedInteger, "Scalar::to_signed_integer", ( + context: &ExecutionContext, + this: Scalar) -> SignedInteger + { + 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)) + } + } + ); + build_method!( + database, + methods::ToUnsignedInteger, "Scalar::to_unsigned_integer", ( + context: &ExecutionContext, + this: Scalar) -> UnsignedInteger + { + if this.dimension.is_zero_dimension() { + 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)) + } + } else { + Err(GenericFailure("Only zero dimensional scalars can be converted into an integer".into()) + .to_error(context.stack_trace)) + } + } + ); + build_method!( + database, + methods::Abs, "Scalar::abs", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + Ok(Scalar { + dimension: this.dimension, + value: Float::new(this.value.abs()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Clamp, "Scalar::clamp", ( + _context: &ExecutionContext, + this: Scalar, + min: Scalar, + max: Scalar) -> Scalar + { + Ok(Scalar { + dimension: this.dimension, + value: this.value.clamp(min.value, max.value) + }) + } + ); + build_method!( + database, + methods::Copysign, "Scalar::copysign", ( + context: &ExecutionContext, + this: Scalar, + sign: Zero) -> Scalar + { + Ok(Scalar { + dimension: Dimension::zero(), + value: Float::new(this.value.copysign(*sign.value)).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Hypot, "Scalar::hypot", ( + context: &ExecutionContext, + this: Scalar, + other: Scalar) -> Scalar + { + Ok(Scalar { + dimension: Dimension::zero(), + value: Float::new(this.value.hypot(*other.value)).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::IsFinite, "Scalar::is_finite", ( + _context: &ExecutionContext, + this: Scalar) -> Boolean + { + Ok(values::Boolean(this.value.is_finite())) + } + ); + build_method!( + database, + methods::IsInfinite, "Scalar::is_infinite", ( + _context: &ExecutionContext, + this: Scalar) -> Boolean + { + Ok(values::Boolean(this.value.is_infinite())) + } + ); + build_method!( + database, + methods::IsNormal, "Scalar::is_normal", ( + _context: &ExecutionContext, + this: Scalar) -> Boolean + { + Ok(values::Boolean(this.value.is_normal())) + } + ); + build_method!( + database, + methods::Cbrt, "Scalar::cbrt", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + Ok(Scalar { + dimension: this.dimension / 3, + value: Float::new(this.value.cbrt()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Pow, "Scalar::pow", ( + context: &ExecutionContext, + this: Scalar, + exp: Zero) -> Scalar + { + Ok(Scalar { + dimension: this.dimension * *exp.value as i8, + value: Float::new(this.value.powf(*exp.value)).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Sqrt, "Scalar::sqrt", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + Ok(Scalar { + dimension: this.dimension / 2, + value: Float::new(this.value.sqrt()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::IsSignNegative, "Scalar::is_sign_negative", ( + _context: &ExecutionContext, + this: Scalar) -> Boolean + { + Ok(Boolean(this.value.is_sign_negative())) + } + ); + build_method!( + database, + methods::IsSignPositive, "Scalar::is_sign_positive", ( + _context: &ExecutionContext, + this: Scalar) -> Boolean + { + Ok(Boolean(this.value.is_sign_positive())) + } + ); + build_method!( + database, + methods::Recip, "Scalar::recip", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + Ok(Scalar { + dimension: -this.dimension, + value: Float::new(this.value.recip()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Round, "Scalar::round", ( + context: &ExecutionContext, + this: Scalar, + unit: Scalar) -> Scalar + { + let unit = this.unpack_same_dimension(context.stack_trace, 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)? + }) + } + ); + build_method!( + database, + methods::Trunc, "Scalar::trunc", ( + context: &ExecutionContext, + this: Scalar, + unit: Scalar) -> Scalar + { + let value = this.value / unit.value; + + Ok(Scalar { + dimension: this.dimension, + value: Float::new(value.trunc() * *unit.value).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Fract, "Scalar::fract", ( + context: &ExecutionContext, + this: Scalar, + unit: Scalar) -> Scalar + { + let value = this.value / unit.value; + + Ok(Scalar { + dimension: this.dimension, + value: Float::new(value.fract() * *unit.value).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Floor, "Scalar::floor", ( + context: &ExecutionContext, + this: Scalar, + unit: Scalar) -> Scalar + { + let value = this.value / unit.value; + + Ok(Scalar { + dimension: this.dimension, + value: Float::new(value.floor() * *unit.value).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Ceil, "Scalar::ceil", ( + context: &ExecutionContext, + this: Scalar, + unit: Scalar) -> Scalar + { + let value = this.value / unit.value; + + Ok(Scalar { + dimension: this.dimension, + value: Float::new(value.ceil() * *unit.value).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Max, "Scalar::max", ( + _context: &ExecutionContext, + this: Scalar, + other: Scalar) -> Scalar + { + Ok(Scalar { + dimension: this.dimension, + value: this.value.max(other.value) + }) + } + ); + build_method!( + database, + methods::Min, "Scalar::min", ( + _context: &ExecutionContext, + this: Scalar, + other: Scalar) -> Scalar + { + Ok(Scalar { + dimension: this.dimension, + value: this.value.min(other.value) + }) + } + ); + build_method!( + database, + methods::Signum, "Scalar::signum", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + Ok(Scalar { + dimension: Dimension::zero(), + value: Float::new(this.value.signum()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Acos, "Scalar::acos", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_inverse_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::angle(), + value: Float::new((this.value * PI).acos()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Acosh, "Scalar::acosh", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_inverse_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::angle(), + value: Float::new((this.value).acosh() / PI).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Cos, "Scalar::cos", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::zero(), + value: Float::new((this.value * PI).cos()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Cosh, "Scalar::cosh", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::zero(), + value: Float::new((this.value * PI).cosh()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Asin, "Scalar::asin", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_inverse_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::angle(), + value: Float::new((this.value * PI).asin()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Asinh, "Scalar::asinh", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_inverse_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::angle(), + value: Float::new((this.value).asinh() / PI).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Sin, "Scalar::sin", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::zero(), + value: Float::new((this.value * PI).sin()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Sinh, "Scalar::sinh", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::zero(), + value: Float::new((this.value * PI).sinh()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::CosSin, "Scalar::cossin", ( + context: &ExecutionContext, + this: Scalar) -> Vector2 + { + this.check_trig_compatible(context.stack_trace)?; + + let (sin, cos) = (this.value * PI).sin_cos(); + Vector2::new(context, Dimension::zero(), [cos, sin]) + } + ); + build_method!( + database, + methods::Atan, "Scalar::atan", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_inverse_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::angle(), + value: Float::new((this.value * PI).atan()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Atanh, "Scalar::atanh", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_inverse_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::angle(), + value: Float::new((this.value).atanh() / PI).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Tan, "Scalar::tan", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::zero(), + value: Float::new((this.value * PI).tan()).unwrap_not_nan(context.stack_trace)? + }) + } + ); + build_method!( + database, + methods::Tanh, "Scalar::tanh", ( + context: &ExecutionContext, + this: Scalar) -> Scalar + { + this.check_trig_compatible(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::zero(), + value: Float::new((this.value * PI).tanh()).unwrap_not_nan(context.stack_trace)? + }) + } + ); +} + +macro_rules! build_scalar_type { + ($name:ident = $dimension:expr) => { + #[derive(Debug, Hash, Clone)] + pub struct $name(Scalar); + + impl StaticType for $name { + fn static_type() -> ValueType { + ValueType::Scalar(Some($dimension)) + } + } + + impl StaticTypeName for $name { + fn static_type_name() -> Cow<'static, str> { + stringify!($name).into() + } + } + + impl enum_downcast::IntoVariant<$name> for Value { + fn into_variant(self) -> Result<$name, Value> { + Ok($name(self.into_variant()?)) + } + } + + impl From<$name> for Scalar { + fn from(value: $name) -> Scalar { + value.0 + } + } + + impl std::ops::Deref for $name { + type Target = Scalar; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl From<$name> for common_data_types::RawFloat { + fn from(value: $name) -> common_data_types::RawFloat { + *value.value + } + } + }; +} + +build_scalar_type!(Zero = Dimension::zero()); +build_scalar_type!(Angle = Dimension::angle()); +build_scalar_type!(Length = Dimension::length()); + +#[cfg(test)] +mod test { + + use crate::execution::{test_run, values::Boolean}; + + use super::*; + + #[test] + fn addition() { + let product = test_run("3m + 2m").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: Float::new(5.0).unwrap(), + } + .into() + ); + } + + #[test] + fn subtraction() { + let product = test_run("3m - 2m").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: Float::new(1.0).unwrap(), + } + .into() + ); + } + + #[test] + fn multiplication() { + let product = test_run("3m * 2m").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::area(), + value: Float::new(6.0).unwrap(), + } + .into() + ); + } + + #[test] + fn division() { + let product = test_run("6'm^2' / 2m").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: Float::new(3.0).unwrap(), + } + .into() + ); + } + + #[test] + fn comparisions() { + let product = test_run("6m > 2m").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("2m > 6m").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("6m >= 2m").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("6m >= 6m").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("2m >= 6m").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("6m == 6m").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("6m == 5m").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("6m <= 5m").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("5m <= 5m").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("5m <= 6m").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("5m < 6m").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("6m < 6m").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("6m != 6m").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("6m != 5m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn conversions() { + let product = test_run("1m + 100cm == 2m").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("2m * 2m == 4'm^2'").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn to_signed_integer() { + let product = test_run("100::to_signed_integer() == 100i").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(-100)::to_signed_integer() == -100i").unwrap(); + assert_eq!(product, Boolean(true).into()); + + 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(); + } + + #[test] + fn to_unsigned_integer() { + let product = test_run("100::to_unsigned_integer() == 100u").unwrap(); + assert_eq!(product, Boolean(true).into()); + + 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(); + } + + #[test] + fn abs() { + let product = test_run("(-100)::abs() == 100").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn clamp() { + let product = test_run("(-100)::clamp(min = -50, max = 50) == -50").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(100)::clamp(min = -50, max = 50) == 50").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn copysign() { + let product = test_run("100::copysign(sign = -50) == -100").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn hypot() { + // 141.4213562373095 was calculated Using `math.sqrt(100 ** 2 + 100 ** 2)` in Python 3.13.9. + let product = test_run("100::hypot(other = 100) - 141.4213562373095 < 0.0001").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn is_finite() { + let product = test_run("std.consts.Infinity::is_finite()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("5::is_finite()").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(std.consts.Infinity * 5)::is_finite()").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn is_infinite() { + let product = test_run("std.consts.Infinity::is_infinite()").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("5::is_infinite()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("(std.consts.Infinity * 5)::is_infinite()").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn is_normal() { + let product = test_run("std.consts.Infinity::is_normal()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("0::is_normal()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("5::is_normal()").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(-5)::is_normal()").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn cbrt() { + let product = test_run("(64 'm^3')::cbrt() == 4m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn pow() { + let product = test_run("2m::pow(exp = 2) == 4 'm^2'").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn sqrt() { + let product = test_run("(16 'm^2')::sqrt() == 4m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn is_sign_negative() { + let product = test_run("0::is_sign_negative()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("(-0)::is_sign_negative()").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("5::is_sign_negative()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("(-5)::is_sign_negative()").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn is_sign_positive() { + let product = test_run("0::is_sign_positive()").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(-0)::is_sign_positive()").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("5::is_sign_positive()").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(-5)::is_sign_positive()").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn recip() { + let product = test_run("16ft::recip() == 1 / 16ft").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn round() { + let product = test_run("16.8ft::round(unit = 1ft) == 17ft").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("16.2ft::round(unit = 1ft) == 16ft").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn trunc() { + let product = test_run("16.8ft::trunc(unit = 1ft) == 16ft").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("16.2ft::trunc(unit = 1ft) == 16ft").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn fract() { + let product = test_run("16.8ft::fract(unit = 1ft) - 0.8ft < 0.0000000000001ft").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn floor() { + let product = test_run("16.5ft::floor(unit = 1ft) == 16ft").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn ceil() { + let product = test_run("16.5ft::ceil(unit = 1ft) == 17ft").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn max() { + let product = test_run("16m::max(other = 15m) == 16m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn min() { + let product = test_run("16m::min(other = 15m) == 15m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn signum() { + let product = test_run("16m::signum() == 1").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(-16m)::signum() == -1").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("0m::signum() == 1").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(-0m)::signum() == -1").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn acos() { + let product = test_run("0::acos() - 90deg < 0.000000000001deg").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn cosh_acosh() { + let product = test_run("1rad::cosh()::acosh() - 1rad < 0.000000000001rad").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn cos() { + let product = test_run("90deg::cos() - 1 < 0.000000000001").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn asin() { + let product = test_run("0::asin() - 90deg < 0.000000000001deg").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn sinh_asinh() { + let product = test_run("1rad::sinh()::asinh() - 1rad < 0.000000000001rad").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn sin() { + let product = test_run("90deg::sin() - 1 < 0.000000000001").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn cossin() { + let product = test_run("let angle = 45deg; in (angle::cossin() - <(angle::cos(), angle::sin())>)::norm() < 0.0000000001").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn atan() { + let product = test_run("0::atan() == 0deg").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn tanh_atanh() { + let product = test_run("1rad::tanh()::atanh() - 1rad < 0.000000000001rad").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn tan() { + let product = test_run("0deg::tan() == 0").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn format() { + let product = test_run( + "\"{a} {b} {c:.2}\"::format(a = 10, b = 10m, c = 10.1234) == \"10 10m 10.12\"", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "\"{a:?} {b:?} {c:?.2}\"::format(a = 10, b = 10m, c = 10.1234) == \"10 10m 10.12\"", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = + test_run("\"{a:e} {b:e} {c:e.2}\"::format(a = 1000, b = 1000m, c = 1234.1234) == \"1e3 1e3m 1.23e3\"") + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = + test_run("\"{a:E} {b:E} {c:E.2}\"::format(a = 1000, b = 1000m, c = 1234.1234) == \"1E3 1E3m 1.23E3\"") + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } +} diff --git a/interpreter/src/execution/values/string/formatting.rs b/interpreter/src/execution/values/string/formatting.rs new file mode 100644 index 0000000..d2a8585 --- /dev/null +++ b/interpreter/src/execution/values/string/formatting.rs @@ -0,0 +1,484 @@ +/* + * Copyright 2024 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use std::fmt::Write; + +use imstr::ImString; +use nom::{ + branch::alt, + bytes::complete::{tag, take_while, take_while1}, + character::complete::char as nom_char, + combinator::{all_consuming, cut, map, recognize, success, value, verify}, + error::context, + multi::{fold_many0, many0}, + sequence::{delimited, pair, preceded}, + IResult, Parser, +}; + +use crate::execution::{ + errors::{ExpressionResult, GenericFailure, Raise}, + logging::{LocatedStr, StackTrace}, + values::{Dictionary, Object, UnsignedInteger}, + ExecutionContext, +}; + +pub type VResult = IResult>; + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum Style { + Default, + Debug, + Octal, + Hex, + CapitalizedHex, + Exponent, + CapitalizedExponent, +} + +impl Style { + fn parse(input: &str) -> VResult<&str, Self> { + alt(( + value(Self::Debug, nom_char('?')), + value(Self::Octal, nom_char('o')), + value(Self::Hex, nom_char('x')), + value(Self::CapitalizedHex, nom_char('X')), + value(Self::Exponent, nom_char('e')), + value(Self::CapitalizedExponent, nom_char('E')), + )) + .parse(input) + } +} + +fn ident(input: &str) -> VResult<&str, &str> { + recognize(pair( + take_while1(|c: char| c.is_alphabetic()), + take_while(|c: char| c.is_numeric()), + )) + .parse(input) +} + +#[derive(Debug, PartialEq, Clone)] +enum Precision { + Default, + Inline(u8), + Referenced(ImString), +} + +impl Precision { + fn parse(input: &str) -> VResult<&str, Self> { + preceded( + nom_char('.'), + alt(( + map(ident, |name| Self::Referenced(ImString::from(name))), + map(number, Self::Inline), + )), + ) + .parse(input) + } +} + +#[derive(Debug, PartialEq)] +struct Parameter { + name: ImString, + style: Style, + precision: Precision, +} + +impl Parameter { + fn parse(input: &str) -> VResult<&str, Self> { + delimited( + nom_char('{'), + cut(map( + pair( + map(ident, ImString::from), + alt(( + preceded( + nom_char(':'), + pair( + alt((Style::parse, success(Style::Default))), + alt((Precision::parse, success(Precision::Default))), + ), + ), + success((Style::Default, Precision::Default)), + )), + ), + |(name, (style, precision))| Parameter { + name, + style, + precision, + }, + )), + cut(context("Expected closing `}` for parameter", nom_char('}'))), + ) + .parse(input) + } +} + +#[derive(Debug, PartialEq)] +enum Component { + Litteral(String), + Parameter(Parameter), +} + +impl Component { + fn parse(input: &str) -> VResult<&str, Self> { + alt((Self::parse_litteral, map(Parameter::parse, Self::Parameter))).parse(input) + } + + fn parse_litteral(input: &str) -> VResult<&str, Self> { + map( + verify( + fold_many0( + alt((take_while1(|c| c != '{'), tag("{{"), tag("}}"))), + String::default, + |mut accum, item: &str| { + match item { + "{{" => accum.push('{'), + "}}" => accum.push('}'), + _ => { + accum.push_str(item); + } + } + + accum + }, + ), + |output: &str| !output.is_empty(), + ), + Self::Litteral, + ) + .parse(input) + } +} + +#[derive(Debug, PartialEq)] +pub struct Format { + components: Vec, +} + +impl Format { + pub fn parse(input: &str) -> VResult<&str, Self> { + map(all_consuming(many0(Component::parse)), |components| Self { + components, + }) + .parse(input) + } + + pub fn format( + &self, + context: &ExecutionContext, + f: &mut dyn Write, + arguments: Dictionary, + ) -> ExpressionResult<()> { + fn get_precision( + context: &ExecutionContext, + precision: &Precision, + arguments: &Dictionary, + ) -> ExpressionResult> { + match precision { + Precision::Default => Ok(Option::None), + Precision::Inline(precision) => Ok(Some(*precision)), + Precision::Referenced(name) => { + 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() + }) { + let precision = argument + .downcast_ref::(context.stack_trace)? + .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)) + } + } else { + Err( + GenericFailure(format!("Could not find argument `{name}`").into()) + .to_error(context.stack_trace), + ) + } + } + } + } + + 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)); + } + } + } + } + + Ok(()) + } +} + +fn number(input: &str) -> VResult<&str, u8> { + map(take_while1(|c| "0123456789".contains(c)), |digits: &str| { + digits.parse::().unwrap() + }) + .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; + + use common_data_types::{Dimension, Float}; + + use crate::execution::{test_context, values::Scalar}; + + use super::*; + + #[test] + fn litterals() { + assert_eq!( + Component::parse_litteral("1234"), + Ok(("", Component::Litteral("1234".into()))) + ); + + assert_eq!( + Component::parse_litteral("1234{{}"), + Ok(("", Component::Litteral("1234{}".into()))) + ); + } + + #[test] + fn parameters() { + assert_eq!( + Parameter::parse("{thing}"), + Ok(( + "", + Parameter { + name: "thing".into(), + style: Style::Default, + precision: Precision::Default + } + )) + ); + assert_eq!( + Parameter::parse("{thing:?}"), + Ok(( + "", + Parameter { + name: "thing".into(), + style: Style::Debug, + precision: Precision::Default + } + )) + ); + assert_eq!( + Parameter::parse("{thing:.5}"), + Ok(( + "", + Parameter { + name: "thing".into(), + style: Style::Default, + precision: Precision::Inline(5) + } + )) + ); + assert_eq!( + Parameter::parse("{thing:.precision}"), + Ok(( + "", + Parameter { + name: "thing".into(), + style: Style::Default, + precision: Precision::Referenced("precision".into()) + } + )) + ); + } + + #[test] + fn litterals_and_parameters() { + assert_eq!(Format::parse(""), Ok(("", Format { components: vec![] }))); + + assert_eq!( + Format::parse(" {{}}"), + Ok(( + "", + Format { + components: vec![Component::Litteral(" {}".into())] + } + )) + ); + + assert_eq!( + Format::parse("{thang}"), + Ok(( + "", + Format { + components: vec![Component::Parameter(Parameter { + name: "thang".into(), + style: Style::Default, + precision: Precision::Default + })] + } + )) + ); + assert_eq!( + Format::parse(" {thang}"), + Ok(( + "", + Format { + components: vec![ + Component::Litteral(" ".into()), + Component::Parameter(Parameter { + name: "thang".into(), + style: Style::Default, + precision: Precision::Default + }) + ] + } + )) + ); + assert_eq!( + Format::parse(" {{}}{thang}"), + Ok(( + "", + Format { + components: vec![ + Component::Litteral(" {}".into()), + Component::Parameter(Parameter { + name: "thang".into(), + style: Style::Default, + precision: Precision::Default + }) + ] + } + )) + ); + } + + #[test] + fn do_format() { + test_context([], |context| { + let mut formatted = String::default(); + Format::parse("Test {value}") + .unwrap() + .1 + .format( + context, + &mut formatted, + Dictionary::new( + context, + HashMap::from_iter([( + "value".into(), + Scalar { + dimension: Dimension::zero(), + value: Float::new(42.24).unwrap(), + } + .into(), + )]), + ), + ) + .unwrap(); + assert_eq!(formatted, "Test 42.24"); + + let mut formatted = String::default(); + Format::parse("Test {one} {two}") + .unwrap() + .1 + .format( + context, + &mut formatted, + Dictionary::new( + &context, + HashMap::from_iter([ + ( + "one".into(), + Scalar { + dimension: Dimension::zero(), + value: Float::new(1.0).unwrap(), + } + .into(), + ), + ( + "two".into(), + Scalar { + dimension: Dimension::zero(), + value: Float::new(2.0).unwrap(), + } + .into(), + ), + ]), + ), + ) + .unwrap(); + assert_eq!(formatted, "Test 1 2"); + }) + } +} diff --git a/interpreter/src/execution/values/string/mod.rs b/interpreter/src/execution/values/string/mod.rs new file mode 100644 index 0000000..03d5ed3 --- /dev/null +++ b/interpreter/src/execution/values/string/mod.rs @@ -0,0 +1,681 @@ +/* + * Copyright 2024 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use common_data_types::{Dimension, Float}; +use hashable_map::HashableMap; +use imstr::ImString; + +use crate::{ + build_closure_type, build_method, + execution::{ + errors::{GenericFailure, Raise}, + logging::{LocatedStr, LogLevel, LogMessage}, + stack::ScopeType, + values::{ + closure::{BuiltinCallable, Signature}, + string::formatting::{Format, Style}, + Boolean, BuiltinCallableDatabase, BuiltinFunction, Dictionary, List, + MissingAttributeError, Scalar, SignedInteger, StaticType, StructDefinition, + UnsignedInteger, ValueNone, + }, + ExecutionContext, + }, +}; + +use super::{value_type::ValueType, ExpressionResult, Object, StaticTypeName, Value}; + +use std::{borrow::Cow, collections::HashMap, sync::Arc}; + +pub mod formatting; + +#[derive(Debug, Hash, Clone, Eq, PartialEq)] +pub struct IString(pub ImString); + +impl Object for IString { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::String + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + if !matches!(style, Style::Default) { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Strings only support default formatting".into(), + }); + } + + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Strings cannot be formatted with precision".into(), + }); + } + + write!(f, "{}", self.0) + } + + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + Ok(self.0 == rhs.0) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + match attribute { + "format" => Ok(BuiltinFunction::new::().into()), + + "append" => Ok(BuiltinFunction::new::().into()), + "slice" => Ok(BuiltinFunction::new::().into()), + "chunks" => Ok(BuiltinFunction::new::().into()), + "lines" => Ok(BuiltinFunction::new::().into()), + + "map" => Ok(BuiltinFunction::new::().into()), + "fold" => Ok(BuiltinFunction::new::().into()), + "retain" => Ok(BuiltinFunction::new::().into()), + + "reverse" => Ok(BuiltinFunction::new::().into()), + "truncate" => Ok(BuiltinFunction::new::().into()), + + "to_lowercase" => Ok(BuiltinFunction::new::().into()), + "to_uppercase" => Ok(BuiltinFunction::new::().into()), + + "parse_scalar" => Ok(BuiltinFunction::new::().into()), + "parse_unsigned_integer" => { + Ok(BuiltinFunction::new::().into()) + } + "parse_signed_integer" => { + Ok(BuiltinFunction::new::().into()) + } + + "contains" => Ok(BuiltinFunction::new::().into()), + _ => Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)), + } + } +} + +impl StaticTypeName for IString { + fn static_type_name() -> Cow<'static, str> { + "String".into() + } +} + +impl StaticType for IString { + fn static_type() -> ValueType { + ValueType::String + } +} + +impl From for IString +where + S: Into, +{ + fn from(value: S) -> Self { + Self(value.into()) + } +} + +mod methods { + pub struct Format; + + pub struct Append; + pub struct Slice; + pub struct Chunks; + pub struct Lines; + + pub struct Map; + pub struct Fold; + pub struct Retain; + + pub struct Reverse; + pub struct Truncate; + + pub struct ToLowercase; + pub struct ToUppercase; + + pub struct ParseScalar; + pub struct ParseUnsignedInteger; + pub struct ParseSignedInteger; + + pub struct Contains; +} + +fn register_format_method(database: &mut BuiltinCallableDatabase) { + struct BuiltFunction { + signature: Arc, + } + + impl BuiltinCallable for BuiltFunction { + fn call( + &self, + context: &ExecutionContext, + argument: Dictionary, + ) -> ExpressionResult { + let this = context + .get_variable(LocatedStr { + location: context.stack_trace.bottom().clone(), + string: "self", + })? + .downcast_ref::(context.stack_trace)? + .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) + })?; + assert!(excess.is_empty()); + + let mut output = String::new(); + format.format(context, &mut output, argument)?; + + Ok(IString(ImString::from(output)).into()) + } + + fn name(&self) -> &str { + "String::format" + } + + fn signature(&self) -> &Arc { + &self.signature + } + + fn scope_type(&self) -> ScopeType { + ScopeType::Inherited + } + } + + let callable = BuiltFunction { + signature: Arc::new(Signature { + argument_type: StructDefinition { + members: Arc::new(HashableMap::from(HashMap::new())), + variadic: true, + }, + return_type: ValueType::String, + }), + }; + + database.register::(Box::new(callable)) +} + +pub fn register_methods(database: &mut BuiltinCallableDatabase) { + build_closure_type!(MapClosure(c: Value) -> Value); + build_closure_type!(FoldClosure(previous: Value, c: Value) -> Value); + build_closure_type!(RetainClosure(c: Value) -> Boolean); + + register_format_method(database); + + build_method!( + database, + methods::Append, "String::append", ( + _context: &ExecutionContext, + this: IString, + rhs: IString + ) -> IString { + let mut new = this.0.into_std_string(); + new.push_str(&rhs.0); + Ok(IString(new.into())) + } + ); + build_method!( + database, + methods::Slice, "String::slice", ( + _context: &ExecutionContext, + this: IString, + start: Option = ValueNone.into(), + end: Option = ValueNone.into() + ) -> IString { + let start = if let Some(start) = start { + start.0 as usize + } else { + 0usize + }; + + let end = if let Some(end) = end { + end.0 as usize + } else { + this.0.len() + }; + + Ok(IString(this.0.slice(start..end))) + } + ); + build_method!( + database, + methods::Chunks, "String::chunks", ( + context: &ExecutionContext, + this: IString, + size: UnsignedInteger, + strict: Boolean = Boolean(true).into() + ) -> List { + let size = size.0 as usize; + let strict = strict.0; + let num_chunks = this.0.len() / size; + let remainder = this.0.len() % size; + let mut chunks: Vec = Vec::with_capacity(num_chunks + if !strict && remainder != 0 { 1 } else { 0 }); + + for index in 0..num_chunks { + let start = index * size; + let end = start + size; + chunks.push(IString(this.0.slice(start..end)).into()); + } + + if !strict { + chunks.push(IString(this.0.slice((num_chunks * size)..this.0.len())).into()); + } + + Ok(List::from_iter(context, chunks)) + } + ); + build_method!( + database, + methods::Lines, "String::lines", ( + context: &ExecutionContext, + this: IString, + include_empty: Boolean = Boolean(true).into() + ) -> List { + let include_empty = include_empty.0; + let lines: Vec = if include_empty { + this.0.lines().map(|line| IString(line).into()).collect() + } else { + this.0.lines().filter(|line| !line.is_empty()).map(|line| IString(line).into()).collect() + }; + Ok(List::from_iter(context, lines)) + } + ); + build_method!( + database, + methods::Map, "String::map", ( + context: &ExecutionContext, + this: IString, + f: MapClosure + ) -> IString { + let mut string = String::new(); + let result: ExpressionResult<()> = this.0.chars().try_for_each(|c| { + let result = f.call(context, Dictionary::new(context, HashMap::from_iter([ + ( + "c".into(), + IString(ImString::from(format!("{c}"))).into() + ) + ])))?.downcast::(context.stack_trace)?; + string.push_str(&result.0); + + Ok(()) + }); + result?; + + Ok(IString(ImString::from(string))) + } + ); + build_method!( + database, + methods::Fold, "String::fold", ( + context: &ExecutionContext, + this: IString, + init: Value, + f: FoldClosure + ) -> Value { + let mut accumulator = init; + for c in this.0.chars() { + accumulator = f.call(context, Dictionary::new(context, HashMap::from_iter([ + ( + "c".into(), + IString(ImString::from(format!("{c}"))).into() + ), + ( + "previous".into(), + accumulator + ) + ])))?; + } + + Ok(accumulator) + } + ); + build_method!( + database, + methods::Retain, "String::retain",( + context: &ExecutionContext, + this: IString, + f: RetainClosure + ) -> IString { + let mut product: String = String::with_capacity(this.0.len()); + + for c in this.0.chars() { + let retain = f.call(context, Dictionary::new(context, HashMap::from_iter([ + ( + "c".into(), + IString(ImString::from(format!("{c}"))).into() + ) + ])))?.downcast::(context.stack_trace)?; + + if retain.0 { + product.push(c); + } + } + + Ok(IString(ImString::from(product))) + } + ); + build_method!( + database, + methods::Reverse, "String::reverse",( + _context: &ExecutionContext, + this: IString + ) -> IString { + let reversed: String = this.0.as_str().chars().rev().collect(); + Ok(IString(ImString::from(reversed))) + } + ); + build_method!( + database, + methods::Truncate, "String::truncate",( + _context: &ExecutionContext, + this: IString, + length: UnsignedInteger + ) -> IString { + let length = length.0 as usize; + let length = this.0.len().min(length); + + Ok(IString(this.0.slice(0..length))) + } + ); + build_method!( + database, + methods::ToLowercase, "String::to_lowercase",( + _context: &ExecutionContext, + this: IString + ) -> IString { + let text = this.0.as_str().to_lowercase(); + Ok(IString(ImString::from(text))) + } + ); + build_method!( + database, + methods::ToUppercase, "String::to_uppercase",( + _context: &ExecutionContext, + this: IString + ) -> IString { + let text = this.0.as_str().to_uppercase(); + Ok(IString(ImString::from(text))) + } + ); + build_method!( + database, + methods::ParseScalar, "String::parse_scalar",( + 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, + }) + } + ); + build_method!( + database, + methods::ParseUnsignedInteger, "String::parse_unsigned_integer",( + 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)) + } + ); + build_method!( + database, + methods::ParseSignedInteger, "String::parse_signed_integer",( + 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)) + } + ); + build_method!( + database, + methods::Contains, "String::contains",( + _context: &ExecutionContext, + this: IString, + pattern: IString + ) -> Boolean { + let contained = this.0.contains(pattern.0.as_str()); + Ok(Boolean(contained)) + } + ); +} + +#[cfg(test)] +mod test { + use crate::execution::{test_run, values::Boolean}; + + #[test] + fn method_format() { + let product = + test_run("let one = 1; in \"Test {one} {two}\"::format(two = 2) == \"Test 1 2\"") + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_append() { + let product = test_run("\"1234\"::append(rhs = \"5678\") == \"12345678\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_slice() { + let product = test_run("\"1234\"::slice(start = 1u, end = 3u) == \"23\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"1234\"::slice(end = 3u) == \"123\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"1234\"::slice(start = 1u) == \"234\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_chunks() { + let product = + test_run("\"1234567\"::chunks(size = 2u) == [\"12\", \"34\", \"56\"]").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = + test_run("\"1234567\"::chunks(size = 2u, strict = true) == [\"12\", \"34\", \"56\"]") + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "\"1234567\"::chunks(size = 2u, strict = false) == [\"12\", \"34\", \"56\", \"7\"]", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_lines() { + let product = test_run("\"12\\n34\\n56\"::lines() == [\"12\", \"34\", \"56\"]").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"12\\n34\\n56\\n\"::lines() == [\"12\", \"34\", \"56\"]").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = + test_run("\"12\\n34\\n\\n56\\n\"::lines() == [\"12\", \"34\", \"\", \"56\"]").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "\"12\\n34\\n56\\n\"::lines(include_empty = false) == [\"12\", \"34\", \"56\"]", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "\"12\\n34\\n\\n56\\n\"::lines(include_empty = false) == [\"12\", \"34\", \"56\"]", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "\"12\\n34\\n56\\n\\n\"::lines(include_empty = true) == [\"12\", \"34\", \"56\", \"\"]", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"12\\n34\\n\\n56\\n\\n\"::lines(include_empty = true) == [\"12\", \"34\", \"\", \"56\", \"\"]").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "\"12\\n34\\n\\n56\"::lines(include_empty = true) == [\"12\", \"34\", \"\", \"56\"]", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "\"12\\n34\\n\\n56\"::lines(include_empty = false) == [\"12\", \"34\", \"56\"]", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_map() { + let product = test_run( + "\"abcd\"::map(f= (c: std.types.String) -> std.types.String: if c == \"b\" then \"X\" else c) == \"aXcd\"", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_fold() { + let product = + test_run("\"aabbabaababb\"::fold(init = 0u, f = (previous: std.types.UInt, c: std.types.String) -> std.types.UInt: if c == \"a\" then previous + 1u else previous) == 6u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_retain() { + let product = test_run( + "\"1234\"::retain(f = (c: std.types.String) -> std.types.Bool: c == \"1\" || c == \"3\") == \"13\"", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_reverse() { + let product = test_run("\"1234\"::reverse() == \"4321\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_truncate() { + let product = test_run("\"1234\"::truncate(length = 2u) == \"12\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_to_lowercase() { + let product = test_run("\"ABcd\"::to_lowercase() == \"abcd\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_to_uppercase() { + let product = test_run("\"ABcd\"::to_uppercase() == \"ABCD\"").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_parse_scalar() { + let product = test_run("\"23\"::parse_scalar() == 23").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"23.45\"::parse_scalar() == 23.45").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(\".45\"::parse_scalar() - 0.45)::abs() < 0.0001").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"-23\"::parse_scalar() == -23").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"-23.45\"::parse_scalar() == -23.45").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("(\"-.45\"::parse_scalar() - (-0.45))::abs() < 0.0001").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_parse_unsigned_integer() { + let product = test_run("\"23\"::parse_unsigned_integer() == 23u").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_parse_signed_integer() { + let product = test_run("\"23\"::parse_signed_integer() == 23i").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"-23\"::parse_signed_integer() == -23i").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn method_contains() { + let product = test_run("\"abcd\"::contains(pattern = \"a\")").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"abcd\"::contains(pattern = \"f\")").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("\"abcd\"::contains(pattern = \"bcd\")").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("\"abcd\"::contains(pattern = \"bcde\")").unwrap(); + assert_eq!(product, Boolean(false).into()); + } + + #[test] + fn format() { + let product = test_run( + "\"outer text: {value}\"::format(value = \"inner text\") == \"outer text: inner text\"", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } +} diff --git a/interpreter/src/execution/values/value_type.rs b/interpreter/src/execution/values/value_type.rs new file mode 100644 index 0000000..2eba6cb --- /dev/null +++ b/interpreter/src/execution/values/value_type.rs @@ -0,0 +1,869 @@ +/* + * Copyright 2024 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ +use std::{borrow::Cow, collections::HashMap, fmt::Display, sync::Arc}; + +use common_data_types::Dimension; +use hashable_map::{HashableMap, HashableSet}; +use imstr::ImString; + +use super::{ + closure::Signature as ClosureSignature, Boolean, Object, SignedInteger, StaticTypeName, + UnsignedInteger, Value, ValueNone, +}; + +use crate::{ + build_method, + compile::{self, AstNode}, + execute_expression, + execution::{ + errors::{ErrorType, ExpressionResult, Raise}, + logging::{LogLevel, LogMessage}, + values::{ + self, closure::BuiltinCallableDatabase, dictionary::DictionaryData, + string::formatting::Style, BuiltinFunction, Dictionary, File, IString, + MissingAttributeError, + }, + ExecutionContext, + }, +}; + +#[derive(Debug, Eq, Clone, PartialEq)] +pub enum ValueType { + TypeNone, + Boolean, + SignedInteger, + UnsignedInteger, + Scalar(Option), + Closure(Arc), + Dictionary(StructDefinition), + List(Option>), + String, + ValueType, + Vector2(Option), + Vector3(Option), + Vector4(Option), + MultiType(Box, Box), + File, + Any, + ConstraintSet(Arc>), + ManifoldMesh3D, +} + +impl From for ValueType { + fn from(value: StructDefinition) -> Self { + Self::Dictionary(value) + } +} + +impl ValueType { + pub fn name(&self) -> Cow<'static, str> { + match self { + Self::TypeNone => ValueNone::static_type_name(), + Self::Boolean => Boolean::static_type_name(), + Self::SignedInteger => SignedInteger::static_type_name(), + Self::UnsignedInteger => UnsignedInteger::static_type_name(), + Self::Scalar(Some(dimension)) => units::get_dimension_name(dimension), + Self::Scalar(Option::None) => "Scalar".into(), + Self::Vector2(Some(dimension)) => { + format!("Vector2<{}>", units::get_dimension_name(dimension)).into() + } + Self::Vector3(Some(dimension)) => { + format!("Vector3<{}>", units::get_dimension_name(dimension)).into() + } + Self::Vector4(Some(dimension)) => { + format!("Vector4<{}>", units::get_dimension_name(dimension)).into() + } + Self::Vector2(Option::None) => "Vector2".into(), + Self::Vector3(Option::None) => "Vector3".into(), + Self::Vector4(Option::None) => "Vector4".into(), + Self::String => IString::static_type_name(), + Self::File => File::static_type_name(), + Self::Any => "Any".into(), + Self::ManifoldMesh3D => "ManifoldMesh3D".into(), + _ => format!("{}", self).into(), + } + } + + pub fn merge(self, rhs: ValueType) -> Self { + // Do not merge if you already accept this type. + if self.check_other_qualifies(&rhs).is_err() { + Self::MultiType(Box::new(self), Box::new(rhs)) + } else { + self + } + } + + pub fn check_other_qualifies( + &self, + value_type: &ValueType, + ) -> Result<(), TypeQualificationError> { + match (self, value_type) { + (Self::Scalar(Some(our_dimension)), Self::Scalar(Some(their_dimension))) + | (Self::Vector2(Some(our_dimension)), Self::Vector2(Some(their_dimension))) + | (Self::Vector3(Some(our_dimension)), Self::Vector3(Some(their_dimension))) + | (Self::Vector4(Some(our_dimension)), Self::Vector4(Some(their_dimension))) => { + if our_dimension == their_dimension { + Ok(()) + } else { + Err(TypeQualificationError::This { + expected: self.clone(), + got: value_type.clone(), + }) + } + } + (Self::Scalar(Option::None), Self::Scalar(_)) => Ok(()), + (Self::Vector2(Option::None), Self::Vector2(_)) => Ok(()), + (Self::Vector3(Option::None), Self::Vector3(_)) => Ok(()), + (Self::Vector4(Option::None), Self::Vector4(_)) => Ok(()), + (Self::List(Option::None), Self::List(_)) => Ok(()), + (Self::List(Some(our_type)), Self::List(Some(their_type))) => { + our_type.check_other_qualifies(their_type) + } + (Self::Closure(our_signature), Self::Closure(their_signature)) => { + our_signature + .argument_type + .check_other_qualifies(&their_signature.argument_type)?; + + our_signature + .return_type + .check_other_qualifies(&their_signature.return_type) + } + (Self::Dictionary(our_definition), Self::Dictionary(their_definition)) => { + our_definition.check_other_qualifies(their_definition) + } + (Self::MultiType(left, right), value_type) => { + match ( + left.check_other_qualifies(value_type), + right.check_other_qualifies(value_type), + ) { + (Ok(_), _) | (_, Ok(_)) => Ok(()), + _ => Err(TypeQualificationError::This { + expected: self.clone(), + got: value_type.clone(), + }), + } + } + (Self::ManifoldMesh3D, Self::ManifoldMesh3D) => Ok(()), + (Self::Any, _) => Ok(()), + (expected, got) => { + if expected == got { + Ok(()) + } else { + Err(TypeQualificationError::This { + expected: expected.clone(), + got: got.clone(), + }) + } + } + } + } +} + +impl Display for ValueType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + // We can avoid a copy operation if we write directly into the formatter. + Self::Closure(signature) => write!(f, "{}", signature), + Self::Dictionary(definition) => write!(f, "{}", definition), + Self::MultiType(left, right) => write!(f, "{left} | {right}"), + Self::List(Some(ty)) => write!(f, "[{ty}]"), + Self::List(Option::None) => write!(f, "[]"), + Self::ConstraintSet(variables) => { + write!(f, "<<<")?; + + // To get a consistent output, sort the variables. + let mut variables: Vec<_> = variables.iter().collect(); + variables.sort_unstable(); + let mut variables = variables.iter().peekable(); + + while let Some(variable) = variables.next() { + if variables.peek().is_some() { + write!(f, "{variable}, ")?; + } else { + write!(f, "{variable}")?; + } + } + + write!(f, ": _ >>>") + } + _ => write!(f, "{}", self.name()), + } + } +} + +impl Object for ValueType { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::ValueType + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + if !matches!(style, Style::Default) { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Value types only support default formatting".into(), + }); + } + + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "Value types cannot be formatted with precision".into(), + }); + } + + write!(f, "{}", self) + } + + fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: Self = rhs.downcast_for_binary_op(context.stack_trace)?; + + Ok(self.merge(rhs).into()) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + match attribute { + "qualify" => Ok(BuiltinFunction::new::().into()), + "try_qualify" => Ok(BuiltinFunction::new::().into()), + _ => Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)), + } + } +} + +impl StaticTypeName for ValueType { + fn static_type_name() -> Cow<'static, str> { + "ValueType".into() + } +} + +mod methods { + pub struct Qualify; + pub struct TryQualify; +} + +pub fn register_methods(database: &mut BuiltinCallableDatabase) { + build_method!( + database, + methods::Qualify, "ValueType::qualify", ( + context: &ExecutionContext, + this: ValueType, + to_qualify: Value) -> ValueNone + { + this.check_other_qualifies(&to_qualify.get_type(context)).map_err(|error| error.to_error(context.stack_trace))?; + Ok(values::ValueNone) + } + ); + + build_method!( + database, + methods::TryQualify, "ValueType::try_qualify", ( + context: &ExecutionContext, + this: ValueType, + to_qualify: Value) -> values::Boolean + { + Ok(values::Boolean(this.check_other_qualifies(&to_qualify.get_type(context)).is_ok())) + } + ); +} + +#[derive(Debug, Eq, Clone, PartialEq)] +pub struct StructMember { + pub ty: ValueType, + pub default: Option, +} + +impl StructMember { + fn new( + context: &ExecutionContext, + source: &AstNode, + ) -> ExpressionResult { + let ty = execute_expression(context, &source.node.ty)? + .downcast::(context.stack_trace)?; + let default = if let Some(default) = source.node.default.as_ref() { + Some(execute_expression(context, default)?) + } else { + None + }; + + Ok(Self { ty, default }) + } +} + +impl Display for StructMember { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(_default) = self.default.as_ref() { + write!(f, "{} (optional)", self.ty) + } else { + write!(f, "{}", self.ty) + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct StructDefinition { + pub members: Arc>, + pub variadic: bool, +} + +impl StructDefinition { + pub fn new( + context: &ExecutionContext, + source: &AstNode, + ) -> ExpressionResult { + let mut members = HashMap::new(); + for member in source.node.members.iter() { + let name = member.node.name.node.clone(); + members.insert(name, StructMember::new(context, member)?); + } + + let members = Arc::new(HashableMap::from(members)); + let variadic = source.node.variadic; + Ok(Self { members, variadic }) + } + + pub fn fill_defaults(&self, dictionary: Dictionary) -> Dictionary { + let data = Arc::unwrap_or_clone(dictionary.data); + + let mut members: HashableMap = data.members; + let struct_def_variadic = data.struct_def.variadic; + let mut struct_def_members = Arc::unwrap_or_clone(data.struct_def.members); + + for (name, member) in self.members.iter() { + if let Some(default_value) = &member.default { + if members.get(name).is_none() { + members.insert(name.clone(), default_value.clone()); + struct_def_members.insert(name.clone(), member.clone()); + } + } + } + + Dictionary { + data: Arc::new(DictionaryData { + members, + struct_def: StructDefinition { + members: Arc::new(struct_def_members), + variadic: struct_def_variadic, + }, + }), + } + } + + pub fn check_other_qualifies( + &self, + other: &StructDefinition, + ) -> Result<(), TypeQualificationError> { + let mut errors = Vec::new(); + + // Check that all fields are present and correct. + for (name, member) in self.members.iter() { + if let Some(other_member) = other.members.get(name) { + if let Err(error) = member.ty.check_other_qualifies(&other_member.ty) { + errors.push(MissmatchedField { + name: name.clone(), + error, + }); + } + } else if member.default.is_none() { + errors.push(MissmatchedField { + name: name.clone(), + error: TypeQualificationError::This { + expected: member.ty.clone(), + got: ValueType::TypeNone, + }, + }); + } + } + + // Checkt that there are no extra fields (unless of course, we're supposed to have extra + // fields) + if !self.variadic { + for (name, member) in other.members.iter() { + if self.members.get(name).is_none() { + errors.push(MissmatchedField { + name: name.clone(), + error: TypeQualificationError::This { + expected: ValueType::TypeNone, + got: member.ty.clone(), + }, + }); + } + } + } + + if errors.is_empty() { + Ok(()) + } else { + Err(TypeQualificationError::Fields { + failed_feilds: errors, + }) + } + } +} + +impl Display for StructDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(")?; + + if self.members.is_empty() { + if self.variadic { + write!(f, "...")?; + } + } else { + let mut member_iter = self.members.iter(); + + // The first member should not have a comma before it. + if let Some((name, member)) = member_iter.next() { + write!(f, "{name}: {member}")?; + } + + // All other members get a comma before them. + for (name, member) in member_iter { + write!(f, ", {name}: {member}")?; + } + + // This struct is variadic. + if self.variadic { + // We're not empty, so something is guaranteed to have been printed before the + // dots. Make sure to put a comma before the dots. + write!(f, ", ...")?; + } + } + + write!(f, ")") + } +} + +impl StaticTypeName for StructDefinition { + fn static_type_name() -> Cow<'static, str> { + "Struct Definition".into() + } +} + +impl From> for StructDefinition { + fn from(map: HashMap) -> Self { + Self { + members: Arc::new(HashableMap::from(map)), + variadic: false, + } + } +} + +#[derive(Debug, Eq, PartialEq)] +pub struct MissmatchedField { + pub name: ImString, + pub error: TypeQualificationError, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum TypeQualificationError { + This { + expected: ValueType, + got: ValueType, + }, + Fields { + failed_feilds: Vec, + }, +} + +impl TypeQualificationError { + fn write_failure(&self, tab_depth: usize, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let tabs = " ".repeat(tab_depth); + + match self { + TypeQualificationError::This { expected, got } => { + write!(f, "{tabs}Expected {expected}, got {got}") + } + TypeQualificationError::Fields { failed_feilds } => { + writeln!(f, "{tabs}Dictionary did not meet requirements:")?; + + for field in failed_feilds { + write!(f, "{tabs} {}: ", field.name)?; + field.error.write_failure(tab_depth + 2, f)?; + } + + Ok(()) + } + } + } +} + +impl ErrorType for TypeQualificationError {} + +impl Display for TypeQualificationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.write_failure(0, f) + } +} + +#[cfg(test)] +mod test { + use pretty_assertions::assert_eq; + + use crate::execution::{test_context, test_run}; + + use super::*; + + #[test] + fn type_none() { + ValueType::TypeNone + .check_other_qualifies(&ValueType::TypeNone) + .unwrap(); + + ValueType::TypeNone + .check_other_qualifies(&ValueType::UnsignedInteger) + .unwrap_err(); + } + + #[test] + fn type_boolean() { + ValueType::Boolean + .check_other_qualifies(&ValueType::Boolean) + .unwrap(); + + ValueType::Boolean + .check_other_qualifies(&ValueType::TypeNone) + .unwrap_err(); + } + + #[test] + fn type_signed_integer() { + ValueType::SignedInteger + .check_other_qualifies(&ValueType::SignedInteger) + .unwrap(); + + ValueType::SignedInteger + .check_other_qualifies(&ValueType::TypeNone) + .unwrap_err(); + } + + #[test] + fn type_unsigned_integer() { + ValueType::UnsignedInteger + .check_other_qualifies(&ValueType::UnsignedInteger) + .unwrap(); + + ValueType::UnsignedInteger + .check_other_qualifies(&ValueType::TypeNone) + .unwrap_err(); + } + + #[test] + fn type_scalar() { + ValueType::Scalar(Some(Dimension::length())) + .check_other_qualifies(&ValueType::Scalar(Some(Dimension::length()))) + .unwrap(); + + ValueType::Scalar(None) + .check_other_qualifies(&ValueType::Scalar(Some(Dimension::length()))) + .unwrap(); + + ValueType::Scalar(Some(Dimension::length())) + .check_other_qualifies(&ValueType::Scalar(Some(Dimension::area()))) + .unwrap_err(); + + ValueType::Scalar(Some(Dimension::length())) + .check_other_qualifies(&ValueType::TypeNone) + .unwrap_err(); + } + + #[test] + fn type_vector2() { + ValueType::Vector2(Some(Dimension::length())) + .check_other_qualifies(&ValueType::Vector2(Some(Dimension::length()))) + .unwrap(); + + ValueType::Vector2(None) + .check_other_qualifies(&ValueType::Vector2(Some(Dimension::length()))) + .unwrap(); + + ValueType::Vector2(Some(Dimension::length())) + .check_other_qualifies(&ValueType::Vector2(Some(Dimension::area()))) + .unwrap_err(); + + ValueType::Vector2(Some(Dimension::length())) + .check_other_qualifies(&ValueType::TypeNone) + .unwrap_err(); + } + + #[test] + fn type_vector3() { + ValueType::Vector3(Some(Dimension::length())) + .check_other_qualifies(&ValueType::Vector3(Some(Dimension::length()))) + .unwrap(); + + ValueType::Vector3(None) + .check_other_qualifies(&ValueType::Vector3(Some(Dimension::length()))) + .unwrap(); + + ValueType::Vector3(Some(Dimension::length())) + .check_other_qualifies(&ValueType::Vector3(Some(Dimension::area()))) + .unwrap_err(); + + ValueType::Vector3(Some(Dimension::length())) + .check_other_qualifies(&ValueType::TypeNone) + .unwrap_err(); + } + + #[test] + fn type_vector4() { + ValueType::Vector4(Some(Dimension::length())) + .check_other_qualifies(&ValueType::Vector4(Some(Dimension::length()))) + .unwrap(); + + ValueType::Vector4(None) + .check_other_qualifies(&ValueType::Vector4(Some(Dimension::length()))) + .unwrap(); + + ValueType::Vector4(Some(Dimension::length())) + .check_other_qualifies(&ValueType::Vector4(Some(Dimension::area()))) + .unwrap_err(); + + ValueType::Vector4(Some(Dimension::length())) + .check_other_qualifies(&ValueType::TypeNone) + .unwrap_err(); + } + + #[test] + fn type_closure() { + test_context([], |context| { + let closure = test_run("() -> std.types.None: std.consts.None").unwrap(); + let closure = closure.as_userclosure().unwrap(); + + closure + .get_type(context) + .check_other_qualifies(&closure.get_type(context)) + .unwrap(); + }) + } + + #[test] + fn type_empty_dictionary() { + test_context([], |context| { + let structure = test_run("()").unwrap(); + let structure = structure.as_dictionary().unwrap(); + + let dictionary = test_run("()").unwrap(); + let dictionary = dictionary.as_dictionary().unwrap(); + + structure + .get_type(context) + .check_other_qualifies(&dictionary.get_type(context)) + .unwrap(); + }) + } + + #[test] + fn type_dictionary_with_value() { + test_context([], |context| { + let structure = test_run("(a: std.types.None)").unwrap(); + let structure = structure.as_valuetype().unwrap(); + + let dictionary = test_run("(a = std.consts.None)").unwrap(); + let dictionary = dictionary.as_dictionary().unwrap(); + + structure + .check_other_qualifies(&dictionary.get_type(context)) + .unwrap(); + }) + } + + #[test] + fn type_dictionary_with_length() { + test_context([], |context| { + let structure = test_run("(a: std.scalar.Length)").unwrap(); + let structure = structure.as_valuetype().unwrap(); + + let dictionary = test_run("(a = 1m)").unwrap(); + let dictionary = dictionary.as_dictionary().unwrap(); + + structure + .check_other_qualifies(&dictionary.get_type(context)) + .unwrap(); + }) + } + + #[test] + fn type_dictionary_with_extra_value() { + test_context([], |context| { + let structure = test_run("(a: std.types.None)").unwrap(); + let structure = structure.as_valuetype().unwrap(); + + let dictionary = test_run("(a = std.consts.None, b = std.consts.None)").unwrap(); + let dictionary = dictionary.as_dictionary().unwrap(); + + structure + .check_other_qualifies(&dictionary.get_type(context)) + .unwrap_err(); + }) + } + + #[test] + fn type_dictionary_varadic() { + test_context([], |context| { + let structure = test_run("(a: std.types.None, ...)").unwrap(); + let structure = structure.as_valuetype().unwrap(); + + let dictionary = test_run("(a = std.consts.None, b = std.consts.None)").unwrap(); + let dictionary = dictionary.as_dictionary().unwrap(); + + structure + .check_other_qualifies(&dictionary.get_type(context)) + .unwrap(); + }) + } + + #[test] + fn type_dictionary_nested() { + test_context([], |context| { + let structure = test_run("(a: (b: std.types.None))").unwrap(); + let structure = structure.as_valuetype().unwrap(); + + let dictionary = test_run("(a = (b = std.consts.None))").unwrap(); + let dictionary = dictionary.as_dictionary().unwrap(); + + structure + .check_other_qualifies(&dictionary.get_type(context)) + .unwrap(); + }) + } + + #[test] + fn type_value_type() { + ValueType::ValueType + .check_other_qualifies(&ValueType::ValueType) + .unwrap(); + + ValueType::UnsignedInteger + .check_other_qualifies(&ValueType::TypeNone) + .unwrap_err(); + } + + #[test] + fn combined_type() { + let value_type = test_run("std.types.None | std.types.UInt").unwrap(); + let value_type = value_type.as_valuetype().unwrap(); + + value_type + .check_other_qualifies(&ValueType::TypeNone) + .unwrap(); + + value_type + .check_other_qualifies(&ValueType::UnsignedInteger) + .unwrap(); + + value_type + .check_other_qualifies(&ValueType::SignedInteger) + .unwrap_err(); + } + + #[test] + fn triple_combined_type() { + let value_type = test_run("std.types.None | std.types.UInt | std.types.SInt").unwrap(); + let value_type = value_type.as_valuetype().unwrap(); + + value_type + .check_other_qualifies(&ValueType::TypeNone) + .unwrap(); + + value_type + .check_other_qualifies(&ValueType::UnsignedInteger) + .unwrap(); + + value_type + .check_other_qualifies(&ValueType::SignedInteger) + .unwrap(); + + value_type + .check_other_qualifies(&ValueType::Boolean) + .unwrap_err(); + } + + #[test] + fn value_type_any_value() { + test_context([], |context| { + ValueType::Any + .check_other_qualifies(&ValueType::TypeNone) + .unwrap(); + + ValueType::Any + .check_other_qualifies(&ValueType::Boolean) + .unwrap(); + + ValueType::Any + .check_other_qualifies(&ValueType::SignedInteger) + .unwrap(); + + ValueType::Any + .check_other_qualifies(&ValueType::UnsignedInteger) + .unwrap(); + + ValueType::Any + .check_other_qualifies(&ValueType::Scalar(Some(Dimension::length()))) + .unwrap(); + + let closure = test_run("() -> std.types.None: std.consts.None").unwrap(); + let closure = closure.as_userclosure().unwrap(); + + ValueType::Any + .check_other_qualifies(&closure.get_type(context)) + .unwrap(); + + let dictionary = test_run("(a = std.consts.None, b = std.consts.None)").unwrap(); + let dictionary = dictionary.as_dictionary().unwrap(); + + ValueType::Any + .check_other_qualifies(&dictionary.get_type(context)) + .unwrap(); + + ValueType::Any + .check_other_qualifies(&ValueType::ValueType) + .unwrap(); + }) + } + + #[test] + fn try_qualify_method() { + let result = test_run("std.types.Bool::try_qualify(to_qualify = true)").unwrap(); + assert_eq!(result, values::Boolean(true).into()); + + let result = test_run("std.types.Bool::try_qualify(to_qualify = 5u)").unwrap(); + assert_eq!(result, values::Boolean(false).into()); + } + + #[test] + fn qualify_method() { + 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(); + } +} diff --git a/interpreter/src/execution/values/vector.rs b/interpreter/src/execution/values/vector.rs new file mode 100644 index 0000000..8f13e63 --- /dev/null +++ b/interpreter/src/execution/values/vector.rs @@ -0,0 +1,1645 @@ +use common_data_types::Dimension; +use enum_downcast::{AsVariant, IntoVariant}; +use nalgebra::{Dim, RawStorage}; + +use crate::{ + build_closure_type, build_method, + compile::{self, AstNode}, + execute_expression, + execution::{ + errors::{ExpressionResult, GenericFailure, Raise as _}, + logging::StackTrace, + values::{ + closure::BuiltinCallableDatabase, scalar::UnwrapNotNan, string::formatting::Style, + BuiltinFunction, DowncastForBinaryOpError, MissingAttributeError, Object, Scalar, + StaticType, StaticTypeName, Value, ValueType, + }, + ExecutionContext, + }, +}; + +use std::{ + borrow::Cow, + hash::Hash, + ops::{Add, Div, Mul, Neg, Sub}, +}; + +type Float = f64; + +pub type Vector2 = Vector>; +pub type Vector3 = Vector>; +pub type Vector4 = Vector>; + +#[derive(Debug, Hash, Clone, Copy, PartialEq)] +pub struct Vector { + dimension: Dimension, + value: I, +} + +impl Eq for Vector where I: PartialEq {} + +impl Object for Vector +where + I: VectorInternalType, + Self: StaticTypeName + Into, + Value: IntoVariant + AsVariant, +{ + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + I::get_type(self.dimension) + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + let mut components = self.value.iter().peekable(); + + write!(f, "<(")?; + + while let Some(value) = components.next() { + let c = Scalar { + dimension: self.dimension, + value: common_data_types::Float::new(value).expect("Vector has NaN component"), + }; + + c.format(context, f, style, precision)?; + + if components.peek().is_some() { + write!(f, ", ")?; + } + } + + write!(f, ")>")?; + + Ok(()) + } + + fn addition(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs = self.unpack_same_dimension(context.stack_trace, 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)?; + 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)?; + 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)?; + 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 { + Ok(self.into()) + } + fn unary_minus(self, _context: &ExecutionContext) -> ExpressionResult { + Ok(Self { + value: -self.value, + ..self + } + .into()) + } + + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + let rhs: Self = rhs.downcast_for_binary_op(context.stack_trace)?; + Ok(self.dimension == rhs.dimension && self.value == rhs.value) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + ) -> ExpressionResult { + if let Some(value) = self + .value + .get_attribute(context, attribute, self.dimension)? + { + Ok(value) + } else { + match attribute { + "abs" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Abs, + >() + .into()), + "add_scalar" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::AddScalar, + >() + .into()), + "amax" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::AMax, + >() + .into()), + "amin" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::AMin, + >() + .into()), + "dot" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Dot, + >() + .into()), + "norm" | "length" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Norm, + >() + .into()), + "normalize" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Normalize, + >() + .into()), + "angle" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Angle, + >() + .into()), + "apply" | "map" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Map, + >() + .into()), + "fold" => Ok(BuiltinFunction::new::< + <::MethodSet as methods::MethodSet>::Fold, + >() + .into()), + _ => Err(MissingAttributeError { + name: attribute.into(), + } + .to_error(context.stack_trace)), + } + } + } +} + +impl Vector +where + I: VectorInternalType + std::fmt::Debug, + Self: StaticTypeName + Into, + Value: IntoVariant + AsVariant, +{ + pub fn new( + context: &ExecutionContext, + dimension: Dimension, + value: I::BuildFrom, + ) -> ExpressionResult { + let value = I::build(value); + + Self::new_raw(context, dimension, value) + } + + pub fn from_ast( + context: &ExecutionContext, + ast_node: &AstNode>, + ) -> ExpressionResult { + I::from_ast(context, ast_node) + } + + fn new_raw( + context: &ExecutionContext, + dimension: Dimension, + value: I, + ) -> ExpressionResult { + if !value.is_nan() { + Ok(Self { dimension, value }) + } else { + Err( + GenericFailure("Result of arithmetic operation is NaN".into()) + .to_error(context.stack_trace), + ) + } + } + + pub fn dimension(&self) -> Dimension { + self.dimension + } + + pub fn raw_value(&self) -> I { + self.value + } + + fn unpack_same_dimension(self, stack_trace: &StackTrace, rhs: Value) -> ExpressionResult { + let rhs: Vector = rhs.downcast_for_binary_op(stack_trace)?; + + if self.dimension == rhs.dimension { + Ok(rhs) + } else { + Err(DowncastForBinaryOpError { + expected: self.type_name(), + got: rhs.type_name(), + } + .to_error(stack_trace)) + } + } +} + +impl StaticTypeName for Vector +where + I: StaticTypeName, +{ + fn static_type_name() -> Cow<'static, str> { + I::static_type_name() + } +} + +impl StaticType for Vector +where + I: StaticType, +{ + fn static_type() -> ValueType { + I::static_type() + } +} + +mod methods { + use stack::ArrayVec; + use std::collections::HashMap; + + use super::*; + use crate::{ + build_method, + execution::values::{BuiltinCallableDatabase, Dictionary}, + }; + + pub trait MethodSet { + type Abs; + type AddScalar; + type AMax; + type AMin; + type Dot; + type Norm; + type Normalize; + type Angle; + type Map; + type Fold; + } + + macro_rules! build_method_set { + ($name:ident) => { + paste::paste! { + pub struct [<$name Abs>]; + pub struct [<$name AddScalar>]; + pub struct [<$name AMax>]; + pub struct [<$name AMin>]; + pub struct [<$name Dot>]; + pub struct [<$name Norm>]; + pub struct [<$name Normalize>]; + pub struct [<$name Angle>]; + pub struct [<$name Map>]; + pub struct [<$name Fold>]; + + pub struct [<$name MethodSet>]; + impl MethodSet for [<$name MethodSet>] { + type Abs = [<$name Abs>]; + type AddScalar = [<$name AddScalar>]; + type AMax = [<$name AMax>]; + type AMin = [<$name AMin>]; + type Dot = [<$name Dot>]; + type Norm = [<$name Norm>]; + type Normalize = [<$name Normalize>]; + type Angle = [<$name Angle>]; + type Map = [<$name Map>]; + type Fold = [<$name Fold>]; + } + } + }; + } + + build_method_set!(Vector2); + build_method_set!(Vector3); + build_method_set!(Vector4); + + pub struct Vector3Cross; + + pub fn register_methods(database: &mut BuiltinCallableDatabase, dimension: usize) + where + I: VectorInternalType, + Vector: StaticTypeName + Into, + Value: IntoVariant> + AsVariant>, + M: MethodSet + 'static, + { + build_closure_type!(MapClosure(c: Scalar) -> Scalar); + build_closure_type!(FoldClosure(previous: Scalar, c: Scalar) -> Scalar); + + build_method!( + database, + M::Abs, format!("Vector{dimension}::abs"), ( + context: &ExecutionContext, + this: Vector) -> Vector + { + let value = this.value.abs(); + Vector::new_raw(context, this.dimension, value) + } + ); + build_method!( + database, + M::AddScalar, format!("Vector{dimension}::add_scalar"), ( + context: &ExecutionContext, + this: Vector, + value: Scalar) -> Vector + { + if this.dimension == value.dimension { + let value = this.value.add_scalar(*value.value); + Vector::new_raw(context, this.dimension, value) + } else { + Err(DowncastForBinaryOpError { + expected: this.type_name(), + got: value.type_name(), + } + .to_error(context.stack_trace)) + } + } + ); + build_method!( + database, + M::AMax, format!("Vector{dimension}::amax"), ( + context: &ExecutionContext, + this: Vector) -> Scalar + { + let value = common_data_types::Float::new(this.value.amax()).unwrap_not_nan(context.stack_trace)?; + + Ok(Scalar { + dimension: this.dimension, + value + }) + } + ); + build_method!( + database, + M::AMin, format!("Vector{dimension}::amin"), ( + context: &ExecutionContext, + this: Vector) -> Scalar + { + let value = common_data_types::Float::new(this.value.amin()).unwrap_not_nan(context.stack_trace)?; + + Ok(Scalar { + dimension: this.dimension, + value + }) + } + ); + build_method!( + database, + M::Dot, format!("Vector{dimension}::dot"), ( + context: &ExecutionContext, + this: Vector, + 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)?; + + Ok(Scalar { + dimension: this.dimension, + value + }) + } else { + Err(DowncastForBinaryOpError { + expected: this.type_name(), + got: rhs.type_name(), + } + .to_error(context.stack_trace)) + } + } + ); + build_method!( + database, + M::Norm, format!("Vector{dimension}::norm"),( + context: &ExecutionContext, + this: Vector) -> Scalar + { + let value = common_data_types::Float::new(this.value.norm()).unwrap_not_nan(context.stack_trace)?; + + Ok(Scalar { + dimension: this.dimension, + value + }) + } + ); + build_method!( + database, + M::Normalize, format!("Vector{dimension}::normalize"),( + context: &ExecutionContext, + this: Vector) -> Vector + { + let value = this.value.normalize(); + Vector::::new_raw(context, Dimension::zero(), value) + } + ); + build_method!( + database, + M::Angle, format!("Vector{dimension}::angle"),( + context: &ExecutionContext, + this: Vector, + other: Vector) -> Scalar + { + let value = common_data_types::Float::new(this.value.angle(&other.value)).unwrap_not_nan(context.stack_trace)?; + + Ok(Scalar { + dimension: Dimension::angle(), + value + }) + } + ); + build_method!( + database, + M::Map, format!("Vector{dimension}::map"),( + context: &ExecutionContext, + this: Vector, + f: MapClosure) -> Vector + { + let operations: ArrayVec<[Value; 4]> = this.value.iter().map(|c| f.call(context, Dictionary::new(context, HashMap::from_iter([ + ( + "c".into(), + Scalar { + dimension: this.dimension, + value: common_data_types::Float::new(c).unwrap_not_nan(context.stack_trace)? + }.into() + ) + ])))).collect::>()?; + + let result: ArrayVec<[Scalar; 4]> = operations.into_iter().map(|v| v.downcast::(context.stack_trace)).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)); + } + } + + Ok(Vector::::new_raw(context, dimension, I::from_iterator(result.iter().map(|c| *c.value)))?) + } + ); + build_method!( + database, + M::Fold, format!("Vector{dimension}::fold"),( + context: &ExecutionContext, + this: Vector, + init: Value, + f: FoldClosure) -> Value + { + + let mut accumulator = init; + for component in this.value.iter() { + accumulator = f.call(context, Dictionary::new(context, HashMap::from_iter([ + ( + "c".into(), + Scalar { + dimension: this.dimension, + value: common_data_types::Float::new(component).unwrap_not_nan(context.stack_trace)? + }.into() + ), + ( + "previous".into(), + accumulator + ) + ])))?; + } + + Ok(accumulator) + } + ); + } +} + +pub fn register_methods(database: &mut BuiltinCallableDatabase) { + methods::register_methods::, methods::Vector2MethodSet>(database, 2); + methods::register_methods::, methods::Vector3MethodSet>(database, 3); + methods::register_methods::, methods::Vector4MethodSet>(database, 4); + + build_method!( + database, + methods::Vector3Cross, "Vector3::cross", ( + context: &ExecutionContext, + this: Vector3, + rhs: Vector3) -> Vector3 + { + if this.dimension == rhs.dimension { + let value = this.value.cross(&rhs.value); + Vector3::new_raw(context, this.dimension, value) + } else { + Err(DowncastForBinaryOpError { + expected: this.type_name(), + got: rhs.type_name(), + } + .to_error(context.stack_trace)) + } + } + ); +} + +pub trait VectorInternalType: + Add + + Div + + Mul + + Sub + + Copy + + PartialEq + + Neg + + StaticTypeName + + StaticType + + IsNan + + std::fmt::Debug + + Send + + Sync + + 'static +{ + type BuildFrom; + type NodeType; + type MethodSet: methods::MethodSet; + + fn get_type(dimension: Dimension) -> ValueType; + fn build(value: Self::BuildFrom) -> Self; + fn from_ast( + context: &ExecutionContext, + ast_node: &AstNode>, + ) -> ExpressionResult>; + fn from_iterator(iterator: I) -> Self + where + I: IntoIterator; + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + dimension: Dimension, + ) -> ExpressionResult>; + + fn abs(&self) -> Self; + fn add_scalar(&self, value: Float) -> Self; + fn amax(&self) -> Float; + fn amin(&self) -> Float; + fn dot(&self, rhs: &Self) -> Float; + fn norm(&self) -> Float; + fn normalize(&self) -> Self; + fn angle(&self, other: &Self) -> Float; + fn iter(&self) -> impl Iterator; +} + +pub trait IsNan { + fn is_nan(&self) -> bool; +} + +impl IsNan for nalgebra::Matrix +where + R: Dim, + C: Dim, + S: RawStorage, +{ + fn is_nan(&self) -> bool { + self.iter().any(|&component| component.is_nan()) + } +} + +macro_rules! get_component { + ($context:ident, $ast_node:ident, $c:ident) => { + execute_expression($context, &$ast_node.node.$c)? + .downcast::($context.stack_trace)? + }; +} + +impl VectorInternalType for nalgebra::Vector2 { + type BuildFrom = [Float; 2]; + type NodeType = compile::Vector2; + type MethodSet = methods::Vector2MethodSet; + + fn get_type(dimension: Dimension) -> ValueType { + ValueType::Vector2(Some(dimension)) + } + + fn build(value: Self::BuildFrom) -> Self { + Self::from_iterator(value) + } + + fn from_ast( + context: &ExecutionContext, + ast_node: &AstNode>, + ) -> ExpressionResult> { + let x = get_component!(context, ast_node, x); + let y = get_component!(context, ast_node, y); + + if x.dimension == y.dimension { + Ok(Vector { + dimension: x.dimension, + value: Self::new(*x.value, *y.value), + }) + } else { + Err( + GenericFailure("All components of a vector must match".into()) + .to_error(context.stack_trace), + ) + } + } + + fn from_iterator(iterator: I) -> Self + where + I: IntoIterator, + { + Self::from_iterator(iterator) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + dimension: Dimension, + ) -> ExpressionResult> { + match attribute { + "x" => Ok(Some( + Scalar { + dimension, + value: common_data_types::Float::new(self.x) + .unwrap_not_nan(context.stack_trace)?, + } + .into(), + )), + "y" => Ok(Some( + Scalar { + dimension, + value: common_data_types::Float::new(self.y) + .unwrap_not_nan(context.stack_trace)?, + } + .into(), + )), + _ => Ok(None), + } + } + + fn abs(&self) -> Self { + nalgebra::Vector2::abs(self) + } + + fn add_scalar(&self, value: Float) -> Self { + nalgebra::Vector2::add_scalar(self, value) + } + + fn amax(&self) -> Float { + nalgebra::Vector2::amax(self) + } + + fn amin(&self) -> Float { + nalgebra::Vector2::amin(self) + } + + fn dot(&self, rhs: &Self) -> Float { + nalgebra::Vector2::dot(self, rhs) + } + + fn norm(&self) -> Float { + nalgebra::Vector2::norm(self) + } + + fn normalize(&self) -> Self { + nalgebra::Vector2::normalize(self) + } + + fn angle(&self, other: &Self) -> Float { + nalgebra::Vector2::angle(self, other) + } + + fn iter(&self) -> impl Iterator { + self.iter().copied() + } +} +impl StaticTypeName for nalgebra::Vector2 { + fn static_type_name() -> Cow<'static, str> { + "Vector2".into() + } +} + +impl StaticType for nalgebra::Vector2 { + fn static_type() -> ValueType { + ValueType::Vector2(None) + } +} + +impl From for boolmesh::Vec2 { + fn from(value: Vector2) -> Self { + Self { + x: value.value.x, + y: value.value.y, + } + } +} + +impl VectorInternalType for nalgebra::Vector3 { + type BuildFrom = [Float; 3]; + type NodeType = compile::Vector3; + type MethodSet = methods::Vector3MethodSet; + + fn get_type(dimension: Dimension) -> ValueType { + ValueType::Vector3(Some(dimension)) + } + + fn build(value: Self::BuildFrom) -> Self { + Self::from_iterator(value) + } + + fn from_ast( + context: &ExecutionContext, + ast_node: &AstNode>, + ) -> ExpressionResult> { + let x = get_component!(context, ast_node, x); + let y = get_component!(context, ast_node, y); + let z = get_component!(context, ast_node, z); + + if x.dimension == y.dimension && x.dimension == z.dimension { + Ok(Vector { + dimension: x.dimension, + 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), + ) + } + } + + fn from_iterator(iterator: I) -> Self + where + I: IntoIterator, + { + Self::from_iterator(iterator) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + dimension: Dimension, + ) -> ExpressionResult> { + match attribute { + "x" => Ok(Some( + Scalar { + dimension, + value: common_data_types::Float::new(self.x) + .unwrap_not_nan(context.stack_trace)?, + } + .into(), + )), + "y" => Ok(Some( + Scalar { + dimension, + value: common_data_types::Float::new(self.y) + .unwrap_not_nan(context.stack_trace)?, + } + .into(), + )), + "z" => Ok(Some( + Scalar { + dimension, + value: common_data_types::Float::new(self.z) + .unwrap_not_nan(context.stack_trace)?, + } + .into(), + )), + "cross" => Ok(Some(BuiltinFunction::new::().into())), + _ => Ok(None), + } + } + + fn abs(&self) -> Self { + nalgebra::Vector3::abs(self) + } + + fn add_scalar(&self, value: Float) -> Self { + nalgebra::Vector3::add_scalar(self, value) + } + + fn amax(&self) -> Float { + nalgebra::Vector3::amax(self) + } + + fn amin(&self) -> Float { + nalgebra::Vector3::amin(self) + } + + fn dot(&self, rhs: &Self) -> Float { + nalgebra::Vector3::dot(self, rhs) + } + + fn norm(&self) -> Float { + nalgebra::Vector3::norm(self) + } + + fn normalize(&self) -> Self { + nalgebra::Vector3::normalize(self) + } + + fn angle(&self, other: &Self) -> Float { + nalgebra::Vector3::angle(self, other) + } + + fn iter(&self) -> impl Iterator { + self.iter().copied() + } +} +impl StaticTypeName for nalgebra::Vector3 { + fn static_type_name() -> Cow<'static, str> { + "Vector3".into() + } +} + +impl StaticType for nalgebra::Vector3 { + fn static_type() -> ValueType { + ValueType::Vector3(None) + } +} + +impl From for boolmesh::Vec3 { + fn from(value: Vector3) -> Self { + Self { + x: value.value.x, + y: value.value.y, + z: value.value.z, + } + } +} + +impl VectorInternalType for nalgebra::Vector4 { + type BuildFrom = [Float; 4]; + type NodeType = compile::Vector4; + type MethodSet = methods::Vector4MethodSet; + + fn get_type(dimension: Dimension) -> ValueType { + ValueType::Vector4(Some(dimension)) + } + + fn build(value: Self::BuildFrom) -> Self { + Self::from_iterator(value) + } + + fn from_ast( + context: &ExecutionContext, + ast_node: &AstNode>, + ) -> ExpressionResult> { + let x = get_component!(context, ast_node, x); + let y = get_component!(context, ast_node, y); + let z = get_component!(context, ast_node, z); + let w = get_component!(context, ast_node, w); + + if x.dimension == y.dimension && x.dimension == z.dimension && x.dimension == w.dimension { + Ok(Vector { + dimension: w.dimension, + 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), + ) + } + } + + fn from_iterator(iterator: I) -> Self + where + I: IntoIterator, + { + Self::from_iterator(iterator) + } + + fn get_attribute( + &self, + context: &ExecutionContext, + attribute: &str, + dimension: Dimension, + ) -> ExpressionResult> { + match attribute { + "x" => Ok(Some( + Scalar { + dimension, + value: common_data_types::Float::new(self.x) + .unwrap_not_nan(context.stack_trace)?, + } + .into(), + )), + "y" => Ok(Some( + Scalar { + dimension, + value: common_data_types::Float::new(self.y) + .unwrap_not_nan(context.stack_trace)?, + } + .into(), + )), + "z" => Ok(Some( + Scalar { + dimension, + value: common_data_types::Float::new(self.z) + .unwrap_not_nan(context.stack_trace)?, + } + .into(), + )), + "w" => Ok(Some( + Scalar { + dimension, + value: common_data_types::Float::new(self.w) + .unwrap_not_nan(context.stack_trace)?, + } + .into(), + )), + _ => Ok(None), + } + } + + fn abs(&self) -> Self { + nalgebra::Vector4::abs(self) + } + + fn add_scalar(&self, value: Float) -> Self { + nalgebra::Vector4::add_scalar(self, value) + } + + fn amax(&self) -> Float { + nalgebra::Vector4::amax(self) + } + fn amin(&self) -> Float { + nalgebra::Vector4::amin(self) + } + + fn dot(&self, rhs: &Self) -> Float { + nalgebra::Vector4::dot(self, rhs) + } + + fn norm(&self) -> Float { + nalgebra::Vector4::norm(self) + } + + fn normalize(&self) -> Self { + nalgebra::Vector4::normalize(self) + } + + fn angle(&self, other: &Self) -> Float { + nalgebra::Vector4::angle(self, other) + } + + fn iter(&self) -> impl Iterator { + self.iter().copied() + } +} +impl StaticTypeName for nalgebra::Vector4 { + fn static_type_name() -> Cow<'static, str> { + "Vector4".into() + } +} + +impl StaticType for nalgebra::Vector4 { + fn static_type() -> ValueType { + ValueType::Vector4(None) + } +} + +impl From for boolmesh::Vec4 { + fn from(value: Vector4) -> Self { + Self { + x: value.value.x, + y: value.value.y, + z: value.value.z, + w: value.value.w, + } + } +} + +macro_rules! equivalent_boolmesh_vector { + (Vector2) => { + boolmesh::Vec2 + }; + (Vector3) => { + boolmesh::Vec3 + }; + (Vector4) => { + boolmesh::Vec4 + }; +} + +macro_rules! build_vector_type { + ($name:ident: $type:tt = $dimension:expr) => { + pub struct $name($type); + + impl StaticTypeName for $name { + fn static_type_name() -> Cow<'static, str> { + stringify!($name).into() + } + } + + impl StaticType for $name { + fn static_type() -> ValueType { + ValueType::$type(Some($dimension)) + } + } + + impl enum_downcast::IntoVariant<$name> for Value { + fn into_variant(self) -> Result<$name, Value> { + Ok($name(self.into_variant()?)) + } + } + + impl From<$name> for $type { + fn from(value: $name) -> $type { + value.0 + } + } + + impl From<$name> for equivalent_boolmesh_vector!($type) { + fn from(value: $name) -> Self { + value.0.into() + } + } + + impl std::ops::Deref for $name { + type Target = $type; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + }; +} + +build_vector_type!(Zero3: Vector3 = Dimension::length()); +build_vector_type!(Length2: Vector2 = Dimension::length()); +build_vector_type!(Length3: Vector3 = Dimension::length()); + +#[cfg(test)] +mod test { + use crate::execution::{test_context, test_run, values::Boolean}; + use pretty_assertions::assert_eq; + + use super::*; + + #[test] + fn construct_vector2() { + test_context([], |context| { + let product = test_run("<(1m, 2m)>").unwrap(); + assert_eq!( + product, + Vector2::new(context, Dimension::length(), [1.0, 2.0]) + .unwrap() + .into() + ); + + let product = test_run("<(-1m, -2m)>").unwrap(); + assert_eq!( + product, + Vector2::new(context, Dimension::length(), [-1.0, -2.0]) + .unwrap() + .into() + ); + }) + } + + #[test] + fn construct_vector3() { + test_context([], |context| { + let product = test_run("<(1m, 2m, 3m)>").unwrap(); + assert_eq!( + product, + Vector3::new(context, Dimension::length(), [1.0, 2.0, 3.0]) + .unwrap() + .into() + ); + + let product = test_run("<(-1m, -2m, -3m)>").unwrap(); + assert_eq!( + product, + Vector3::new(context, Dimension::length(), [-1.0, -2.0, -3.0]) + .unwrap() + .into() + ); + }) + } + + #[test] + fn construct_vector4() { + test_context([], |context| { + let product = test_run("<(1m, 2m, 3m, 4m)>").unwrap(); + assert_eq!( + product, + Vector4::new(context, Dimension::length(), [1.0, 2.0, 3.0, 4.0]) + .unwrap() + .into() + ); + + let product = test_run("<(-1m, -2m, -3m, -4m)>").unwrap(); + assert_eq!( + product, + Vector4::new(context, Dimension::length(), [-1.0, -2.0, -3.0, -4.0]) + .unwrap() + .into() + ); + }) + } + + #[test] + fn missmatched_dimensions_vector2() { + test_run("<(1deg, 2m)>").unwrap_err(); + test_run("<(1m, 2deg)>").unwrap_err(); + } + + #[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(); + } + + #[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(); + } + + #[test] + fn construccomponent_access_vector2() { + let product = test_run("let vec = <(1m, 2m)>; in vec.x").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(1.0).unwrap() + } + .into() + ); + + let product = test_run("let vec = <(1m, 2m)>; in vec.y").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(2.0).unwrap() + } + .into() + ); + } + + #[test] + fn construccomponent_access_vector3() { + let product = test_run("let vec = <(1m, 2m, 3m)>; in vec.x").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(1.0).unwrap() + } + .into() + ); + + let product = test_run("let vec = <(1m, 2m, 3m)>; in vec.y").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(2.0).unwrap() + } + .into() + ); + + let product = test_run("let vec = <(1m, 2m, 3m)>; in vec.z").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(3.0).unwrap() + } + .into() + ); + } + + #[test] + fn construccomponent_access_vector4() { + let product = test_run("let vec = <(1m, 2m, 3m, 4m)>; in vec.x").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(1.0).unwrap() + } + .into() + ); + + let product = test_run("let vec = <(1m, 2m, 3m, 4m)>; in vec.y").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(2.0).unwrap() + } + .into() + ); + + let product = test_run("let vec = <(1m, 2m, 3m, 4m)>; in vec.z").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(3.0).unwrap() + } + .into() + ); + + let product = test_run("let vec = <(1m, 2m, 3m, 4m)>; in vec.w").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(4.0).unwrap() + } + .into() + ); + } + + #[test] + fn compare_vector2() { + let product = test_run("<(1m, 2m)> == <(1m, 2m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("<(1m, 2m)> != <(1m, 2m)>").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("<(2m, 2m)> == <(1m, 2m)>").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("<(2m, 2m)> != <(1m, 2m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn compare_vector3() { + let product = test_run("<(1m, 2m, 3m)> == <(1m, 2m, 3m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("<(1m, 2m, 3m)> != <(1m, 2m, 3m)>").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("<(2m, 2m, 3m)> == <(1m, 2m, 3m)>").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("<(2m, 2m, 3m)> != <(1m, 2m, 3m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn compare_vector4() { + let product = test_run("<(1m, 2m, 3m, 4m)> == <(1m, 2m, 3m, 4m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run("<(1m, 2m, 3m, 4m)> != <(1m, 2m, 3m, 4m)>").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("<(2m, 2m, 3m, 4m)> == <(1m, 2m, 3m, 4m)>").unwrap(); + assert_eq!(product, Boolean(false).into()); + + let product = test_run("<(2m, 2m, 3m, 4m)> != <(1m, 2m, 3m, 4m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn add_vector2() { + let product = test_run("<(1m, 2m)> + <(2m, 3m)> == <(3m, 5m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn add_vector3() { + let product = test_run("<(1m, 2m, 3m)> + <(2m, 3m, 4m)> == <(3m, 5m, 7m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn add_vector4() { + let product = + test_run("<(1m, 2m, 3m, 4m)> + <(2m, 3m, 4m, 5m)> == <(3m, 5m, 7m, 9m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn sub_vector2() { + let product = test_run("<(1m, 2m)> - <(2m, 3m)> == <(-1m, -1m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn sub_vector3() { + let product = test_run("<(1m, 2m, 3m)> - <(2m, 3m, 4m)> == <(-1m, -1m, -1m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn sub_vector4() { + let product = + test_run("<(1m, 2m, 3m, 4m)> - <(2m, 3m, 4m, 5m)> == <(-1m, -1m, -1m, -1m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn multiply_vector2() { + let product = test_run("<(1m, 2m)> * 2.0 == <(2m, 4m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn multiply_vector3() { + let product = test_run("<(1m, 2m, 3m)> * 2.0 == <(2m, 4m, 6m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn multiply_vector4() { + let product = test_run("<(1m, 2m, 3m, 4m)> * 2.0 == <(2m, 4m, 6m, 8m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn divide_vector2() { + let product = test_run("<(2m, 4m)> / 2.0 == <(1m, 2m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn divide_vector3() { + let product = test_run("<(2m, 4m, 6m)> / 2.0 == <(1m, 2m, 3m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn divide_vector4() { + let product = test_run("<(2m, 4m, 6m, 8m)> / 2.0 == <(1m, 2m, 3m, 4m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn abs_vector2() { + let product = test_run("<(-1m, -2m)>::abs() == <(1m, 2m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn abs_vector3() { + let product = test_run("<(-1m, -2m, -3m)>::abs() == <(1m, 2m, 3m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn abs_vector4() { + let product = test_run("<(-1m, -2m, -3m, -4m)>::abs() == <(1m, 2m, 3m, 4m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn add_scalar_vector2() { + let product = test_run("<(1m, 2m)>::add_scalar(value = 1m) == <(2m, 3m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn add_scalar_vector3() { + let product = test_run("<(1m, 2m, 3m)>::add_scalar(value = 1m) == <(2m, 3m, 4m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn add_scalar_vector4() { + let product = + test_run("<(1m, 2m, 3m, 4m)>::add_scalar(value = 1m) == <(2m, 3m, 4m, 5m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn amax_vector2() { + let product = test_run("<(1m, 2m)>::amax() == 2m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn amax_vector3() { + let product = test_run("<(1m, 2m, 3m)>::amax() == 3m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn amax_vector4() { + let product = test_run("<(1m, 2m, 3m, 4m)>::amax() == 4m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn dot_vector2() { + let product = test_run("<(1m, 0m)>::dot(rhs = <(0.5m, 10m)>) == 0.5m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn dot_vector3() { + let product = test_run("<(1m, 0m, 0m)>::dot(rhs = <(0.5m, 10m, 10m)>) == 0.5m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn dot_vector4() { + let product = + test_run("<(1m, 0m, 0m, 0m)>::dot(rhs = <(0.5m, 10m, 10m, 10m)>) == 0.5m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn norm_vector2() { + let product = test_run("<(1m, 0m)>::norm() == 1m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn norm_vector3() { + let product = test_run("<(1m, 0m, 0m)>::norm() == 1m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn norm_vector4() { + let product = test_run("<(1m, 0m, 0m, 0m)>::norm() == 1m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn length_vector2() { + let product = test_run("<(1m, 0m)>::length() == 1m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn length_vector3() { + let product = test_run("<(1m, 0m, 0m)>::length() == 1m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn length_vector4() { + let product = test_run("<(1m, 0m, 0m, 0m)>::length() == 1m").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn normalize_vector2() { + let product = test_run("<(5m, 0m)>::normalize() == <(1, 0)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn normalize_vector3() { + let product = test_run("<(5m, 0m, 0m)>::normalize() == <(1, 0, 0)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn normalize_vector4() { + let product = test_run("<(5m, 0m, 0m, 0m)>::normalize() == <(1, 0, 0, 0)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn normalize_zero_vector2() { + test_run("<(0m, 0m)>::normalize()").unwrap_err(); + } + + #[test] + fn normalize_zero_vector3() { + test_run("<(0m, 0m, 0m)>::normalize()").unwrap_err(); + } + + #[test] + fn normalize_zero_vector4() { + test_run("<(0m, 0m, 0m, 0m)>::normalize()").unwrap_err(); + } + + #[test] + fn cross_vector3() { + let product = + test_run("<(1m, 0m, 0m)>::cross(rhs = <(0m, 1m, 0m)>) == <(0m, 0m, 1m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn angle_vector2() { + let product = test_run("<(1m, 0m)>::angle(other = <(0m, 1m)>) - 90deg < 0.001deg").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn angle_vector3() { + let product = + test_run("<(1m, 0m, 0m)>::angle(other = <(0m, 1m, 0m)>) - 90deg < 0.001deg").unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn angle_vector4() { + let product = + test_run("<(1m, 0m, 0m, 0m)>::angle(other = <(0m, 1m, 0m, 0m)>) - 90deg < 0.001deg") + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } + + #[test] + fn apply_vector2() { + let product = test_run("<(0m, 1m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Length: c + 1m) == <(1m, 2m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + + 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(); + } + + #[test] + fn apply_vector3() { + let product = test_run("<(0m, 1m, 2m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Length: c + 1m) == <(1m, 2m, 3m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + + 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(); + } + + #[test] + fn apply_vector4() { + let product = test_run("<(0m, 1m, 2m, 3m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Length: c + 1m) == <(1m, 2m, 3m, 4m)>").unwrap(); + assert_eq!(product, Boolean(true).into()); + + 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(); + } + + #[test] + fn fold_vector2() { + let product = test_run("<(1m, 2m)>::fold(init = 0m, f = (previous: std.scalar.Length, c: std.scalar.Length) -> std.scalar.Length: previous + c)").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(3.0).unwrap() + } + .into() + ); + } + + #[test] + fn fold_vector3() { + let product = test_run("<(1m, 2m, 3m)>::fold(init = 0m, f = (previous: std.scalar.Length, c: std.scalar.Length) -> std.scalar.Length: previous + c)").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(6.0).unwrap() + } + .into() + ); + } + + #[test] + fn fold_vector4() { + let product = test_run("<(1m, 2m, 3m, 4m)>::fold(init = 0m, f = (previous: std.scalar.Length, c: std.scalar.Length) -> std.scalar.Length: previous + c)").unwrap(); + assert_eq!( + product, + Scalar { + dimension: Dimension::length(), + value: common_data_types::Float::new(10.0).unwrap() + } + .into() + ); + } + + #[test] + fn format() { + let product = test_run( + "\"{a} {b} {c:.2}\"::format(a = <(1, 2)>, b = <(1m, 2m)>, c = <(1.234, 2.345)>) == \"<(1, 2)> <(1m, 2m)> <(1.23, 2.34)>\"", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "\"{a} {b} {c:.2}\"::format(a = <(1, 2, 3)>, b = <(1m, 2m, 3m)>, c = <(1.234, 2.345, 3.456)>) == \"<(1, 2, 3)> <(1m, 2m, 3m)> <(1.23, 2.34, 3.46)>\"", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + + let product = test_run( + "\"{a} {b} {c:.2}\"::format(a = <(1, 2, 3, 4)>, b = <(1m, 2m, 3m, 4m)>, c = <(1.234, 2.345, 3.456, 4.567)>) == \"<(1, 2, 3, 4)> <(1m, 2m, 3m, 4m)> <(1.23, 2.34, 3.46, 4.57)>\"", + ) + .unwrap(); + assert_eq!(product, Boolean(true).into()); + } +} diff --git a/interpreter/src/execution/values/void.rs b/interpreter/src/execution/values/void.rs new file mode 100644 index 0000000..13e4a82 --- /dev/null +++ b/interpreter/src/execution/values/void.rs @@ -0,0 +1,85 @@ +/* + * Copyright 2024 James Carl + * AGPL-3.0-only or AGPL-3.0-or-later + * + * This file is part of Command Cad. + * + * Command CAD is free software: you can redistribute it and/or modify it under the terms of + * the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ + +use crate::execution::{ + logging::{LogLevel, LogMessage}, + values::{string::formatting::Style, StaticType}, + ExecutionContext, +}; + +use super::{value_type::ValueType, Object, StaticTypeName}; + +use std::borrow::Cow; + +#[derive(Debug, Hash, Clone, Copy, Eq, PartialEq)] +pub struct ValueNone; + +impl Object for ValueNone { + fn get_type(&self, _context: &ExecutionContext) -> ValueType { + ValueType::TypeNone + } + + fn format( + &self, + context: &ExecutionContext, + f: &mut dyn std::fmt::Write, + style: Style, + precision: Option, + ) -> std::fmt::Result { + if !matches!(style, Style::Default) { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "None values only support default formatting".into(), + }); + } + + if precision.is_some() { + context.log.push_message(LogMessage { + origin: context.stack_trace.bottom().clone(), + level: LogLevel::Warning, + message: "None values cannot be formatted with precision".into(), + }); + } + + write!(f, "None") + } +} + +impl StaticTypeName for ValueNone { + fn static_type_name() -> Cow<'static, str> { + "None".into() + } +} + +impl StaticType for ValueNone { + fn static_type() -> ValueType { + ValueType::TypeNone + } +} + +#[cfg(test)] +mod test { + use crate::execution::{test_run, values}; + + #[test] + fn format() { + let product = test_run("\"{value}\"::format(value = std.consts.None) == \"None\"").unwrap(); + assert_eq!(product, values::Boolean(true).into()); + } +} diff --git a/src/script/execution/types/none.rs b/interpreter/src/lib.rs similarity index 57% rename from src/script/execution/types/none.rs rename to interpreter/src/lib.rs index 653b8ad..8ca108b 100644 --- a/src/script/execution/types/none.rs +++ b/interpreter/src/lib.rs @@ -1,5 +1,5 @@ /* - * Copyright 2024 James Carl + * Copyright 2025 James Carl * AGPL-3.0-only or AGPL-3.0-or-later * * This file is part of Command Cad. @@ -16,26 +16,14 @@ * program. If not, see . */ -use crate::script::{logging::RuntimeLog, parsing::VariableType, Span}; +pub mod compile; +pub mod execution; -use super::{NamedObject, Object, OperatorResult}; - -#[derive(Debug, Clone, PartialEq)] -pub struct NoneType; - -impl Object for NoneType { - fn matches_type( - &self, - _ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(false) - } -} - -impl NamedObject for NoneType { - fn static_type_name() -> &'static str { - "None" - } -} +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, + Store, +}; +pub use imstr::ImString; +pub use tree_sitter::{Point as TextPoint, Range as TextRange}; diff --git a/interpreter/test_assets/import_me.ccm b/interpreter/test_assets/import_me.ccm new file mode 100644 index 0000000..fea6818 --- /dev/null +++ b/interpreter/test_assets/import_me.ccm @@ -0,0 +1,2 @@ +# This file is imported as part of the `import` unit test. +5u diff --git a/interpreter/test_assets/infinite_recursion_import.ccm b/interpreter/test_assets/infinite_recursion_import.ccm new file mode 100644 index 0000000..7348f46 --- /dev/null +++ b/interpreter/test_assets/infinite_recursion_import.ccm @@ -0,0 +1,2 @@ +# Import ourselves infinitely. Very bad. +std.import(path = "./infinite_recursion_import.ccm") diff --git a/interpreter/test_assets/recursive_import.ccm b/interpreter/test_assets/recursive_import.ccm new file mode 100644 index 0000000..6d38be2 --- /dev/null +++ b/interpreter/test_assets/recursive_import.ccm @@ -0,0 +1 @@ +std.import(path = "import_me.ccm") diff --git a/src/arguments.rs b/src/arguments.rs deleted file mode 100644 index 57340da..0000000 --- a/src/arguments.rs +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use clap::builder::{NonEmptyStringValueParser, PossibleValuesParser, TypedValueParser}; -use std::{fmt::Display, path::PathBuf, str::FromStr}; - -use clap::Parser; - -#[derive(Clone, Debug)] -pub enum OutputTarget { - Stdout, - File(PathBuf), -} - -impl Display for OutputTarget { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Stdout => write!(f, "[stdout]"), - Self::File(path) => write!(f, "{:?}", path), - } - } -} - -impl Default for OutputTarget { - fn default() -> Self { - Self::Stdout - } -} - -impl FromStr for OutputTarget { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "-" => Ok(Self::Stdout), - "[stdout]" => Ok(Self::Stdout), - _ => Ok(Self::File(PathBuf::from(s))), - } - } -} - -#[derive(Parser)] -#[command(name = "ccad")] -#[command(bin_name = "ccad")] -/// Create fabricatable 3D and 2D models from code and generate output with commands. -pub enum Command { - Run(RunArgs), - Sketch(SketchArgs), - Form(FormArgs), - ListUnits(ListUnitsArgs), -} - -#[derive(Clone, Debug)] -pub enum TaskOutputFormat { - Yaml, - Json, -} - -impl Default for TaskOutputFormat { - fn default() -> Self { - Self::Json - } -} - -impl Display for TaskOutputFormat { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TaskOutputFormat::Yaml => write!(f, "yaml"), - TaskOutputFormat::Json => write!(f, "json"), - } - } -} - -impl FromStr for TaskOutputFormat { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "yaml" => Ok(Self::Yaml), - "json" => Ok(Self::Json), - _ => Err(()), - } - } -} - -#[derive(clap::Args)] -#[command(version, about)] -/// Run a task and write the output to a file, or stdout. -pub struct RunArgs { - /// Path to script to run - pub script: PathBuf, - - /// Name of the task within the script to be ran - pub task_name: String, - - /// The format to output the result in (is automatically derived if outputting to a file, or defaults to json if outputting to stdout) - #[arg(long, default_value = Option::None, - value_parser = PossibleValuesParser::new(["json", "yaml"]) - .map(|s| s.parse::().unwrap()),)] - pub output_format: Option, - - /// Path to target output file. Set to - or leave blank to output to stdout - #[arg(long, default_value_t = OutputTarget::default(), - value_parser = NonEmptyStringValueParser::new().map(|s| s.parse::().unwrap()))] - pub output: OutputTarget, - - /// Arguments to be passed to the task function - #[arg(last = true)] - pub arguments: Vec, -} - -#[derive(clap::Args)] -#[command(version, about)] -/// Build a 2D sketch. -pub struct SketchArgs { - pub script: PathBuf, - pub sketch_name: String, - - #[arg(last = true)] - pub arguments: Vec, -} - -#[derive(Clone, Debug)] -pub enum SolidOutputFormat { - ThreeMF, - Stl, - Obj, -} - -impl Default for SolidOutputFormat { - fn default() -> Self { - Self::Stl - } -} - -impl Display for SolidOutputFormat { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - SolidOutputFormat::ThreeMF => write!(f, "3mf"), - SolidOutputFormat::Stl => write!(f, "stl"), - SolidOutputFormat::Obj => write!(f, "obj"), - } - } -} - -impl FromStr for SolidOutputFormat { - type Err = (); - - fn from_str(s: &str) -> Result { - match s.to_ascii_lowercase().as_str() { - "3mf" => Ok(Self::ThreeMF), - "stl" => Ok(Self::Stl), - "obj" => Ok(Self::Obj), - _ => Err(()), - } - } -} - -#[derive(clap::Args)] -#[command(version, about)] -/// Form a 3D solid. -pub struct FormArgs { - pub script: PathBuf, - pub solid_name: String, - - /// File to write solid to (3MF, STL, or STL) - #[arg(long, default_value = Option::None, - value_parser = PossibleValuesParser::new(["3mf", "stl", "obj"]) - .map(|s| s.parse::().unwrap()),)] - pub output_format: Option, - - pub tolerance: Option, - - /// Path to target output file. Set to - or leave blank to output to stdout - #[arg(long, default_value_t = OutputTarget::default(), - value_parser = NonEmptyStringValueParser::new().map(|s| s.parse::().unwrap()))] - pub output: OutputTarget, - - #[arg(last = true)] - pub arguments: Vec, -} - -#[derive(clap::Args)] -#[command(version, about)] -/// List builtin units of measurement -pub struct ListUnitsArgs { - /// Optional filter. Will only print results containing the keyword. - pub search: Option, -} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 9ddf3c7..0000000 --- a/src/main.rs +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::{ - fs::{self, File}, - io::{IsTerminal, Seek, Write}, - ops::Deref, - path::PathBuf, - str::FromStr, -}; - -use anyhow::{anyhow, bail, Context, Result}; -use arguments::{ListUnitsArgs, OutputTarget, SolidOutputFormat, TaskOutputFormat}; -use clap::Parser; - -mod arguments; -// mod package; -mod script; - -use fj_core::algorithms::{ - approx::Tolerance, bounding_volume::BoundingVolume, triangulate::Triangulate, -}; -use fj_export::{export_3mf, export_obj, export_stl}; -use fj_math::{Aabb, Point, Scalar as FornjotScalar}; -use imstr::ImString; -use nom_locate::LocatedSpan; -use script::{Failure, Runtime, SerializableValue}; -use tempfile::SpooledTempFile; -use uom::si::{f64::Length, length::millimeter}; - -use crate::script::{print_all_supported_units, Scalar}; - -fn main() { - stderrlog::new() - .show_level(true) - .verbosity(3) - .init() - .unwrap(); - let command = arguments::Command::parse(); - - match command { - arguments::Command::Run(run_args) => run(run_args), - arguments::Command::Sketch(sketch_args) => sketch(sketch_args), - arguments::Command::Form(form_args) => form(form_args), - arguments::Command::ListUnits(list_args) => list_units(list_args), - } -} - -fn list_units(list_args: ListUnitsArgs) { - let mut output = std::io::stdout(); - if let Err(error) = print_all_supported_units(&mut output, list_args.search.as_deref()) { - log::error!("Output failure: {:?}", error); - } -} - -fn run(run_args: arguments::RunArgs) { - if let Err(error) = trampoline( - run_args.script, - run_args.arguments, - |runtime, arguments| runtime.run_task(&run_args.task_name, arguments), - |value, _runtime| { - let (mut output, format): (Box, TaskOutputFormat) = match run_args.output { - OutputTarget::Stdout => { - let format = run_args.output_format.unwrap_or_default(); - - (Box::new(std::io::stdout()), format) - } - OutputTarget::File(path) => { - let format = if let Some(output_format) = run_args.output_format { - output_format - } else { - path.extension() - .and_then(|ext| ext.to_str()) - .and_then(|s| TaskOutputFormat::from_str(s).ok()) - .ok_or(anyhow!("Could not infer output format from file name"))? - }; - - (Box::new(File::create(path)?), format) - } - }; - - match format { - TaskOutputFormat::Yaml => serde_yaml::to_writer(&mut output, &value) - .context("Failed to serialize results")?, - TaskOutputFormat::Json => serde_json::to_writer(&mut output, &value) - .context("Failed to serialize results")?, - }; - - writeln!(&mut output)?; - - Ok(()) - }, - ) { - log::error!("Failed to run task: {:?}", error); - } -} - -fn sketch(sketch_args: arguments::SketchArgs) { - if let Err(error) = trampoline( - sketch_args.script, - sketch_args.arguments, - |runtime, arguments| runtime.run_sketch(&sketch_args.sketch_name, arguments), - |_value, _runtime| Ok(()), // Sketches are not yet serializable. - ) { - log::error!("Failed to build sketch: {:?}", error); - } -} - -fn form(form_args: arguments::FormArgs) { - if let Err(error) = trampoline( - form_args.script, - form_args.arguments, - |runtime, arguments| runtime.run_solid(&form_args.solid_name, arguments), - |solid, runtime| { - let tolerance = match form_args.tolerance { - Some(tolerance) => { - let tolerance = Scalar::from_str(&tolerance)?; - let tolerance: Length = tolerance - .try_into() - .map_err(|_| anyhow!("Failed to parse tolerance as a length"))?; - - Tolerance::from_scalar(tolerance.get::())? - } - None => { - // Compute a default tolerance derived from the bounding box. - let aabb = solid - .handle - .deref() - .aabb(&runtime.global_resources.fornjot_core.layers.geometry) - .unwrap_or(Aabb { - min: Point::origin(), - max: Point::origin(), - }); - - // Find the smallest face. - let mut min_extent = FornjotScalar::MAX; - for extent in aabb.size().components { - if extent > FornjotScalar::ZERO && extent < min_extent { - min_extent = extent; - } - } - - // Our smallest face will be divided into 1000 parts. - let tolerance = min_extent / FornjotScalar::from_f64(1000.0); - Tolerance::from_scalar(tolerance)? - } - }; - - let mesh = (solid.handle.deref(), tolerance) - .triangulate(&mut runtime.global_resources.fornjot_core); - - match form_args.output { - OutputTarget::Stdout => { - let format = form_args.output_format.unwrap_or_default(); - let mut output = std::io::stdout(); - - let is_binary_format = match format { - SolidOutputFormat::ThreeMF => true, - SolidOutputFormat::Stl => true, - SolidOutputFormat::Obj => false, - }; - - if is_binary_format && output.is_terminal() { - bail!("Refusing to output binary data to terminal."); - } - - match format { - SolidOutputFormat::ThreeMF => { - // We'll use a bout a megabyte of memory before buffering to the filesystem. - let mut tempfile = SpooledTempFile::new(1024 * 1024); - - export_3mf(&mesh, &mut tempfile)?; - tempfile.seek(std::io::SeekFrom::Start(0))?; - - std::io::copy(&mut tempfile, &mut output)?; - } - SolidOutputFormat::Stl => export_stl(&mesh, &mut output)?, - SolidOutputFormat::Obj => export_obj(&mesh, &mut output)?, - } - } - OutputTarget::File(path) => { - let format = if let Some(output_format) = form_args.output_format { - output_format - } else { - path.extension() - .and_then(|ext| ext.to_str()) - .and_then(|s| SolidOutputFormat::from_str(s).ok()) - .ok_or(anyhow!("Could not infer output format from file name"))? - }; - - let mut output = File::create(path)?; - - match format { - SolidOutputFormat::ThreeMF => export_3mf(&mesh, &mut output)?, - SolidOutputFormat::Stl => export_stl(&mesh, &mut output)?, - SolidOutputFormat::Obj => export_obj(&mesh, &mut output)?, - } - } - }; - - Ok(()) - }, - ) { - log::error!("Failed to build solid: {:?}", error); - } -} - -fn trampoline( - script: PathBuf, - arguments: Vec, - runtime_action: A, - serialize_action: S, -) -> Result<()> -where - A: FnOnce( - &mut Runtime>, - Vec, - ) -> std::result::Result>>, - S: FnOnce(R, &mut Runtime>) -> Result<()>, -{ - fn parse_argument(argument: &str) -> Result { - match serde_json::from_str(argument) { - Ok(value) => Ok(value), - Err(json_error) => match serde_yaml::from_str(argument) { - Ok(value) => Ok(value), - Err(yaml_error) => Err(anyhow!( - "Could not parse as Json or Yaml.\nJson Error: {:?}\n Yaml Error: {:?}", - json_error, - yaml_error - )), - }, - } - } - - let module_name = script - .file_name() - .context("Script file does not have a name")? - .to_str() - .context("Script name could not be UTF8 encoded")?; - - let code = fs::read_to_string(&script).context("Failed to read script into memory")?; - let code: ImString = code.into(); - let code = LocatedSpan::new(code); - - let mut runtime = script::Runtime::load(module_name, code).context("Failed to load runtime")?; - - let mut unpacked_arguments = Vec::with_capacity(arguments.len()); - - for (index, argument) in arguments.into_iter().enumerate() { - let argument = parse_argument(&argument) - .with_context(|| format!("Failed to parse function argument {}", index))?; - - unpacked_arguments.push(argument); - } - - let result = runtime_action(&mut runtime, unpacked_arguments); - - match result { - Ok(result) => { - serialize_action(result, &mut runtime)?; - } - Err(failure) => { - log::error!("{}", failure); - } - } - - Ok(()) -} diff --git a/src/package.rs b/src/package.rs deleted file mode 100644 index 028f77e..0000000 --- a/src/package.rs +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use anyhow::{bail, Context, Result}; -use semver::Version; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct PackageManifest { - name: String, - #[serde(default)] - authors: Vec, - version: Version, - edition: Edition, -} - -impl PackageManifest { - pub fn create_new(name: String, path: impl Into) -> Result<()> { - let path = path.into(); - - if path.exists() { - bail!("Target directory for package already exists"); - } - - fs::create_dir_all(&path).context("Failed to create project directory")?; - - let authors = get_author().map(|author| vec![author]).unwrap_or_default(); - - let package = Self { - name, - authors, - version: Version::new(0, 1, 0), - edition: Edition::default(), - }; - - let package = - serde_yaml::to_string(&package).context("Failed to serialize package manifest")?; - - let manifest_path = path.join("manifest.yaml"); - fs::write(&manifest_path, package).context("Failed to write project manifest.")?; - - if let Err(error) = init_git(&path, &manifest_path) { - log::warn!("Failed to initalize project as git repository: {:?}", error); - } - - Ok(()) - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum Edition { - #[serde(rename = "2024")] - E2024, -} - -impl Default for Edition { - fn default() -> Self { - Self::E2024 - } -} - -fn init_git(path: &Path, manifest_path: &Path) -> Result<()> { - let repository = git2::Repository::init(path).context("Failed to initalize")?; - let mut index = repository.index().context("Failed to get index")?; - index - .add_path(manifest_path) - .context("Failed to add manifest to index.")?; - - Ok(()) -} - -/// Attempts to get the name of the developer authoring this package. -fn get_author() -> Option { - match get_author_from_git() { - Ok(author) => Some(author), - Err(error) => { - log::warn!("Failed to get author name from git: {:?}", error); - log::info!("Will fallback to using system user's name instead"); - Some(get_author_from_username()) - } - } -} - -fn get_author_from_username() -> String { - whoami::realname() -} - -fn get_author_from_git() -> Result { - use git2::Config; - - let git_config = Config::open_default().context("Failed to open git config")?; - let name = git_config.get_string("user.name").ok(); - let email = git_config.get_string("user.email").ok(); - - match (name, email) { - (Some(name), Some(email)) => Ok(format!("{}: {}", name, email)), - (Some(name), None) => { - log::warn!("Designer email not set in git config"); - Ok(name) - } - (None, Some(email)) => { - log::warn!("Designer name not set in git config"); - Ok(email) - } - _ => bail!("Git configuration did not contain a name or email"), - } -} diff --git a/src/script/execution/expressions.rs b/src/script/execution/expressions.rs deleted file mode 100644 index d54736e..0000000 --- a/src/script/execution/expressions.rs +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::cmp::Ordering; - -use crate::script::{ - parsing::{ArithmeticExpression, Comparison, Expression, Factor, Term, Trailer}, - Span, -}; - -use super::{ - types::{Object, Range, Structure, Value}, - ExecutionContext, Failure, -}; - -type Result = std::result::Result>; - -// TODO this should not be returning a control flow. -pub fn run_expression( - context: &mut ExecutionContext, - expression: &Expression, -) -> Result> { - match expression { - Expression::And(a, b) => { - let a_value = run_expression(context, a)?; - let b_value = run_comparison(context, b)?; - - a_value.and(context.log, a.get_span(), &b_value) - } - Expression::Or(a, b) => { - let a_value = run_expression(context, a)?; - let b_value = run_comparison(context, b)?; - - a_value.or(context.log, a.get_span(), &b_value) - } - Expression::Buffer(comparison) => run_comparison(context, comparison), - } -} - -pub fn run_comparison( - context: &mut ExecutionContext, - comparison: &Comparison, -) -> Result> { - fn cmp( - context: &mut ExecutionContext, - a: &Comparison, - b: &ArithmeticExpression, - ) -> Result { - let value_a = run_comparison(context, a)?; - let value_b = run_arithmetic_expression(context, b)?; - - value_a.cmp(context.log, a.get_span(), &value_b) - } - - match comparison { - // TODO it would be valuable to test if both sides are equivalent statements and print a warning if so. - Comparison::LessThan(a, b) => Ok(matches!(cmp(context, a, b)?, Ordering::Less).into()), - Comparison::LessThanEqual(a, b) => { - Ok(matches!(cmp(context, a, b)?, Ordering::Less | Ordering::Equal).into()) - } - Comparison::Equal(a, b) => Ok(matches!(cmp(context, a, b)?, Ordering::Equal).into()), - Comparison::GreaterThanEqual(a, b) => { - Ok(matches!(cmp(context, a, b)?, Ordering::Greater | Ordering::Equal).into()) - } - Comparison::GreaterThan(a, b) => { - Ok(matches!(cmp(context, a, b)?, Ordering::Greater).into()) - } - Comparison::None(expression) => run_arithmetic_expression(context, expression), - } -} - -pub fn run_arithmetic_expression( - context: &mut ExecutionContext, - expression: &ArithmeticExpression, -) -> Result> { - match expression { - ArithmeticExpression::Addition(a, b) => { - let value_a = run_arithmetic_expression(context, a)?; - let value_b = run_term(context, b)?; - - value_a.addition(context.log, a.get_span(), &value_b) - } - ArithmeticExpression::Subtraction(a, b) => { - let value_a = run_arithmetic_expression(context, a)?; - let value_b = run_term(context, b)?; - - value_a.subtraction(context.log, a.get_span(), &value_b) - } - ArithmeticExpression::Term(term) => run_term(context, term), - } -} - -pub fn run_term(context: &mut ExecutionContext, term: &Term) -> Result> { - match term { - Term::Multiply(a, b) => { - let a_value = run_term(context, a)?; - let b_value = run_trailer(context, b)?; - - a_value.multiply(context.log, a.get_span(), &b_value) - } - Term::Divide(a, b) => { - let a_value = run_term(context, a)?; - let b_value = run_trailer(context, b)?; - - a_value.divide(context.log, a.get_span(), &b_value) - } - Term::Range(range) => Ok(Range::from_parsed(context, range)?.into()), - Term::Trailer(trailer) => run_trailer(context, trailer), - } -} - -pub fn run_trailer( - context: &mut ExecutionContext, - trailer: &Trailer, -) -> Result> { - match trailer { - Trailer::None(factor) => run_factor(context, factor), - Trailer::Attribute(trailer, attribute) => { - let value = run_trailer(context, trailer)?; - - value.attribute(context.log, trailer.get_span(), attribute) - } - Trailer::Call(trailer, arguments) => { - let value = run_trailer(context, trailer)?; - - let mut evaluated_arguments = Vec::with_capacity(arguments.len()); - - for argument in arguments.iter() { - let argument = run_expression(context, argument)?; - evaluated_arguments.push(argument); - } - - value.call(context, trailer.get_span(), evaluated_arguments, arguments) - } - Trailer::MethodCall(trailer, attribute, arguments) => { - let value = run_trailer(context, trailer)?; - - let mut evaluated_arguments = Vec::with_capacity(arguments.len()); - - for argument in arguments.iter() { - let argument = run_expression(context, argument)?; - evaluated_arguments.push(argument); - } - - value.method_call( - context, - trailer.get_span(), - attribute, - evaluated_arguments, - arguments, - ) - } - Trailer::StructInitalization(definition, initalization) => { - Structure::initalization(context, definition, initalization) - } - Trailer::Index(trailer, index) => { - let value = run_trailer(context, trailer)?; - let index = run_expression(context, index)?; - - value.index(context.log, trailer.get_span(), index) - } - } -} - -pub fn run_factor( - context: &mut ExecutionContext, - factor: &Factor, -) -> Result> { - match factor { - Factor::Litteral(litteral) => Value::from_litteral(context, litteral), - Factor::Variable(variable) => Ok(context.stack.get_variable(variable)?.clone()), - Factor::Parenthesis(expression) => run_expression(context, expression), - Factor::UnaryPlus(factor) => { - run_factor(context, factor)?.unary_plus(context.log, factor.get_span()) - } - Factor::UnaryMinus(factor) => { - run_factor(context, factor)?.unary_minus(context.log, factor.get_span()) - } - Factor::UnaryLogicalNot(factor) => { - run_factor(context, factor)?.unary_logical_not(context.log, factor.get_span()) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - use common_data_types::Float; - - use crate::script::{ - execution::{ - types::{function::IntoBuiltinFunction, DefaultValue, List, SString, Scalar}, - Module, - }, - parsing::Litteral, - Runtime, - }; - - #[test] - fn expression_straight_number() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("24").unwrap().1), - Ok(Float::new(24.0).unwrap().into()) - ); - }); - } - - #[test] - fn expression_logical_operators() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("true").unwrap().1), - Ok(Value::Boolean(true)) - ); - assert_eq!( - run_expression(context, &Expression::parse("false").unwrap().1), - Ok(Value::Boolean(false)) - ); - - // Not - assert_eq!( - run_expression(context, &Expression::parse("!true").unwrap().1), - Ok(Value::Boolean(false)) - ); - assert_eq!( - run_expression(context, &Expression::parse("!false").unwrap().1), - Ok(Value::Boolean(true)) - ); - - // And - assert_eq!( - run_expression(context, &Expression::parse("false && false").unwrap().1), - Ok(Value::Boolean(false)) - ); - assert_eq!( - run_expression(context, &Expression::parse("true && false").unwrap().1), - Ok(Value::Boolean(false)) - ); - assert_eq!( - run_expression(context, &Expression::parse("false && true").unwrap().1), - Ok(Value::Boolean(false)) - ); - assert_eq!( - run_expression(context, &Expression::parse("true && true").unwrap().1), - Ok(Value::Boolean(true)) - ); - - // Or - assert_eq!( - run_expression(context, &Expression::parse("false || false").unwrap().1), - Ok(Value::Boolean(false)) - ); - assert_eq!( - run_expression(context, &Expression::parse("true || false").unwrap().1), - Ok(Value::Boolean(true)) - ); - assert_eq!( - run_expression(context, &Expression::parse("false || true").unwrap().1), - Ok(Value::Boolean(true)) - ); - assert_eq!( - run_expression(context, &Expression::parse("true || true").unwrap().1), - Ok(Value::Boolean(true)) - ); - }); - } - - #[test] - fn value_from_litteral() { - ExecutionContext::create(&mut Runtime::default(), |context| { - // Scalar - assert_eq!( - Value::from_litteral(context, &Litteral::parse("22mm").unwrap().1), - Ok( - Scalar::try_from(uom::si::f64::Length::new::( - 22.0 - )) - .unwrap() - .into() - ) - ); - // Number - assert_eq!( - Value::from_litteral(context, &Litteral::parse("22").unwrap().1), - Ok(Float::new(22.0).unwrap().into()) - ); - // String - assert_eq!( - Value::from_litteral(context, &Litteral::parse("\"test\"").unwrap().1), - Ok(SString::from("test").into()) - ); - // List - assert_eq!( - Value::from_litteral(context, &Litteral::parse("[1, 2, 3]").unwrap().1), - Ok(List::from([ - Float::new(1.0).unwrap().into(), - Float::new(2.0).unwrap().into(), - Float::new(3.0).unwrap().into() - ]) - .into()) - ); - // Boolean - assert_eq!( - Value::from_litteral(context, &Litteral::parse("true").unwrap().1), - Ok(true.into()) - ); - assert_eq!( - Value::from_litteral(context, &Litteral::parse("false").unwrap().1), - Ok(false.into()) - ); - // Default - assert_eq!( - Value::from_litteral(context, &Litteral::parse("default").unwrap().1), - Ok(DefaultValue.into()) - ); - }); - } - - #[test] - fn variable_access() { - ExecutionContext::create(&mut Runtime::default(), |context| { - context.stack.new_variable(&"global_scope", true.into()); - - context.new_scope(|context| { - context.stack.new_variable(&"sub_scope", false.into()); - - assert_eq!( - run_expression( - context, - Box::leak(Box::new(Expression::parse("sub_scope").unwrap().1)) - ), - Ok(Value::Boolean(false)) - ); - - assert_eq!( - run_expression( - context, - Box::leak(Box::new(Expression::parse("global_scope").unwrap().1)) - ), - Ok(Value::Boolean(true)) - ); - - assert_eq!( - run_expression( - context, - Box::leak(Box::new(Expression::parse("non_existant_scope").unwrap().1)) - ), - Err(Failure::VariableNotInScope( - "non_existant_scope", - "non_existant_scope".into() - )) - ); - }); - }); - } - - #[test] - fn unary_operators() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("+15").unwrap().1), - Ok(Float::new(15.0).unwrap().into()) - ); - - assert_eq!( - run_expression(context, &Expression::parse("-15").unwrap().1), - Ok(Float::new(-15.0).unwrap().into()) - ); - }); - } - - #[test] - fn parenthasis() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("(1 + 2) * 3").unwrap().1), - Ok(Float::new(9.0).unwrap().into()) - ); - }); - } - - #[test] - fn attribute_access() { - let mut log = Vec::new(); - - let module = Module::load( - &mut log, - "test_module.ccm", - "struct DefaultStruct { value: Number = 42 }", - ) - .unwrap(); - - assert!(log.is_empty()); - - ExecutionContext::create(&mut module.into(), |context| { - assert_eq!( - run_expression( - context, - &Expression::parse("DefaultStruct { ..default }.value") - .unwrap() - .1 - ), - Ok(Float::new(42.0).unwrap().into()) - ); - }); - } - - #[test] - fn method_call() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("24.25.floor()").unwrap().1), - Ok(Float::new(24.0).unwrap().into()) - ); - }); - } - - #[test] - fn call() { - ExecutionContext::create(&mut Runtime::default(), |context| { - fn my_function( - _context: &mut ExecutionContext, - _span: &S, - ) -> Result> { - Ok(Float::new(42.0).unwrap().into()) - } - - let my_function = my_function::<&'static str>.into_builtin_function(); - - context - .stack - .new_variable(&"my_function", my_function.into()); - - assert_eq!( - run_expression(context, &Expression::parse("my_function()").unwrap().1), - Ok(Float::new(42.0).unwrap().into()) - ); - }); - } - - #[test] - fn index() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("[1, 2, 3][0]").unwrap().1), - Ok(Float::new(1.0).unwrap().into()) - ); - }); - } - - #[test] - fn multiply() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("1 + 2 * 3").unwrap().1), - Ok(Float::new(7.0).unwrap().into()) - ); - }); - } - - #[test] - fn divide() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("9 / 3").unwrap().1), - Ok(Float::new(3.0).unwrap().into()) - ); - }); - } - - #[test] - fn range() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("..").unwrap().1), - Ok(Range { - lower_bound: None, - upper_bound_is_inclusive: false, - upper_bound: None - } - .into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("..=").unwrap().1), - Err(Failure::MissingUpperBound("..=")) - ); - assert_eq!( - run_expression(context, &Expression::parse("5..").unwrap().1), - Ok(Range { - lower_bound: Some(Float::new(5.0).unwrap().into()), - upper_bound_is_inclusive: false, - upper_bound: None - } - .into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("..5").unwrap().1), - Ok(Range { - lower_bound: None, - upper_bound_is_inclusive: false, - upper_bound: Some(Float::new(5.0).unwrap().into()) - } - .into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("..=5").unwrap().1), - Ok(Range { - lower_bound: None, - upper_bound_is_inclusive: true, - upper_bound: Some(Float::new(5.0).unwrap().into()) - } - .into()) - ); - }); - } - - #[test] - fn addition() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("1 + 2").unwrap().1), - Ok(Float::new(3.0).unwrap().into()) - ); - }); - } - - #[test] - fn subtraction() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("1 + 2").unwrap().1), - Ok(Float::new(3.0).unwrap().into()) - ); - }); - } - - #[test] - fn comparisions() { - ExecutionContext::create(&mut Runtime::default(), |context| { - // LessThan(_, _) - assert_eq!( - run_expression(context, &Expression::parse("1 < 2").unwrap().1), - Ok(true.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 < 2").unwrap().1), - Ok(false.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 < 1").unwrap().1), - Ok(false.into()) - ); - - // LessThanEqual(_, _) - assert_eq!( - run_expression(context, &Expression::parse("1 <= 2").unwrap().1), - Ok(true.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 <= 2").unwrap().1), - Ok(true.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 <= 1").unwrap().1), - Ok(false.into()) - ); - - // Equal(_, _) - assert_eq!( - run_expression(context, &Expression::parse("1 == 2").unwrap().1), - Ok(false.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 == 2").unwrap().1), - Ok(true.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 == 1").unwrap().1), - Ok(false.into()) - ); - - // GreaterThanEqual(_, _) - assert_eq!( - run_expression(context, &Expression::parse("1 >= 2").unwrap().1), - Ok(false.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 >= 2").unwrap().1), - Ok(true.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 >= 1").unwrap().1), - Ok(true.into()) - ); - - // GreaterThan(_, _) - assert_eq!( - run_expression(context, &Expression::parse("1 > 2").unwrap().1), - Ok(false.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 > 2").unwrap().1), - Ok(false.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2 > 1").unwrap().1), - Ok(true.into()) - ); - }); - } -} diff --git a/src/script/execution/failure_message.rs b/src/script/execution/failure_message.rs deleted file mode 100644 index b5396b9..0000000 --- a/src/script/execution/failure_message.rs +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::{borrow::Cow, num::ParseFloatError}; - -use common_data_types::ParseNotNanError; - -use crate::script::Span; - -// TODO We need stack traces. -// TODO we should separate out the failure span and failure type. -#[derive(Debug, Eq, PartialEq)] -pub enum Failure { - UnclosedStatement(S), - ReservedKeyword(S, &'static str), - DuplicateGlobal(S, S), - ExpectedGot(S, Cow<'static, str>, Cow<'static, str>), - NumberConversion(S, ParseNotNanError), - VariableNotInScope(S, Cow<'static, str>), - StructMissingAssignment(S, S), - StructExcessAssignment(S), - StructWrongInheritanceType(S, S, S), - NoDefault(S, S), - UnsupportedOperation(S, Cow<'static, str>, &'static str), - UnknownAttribute(S), - ResultIsNan(S), - MissingArguments(S), - IndexOutOfRange(S, isize), - ListIsEmpty(S), - UnknownUnitType(S, Cow<'static, str>), - CannotConvertFromTo(S, Cow<'static, str>, Cow<'static, str>), - DimensionalMissmatch(S, Cow<'static, str>, Cow<'static, str>), - InvalidCharIndex(S, isize), - Formatting(S, std::fmt::Error), - FormatArgumentIndexOutOfRange(S, isize), - ParseFormatter(S), - InvalidPrecision(S, isize), - MissingUpperBound(S), - ListLengthsDontMatch(S), - DidNotMatch(S), - BreakOutsideOfLoop(S), - BreakLabelNotFound(S, S), - ContinueOutsideOfLoop(S), - ContinueLabelNotFound(S, S), - BadArgumentTypes(S, Vec>), - StructConstruction(S, Vec>), - SliceOutOfRange(S, Option, &'static str, Option), - TooManyArguments(S), - ListWrongLength(S, usize, usize), - ListElement(S, usize, Box>), - ListContainsDuplicate(S, usize), - ShellNotInSolid(S), - FaceNotInShell(S), - RegionNotInFace(S), - User(S, String), - InverseTrigIncompatible(S), - TrigIncompatible(S), - ExpectedZeroDimension(S), - NumberMustBePositive(S), - FunctionCall(Box>), - CharacterNotInVector(S, char), - SwizzleTooLong(S, usize), - CannotBorrowImmutably(S, S), - CannotBorrowMutably(S, S), -} - -impl Failure { - pub fn make_from_function_call(self) -> Self { - Self::FunctionCall(Box::new(self)) - } -} - -impl std::fmt::Display for Failure { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::UnclosedStatement(span) => { - write!( - f, - "{}: Non-tail expressions must be closed with a semicolon", - span.format_span() - ) - } - Self::ReservedKeyword(span, keyword) => { - write!( - f, - "{}: Keyword `{}` is reserved", - span.format_span(), - keyword - ) - } - Self::DuplicateGlobal(first_instance, second_instance) => { - write!( - f, - "Duplicate global {}:{}", - first_instance.format_span(), - second_instance.format_span() - ) - } - Self::ExpectedGot(span, expected, got) => { - write!( - f, - "{}: Expected `{}`, got `{}`", - span.format_span(), - expected, - got - ) - } - Self::NumberConversion(span, error) => { - write!( - f, - "{}: Failed to convert to number: {:?}", - span.format_span(), - error - ) - } - Self::VariableNotInScope(span, name) => { - write!( - f, - "{}: Variable `{}` was not found in current scope", - span.format_span(), - name - ) - } - Self::StructMissingAssignment(span, name) => { - write!( - f, - "{}: Missing assignment for struct: {}", - span.format_span(), - name.as_str() - ) - } - Self::StructExcessAssignment(assignment) => { - write!( - f, - "{}: Excess value assigned to struct", - assignment.format_span() - ) - } - Self::StructWrongInheritanceType(span, expected_type, given_type) => { - write!( - f, - "{}: Wrong type given for in inheritance. Expected `{}`, got `{}`", - span.format_span(), - expected_type.as_str(), - given_type.as_str() - ) - } - Self::NoDefault(span, name) => { - write!( - f, - "{}: Default value not available for memeber `{}`", - span.format_span(), - name.as_str() - ) - } - Self::UnsupportedOperation(span, name, operation) => { - write!( - f, - "{}: Type `{}` does not support {} operation", - span.format_span(), - name, - operation - ) - } - Self::UnknownAttribute(attribute) => write!( - f, - "{}: Unknown attribute: {}", - attribute.format_span(), - attribute.as_str() - ), - Self::ResultIsNan(span) => write!( - f, - "{}: Result of arithmetic operation is NaN", - span.format_span() - ), - Self::MissingArguments(span) => write!( - f, - "{}: Not enough arguments for function", - span.format_span() - ), - Self::IndexOutOfRange(span, index) => write!( - f, - "{}: Index `{}` is out of range", - span.format_span(), - index - ), - Self::ListIsEmpty(span) => write!(f, "{}: List is empty", span.format_span()), - Self::UnknownUnitType(span, unit_type) => write!( - f, - "{}: Unknown unit type `{}`", - span.format_span(), - unit_type - ), - Self::CannotConvertFromTo(span, from, to) => { - write!( - f, - "{}: Cannot convert from `{}` to `{}`", - span.format_span(), - from, - to - ) - } - Self::DimensionalMissmatch(span, from, to) => { - write!( - f, - "{}: Cannot convert from `{}` to `{}` because they are not of the same dimension", - span.format_span(), - from, - to - ) - } - Self::InvalidCharIndex(span, index) => { - write!( - f, - "{}: Index `{}` does not lie on a character boundary", - span.format_span(), - index - ) - } - Self::Formatting(span, error) => { - write!( - f, - "{}: Error formatting text: {:?}", - span.format_span(), - error - ) - } - Self::FormatArgumentIndexOutOfRange(span, index) => { - write!( - f, - "{}: Format requests argument index `{}` which is not present", - span.format_span(), - *index, - ) - } - Self::ParseFormatter(span) => { - write!( - f, - "{}: Failed to parse string as format", - span.format_span() - ) - } - Self::InvalidPrecision(span, precision) => { - write!( - f, - "{}: Invalid precision `{}`. Precision must be greater than zero", - span.format_span(), - *precision - ) - } - Self::MissingUpperBound(span) => { - write!( - f, - "{}: Upper bound of range be specified when inclusive", - span.format_span() - ) - } - Self::ListLengthsDontMatch(span) => { - write!( - f, - "{}: Attempt to assign list from list of a different length. You can use range access `[..]` to trim an oversized list down to the expected size", - span.format_span() - ) - } - Self::DidNotMatch(span) => { - write!( - f, - "{}: No branches in match statement matched input", - span.format_span() - ) - } - Self::BreakOutsideOfLoop(span) => { - write!(f, "{}: Break outside of loop", span.format_span()) - } - Self::BreakLabelNotFound(span, label) => { - write!( - f, - "{}: Could not find loop with label `{}`", - span.format_span(), - label.as_str() - ) - } - Self::ContinueOutsideOfLoop(span) => { - write!(f, "{}: Continue outside of loop", span.format_span()) - } - Self::ContinueLabelNotFound(span, label) => { - write!( - f, - "{}: Could not find loop with label `{}`", - span.format_span(), - label.as_str() - ) - } - Self::BadArgumentTypes(span, failures) => { - writeln!(f, "{}: Bad arguments to function call:", span.format_span())?; - - for failure in failures.iter() { - writeln!(f, "\t{}", failure)?; - } - - Ok(()) - } - Self::StructConstruction(span, failures) => { - writeln!(f, "{}: Faild to build struct:", span.format_span())?; - - for failure in failures.iter() { - writeln!(f, "\t{}", failure)?; - } - - Ok(()) - } - Self::SliceOutOfRange(span, lower, ty, upper) => match (lower, upper) { - (None, None) => write!(f, "{}: Slice out of range [{}]", span.format_span(), ty,), - (Some(lower), None) => write!( - f, - "{}: Slice out of range [{}{}]", - span.format_span(), - lower, - ty, - ), - (Some(lower), Some(upper)) => write!( - f, - "{}: Slice out of range [{}{}{}]", - span.format_span(), - lower, - ty, - upper - ), - (None, Some(upper)) => write!( - f, - "{}: Slice out of range [{}{}]", - span.format_span(), - ty, - upper - ), - }, - Self::TooManyArguments(span) => { - write!( - f, - "{}: Too many arguemnts for function call", - span.format_span() - ) - } - Self::ListWrongLength(span, expected_length, actual_length) => write!( - f, - "{}: Expected list of length {}; got a length of {}", - span.format_span(), - expected_length, - actual_length - ), - Self::ListElement(span, index, failure) => { - write!( - f, - "{}: Error with element {} of list: {}", - span.format_span(), - index, - failure - ) - } - Self::ListContainsDuplicate(span, index) => { - write!( - f, - "{}: Element {} is a duplicate", - span.format_span(), - index - ) - } - Self::ShellNotInSolid(span) => { - write!(f, "{}: Could not find shell in solid", span.format_span()) - } - Self::FaceNotInShell(span) => { - write!(f, "{}: Could not find face in shell", span.format_span()) - } - Self::RegionNotInFace(span) => { - write!(f, "{}: Could not find region in face", span.format_span()) - } - Self::User(span, message) => { - write!(f, "{}: {}", span.format_span(), message) - } - Self::InverseTrigIncompatible(span) => { - write!(f, "{}: Inverse trigonometric functions can only be used with zero dimensional types (Angles, Ratios)", span.format_span()) - } - Self::TrigIncompatible(span) => { - write!( - f, - "{}: Trigonometric functions can only be used with angles", - span.format_span() - ) - } - Self::ExpectedZeroDimension(span) => { - write!( - f, - "{}: Operation expects zero dimensional type, such as numbers, ratios, angles, etc", - span.format_span() - ) - } - Self::NumberMustBePositive(span) => { - write!( - f, - "{}: Operation requires a positive value", - span.format_span() - ) - } - // Failures related to the signature of a function call are wrapped like this so that we can catch them - // and use it for multi-signature functions - Self::FunctionCall(failure) => failure.fmt(f), - Self::CharacterNotInVector(span, c) => { - write!( - f, - "{}: Vector does not contain component `{}`", - span.format_span(), - c - ) - } - Self::SwizzleTooLong(span, length) => { - write!( - f, - "{}: A swizzle of {} is too long. It must be 4 components or less", - span.format_span(), - length - ) - } - Self::CannotBorrowImmutably(span, name) => { - write!( - f, - "{}: Failed to immutably borrow `{}`, because it currently has an exclusively mutable borrow on it", - span.format_span(), - name.as_str() - ) - } - Self::CannotBorrowMutably(span, name) => { - write!( - f, - "{}: Failed to get an exclusively mutable borrow on `{}`, because it already has another borrow on it", - span.format_span(), - name.as_str() - ) - } - } - } -} diff --git a/src/script/execution/mod.rs b/src/script/execution/mod.rs deleted file mode 100644 index c123560..0000000 --- a/src/script/execution/mod.rs +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use crate::script::execution::types::NoneType; - -use self::{ - stack::ScopeType, - types::{Object, OperatorResult, Scalar}, -}; - -use super::{ - logging::{self, RuntimeLog, StandardLog, UnpackValidationWarnings}, - parsing::{self, Block, CallableBlock, MemberVariable}, - Runtime, Span, -}; - -pub mod types; -use common_data_types::ConversionFactor; -use fj_core::Core; -pub use types::print_all_supported_units; -use types::Value; - -mod expressions; -mod failure_message; -mod module; -mod stack; -pub mod statements; -pub use module::Module; - -pub use failure_message::Failure; -pub use stack::Stack; - -pub type ExecutionResult = std::result::Result>; - -#[derive(Debug, PartialEq)] -pub enum ControlFlow { - Failure(Failure), - Break { - span: S, - label: Option, - value: Value, - }, - Continue { - span: S, - label: Option, - }, - Return { - value: Value, - }, -} - -impl From> for ControlFlow { - fn from(value: Failure) -> Self { - Self::Failure(value) - } -} - -pub struct GlobalResources { - pub fornjot_unit_conversion_factor: &'static ConversionFactor, - pub fornjot_core: Core, -} - -impl Default for GlobalResources { - fn default() -> Self { - Self { - fornjot_unit_conversion_factor: Scalar::get_conversion_factor("mm").unwrap(), - fornjot_core: Default::default(), - } - } -} - -pub struct ExecutionContext<'a, S: Span> { - pub global_resources: &'a mut GlobalResources, - pub log: &'a mut dyn RuntimeLog, - pub stack: &'a mut Stack, -} - -impl<'a, S: Span> ExecutionContext<'a, S> { - pub fn create(runtime: &'a mut Runtime, run: impl FnOnce(&mut Self) -> R) -> R { - let mut context = Self { - global_resources: &mut runtime.global_resources, - log: StandardLog::global(), - stack: &mut runtime.stack, - }; - - // FIXME this registers the global functions as part of the module, - // which is not actually global. This is a bad way to do this because - // other modules won't have access to the global functions. - // TODO add constants: std::constants::PI, Angle::HALF_TURN, Angle::FULL_TURN, Scalar::ZERO for all measurement types, SolidAngle::SPHERE. - types::register_globals(&mut context); - logging::register_globals(&mut context); - - run(&mut context) - } - - pub fn unpack_validation_warnings(&mut self, span: &S) { - self.global_resources - .unpack_validation_warnings(span, self.log) - } - - pub fn new_scope(&mut self, scope: impl FnOnce(&mut Self) -> R) -> R { - // This doesn't copy any variables, so it can't fail. - self.stack - .push_scope(std::iter::empty(), ScopeType::Inherited) - .unwrap(); - let result = scope(self); - self.stack.pop_scope(); - - result - } - - pub fn new_isolated_scope(&mut self, scope: impl FnOnce(&mut Self) -> R) -> R { - // This doesn't copy any variables, so it can't fail. - self.stack - .push_scope(std::iter::empty(), ScopeType::Isolated) - .unwrap(); - let result = scope(self); - self.stack.pop_scope(); - - result - } - - pub fn new_closure_scope<'s, R>( - &mut self, - references: impl Iterator>, - copies: impl Iterator, - scope: impl FnOnce(&mut Self) -> R, - ) -> OperatorResult { - self.stack.push_scope( - copies, - ScopeType::Closure { - references: references.map(|s| s.into()).collect(), - }, - )?; - let result = scope(self); - self.stack.pop_scope(); - - Ok(result) - } -} - -pub fn validate_assignment_type( - context: &mut ExecutionContext, - member: &MemberVariable, - variable_assignment: &S, - value: Value, - value_name: &S, -) -> OperatorResult> { - match value { - Value::Default(_) => { - // They want to use a default value. - if let Some(default) = member.ty.default_value.as_ref() { - Value::from_litteral(context, default) - } else { - Err(Failure::NoDefault( - variable_assignment.clone(), - variable_assignment.clone(), - )) - } - } - // No request for default. Check the type. - value => { - if value.matches_type(&member.ty.ty, context.log, value_name)? { - Ok(value) - } else { - Err(Failure::ExpectedGot( - variable_assignment.clone(), - member.ty.ty.name(), - value.type_name(), - )) - } - } - } -} - -fn run_block( - context: &mut ExecutionContext, - block: &Block, -) -> ExecutionResult> { - let mut result = NoneType.into(); - - for statement in block.statements.iter() { - if let Some(statement) = statement.get() { - result = statements::run_statement(context, statement)?; - } - } - - Ok(result) -} - -fn run_callable_block( - context: &mut ExecutionContext, - block: &CallableBlock, - arguments: Vec>, - spans: &[parsing::Expression], - default_span: &S, -) -> Result, Failure> { - // We do not return a ControlFlow because control flow does not - // pass through named blocks (we can't continue or break a for loop outside of this named block) - match arguments.len().cmp(&block.parameters.len()) { - std::cmp::Ordering::Equal => { - let mut failures = Vec::new(); - - // Validate the arguments and put them into scope.. - for (span, (argument, parameter)) in spans - .iter() - .map(|expression| expression.get_span()) - .chain(std::iter::repeat(default_span)) - .zip(arguments.into_iter().zip(&block.parameters)) - { - match validate_assignment_type(context, parameter, span, argument, ¶meter.name) - { - Ok(value) => { - context.stack.new_variable(¶meter.name, value); - } - Err(failure) => failures.push(failure), - } - } - - if failures.is_empty() { - match run_block(context, &block.block) { - Ok(value) => Ok(value), - Err(control_flow) => match control_flow { - ControlFlow::Return { value } => Ok(value), // Oh that's normal behavior. - ControlFlow::Failure(failure) => Err(failure), - ControlFlow::Break { - span, - label: None, - value: _, - } => Err(Failure::BreakOutsideOfLoop(span)), - ControlFlow::Break { - span, - label: Some(label), - value: _, - } => Err(Failure::BreakLabelNotFound(span, label)), - ControlFlow::Continue { span, label: None } => { - Err(Failure::ContinueOutsideOfLoop(span)) - } - ControlFlow::Continue { - span, - label: Some(label), - } => Err(Failure::ContinueLabelNotFound(span, label)), - }, - } - } else { - Err(Failure::BadArgumentTypes( - block.parameter_span.clone(), - failures, - )) - } - } - std::cmp::Ordering::Less => Err(Failure::MissingArguments(block.parameter_span.clone())), - std::cmp::Ordering::Greater => Err(Failure::MissingArguments(block.parameter_span.clone())), - } -} - -#[cfg(test)] -mod test { - use super::*; - - use crate::script::{ - execution::{expressions::run_expression, run_block, ExecutionContext}, - parsing::Expression, - }; - use common_data_types::Float; - - #[test] - fn functions() { - let mut log = Vec::new(); - - let module = Module::load( - &mut log, - "test_module.ccm", - "function my_function(input: Number) -> Number { input }", - ) - .unwrap(); - - assert!(log.is_empty()); - - ExecutionContext::create(&mut module.into(), |context| { - let block = parsing::Block::parse("{ my_function(5) }").unwrap().1; - - let result = run_block(context, Box::leak(Box::new(block))); - assert_eq!(result, Ok(Float::new(5.0).unwrap().into())); - }); - } - - #[test] - fn default_function() { - let mut log = Vec::new(); - - let module = Module::load( - &mut log, - "test_module.ccm", - "function my_function(input: Number = 5) -> Number { input }", - ) - .unwrap(); - - assert!(log.is_empty()); - - ExecutionContext::create(&mut module.into(), |context| { - let block = parsing::Block::parse("{ my_function(default) }").unwrap().1; - - let result = run_block(context, Box::leak(Box::new(block))); - assert_eq!(result, Ok(Float::new(5.0).unwrap().into())); - }); - } - - #[test] - fn function_scope() { - let mut log = Vec::new(); - - let module = Module::load( - &mut log, - "test_module.ccm", - "function my_function(input: Number) -> Number { value = input; input }", - ) - .unwrap(); - - assert!(log.is_empty()); - - ExecutionContext::create(&mut module.into(), |context| { - let block = parsing::Block::parse("{ let value = 0; my_function(5); value }") - .unwrap() - .1; - - let result = - context.new_scope(|context| run_block(context, Box::leak(Box::new(block)))); - assert_eq!( - result, - Err(ControlFlow::Failure(Failure::VariableNotInScope( - "value", - "value".into() - ))) - ); - }); - } - - #[test] - fn function_hygene() { - let mut log = Vec::new(); - - let module = Module::load( - &mut log, - "test_module.ccm", - "struct MyStruct {} function my_function() -> struct MyStruct { MyStruct {} }", - ) - .unwrap(); - - assert!(log.is_empty()); - - ExecutionContext::create(&mut module.into(), |context| { - let block = parsing::Block::parse("{ my_function() }").unwrap().1; - - let result = - context.new_scope(|context| run_block(context, Box::leak(Box::new(block)))); - assert_eq!( - result.unwrap(), - run_expression( - context, - Box::leak(Box::new(Expression::parse("MyStruct {}").unwrap().1)) - ) - .unwrap() - ); - }); - } -} diff --git a/src/script/execution/module.rs b/src/script/execution/module.rs deleted file mode 100644 index 2685749..0000000 --- a/src/script/execution/module.rs +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::collections::HashMap; - -use anyhow::{anyhow, Result}; - -use super::{parsing, Failure}; -use parsing::Span; - -const RESERVED_KEYWORDS: &[&str] = &[ - "let", "import", "extern", "struct", "sketch", "widget", "function", "return", "if", "else", - "match", "for", "while", "loop", "in", "break", "continue", "struct", "true", "false", - "default", "self", -]; - -fn is_reserved_keyword(word: &S) -> Option<&'static str> { - RESERVED_KEYWORDS - .iter() - .find(|keyword| word.as_str() == **keyword) - .copied() -} - -#[derive(Debug, Eq, PartialEq)] -enum CallableReference { - Struct(usize), - Function(usize), - Sketch(usize), - Widget(usize), -} - -#[derive(Debug, Eq, PartialEq)] -pub struct Module { - pub(super) file_name: String, - pub(super) root_elements: RootElements, - span: S, -} - -impl Default for Module { - fn default() -> Self { - Self { - file_name: String::from("default"), - root_elements: Default::default(), - span: S::default(), - } - } -} - -impl Module { - pub fn load( - log: &mut Vec>, - file_name: impl Into, - code: impl Into, - ) -> Result { - let file_name = file_name.into(); - let code = code.into(); - - let root_elements = Self::load_ast(code.clone())?; - let mut callables = HashMap::new(); - - // TODO Validation should have its own message types and not be considered a runtime failure. - // It should probably be moved into parsing, whenever the parser overhaul happens. - Self::check_for_name_conflicts( - log, - &root_elements, - &mut callables, - || root_elements.structs.iter(), - |structure| structure.name.clone(), - |index| root_elements.structs[index].name.clone(), - CallableReference::Struct, - ); - Self::check_for_name_conflicts( - log, - &root_elements, - &mut callables, - || root_elements.functions.iter(), - |function| function.named_block.name.clone(), - |index| root_elements.functions[index].named_block.name.clone(), - CallableReference::Function, - ); - Self::check_for_name_conflicts( - log, - &root_elements, - &mut callables, - || root_elements.sketches.iter(), - |sketch| sketch.named_block.name.clone(), - |index| root_elements.sketches[index].named_block.name.clone(), - CallableReference::Sketch, - ); - Self::check_for_name_conflicts( - log, - &root_elements, - &mut callables, - || root_elements.solids.iter(), - |widget| widget.named_block.name.clone(), - |index| root_elements.solids[index].named_block.name.clone(), - CallableReference::Widget, - ); - - let module = Self { - file_name, - root_elements, - span: code, - }; - - module.validate(log); - - Ok(module) - } - - pub fn get_span(&self) -> &S { - &self.span - } - - fn load_ast(code: S) -> Result> { - let (_, ast) = parsing::FileAST::parse(code) - .map_err(|error| anyhow!("Failed to parse file: {:?}", error))?; - - let mut imports = Vec::new(); - let mut structs = Vec::new(); - let mut functions = Vec::new(); - let mut tasks = Vec::new(); - let mut sketches = Vec::new(); - let mut solids = Vec::new(); - - for element in ast.root_elements.into_iter() { - match element { - parsing::RootElement::Import(import) => imports.push(import), - parsing::RootElement::Struct(sstruct) => structs.push(sstruct), - parsing::RootElement::Function(function) => match function.signature { - parsing::FunctionSignature::Function { .. } => functions.push(function), - parsing::FunctionSignature::Task { .. } => tasks.push(function), - parsing::FunctionSignature::Sketch { .. } => sketches.push(function), - parsing::FunctionSignature::Solid { .. } => solids.push(function), - }, - } - } - - Ok(RootElements { - imports, - structs, - functions, - tasks, - sketches, - solids, - }) - } - - fn check_for_name_conflicts( - log: &mut Vec>, - root_elements: &RootElements, - callables: &mut HashMap, - get_iter: FI, - get_name: FN, - get_span: FS, - build_reference: FBR, - ) where - I: Iterator, - FI: FnOnce() -> I, - FN: Fn(&T) -> S, - FS: Fn(usize) -> S, - FBR: Fn(usize) -> CallableReference, - { - for (index, item) in get_iter().enumerate() { - if let Some(old) = callables.insert(get_name(&item).to_string(), build_reference(index)) - { - let old_span = match old { - CallableReference::Struct(index) => root_elements.structs[index].name.clone(), - CallableReference::Function(index) => { - root_elements.functions[index].named_block.name.clone() - } - CallableReference::Sketch(index) => { - root_elements.sketches[index].named_block.name.clone() - } - CallableReference::Widget(index) => { - root_elements.solids[index].named_block.name.clone() - } - }; - let new_span = get_span(index); - - log.push(Failure::DuplicateGlobal(old_span, new_span)); - } - } - } - - /// Validation is checking for errors that don't require the context of execution. - fn validate(&self, log: &mut Vec>) { - self.root_elements.validate(log); - } -} - -#[derive(Debug, Eq, PartialEq)] -pub(super) struct RootElements { - pub(super) imports: Vec>, - pub(super) structs: Vec>, - pub(super) functions: Vec>, - pub(super) tasks: Vec>, - pub(super) sketches: Vec>, - pub(super) solids: Vec>, -} - -impl Default for RootElements { - fn default() -> Self { - Self { - imports: Default::default(), - structs: Default::default(), - functions: Default::default(), - tasks: Default::default(), - sketches: Default::default(), - solids: Default::default(), - } - } -} - -impl RootElements { - /// Validation is checking for errors that don't require the context of execution.1 - fn validate(&self, log: &mut Vec>) { - // There is no validation to be done for imports. - - for structure in self.structs.iter() { - Self::validate_struct(log, structure); - } - for function in self.functions.iter() { - Self::validate_named_block(log, &function.named_block); - } - for task in self.tasks.iter() { - Self::validate_named_block(log, &task.named_block); - } - for sketch in self.sketches.iter() { - Self::validate_named_block(log, &sketch.named_block); - } - for solid in self.solids.iter() { - Self::validate_named_block(log, &solid.named_block); - } - } - - fn validate_struct(log: &mut Vec>, structure: &parsing::StructDefinition) { - // Name should not be a reserved keyword. - if let Some(keyword) = is_reserved_keyword(&structure.name) { - log.push(Failure::ReservedKeyword(structure.name.clone(), keyword)); - } - - // None of the members should have a reserved keyword for a name. - for member in structure.members.iter() { - if let Some(keyword) = is_reserved_keyword(&member.name) { - log.push(Failure::ReservedKeyword(member.name.clone(), keyword)); - } - } - } - - fn validate_named_block(log: &mut Vec>, block: &parsing::NamedBlock) { - // Name should not be a reserved keyword. - if let Some(keyword) = is_reserved_keyword(&block.name) { - log.push(Failure::ReservedKeyword(block.name.clone(), keyword)); - } - - // Parameter names should not be reserved keywords. - for parameter in block.callable.parameters.iter() { - if let Some(keyword) = is_reserved_keyword(¶meter.name) { - log.push(Failure::ReservedKeyword(parameter.name.clone(), keyword)); - } - } - - Self::validate_block(log, &block.callable.block); - } - - fn validate_block(log: &mut Vec>, block: &parsing::Block) { - let mut core_iter = block.statements.iter().peekable(); - let statement_iter = std::iter::from_fn(|| { - core_iter - .next() - .map(|next| (next, core_iter.peek().is_none())) - }); - - for (statement, is_last) in statement_iter { - // Variables should not contain keywords. - if let Some(parsing::Statement::Assign(assignment)) = statement.get() { - for to_assign in assignment.to_assign.iter() { - if let Some(keyword) = is_reserved_keyword(&to_assign.name) { - log.push(Failure::ReservedKeyword(to_assign.name.clone(), keyword)); - } - } - } - - // Some statements require that they have a semicolon behind them, unless they are the last line. - // Enforce that. - if let parsing::BlockStatement::Open(statement) = statement { - if !is_last { - if let Some(span) = match statement { - parsing::Statement::Expression(spanable) => Some(spanable.get_span()), - parsing::Statement::Assign(spanable) => Some(spanable.get_span()), - parsing::Statement::Return(spanable) => Some(spanable.get_span()), - parsing::Statement::Break(spanable) => Some(spanable.get_span()), - parsing::Statement::Continue(spanable) => Some(spanable.get_span()), - - _ => None, - } { - log.push(Failure::UnclosedStatement(span.clone())); - } - } - } - } - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::{CallableBlock, FunctionSignature, VariableType}; - - use super::*; - - const TEST_AST_CODE: &str = r#" - import path::to::module; - struct MyStruct {} - sketch my_sketch() {} - solid my_solid() {} - function my_function() -> Length {} -"#; - - #[test] - fn load_module() { - let mut log = Vec::new(); - assert!(Module::<&str>::load(&mut log, "my_module.ccm", "").is_ok()); - } - - #[test] - fn load_ast() { - let root: RootElements<&str> = Module::load_ast(TEST_AST_CODE).unwrap(); - - assert_eq!( - root.imports, - [parsing::Import { - path: vec!["path", "to", "module"], - external: false - }] - ); - - assert_eq!( - root.structs, - [parsing::StructDefinition { - name: "MyStruct", - members: vec![] - }] - ); - assert_eq!( - root.sketches, - [parsing::Function { - starting_span: "sketch", - named_block: parsing::NamedBlock { - name: "my_sketch", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: parsing::Block { statements: vec![] } - } - }, - signature: FunctionSignature::Sketch { arguments: vec![] }, - }] - ); - assert_eq!( - root.solids, - [parsing::Function { - starting_span: "solid", - named_block: parsing::NamedBlock { - name: "my_solid", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: parsing::Block { statements: vec![] } - } - }, - signature: FunctionSignature::Solid { arguments: vec![] }, - }] - ); - - // TODO test tasks. - - assert_eq!( - root.functions, - [parsing::Function { - starting_span: "function", - named_block: parsing::NamedBlock { - name: "my_function", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: parsing::Block { statements: vec![] } - } - }, - signature: FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Length")), - arguments: vec![] - }, - }] - ); - } - - fn validate(code: &'static str) -> Vec> { - let mut log = Vec::new(); - Module::load(&mut log, "my_module.ccs", code).unwrap(); // Even if validation "fails" we get the validation log back. - - log - } - - #[test] - fn validate_block_empty() { - assert_eq!(validate("solid MySolid() {}"), []); - } - - #[test] - fn validate_block_tail_expressions() { - assert_eq!(validate("solid MySolid() { let a = b; }"), []); - assert_eq!(validate("solid MySolid() { let a = b }"), []); - assert_eq!(validate("solid MySolid() { let a = b; let c = d }"), []); - assert_eq!( - validate("solid MySolid() { let a = b let c = d }"), - [Failure::UnclosedStatement("let"),] - ); - } - - #[test] - fn validate_assignment_keyword_resurvation() { - assert_eq!( - validate("solid MySolid() { let break = b; }"), - [Failure::ReservedKeyword("break", "break")] - ); - - assert_eq!(validate("solid MySolid() { let break_beat = b; }"), []); - } - - #[test] - fn validate_named_block_keyword_resurvation() { - assert_eq!( - validate("solid break() { }"), - [Failure::ReservedKeyword("break", "break")] - ); - } - - #[test] - fn validate_parameter_names_are_not_keywords() { - assert_eq!(validate("solid MySolid() { }"), []); - - assert_eq!(validate("solid MySolid(okay: Length) { }"), []); - - assert_eq!( - validate("solid MySolid(break: Length) { }"), - [Failure::ReservedKeyword("break", "break")] - ); - } - - #[test] - fn validate_struct_name_not_keyword() { - assert_eq!(validate("struct MyStruct { }"), []); - assert_eq!( - validate("struct break { }"), - [Failure::ReservedKeyword("break", "break")] - ); - } - - #[test] - fn validate_struct_member_not_keyword() { - assert_eq!(validate("struct MyStruct { }"), []); - assert_eq!(validate("struct MyStruct { okay: Length }"), []); - assert_eq!( - validate("struct MyStruct { break: Length }"), - [Failure::ReservedKeyword("break", "break")] - ); - } - - #[test] - fn validate_no_duplicate_global_names() { - assert_eq!(validate("struct MyThing1 {} function MyThing2() -> Length {} sketch MyThing3() {} solid MyThing4() {}"), []); - assert_eq!(validate("struct MyThing {} function MyThing() -> Length {} sketch MyThing() {} solid MyThing() {}"), [ - Failure::DuplicateGlobal("MyThing", "MyThing"), - Failure::DuplicateGlobal("MyThing", "MyThing"), - Failure::DuplicateGlobal("MyThing", "MyThing"), - ]); - } -} diff --git a/src/script/execution/stack.rs b/src/script/execution/stack.rs deleted file mode 100644 index eca4f15..0000000 --- a/src/script/execution/stack.rs +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::{ - collections::{HashMap, HashSet}, - rc::Rc, -}; - -use compact_str::CompactString; - -use crate::script::Span; - -use super::{ - types::{OperatorResult, StructDefinition, UserFunction, Value}, - Failure, Module, -}; - -#[derive(Debug)] -pub(super) enum ScopeType { - Inherited, - Isolated, - Module, - Closure { references: HashSet }, -} - -impl Default for ScopeType { - fn default() -> Self { - Self::Inherited - } -} - -#[derive(Debug)] -struct Scope { - ty: ScopeType, - variables: HashMap>, -} - -impl Default for Scope { - fn default() -> Self { - Self { - ty: ScopeType::Module, - variables: Default::default(), - } - } -} - -macro_rules! optionally_mut_ref { - (mutable $l:lifetime $ty:ty) => { - & $l mut $ty - }; - (immutable $l:lifetime $ty:ty) => { - & $l $ty - }; -} - -macro_rules! generate_variable_getter { - ($self:ident, $span:ident, $name:ident, $iter:ident, $get:ident, $mutable:ident) => { - { - fn check_module_scope<'a, 'b, S: Span>( - mut scope_iterator: impl Iterator)>, - name: &str, - ) -> Option)> { - if let Some(value) = scope_iterator - .find(|scope| matches!(&scope.ty, ScopeType::Module)) - .and_then(|scope| scope.variables.$get(name)) - { - Some(value) - } else { - None - } - } - - // TODO we should refuse to provide module level scopes when doing an immutable access, since those need to be fully immutable. - let mut scope_iterator = $self.scopes[..=$self.active_scope].$iter().rev(); - - for scope in &mut scope_iterator { - if let Some(value) = scope.variables.$get($name) { - return Ok(value); - } - - match &scope.ty { - // This is the scope of a closure. If the variable we are looking for is referenced, keep searching up the stack for it. - ScopeType::Closure { references } => { - if references.contains($name) { - continue; - } else { - // Oh, well then let's skip to the module scope. - if let Some(value) = check_module_scope(scope_iterator, $name) { - return Ok(value); - } - } - break; - } - // If this scope is isolated, then we should skip to the module scope. - ScopeType::Isolated => { - if let Some(value) = check_module_scope(scope_iterator, $name) { - return Ok(value); - } - break; - } - _ => {} - } - } - - Err(Failure::VariableNotInScope( - $span.clone(), - $name.to_string().into(), - )) - } - }; -} - -// TODO do we want to implement a stack limit? -#[derive(Debug)] -pub struct Stack { - scopes: Vec>, - active_scope: usize, -} - -impl Default for Stack { - fn default() -> Self { - Self { - scopes: vec![Scope { - ty: ScopeType::Module, - ..Default::default() - }], - active_scope: 0, - } - } -} - -impl Stack { - pub fn new(module: Module) -> Self { - let mut root_scope = Scope { - ty: ScopeType::Module, - ..Default::default() - }; - - let structs = module - .root_elements - .structs - .into_iter() - .map(|structure| (structure.name.to_string(), structure)); - for (name, definition) in structs { - root_scope.variables.insert( - name.into(), - StructDefinition { - definition: Rc::new(definition), - } - .into(), - ); - } - - let functions = module - .root_elements - .functions - .into_iter() - .map(|function| (function.named_block.name.to_string(), function)); - for (name, function) in functions { - root_scope - .variables - .insert(name.into(), UserFunction::new(function).into()); - } - - let tasks = module - .root_elements - .tasks - .into_iter() - .map(|task| (task.named_block.name.to_string(), task)); - for (name, task) in tasks { - root_scope - .variables - .insert(name.into(), UserFunction::new(task).into()); - } - - let sketches = module - .root_elements - .sketches - .into_iter() - .map(|sketch| (sketch.named_block.name.to_string(), sketch)); - for (name, sketch) in sketches { - root_scope - .variables - .insert(name.into(), UserFunction::new(sketch).into()); - } - - let solids = module - .root_elements - .solids - .into_iter() - .map(|wolid| (wolid.named_block.name.to_string(), wolid)); - for (name, solid) in solids { - root_scope - .variables - .insert(name.into(), UserFunction::new(solid).into()); - } - - Self { - scopes: vec![root_scope], - active_scope: 0, - } - } - - pub(super) fn push_scope<'a>( - &mut self, - variables_to_copy: impl Iterator, - mode: ScopeType, - ) -> OperatorResult - where - S: 'a, - { - let next_scope_index = self.active_scope + 1; - if next_scope_index >= self.scopes.len() { - self.scopes.push(Scope::default()); - } - - self.scopes[next_scope_index].ty = mode; - - for variable in variables_to_copy { - let value = self.get_variable(variable)?.clone(); - self.scopes[next_scope_index] - .variables - .insert(variable.as_str().into(), value); - } - - self.active_scope = next_scope_index; - - Ok(()) - } - - pub(super) fn pop_scope(&mut self) { - self.scopes[self.active_scope].variables.clear(); - self.active_scope -= 1; - } - - // TODO Recommending similar named variables would help users to notice typos. - pub fn get_variable(&self, name: &S) -> std::result::Result<&Value, Failure> { - self.get_variable_str(name, name.as_str()) - } - - pub fn get_variable_mut(&mut self, name: &S) -> Result<&mut Value, Failure> { - self.get_variable_str_mut(name, name.as_str()) - } - - pub fn get_variable_str(&self, span: &S, name: &str) -> Result<&Value, Failure> { - generate_variable_getter!(self, span, name, iter, get, immutable) - } - pub fn get_variable_str_mut( - &mut self, - span: &S, - name: &str, - ) -> Result<&mut Value, Failure> { - generate_variable_getter!(self, span, name, iter_mut, get_mut, mutable) - } - - pub fn new_variable(&mut self, name: &S, value: Value) { - self.new_variable_str(name.as_str(), value) - } - - pub fn new_variable_str(&mut self, name: impl Into, value: Value) { - let current_scope = &mut self.scopes[self.active_scope]; - - current_scope.variables.insert(name.into(), value); - } -} diff --git a/src/script/execution/statements.rs b/src/script/execution/statements.rs deleted file mode 100644 index ef3eb2f..0000000 --- a/src/script/execution/statements.rs +++ /dev/null @@ -1,959 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::rc::Rc; - -use crate::script::{ - execution::{types::List, Failure}, - parsing::{ - self, Assign, AssignableVariable, Break, Continue, For, Function, If, Loop, Match, Return, - Statement, While, - }, - Span, -}; - -use super::{ - expressions::{self, run_expression}, - run_block, - types::{NoneType, Object, /*List, */ StructDefinition, UserFunction, Value}, - ControlFlow, ExecutionContext, ExecutionResult, -}; - -pub fn run_statement( - context: &mut ExecutionContext, - statement: &Statement, -) -> ExecutionResult> { - match statement { - Statement::Expression(expression) => Ok(expressions::run_expression(context, expression)?), - Statement::Assign(assignment) => run_assignment(context, assignment), - Statement::Return(return_statement) => run_return(context, return_statement), - Statement::If(if_statement) => run_if(context, if_statement), - Statement::Match(match_statement) => run_match(context, match_statement), - Statement::For(for_statement) => run_for(context, for_statement), - Statement::While(while_statement) => run_while(context, while_statement), - Statement::Loop(loop_statement) => run_loop(context, loop_statement), - Statement::Break(break_statement) => run_break(context, break_statement), - Statement::Continue(continue_statement) => run_continue(context, continue_statement), - Statement::DefineFunction(function) => run_define_function(context, function), - Statement::DefineStruct(structure) => run_define_structure(context, structure), - } -} - -fn assign_values( - context: &mut ExecutionContext, - to_assign: &parsing::Assignable, - assignment_span: &S, - source_span: &S, - value: Value, - mut assign: impl FnMut(&mut ExecutionContext, &S, Value) -> ExecutionResult>, -) -> ExecutionResult> { - fn assign_single_value( - context: &mut ExecutionContext, - assignment_span: &S, - value: Value, - variable: &AssignableVariable, - assign: impl FnOnce(&mut ExecutionContext, &S, Value) -> ExecutionResult>, - ) -> ExecutionResult> { - if let Some(ty) = &variable.ty { - if value.matches_type(ty, context.log, assignment_span)? { - assign(context, &variable.name, value) - } else { - Err(ControlFlow::Failure(Failure::ExpectedGot( - assignment_span.clone(), - ty.name(), - value.type_name(), - ))) - } - } else { - assign(context, &variable.name, value) - } - } - - match to_assign { - parsing::Assignable::Variable(variable) => { - assign_single_value(context, assignment_span, value, variable, &mut assign) - } - parsing::Assignable::List(span, variables) => { - let values = value.downcast::>(source_span)?; - - if values.len(span)? != variables.len() { - return Err(ControlFlow::Failure(Failure::ListLengthsDontMatch( - source_span.clone(), - ))); - } - - let value_iter = values.iter(span)?; - for (variable, value) in variables.iter().zip(value_iter) { - assign_single_value(context, assignment_span, value, variable, &mut assign)?; - } - - Ok(NoneType.into()) - } - } -} - -fn run_assignment( - context: &mut ExecutionContext, - assignment: &Assign, -) -> ExecutionResult> { - let value = run_statement(context, &assignment.statement)?; - - if assignment.is_new { - let assign = |context: &mut ExecutionContext, - name: &S, - value: Value| - -> ExecutionResult> { - context.stack.new_variable(name, value); - - Ok(NoneType.into()) - }; - - assign_values( - context, - &assignment.to_assign, - assignment.get_span(), - assignment.statement.get_span(), - value, - assign, - ) - } else { - let assign = |context: &mut ExecutionContext, - name: &S, - value: Value| - -> ExecutionResult> { - let variable = context.stack.get_variable_mut(name)?; - *variable = value; - - Ok(NoneType.into()) - }; - - assign_values( - context, - &assignment.to_assign, - assignment.get_span(), - assignment.statement.get_span(), - value, - assign, - ) - } -} - -fn run_return( - context: &mut ExecutionContext, - return_statement: &Return, -) -> ExecutionResult> { - let value = if let Some(expression) = return_statement.expression.as_ref() { - run_expression(context, expression)? - } else { - NoneType.into() - }; - - Err(ControlFlow::Return { value }) -} - -fn run_if( - context: &mut ExecutionContext, - if_statement: &If, -) -> ExecutionResult> { - let condition = expressions::run_expression(context, &if_statement.expression)?; - - if condition.downcast::(if_statement.expression.get_span())? { - context.new_scope(|context| run_block(context, &if_statement.block)) - } else { - match &if_statement.else_statement { - None => Ok(NoneType.into()), - Some(parsing::Else::Else(block)) => { - context.new_scope(|context| run_block(context, block)) - } - Some(parsing::Else::IfElse(if_statement)) => run_if(context, if_statement), // FIXME an if else chain that's too deep can cause a stack overflow. - } - } -} - -fn run_match( - context: &mut ExecutionContext, - match_statement: &Match, -) -> ExecutionResult> { - fn check_branch_matches( - span: &S, - value: &Value, - branch_value: &Value, - ) -> ExecutionResult { - if value == branch_value || matches!(branch_value, &Value::Default(_)) { - Ok(true) - } else if let (Value::List(value_list), Value::List(branch_value_list)) = - (value, branch_value) - { - // These are lists. We need to check the individual components. - if value_list.len(span)? == branch_value_list.len(span)? { - let branch_value_list = branch_value_list.iter(span)?; - for (value, branch_value) in value_list.iter(span)?.zip(branch_value_list) { - if !check_branch_matches(span, &value, &branch_value)? { - return Ok(false); - } - } - - Ok(true) - } else { - Ok(false) - } - } else { - Ok(false) - } - } - - let value = run_expression(context, &match_statement.expression)?; - - // TODO should we cache these values in a hash map for situations where we may run this match statement several times? - for branch in match_statement.branches.iter() { - let branch_value = Value::from_litteral(context, &branch.litteral)?; - - if check_branch_matches(branch.litteral.get_span(), &value, &branch_value)? { - return context.new_scope(|context| run_block(context, &branch.block)); - } - } - - Err(ControlFlow::Failure(Failure::DidNotMatch( - match_statement.expression.get_span().clone(), - ))) -} - -fn loop_impl( - mut loop_control: F, - context: &mut ExecutionContext, - name: Option<&S>, - block: &parsing::Block, -) -> ExecutionResult> -where - S: Span, - F: FnMut(&mut ExecutionContext) -> ExecutionResult, -{ - context.new_scope(|context| { - loop { - if loop_control(context)? { - let result = run_block(context, block); - - match result { - Ok(result) => { - // Loops must end with a none-type. - if !matches!(result, Value::NoneType(_)) { - // If it returned something that's not None, then there must have been a final statement. - let final_statement = block.statements.last().unwrap(); - - break Err(ControlFlow::Failure(Failure::ExpectedGot( - final_statement.get_span().clone(), - "None".into(), - result.type_name(), - ))); - } - } - Err(exit_reason) => match exit_reason { - ControlFlow::Failure(failure) => break Err(ControlFlow::Failure(failure)), - ControlFlow::Return { value } => break Err(ControlFlow::Return { value }), - ControlFlow::Break { span, label, value } => match (label, name) { - (None, _) => break Ok(value), - (Some(label), Some(loop_name)) => { - let label_str = label.as_str(); - let loop_name = loop_name.as_str(); - - if label_str != loop_name { - // We are not the loop breaking. - // Pass it to our caller. - break Err(ControlFlow::Break { - span, - value, - label: Some(label), - }); - } else { - // We are the loop breaking. - break Ok(value); - } - } - (Some(label), None) => { - // This is for a labelled loop, but we are not labeled. Not for us it seems. - break Err(ControlFlow::Break { - span, - value, - label: Some(label), - }); - } - }, - ControlFlow::Continue { span, label } => match (label, name) { - (None, _) => break Ok(NoneType.into()), - (Some(label), Some(loop_name)) => { - let label_str = label.as_str(); - let loop_name = loop_name.as_str(); - - if label_str != loop_name { - // We are not the loop breaking. - // Pass it to our caller. - break Err(ControlFlow::Continue { - span, - label: Some(label), - }); - } else { - // We are the loop breaking. - break Ok(NoneType.into()); - } - } - (Some(label), None) => { - // This is for a labelled loop, but we are not labeled. Not for us it seems. - break Err(ControlFlow::Continue { - span, - label: Some(label), - }); - } - }, - }, - } - } else { - break Ok(NoneType.into()); - } - } - }) -} - -fn run_for( - context: &mut ExecutionContext, - for_statement: &For, -) -> ExecutionResult> { - let iter_expression = run_expression(context, &for_statement.iterator_expression)?; - let mut iterator = - iter_expression.iterate(context.log, for_statement.iterator_expression.get_span())?; - - loop_impl( - |context| { - if let Some(next_value) = iterator.next() { - let assign = |context: &mut ExecutionContext, - name: &S, - value: Value| - -> ExecutionResult> { - context.stack.new_variable(name, value); - - Ok(NoneType.into()) - }; - let next_value = next_value.clone(); - - assign_values( - context, - &for_statement.variable_assignment, - for_statement.variable_assignment.get_span(), - for_statement.iterator_expression.get_span(), - next_value, - assign, - )?; - - Ok(true) - } else { - Ok(false) - } - }, - context, - for_statement.name.as_ref(), - &for_statement.block, - ) -} - -fn run_while( - context: &mut ExecutionContext, - while_statement: &While, -) -> ExecutionResult> { - loop_impl( - |context| { - Ok(run_expression(context, &while_statement.expression)? - .downcast::(while_statement.expression.get_span())?) - }, - context, - while_statement.name.as_ref(), - &while_statement.block, - ) -} - -fn run_loop( - context: &mut ExecutionContext, - loop_statement: &Loop, -) -> ExecutionResult> { - loop_impl( - |_context| Ok(true), - context, - loop_statement.name.as_ref(), - &loop_statement.block, - ) -} - -fn run_break( - context: &mut ExecutionContext, - break_statement: &Break, -) -> ExecutionResult> { - let value = if let Some(expression) = &break_statement.expression { - run_expression(context, expression)? - } else { - NoneType.into() - }; - - Err(ControlFlow::Break { - span: break_statement.get_span().clone(), - label: break_statement.loop_name.clone(), - value, - }) -} - -fn run_continue( - _context: &mut ExecutionContext, - continue_statement: &Continue, -) -> ExecutionResult> { - Err(ControlFlow::Continue { - span: continue_statement.get_span().clone(), - label: continue_statement.loop_name.clone(), - }) -} - -fn run_define_function( - context: &mut ExecutionContext, - function: &Rc>, -) -> ExecutionResult> { - context.stack.new_variable( - &function.named_block.name, - UserFunction::from(function).into(), - ); - - Ok(NoneType.into()) -} - -fn run_define_structure( - context: &mut ExecutionContext, - definition: &Rc>, -) -> ExecutionResult> { - context.stack.new_variable( - &definition.name, - StructDefinition { - definition: Rc::clone(definition), - } - .into(), - ); - - Ok(NoneType.into()) -} - -#[cfg(test)] -mod test { - use crate::script::{ - execution::{ControlFlow, Failure}, - Runtime, - }; - use common_data_types::Float; - - use super::*; - - #[test] - fn assignment() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_statement(context, &parsing::Statement::parse("value = 1").unwrap().1), - Err(ControlFlow::Failure(Failure::VariableNotInScope( - "value", - "value".into(), - ))) - ); - assert!(context.stack.get_variable(&"value").is_err()); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("let value = 1").unwrap().1 - ), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"value"), - Ok(&Float::new(1.0).unwrap().into()) - ); - - assert_eq!( - run_statement(context, &parsing::Statement::parse("value = 2").unwrap().1), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"value"), - Ok(&Float::new(2.0).unwrap().into()) - ); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("let [one, two] = [1, 2]") - .unwrap() - .1 - ), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"one"), - Ok(&Float::new(1.0).unwrap().into()) - ); - assert_eq!( - context.stack.get_variable(&"two"), - Ok(&Float::new(2.0).unwrap().into()) - ); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("[one, two] = [3, 4]").unwrap().1 - ), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"one"), - Ok(&Float::new(3.0).unwrap().into()) - ); - assert_eq!( - context.stack.get_variable(&"two"), - Ok(&Float::new(4.0).unwrap().into()) - ); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("let [one, two] = [1, 2, 3]") - .unwrap() - .1 - ), - Err(ControlFlow::Failure(Failure::ListLengthsDontMatch("["))) - ); - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("let [one, two] = [1]").unwrap().1 - ), - Err(ControlFlow::Failure(Failure::ListLengthsDontMatch("["))) - ); - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("let [one, two] = 1").unwrap().1 - ), - Err(ControlFlow::Failure(Failure::ExpectedGot( - "1", - "List".into(), - "Number".into() - ))) - ); - }); - } - - #[test] - fn assign_scopes() { - ExecutionContext::create(&mut Runtime::default(), |context| { - context - .stack - .new_variable(&"one", Float::new(1.0).unwrap().into()); - context - .stack - .new_variable(&"value", Float::new(1.0).unwrap().into()); - - context.new_scope(|context| { - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("let one = 2").unwrap().1 - ), - Ok(NoneType.into()) - ); - - assert_eq!( - context.stack.get_variable(&"value"), - Ok(&Float::new(1.0).unwrap().into()) - ); - assert_eq!( - run_statement(context, &parsing::Statement::parse("value = 2").unwrap().1), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"value"), - Ok(&Float::new(2.0).unwrap().into()) - ); - }); - - assert_eq!( - context.stack.get_variable(&"value"), - Ok(&Float::new(2.0).unwrap().into()) - ); - assert_eq!( - context.stack.get_variable(&"one"), - Ok(&Float::new(1.0).unwrap().into()) - ); - }); - } - - #[test] - fn loop_statement() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("loop { break; }").unwrap().1 - ), - Ok(NoneType.into()) - ); - }); - } - - #[test] - fn if_statement() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("if true { 1.0 }").unwrap().1 - ), - Ok(Float::new(1.0).unwrap().into()) - ); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("if false { 1.0 }").unwrap().1 - ), - Ok(NoneType.into()) - ); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("if true { 1.0 } else { 2.0 }") - .unwrap() - .1 - ), - Ok(Float::new(1.0).unwrap().into()) - ); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("if false { 1.0 } else { 2.0 }") - .unwrap() - .1 - ), - Ok(Float::new(2.0).unwrap().into()) - ); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("if false { 1.0 } else if true { 2.0 }") - .unwrap() - .1 - ), - Ok(Float::new(2.0).unwrap().into()) - ); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("if true { 1.0 } else if true { 2.0 }") - .unwrap() - .1 - ), - Ok(Float::new(1.0).unwrap().into()) - ); - - assert_eq!( - run_statement( - context, - &parsing::Statement::parse( - "if false { 1.0 } else if false { 2.0 } else { 3.0 }" - ) - .unwrap() - .1 - ), - Ok(Float::new(3.0).unwrap().into()) - ); - }); - } - - #[test] - fn break_statement() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("loop { break; }").unwrap().1 - ), - Ok(NoneType.into()) - ); - - assert_eq!( - run_block( - context, - &parsing::Block::parse( - "{ let test_one = 0; loop { test_one = 1; break; test_one = 2 } }", - ) - .unwrap() - .1 - ), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"test_one"), - Ok(&Float::new(1.0).unwrap().into()) - ); - - assert_eq!( - run_block(context, &parsing::Block::parse( - "{ let test_one = 0; loop { if test_one >= 5 { break; } test_one = test_one + 1; } }", - ) - .unwrap() - .1), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"test_one"), - Ok(&Float::new(5.0).unwrap().into()) - ); - - assert_eq!( - run_block(context, &parsing::Block::parse( - "{ let test_one = 0; 'parent: loop { loop { break 'parent; } test_one = 1 } }", - ) - .unwrap() - .1), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"test_one"), - Ok(&Float::new(0.0).unwrap().into()) - ); - - assert_eq!( - run_block( - context, - &parsing::Block::parse("{ loop { break 1.0; } }").unwrap().1 - ), - Ok(Float::new(1.0).unwrap().into()) - ); - }); - } - - #[test] - fn continue_statement() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_block(context, &parsing::Block::parse( - "{ let a = 0; let b = false; loop { a = a + 1; if a >= 2 { break; } continue; b = true; } }", - ) - .unwrap() - .1), - Ok(NoneType.into()) - ); - assert_eq!(context.stack.get_variable(&"b"), Ok(&false.into())); - - assert_eq!( - run_block(context, &parsing::Block::parse( - "{ let a = 0; let b = false; 'parent: loop { a = a + 1; if a >= 2 { break; } loop { continue 'parent; } b = true; } }", - ) - .unwrap() - .1), - Ok(NoneType.into()) - ); - assert_eq!(context.stack.get_variable(&"b"), Ok(&false.into())); - }); - } - - #[test] - fn while_statement() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_block( - context, - &parsing::Block::parse( - "{ let count = 0; while count < 5 { count = count + 1; } }" - ) - .unwrap() - .1 - ), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"count"), - Ok(&Float::new(5.0).unwrap().into()) - ); - }); - } - - #[test] - fn for_statement() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_block( - context, - &parsing::Block::parse( - "{ let count = 0; for i in 0..5 { count = count + 1; } }" - ) - .unwrap() - .1 - ), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"count"), - Ok(&Float::new(5.0).unwrap().into()) - ); - - assert_eq!( - run_block( - context, - &parsing::Block::parse( - "{ let count = 0; for i in 0..5 { count = count + i; } }" - ) - .unwrap() - .1 - ), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"count"), - Ok(&Float::new(10.0).unwrap().into()) - ); - - assert_eq!( - run_block(context, &parsing::Block::parse( - "{ let a = 0; let b = 0; for [x, y] in [[1, 2], [3, 4]] { a = a + x; b = b + y; } }", - ) - .unwrap() - .1), - Ok(NoneType.into()) - ); - assert_eq!( - context.stack.get_variable(&"a"), - Ok(&Float::new(4.0).unwrap().into()) - ); - assert_eq!( - context.stack.get_variable(&"b"), - Ok(&Float::new(6.0).unwrap().into()) - ); - }); - } - - #[test] - fn match_statement() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_block( - context, - &parsing::Block::parse("{ match 1 { 1 => true, default => false } }") - .unwrap() - .1 - ), - Ok(true.into()) - ); - - assert_eq!( - run_block( - context, - &parsing::Block::parse("{ match 2 { 1 => true, default => false } }") - .unwrap() - .1 - ), - Ok(false.into()) - ); - - assert_eq!( - run_block( - context, - &parsing::Block::parse( - "{ match [1, 2] { [1, 1] => 2, [1, 2] => 1, default => 0 } }" - ) - .unwrap() - .1 - ), - Ok(Float::new(1.0).unwrap().into()) - ); - - assert_eq!( - run_block( - context, - &parsing::Block::parse( - "{ match [1, 1] { [1, 1] => 2, [1, 2] => 1, default => 0 } }" - ) - .unwrap() - .1 - ), - Ok(Float::new(2.0).unwrap().into()) - ); - - assert_eq!( - run_block( - context, - &parsing::Block::parse("{ match [1, 2] { [1, default] => 1, default => 0 } }") - .unwrap() - .1 - ), - Ok(Float::new(1.0).unwrap().into()) - ); - - assert_eq!( - run_block( - context, - &parsing::Block::parse("{ match [1, 1] { [1, default] => 1, default => 0 } }") - .unwrap() - .1 - ), - Ok(Float::new(1.0).unwrap().into()) - ); - }); - } - - #[test] - fn define_function() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("function my_function() -> Number { 0 }") - .unwrap() - .1 - ), - Ok(NoneType.into()) - ); - - assert!(matches!( - context.stack.get_variable(&"my_function"), - Ok(Value::UserFunction(_)) - )); - }); - } - - #[test] - fn define_structure() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_statement( - context, - &parsing::Statement::parse("struct MyStruct {}").unwrap().1 - ), - Ok(NoneType.into()) - ); - - assert!(matches!( - context.stack.get_variable(&"MyStruct"), - Ok(Value::StructDefinition(_)) - )); - }); - } -} diff --git a/src/script/execution/types/boolean.rs b/src/script/execution/types/boolean.rs deleted file mode 100644 index a6698be..0000000 --- a/src/script/execution/types/boolean.rs +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::fmt::Write; - -use crate::script::{logging::RuntimeLog, parsing::VariableType, Span}; - -use super::{ - serializable::SerializableValue, - string::formatting::{Style, UnsupportedMessage, UnwrapFormattingResult}, - NamedObject, Object, OperatorResult, TypedObject, Value, -}; - -pub type Boolean = bool; - -impl Object for Boolean { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Boolean)) - } - - fn format( - &self, - _context: &mut dyn RuntimeLog, - span: &S, - f: &mut dyn Write, - style: Style, - precision: Option, - ) -> OperatorResult { - match (style, precision) { - (Style::Default | Style::Debug, None) => { - write!(f, "{}", self).unwrap_formatting_result(span) - } - (_, None) => style.unsupported_message(self, span), - (Style::Default | Style::Debug, _) => style.unsupported_message(self, span), - _ => { - style.unsupported_message(self, span).ok(); - precision.unsupported_message(self, span) - } - } - } - - fn eq( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult { - let rhs = rhs.downcast_ref::(span)?; - Ok(*self == *rhs) - } - - fn and( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = rhs.downcast_ref(span)?; - Ok((*self && *rhs).into()) - } - - fn or( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = rhs.downcast_ref(span)?; - Ok((*self || *rhs).into()) - } - - fn unary_logical_not( - &self, - _log: &mut dyn RuntimeLog, - _span: &S, - ) -> OperatorResult> { - Ok((!self).into()) - } - - fn export( - &self, - _log: &mut dyn RuntimeLog, - _span: &S, - ) -> OperatorResult { - Ok(SerializableValue::Boolean(*self)) - } -} - -impl TypedObject for Boolean { - fn get_type() -> VariableType { - VariableType::Boolean - } -} - -impl NamedObject for Boolean { - fn static_type_name() -> &'static str { - "Boolean" - } -} diff --git a/src/script/execution/types/closure.rs b/src/script/execution/types/closure.rs deleted file mode 100644 index 9102d35..0000000 --- a/src/script/execution/types/closure.rs +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::rc::Rc; - -use crate::script::{ - execution::{run_callable_block, ExecutionContext}, - logging::RuntimeLog, - parsing::{self, CapturedVariable, Expression, VariableType}, - Span, -}; - -use super::{NamedObject, Object, OperatorResult, Value}; - -#[derive(Clone)] -pub struct Closure { - pub source: Rc>, -} - -impl Object for Closure { - fn matches_type( - &self, - _ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(false) - } - - fn call( - &self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - spans: &[Expression], - ) -> OperatorResult> { - let referenced_variables = self.source.captured_variables.iter().filter_map(|v| { - if let CapturedVariable::Reference(name) = v { - Some(name.as_str()) - } else { - None - } - }); - - let copied_variables = self.source.captured_variables.iter().filter_map(|v| { - if let CapturedVariable::Copy(name) = v { - Some(name) - } else { - None - } - }); - - context.new_closure_scope(referenced_variables, copied_variables, |context| { - run_callable_block(context, &self.source.callable, arguments, spans, span) - })? - } -} - -impl<'a, S: Span> From<&'a Rc>> for Closure { - fn from(source: &'a Rc>) -> Self { - Self { - source: Rc::clone(source), - } - } -} - -impl std::fmt::Debug for Closure { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Closure") - .field("address", &Rc::as_ptr(&self.source)) - .finish() - } -} - -impl PartialEq for Closure { - fn eq(&self, _other: &Self) -> bool { - false - } -} - -impl NamedObject for Closure { - fn static_type_name() -> &'static str { - "Closure" - } -} - -#[cfg(test)] -mod test { - use common_data_types::Float; - - use crate::script::{ - execution::{ - expressions::run_expression, run_block, types::NoneType, ControlFlow, Failure, - }, - Runtime, - }; - - use super::*; - - #[test] - fn call_closure() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression( - context, - &Expression::parse("[]() -> Number { 1 }()").unwrap().1 - ), - Ok(Float::new(1.0).unwrap().into()) - ); - }); - } - - #[test] - fn closure_hygene() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let block = parsing::Block::parse( - "{ let closure = []() -> Number { let value = 1; }; closure(); }", - ) - .unwrap() - .1; - - assert_eq!(run_block(context, &block), Ok(NoneType.into())); - - assert_eq!( - context.stack.get_variable(&"value"), - Err(Failure::VariableNotInScope("value", "value".into())) - ); - - let block = parsing::Block::parse( - "{ let closure = []() -> Number { value = 1; }; closure(); }", - ) - .unwrap() - .1; - - assert_eq!( - run_block(context, &block), - Err(ControlFlow::Failure(Failure::VariableNotInScope( - "value", - "value".into() - ))) - ); - - let block = parsing::Block::parse( - "{ let value = 2; let closure = []() -> Number { value = 1; }; closure(); }", - ) - .unwrap() - .1; - - // Running the test right on the module scope creates issues, so we need to create a new scope. - context.new_scope(|context| { - assert_eq!( - run_block(context, &block), - Err(ControlFlow::Failure(Failure::VariableNotInScope( - "value", - "value".into() - ))) - ); - - assert_eq!( - context.stack.get_variable(&"value"), - Ok(&Float::new(2.0).unwrap().into()) - ); - }); - }); - } - - #[test] - fn pass_by_copy() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let block = parsing::Block::parse( - "{ let value = 1; let closure = [value]() -> Number { value = 2; value }; closure() }", - ) - .unwrap() - .1; - - let result = run_block(context, &block); - assert_eq!(result, Ok(Float::new(2.0).unwrap().into())); - - assert_eq!( - context.stack.get_variable(&"value"), - Ok(&Float::new(1.0).unwrap().into()) - ); - }); - } - - #[test] - fn pass_by_reference() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let block = parsing::Block::parse( - "{ let value = 1; let closure = [&value]() -> Number { value = 2; value }; closure() }", - ) - .unwrap() - .1; - - let result = run_block(context, &block); - assert_eq!(result, Ok(Float::new(2.0).unwrap().into())); - - assert_eq!( - context.stack.get_variable(&"value"), - Ok(&Float::new(2.0).unwrap().into()) - ); - }); - } -} diff --git a/src/script/execution/types/default.rs b/src/script/execution/types/default.rs deleted file mode 100644 index f821c20..0000000 --- a/src/script/execution/types/default.rs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use crate::script::{logging::RuntimeLog, parsing::VariableType, Span}; - -use super::{NamedObject, Object, OperatorResult}; - -#[derive(Debug, Clone, PartialEq)] -pub struct DefaultValue; - -impl Object for DefaultValue { - fn matches_type( - &self, - _ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(false) - } -} - -impl NamedObject for DefaultValue { - fn static_type_name() -> &'static str { - "Default" - } -} diff --git a/src/script/execution/types/function.rs b/src/script/execution/types/function.rs deleted file mode 100644 index 0186c3a..0000000 --- a/src/script/execution/types/function.rs +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::rc::Rc; - -use fortuples::fortuples; - -use crate::script::{ - execution::{run_callable_block, types::NoneType, ExecutionContext, Failure}, - logging::RuntimeLog, - parsing::{Expression, Function, VariableType}, - Span, -}; - -use super::{NamedObject, Object, OperatorResult, Value}; - -#[derive(Clone)] -pub struct UserFunction { - pub source: Rc>, -} - -impl std::fmt::Debug for UserFunction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("UserFunction") - .field("address", &Rc::as_ptr(&self.source)) - .finish() - } -} - -impl From<&'_ Rc>> for UserFunction { - fn from(value: &'_ Rc>) -> Self { - Self { - source: Rc::clone(value), - } - } -} - -impl UserFunction { - pub fn new(source: Function) -> Self { - Self { - source: Rc::new(source), - } - } -} - -impl Object for UserFunction { - fn matches_type( - &self, - _ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(false) - } - - fn call( - &self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - spans: &[Expression], - ) -> OperatorResult> { - context.new_isolated_scope(|context| { - run_callable_block( - context, - &self.source.named_block.callable, - arguments, - spans, - span, - ) - }) - } -} - -impl PartialEq for UserFunction { - fn eq(&self, _other: &Self) -> bool { - false - } -} - -impl NamedObject for UserFunction { - fn static_type_name() -> &'static str { - "Function" - } -} - -pub type BuiltinFunction = dyn Fn( - &mut ExecutionContext, - &S, - Vec>, - &[Expression], -) -> OperatorResult>; - -pub struct BuiltinFunctionRef(Rc>); - -impl std::ops::Deref for BuiltinFunctionRef { - type Target = BuiltinFunction; - - fn deref(&self) -> &Self::Target { - &*self.0 - } -} - -impl std::cmp::Eq for BuiltinFunctionRef {} -impl std::cmp::PartialEq for BuiltinFunctionRef { - fn eq(&self, other: &Self) -> bool { - std::ptr::addr_eq(Rc::as_ptr(&self.0), Rc::as_ptr(&other.0)) - } -} - -impl Clone for BuiltinFunctionRef { - fn clone(&self) -> Self { - Self(Rc::clone(&self.0)) - } -} - -impl std::fmt::Debug for BuiltinFunctionRef { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut debug_struct = f.debug_struct("BuiltinFunction"); - debug_struct.field("address", &Rc::as_ptr(&self.0)); - debug_struct.finish() - } -} - -impl From>> for BuiltinFunctionRef { - fn from(value: Box>) -> Self { - Self(value.into()) - } -} - -impl Object for BuiltinFunctionRef { - fn matches_type( - &self, - _ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(false) - } - - fn call( - &self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - (*self)(context, span, arguments, expressions) - } -} - -impl NamedObject for BuiltinFunctionRef { - fn static_type_name() -> &'static str { - "BuiltinFunction" - } -} - -pub trait UnpackArguments { - fn unpack_arguments( - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult; - - fn unpack_arguments_optional( - _span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult; -} - -#[rustfmt::skip] -fortuples! { - impl UnpackArguments for #Tuple - where - S: Span, - #(#Member: NamedObject),* - #(Value: TryInto<#Member>),* - { - fn unpack_arguments( - _span: &S, - mut arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult { - arguments.reverse(); - let mut expression_iter = expressions.iter(); - - #(let casey::lower!(#Member) = { - if let Some(value) = arguments.pop() { - value.downcast(expression_iter.next().unwrap().get_span()).map_err(|f| f.make_from_function_call())? - } else { - return Err(Failure::MissingArguments(_span.clone()).make_from_function_call()); - } - };)* - - if let Some(extra_expression) = expression_iter.next() { - Err(Failure::TooManyArguments(extra_expression.get_span().clone()).make_from_function_call()) - } else { - Ok((#(casey::lower!(#Member)),*)) - } - } - - fn unpack_arguments_optional( - _span: &S, - mut arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult { - arguments.reverse(); - let mut expression_iter = expressions.iter(); - - #(let casey::lower!(#Member) = { - if let Some(value) = arguments.pop() { - value.downcast(expression_iter.next().unwrap().get_span()).map_err(|f| f.make_from_function_call())? - } else { - Value::::from(NoneType).downcast(_span)? - } - };)* - - if let Some(extra_expression) = expression_iter.next() { - Err(Failure::TooManyArguments(extra_expression.get_span().clone()).make_from_function_call()) - } else { - Ok((#(casey::lower!(#Member)),*)) - } - } - } -} - -pub trait AutoCall -where - S: Span, -{ - type Unpacker: UnpackArguments; - - fn auto_call( - self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult>; - - fn auto_call_optional( - self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult>; -} - -#[rustfmt::skip] -fortuples! { - impl AutoCall for F - where - S: Span, - #(#Member: NamedObject),* - #(Value: TryInto<#Member>),* - F: FnOnce(&mut ExecutionContext, &S, #(#Member),*) -> OperatorResult>, - { - type Unpacker = #Tuple; - - fn auto_call( - self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - let (#(casey::lower!(#Member)),*) = Self::Unpacker::unpack_arguments(span, arguments, expressions)?; - - (self)(context, span, #(casey::lower!(#Member)),*) - } - - fn auto_call_optional( - self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - let (#(casey::lower!(#Member)),*) = Self::Unpacker::unpack_arguments_optional(span, arguments, expressions)?; - - (self)(context, span, #(casey::lower!(#Member)),*) - } - } -} - -pub trait IntoBuiltinFunction: AutoCall { - fn into_builtin_function(self) -> BuiltinFunctionRef; - fn into_builtin_function_optional(self) -> BuiltinFunctionRef; -} - -#[rustfmt::skip] - fortuples! { - impl IntoBuiltinFunction for F - where - S: Span, - F: Fn(&mut ExecutionContext, &S, #(#Member),*) -> OperatorResult> + Clone + 'static, - #(#Member: NamedObject),* - #(Value: TryInto<#Member>),* - { - fn into_builtin_function(self) -> BuiltinFunctionRef { - let function: Box> = Box::new(move |context: &mut ExecutionContext, span: &S, arguments: Vec>, expressions: &[Expression]| -> OperatorResult> { - self.clone().auto_call(context, span, arguments, expressions) - }); - - BuiltinFunctionRef::from(function) - } - - fn into_builtin_function_optional(self) -> BuiltinFunctionRef { - let function: Box> = Box::new(move |context: &mut ExecutionContext, span: &S, arguments: Vec>, expressions: &[Expression]| -> OperatorResult> { - self.clone().auto_call_optional(context, span, arguments, expressions) - }); - - BuiltinFunctionRef::from(function) - } - } - } diff --git a/src/script/execution/types/list.rs b/src/script/execution/types/list.rs deleted file mode 100644 index 884ce7e..0000000 --- a/src/script/execution/types/list.rs +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use common_data_types::Float; -use enum_downcast::{AsVariant, EnumDowncast, IntoVariant}; -use ouroboros::self_referencing; -use std::{cell::RefCell, fmt::Write, isize, rc::Rc}; - -use crate::script::{ - execution::{expressions::run_expression, ExecutionContext, Failure}, - logging::RuntimeLog, - parsing::{self, Expression, VariableType}, - Span, -}; - -use super::{ - function::AutoCall, math::Number, number::UnwrapNotNan, serializable::SerializableValue, - string::formatting::Style, NamedObject, NoneType, Object, OperatorResult, Scalar, TypedObject, - UnwrapBorrowFailure, Value, -}; - -#[derive(Debug, Clone, PartialEq)] -pub struct List { - vector: Rc>>>, -} - -impl From for List -where - I: IntoIterator>, -{ - fn from(value: I) -> Self { - let value = value.into_iter(); - - Self { - vector: Rc::new(RefCell::new(value.collect())), - } - } -} - -impl List { - pub(crate) fn from_parsed( - context: &mut ExecutionContext, - list: &parsing::List, - ) -> OperatorResult> { - let mut vector = Vec::with_capacity(list.expressions.len()); - - for expression in list.expressions.iter() { - let value = run_expression(context, expression)?; - - vector.push(value); - } - - Ok(Self { - vector: Rc::new(RefCell::new(vector)), - } - .into()) - } - - fn internalize_index(&self, span: &S, index: isize) -> OperatorResult { - let vector = self.vector.try_borrow().unwrap_borrow_failure(span)?; - - let new_index = if index >= 0 { - Ok(index as usize) - } else if let Some(new_index) = vector.len().checked_sub(index.unsigned_abs()) { - Ok(new_index) - } else { - Err(Failure::IndexOutOfRange(span.clone(), index)) - }?; - - if new_index >= vector.len() { - Err(Failure::IndexOutOfRange(span.clone(), index)) - } else { - Ok(new_index) - } - } - - pub fn len(&self, span: &S) -> OperatorResult { - let vector = self.vector.try_borrow().unwrap_borrow_failure(span)?; - Ok(vector.len()) - } - - pub fn iter(&self, span: &S) -> OperatorResult>> { - let vector = Rc::clone(&self.vector); - - #[self_referencing] - struct ListIter { - vector: Rc>>>, - - #[borrows(vector)] - #[not_covariant] - reference: std::cell::Ref<'this, Vec>>, - - #[borrows(reference)] - #[not_covariant] - iterator: std::iter::Cloned>>, - } - - impl Iterator for ListIter { - type Item = Value; - - fn next(&mut self) -> Option { - self.with_iterator_mut(|iterator| iterator.next()) - } - } - - ListIter::try_new( - vector, - |vector| vector.try_borrow().unwrap_borrow_failure(span), - |reference| Ok(reference.iter().cloned()), - ) - } -} - -impl Object for List { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::List)) - } - - fn format( - &self, - log: &mut dyn RuntimeLog, - span: &S, - f: &mut dyn Write, - style: Style, - precision: Option, - ) -> OperatorResult { - let vector = self.vector.try_borrow().unwrap_borrow_failure(span)?; - - for item in vector.iter() { - item.format(log, span, f, style, precision)?; - } - - Ok(()) - } - - fn index( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - index: Value, - ) -> OperatorResult> { - match index { - Value::Scalar(scalar) => { - let index = Number::try_from(scalar.clone()).map_err(|_| { - Failure::ExpectedGot( - span.clone(), - Number::static_type_name().into(), - >::type_name(&scalar), - ) - })?; - let index = index.to_index(); - - let localized_index = self.internalize_index(span, index)?; - - self.vector - .try_borrow() - .unwrap_borrow_failure(span)? - .get(localized_index) - .cloned() - .ok_or(Failure::IndexOutOfRange(span.clone(), index)) - } - Value::Range(range) => { - let vector = self.vector.try_borrow().unwrap_borrow_failure(span)?; - - // TODO could we keep an immutable reference to the original list to avoid a copy? - let slice = match ( - range.lower_bound, - range.upper_bound, - range.upper_bound_is_inclusive, - ) { - (None, None, false) => vector.get(..).ok_or((None, None)), - (Some(lower_bound), None, false) => { - let signed_lower_bound = lower_bound.to_index(); - let lower_bound = self.internalize_index(span, signed_lower_bound)?; - vector - .get(lower_bound..) - .ok_or((Some(signed_lower_bound), None)) - } - (None, Some(upper_bound), false) => { - let signed_upper_bound = upper_bound.to_index(); - let upper_bound = self.internalize_index(span, signed_upper_bound)?; - vector - .get(..upper_bound) - .ok_or((None, Some(signed_upper_bound))) - } - (None, Some(upper_bound), true) => { - let signed_upper_bound = upper_bound.to_index(); - let upper_bound = self.internalize_index(span, signed_upper_bound)?; - vector - .get(..=upper_bound) - .ok_or((None, Some(signed_upper_bound))) - } - (Some(lower_bound), Some(upper_bound), false) => { - let signed_lower_bound = lower_bound.to_index(); - let lower_bound = self.internalize_index(span, signed_lower_bound)?; - let signed_upper_bound = upper_bound.to_index(); - let upper_bound = self.internalize_index(span, signed_upper_bound)?; - vector - .get(lower_bound..upper_bound) - .ok_or((Some(signed_lower_bound), Some(signed_upper_bound))) - } - (Some(lower_bound), Some(upper_bound), true) => { - let signed_lower_bound = lower_bound.to_index(); - let lower_bound = self.internalize_index(span, signed_lower_bound)?; - let signed_upper_bound = upper_bound.to_index(); - let upper_bound = self.internalize_index(span, signed_upper_bound)?; - vector - .get(lower_bound..=upper_bound) - .ok_or((Some(signed_lower_bound), Some(signed_upper_bound))) - } - (_, None, true) => unreachable!(), // Inclusive ranges without an upper bound are illegal to construct. - }; - - // TODO String has an identical error handling. We should probably move this to a common library. - let range_type = if range.upper_bound_is_inclusive { - "..=" - } else { - ".." - }; - - slice - .map(|slice| Self::from(slice.iter().cloned()).into()) - .map_err(|(lower_bound, upper_bound)| { - Failure::SliceOutOfRange(span.clone(), lower_bound, range_type, upper_bound) - }) - } - _ => Err(Failure::ExpectedGot( - span.clone(), - "Number or Range".into(), - index.type_name(), - )), - } - } - - fn iterate( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - ) -> OperatorResult>>> { - Ok(Box::new(self.iter(span)?)) - } - - fn method_call( - &self, - context: &mut ExecutionContext, - span: &S, - attribute: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - match attribute.as_str() { - "append" => |_context: &mut ExecutionContext, - span: &S, - other: List| - -> OperatorResult> { - let mut other = other.vector.try_borrow_mut().unwrap_borrow_failure(span)?; - - self.vector - .try_borrow_mut() - .unwrap_borrow_failure(span)? - .append(&mut other); - - Ok(NoneType.into()) - } - .auto_call(context, span, arguments, expressions), - "dedup" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - self.vector - .try_borrow_mut() - .unwrap_borrow_failure(span)? - .dedup(); - Ok(NoneType.into()) - } - .auto_call(context, span, arguments, expressions) - } - "insert" => move |_context: &mut ExecutionContext, - span: &S, - index: Number, - value: Value| - -> OperatorResult> { - let index = index.to_index(); - let index = self.internalize_index(span, index)?; - self.vector - .try_borrow_mut() - .unwrap_borrow_failure(span)? - .insert(index, value); - - Ok(NoneType.into()) - } - .auto_call(context, span, arguments, expressions), - "is_empty" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - Ok(self - .vector - .try_borrow() - .unwrap_borrow_failure(span)? - .is_empty() - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "len" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - Float::new(self.vector.try_borrow().unwrap_borrow_failure(span)?.len() as f64) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "push" => |_context: &mut ExecutionContext, - span: &S, - other: Value| - -> OperatorResult> { - self.vector - .try_borrow_mut() - .unwrap_borrow_failure(span)? - .push(other); - - Ok(NoneType.into()) - } - .auto_call(context, span, arguments, expressions), - "remove" => |_context: &mut ExecutionContext, - span: &S, - index: Number| - -> OperatorResult> { - let index = index.to_index(); - let index = self.internalize_index(span, index)?; - - self.vector - .try_borrow_mut() - .unwrap_borrow_failure(span)? - .remove(index); - - Ok(NoneType.into()) - } - .auto_call(context, span, arguments, expressions), - "contains" => |_context: &mut ExecutionContext, - span: &S, - search: Value| - -> OperatorResult> { - Ok(self - .vector - .try_borrow() - .unwrap_borrow_failure(span)? - .contains(&search) - .into()) - } - .auto_call(context, span, arguments, expressions), - "last" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - let vector = self.vector.try_borrow().unwrap_borrow_failure(span)?; - let last = vector.last(); - - if let Some(last) = last { - Ok(last.clone()) - } else { - Err(Failure::ListIsEmpty(span.clone())) - } - } - .auto_call(context, span, arguments, expressions) - } - "first" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - let vector = self.vector.try_borrow().unwrap_borrow_failure(span)?; - let first = vector.first(); - - if let Some(first) = first { - Ok(first.clone()) - } else { - Err(Failure::ListIsEmpty(span.clone())) - } - } - .auto_call(context, span, arguments, expressions) - } - "reverse" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.vector - .try_borrow_mut() - .unwrap_borrow_failure(span)? - .reverse(); - - Ok(NoneType.into()) - } - .auto_call(context, span, arguments, expressions) - } - "rotate_left" => |_context: &mut ExecutionContext, - span: &S, - mid: Number| - -> OperatorResult> { - let mid = mid.to_index(); - if mid.is_positive() { - let mut vector = self.vector.try_borrow_mut().unwrap_borrow_failure(span)?; - let mid = mid as usize % vector.len(); - vector.rotate_left(mid); - - Ok(NoneType.into()) - } else { - Err(Failure::NumberMustBePositive(span.clone())) - } - } - .auto_call(context, span, arguments, expressions), - "rotate_right" => |_context: &mut ExecutionContext, - span: &S, - mid: Number| - -> OperatorResult> { - let mid = mid.to_index(); - if mid.is_positive() { - let mut vector = self.vector.try_borrow_mut().unwrap_borrow_failure(span)?; - let mid = mid as usize % vector.len(); - vector.rotate_right(mid); - - Ok(NoneType.into()) - } else { - Err(Failure::NumberMustBePositive(span.clone())) - } - } - .auto_call(context, span, arguments, expressions), - "clone" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - Ok(List::from(self.iter(span)?).into()) - } - .auto_call(context, span, arguments, expressions) - } - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } - - fn export( - &self, - log: &mut dyn RuntimeLog, - span: &S, - ) -> OperatorResult { - let vector = self.vector.try_borrow().unwrap_borrow_failure(span)?; - - let mut list = Vec::with_capacity(vector.len()); - - for item in vector.iter() { - let serializable = item.export(log, span)?; - list.push(serializable); - } - - Ok(SerializableValue::List(list)) - } -} - -impl TypedObject for List { - fn get_type() -> VariableType { - VariableType::List - } -} - -impl NamedObject for List { - fn static_type_name() -> &'static str { - "List" - } -} - -impl List { - pub fn unpack_dynamic_length(self, span: &S) -> OperatorResult> - where - T: NamedObject, - Value: IntoVariant + AsVariant + TryInto, - { - // We're going to return copies of all the values in the end anyway, so may as well just own/copy it now. - let vector = Rc::unwrap_or_clone(self.vector).into_inner(); - - // Verify that they're all of the right type. - for (index, item) in vector.iter().enumerate() { - if item.enum_downcast_ref::().is_none() { - return Err(Failure::ListElement( - span.clone(), - index, - Box::new(Failure::ExpectedGot( - span.clone(), - T::static_type_name().into(), - item.type_name(), - )), - )); - } - } - - // Okay, we've validated them. Now we can really take them. - // The unwraps will not fail because we've already validated the types. - let iter = vector.into_iter().map(|v| v.enum_downcast::().unwrap()); - Ok(iter) - } - - pub fn unpack_fixed_length( - self, - span: &S, - ) -> OperatorResult> - where - T: NamedObject, - Value: IntoVariant + AsVariant + TryInto, - { - let length = self.vector.try_borrow().unwrap_borrow_failure(span)?.len(); - if length == D { - // This cannot exceed length D because we already validated that the list matched that length. - self.unpack_dynamic_length(span) - } else { - Err(Failure::ListWrongLength(span.clone(), D, length)) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - use crate::script::{ - execution::{expressions::run_expression, ExecutionContext}, - parsing::Expression, - Runtime, Scalar, - }; - - #[test] - fn index() { - let mut runtime = Runtime::default(); - ExecutionContext::create(&mut runtime, |context| { - assert_eq!( - run_expression(context, &Expression::parse("[1, 2, 3][0]").unwrap().1), - Ok(Float::new(1.0).unwrap().into()) - ); - - assert_eq!( - run_expression(context, &Expression::parse("[1, 2, 3][-1]").unwrap().1), - Ok(Float::new(3.0).unwrap().into()) - ); - - assert_eq!( - run_expression(context, &Expression::parse("[1, 2, 3][3]").unwrap().1), - Err(Failure::IndexOutOfRange("[", 3)) - ); - assert_eq!( - run_expression(context, &Expression::parse("[1, 2, 3][-4]").unwrap().1), - Err(Failure::IndexOutOfRange("[", -4)) - ); - }); - } - - #[test] - fn test_unpack() { - assert_eq!( - List::<&'static str>::from([ - Float::new(1.0).unwrap().into(), - Float::new(2.0).unwrap().into(), - Float::new(3.0).unwrap().into(), - ]) - .unpack_fixed_length::(&"span",) - .map(|v| v.collect::>()), - Ok(vec![ - Float::new(1.0).unwrap().into(), - Float::new(2.0).unwrap().into(), - Float::new(3.0).unwrap().into(), - ]) - ); - - let values = [ - Float::new(1.0).unwrap().into(), - Float::new(2.0).unwrap().into(), - ]; - - assert_eq!( - List::<&'static str>::from(values) - .unpack_fixed_length::(&"span",) - .map(|v| v.collect::>()), - Err(Failure::ListWrongLength("span", 3, 2)) - ); - - let values = [Float::new(1.0).unwrap().into(), true.into()]; - - assert_eq!( - List::<&'static str>::from(values) - .unpack_fixed_length::(&"span") - .map(|v| v.collect::>()), - Err(Failure::ListElement( - "span", - 1, - Box::new(Failure::ExpectedGot( - "span", - "Scalar".into(), - "Boolean".into() - )) - )) - ); - } -} diff --git a/src/script/execution/types/math/mod.rs b/src/script/execution/types/math/mod.rs deleted file mode 100644 index 88fea51..0000000 --- a/src/script/execution/types/math/mod.rs +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::{borrow::Cow, io}; - -use common_data_types::{ - BaseUnits, ConversionFactor, ConversionFactorDatabase, Dimension, DimensionNameDatabase, Float, - RatioTypeHint, RawFloat, UnitDescription, UnitList, -}; -use nalgebra::{DefaultAllocator, DimName}; - -use crate::script::{ - execution::{ExecutionContext, Failure}, - Span, -}; - -mod scalar; -pub use scalar::{Angle, Length, Number, Scalar}; - -mod vector; -use self::vector::Vector; -pub use vector::{LengthVector2, LengthVector3, Vector2, Vector3, Vector4}; - -mod transform; -pub use transform::{Transform2D, Transform3D}; - -mod quaternion; -pub use quaternion::Quaternion; - -use super::OperatorResult; - -lazy_static::lazy_static! { - static ref CONVERSION_FACTORS: ConversionFactorDatabase = include!(concat!(env!("OUT_DIR"), "/conversion_factors.rs")); - static ref DIMENSION_NAMES: DimensionNameDatabase = include!(concat!(env!("OUT_DIR"), "/dimension_names.rs")); - static ref UNIT_LIST: UnitList = include!(concat!(env!("OUT_DIR"), "/unit_list.rs")); - static ref BASE_UNITS: BaseUnits = include!(concat!(env!("OUT_DIR"), "/base_units.rs")); -} - -pub fn register_globals(context: &mut ExecutionContext) { - // TODO this would be a good point call into scalar and create some global constants. - vector::register_globals(context); - transform::register_globals(context); - quaternion::register_globals(context); -} - -trait CheckNan { - fn check_nan(self, span: &S) -> OperatorResult; -} - -impl< - R: nalgebra::base::Dim, - C: nalgebra::base::Dim, - S: nalgebra::base::storage::RawStorage, - > CheckNan for nalgebra::Matrix -{ - fn check_nan(self, span: &SP) -> OperatorResult { - if !self.iter().any(|c| c.is_nan()) { - Ok(()) - } else { - Err(Failure::ResultIsNan(span.clone())) - } - } -} - -fn format_dimension(dimension: &Dimension) -> Cow<'static, str> { - format!( - "Scalar", - dimension.length, - dimension.mass, - dimension.time, - dimension.electric_current, - dimension.thermodynamic_temprature, - dimension.amount_of_substance, - dimension.luminous_intensity - ) - .into() -} - -fn get_dimension_name(dimension: &Dimension) -> Cow<'static, str> { - if let Some(name) = DIMENSION_NAMES.get(dimension) { - name.clone() - } else { - format_dimension(dimension) - } -} - -pub trait ConvertUnit { - fn convert_to_base_unit(&self, input: Float) -> Float; - fn convert_from_base_unit(&self, input: Float) -> Float; - fn convert_from_measurement_to_number( - &self, - span: &S, - measurement: &Scalar, - ) -> OperatorResult; - fn convert_from_vector_to_iter( - &self, - span: &S, - vector: &Vector, - ) -> OperatorResult> - where - DefaultAllocator: nalgebra::allocator::Allocator; - fn convert_from_vector_to_iter_without_dimension_check( - &self, - vector: &vector::NVector, - ) -> impl Iterator - where - DefaultAllocator: nalgebra::allocator::Allocator; -} - -impl ConvertUnit for ConversionFactor { - fn convert_to_base_unit(&self, input: Float) -> Float { - input * self.coefficient + self.constant - } - - fn convert_from_base_unit(&self, input: Float) -> Float { - (input - self.constant) / self.coefficient - } - - fn convert_from_measurement_to_number( - &self, - span: &S, - measurement: &Scalar, - ) -> OperatorResult { - if measurement.dimension == self.dimension { - Ok(self.convert_from_base_unit(measurement.value)) - } else { - Err(Failure::ExpectedGot( - span.clone(), - get_dimension_name(&self.dimension), - get_dimension_name(&measurement.dimension), - )) - } - } - - fn convert_from_vector_to_iter_without_dimension_check( - &self, - vector: &vector::NVector, - ) -> impl Iterator - where - DefaultAllocator: nalgebra::allocator::Allocator, - { - vector - .iter() - .copied() - .map(|c| self.convert_from_base_unit(Float::new(c).unwrap())) - } - - fn convert_from_vector_to_iter( - &self, - span: &S, - vector: &Vector, - ) -> OperatorResult> - where - DefaultAllocator: nalgebra::allocator::Allocator, - { - if vector.dimension == self.dimension { - Ok(vector - .value - .iter() - .copied() - .map(|c| self.convert_from_base_unit(Float::new(c).unwrap()))) - } else { - Err(Failure::ExpectedGot( - span.clone(), - get_dimension_name(&self.dimension), - get_dimension_name(&vector.dimension), - )) - } - } -} - -pub fn print_all_supported_units( - f: &mut dyn io::Write, - filter: Option<&str>, -) -> std::result::Result<(), io::Error> { - for (dimension, units) in UNIT_LIST.iter() { - let units = units.iter().filter(|unit| { - if let Some(filter) = filter { - unit.name.contains(filter) || unit.abbreviation.contains(filter) - } else { - true - } - }); - - let name = "Name"; - let abbreviation = "Abbreviation"; - let keyboard_friendly_abbreviation = "Keyboard Friendly Abbreviation"; - let plural_name = "Plural Name"; - - let mut name_length = name.len(); - let mut abbreviation_length = abbreviation.len(); - let mut keyboard_friendly_abbreviation_length = keyboard_friendly_abbreviation.len(); - let mut plural_name_length = plural_name.len(); - - let mut contained_any = false; - - for unit in units.clone() { - contained_any = true; - - name_length = name_length.max(unit.name.len()); - abbreviation_length = abbreviation_length.max(unit.abbreviation.len()); - keyboard_friendly_abbreviation_length = keyboard_friendly_abbreviation_length - .max(unit.keyboard_friendly_abbreviation.len()); - plural_name_length = plural_name_length.max(unit.plural_name.len()); - } - - if contained_any { - writeln!(f, "{}:\n", dimension)?; - let header = format!( - "[{:", unit.keyboard_friendly_abbreviation)) - }; - - writeln!( - f, - " {:. - */ - -use std::borrow::Cow; - -use common_data_types::{Dimension, RawFloat}; -use nalgebra::Unit; - -use crate::script::{ - execution::{ - types::{function::IntoBuiltinFunction, NamedObject, Object, OperatorResult, Value}, - ExecutionContext, - }, - logging::RuntimeLog, - parsing::VariableType, - Span, -}; - -use super::{Angle, Vector3}; - -pub type Quaternion = nalgebra::UnitQuaternion; - -pub fn register_globals(context: &mut ExecutionContext) { - context.stack.new_variable_str( - "new_quaternion", - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Quaternion::identity().into()) - } - .into_builtin_function() - .into(), - ); - - context.stack.new_variable_str( - "vector_to_quaternion", - |_context: &mut ExecutionContext, - span: &S, - axis: Vector3| - -> OperatorResult> { - axis.check_dimension(span, Dimension::zero())?; - Ok(Quaternion::new(axis.value).into()) - } - .into_builtin_function() - .into(), - ); - - context.stack.new_variable_str( - "axis_quaternion", - |_context: &mut ExecutionContext, - span: &S, - axis: Vector3, - angle: Angle| - -> OperatorResult> { - axis.check_dimension(span, Dimension::zero())?; - - let angle = angle.value; - - Ok(Quaternion::from_axis_angle(&Unit::new_normalize(axis.value), *angle).into()) - } - .into_builtin_function() - .into(), - ); -} - -impl Object for Quaternion -where - S: Span, -{ - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Quaternion)) - } - - fn type_name(&self) -> Cow<'static, str> { - Self::static_type_name().into() - } - - fn addition( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = rhs.downcast_ref::(span)?; - - Ok(Quaternion::new_normalize(self.into_inner() * rhs.into_inner()).into()) - } - fn subtraction( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = rhs.downcast_ref::(span)?; - - Ok(Quaternion::new_normalize(self.into_inner() * rhs.inverse().into_inner()).into()) - } -} - -impl NamedObject for Quaternion { - fn static_type_name() -> &'static str { - "Quaternion" - } -} - -#[cfg(test)] -mod test { - use nalgebra::Const; - - use crate::script::{ - execution::{ - expressions::run_expression, - types::math::vector::{NVector, Vector}, - }, - parsing::Expression, - Runtime, - }; - - use super::*; - - #[test] - fn rotate() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let result = run_expression( - context, - & - Expression::parse( - "(new_transform3D() * axis_quaternion(vec3(0, 0, 1), 90deg)).apply_to_vector(vec3(1m, 0m, 0m))", - ) - .unwrap() - .1, - ) - .unwrap(); - let vector = result.downcast::>>(&"").unwrap(); - - assert!((vector.value - NVector::>::new(0.0, 1.0, 0.0)).norm() < 0.01); - }); - } - - #[test] - fn addition() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let result = run_expression( - context, - &Expression::parse( - "(new_transform3D() * (axis_quaternion(vec3(0, 0, 1), 45deg) + axis_quaternion(vec3(0, 0, 1), 45deg))).apply_to_vector(vec3(1m, 0m, 0m))", - ) - .unwrap() - .1, - ) - .unwrap(); - let vector = result.downcast::>>(&"").unwrap(); - - assert!((vector.value - NVector::>::new(0.0, 1.0, 0.0)).norm() < 0.01); - }); - } - - #[test] - fn subtraction() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let result = run_expression( - context, - &Expression::parse( - "(new_transform3D() * (axis_quaternion(vec3(0, 0, 1), 45deg) - axis_quaternion(vec3(0, 0, 1), 45deg))).apply_to_vector(vec3(1m, 0m, 0m))", - ) - .unwrap() - .1, - ) - .unwrap(); - let vector = result.downcast::>>(&"").unwrap(); - - assert!((vector.value - NVector::>::new(1.0, 0.0, 0.0)).norm() < 0.01); - }); - } -} diff --git a/src/script/execution/types/math/scalar.rs b/src/script/execution/types/math/scalar.rs deleted file mode 100644 index 5de37ff..0000000 --- a/src/script/execution/types/math/scalar.rs +++ /dev/null @@ -1,1275 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use std::{borrow::Cow, cmp::Ordering, fmt, str::FromStr}; - -use enum_downcast::EnumDowncast; -use fj_math::Scalar as FornjotScalar; -use imstr::ImString; -use nalgebra::Const; -use serde::{ - de::{self, Visitor}, - Deserialize, Serialize, -}; -use uom::{ - si::{Dimension as UomDimension, Quantity, Units}, - typenum::ToInt, -}; - -use common_data_types::{consts, ConversionFactor, Dimension, Float, RatioTypeHint, RawFloat}; - -use crate::script::{ - execution::{ - types::{ - function::AutoCall, - number::{unwrap_float, UnwrapNotNan}, - NamedObject, Object, OperatorResult, SString, SerializableValue, Style, TypedObject, - UnwrapFormattingResult, Value, - }, - ExecutionContext, Failure, - }, - logging::{LogMessage, RuntimeLog}, - parsing::{self, Expression, VariableType}, - Span, -}; - -use super::{ - format_dimension, get_dimension_name, vector::NVector, ConvertUnit, Vector2, BASE_UNITS, - CONVERSION_FACTORS, -}; - -macro_rules! define_fixed_dimension_scalar { - ($name:ident, $dimension:expr) => { - #[derive(Debug, Clone, Copy, Eq, PartialEq)] - pub struct $name { - pub value: Float, - } - - impl From<$name> for Scalar { - fn from(value: $name) -> Scalar { - Scalar { - dimension: $dimension, - value: value.value, - } - } - } - - impl From<$name> for Value { - fn from(value: $name) -> Value { - Value::from(Scalar::from(value)) - } - } - - impl TryFrom> for $name { - type Error = (); - - fn try_from(value: Value) -> Result { - let scalar: Scalar = value.enum_downcast().map_err(|_| ())?; - Self::try_from(scalar).map_err(|_| ()) - } - } - - impl TryFrom for $name { - type Error = Scalar; - - fn try_from(value: Scalar) -> Result { - if value.dimension == $dimension { - Ok(Self { value: value.value }) - } else { - Err(value) - } - } - } - - impl<'a> TryFrom<&'a Scalar> for $name { - type Error = &'a Scalar; - - fn try_from(value: &'a Scalar) -> Result { - if value.dimension == $dimension { - Ok(Self { value: value.value }) - } else { - Err(value) - } - } - } - - impl TypedObject for $name { - fn get_type() -> VariableType { - VariableType::Scalar(S::from_str(stringify!($name))) - } - } - - impl NamedObject for $name { - fn static_type_name() -> &'static str { - "Scalar" - } - } - }; -} - -define_fixed_dimension_scalar!(Length, Dimension::length()); - -impl Length { - pub fn as_fornjot_scale_float(&self, context: &ExecutionContext) -> Float { - context - .global_resources - .fornjot_unit_conversion_factor - .convert_from_base_unit(self.value) - } - - pub fn as_fornjot_scalar(&self, context: &ExecutionContext) -> FornjotScalar { - FornjotScalar::from_f64(self.as_fornjot_scale_float(context).into_inner()) - } -} - -define_fixed_dimension_scalar!(Angle, Dimension::angle()); -define_fixed_dimension_scalar!(Number, Dimension::zero()); - -impl Number { - pub fn to_index(self) -> isize { - self.value.round() as isize - } -} - -impl From for Number { - fn from(value: Float) -> Self { - Self { value } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Scalar { - pub(super) dimension: Dimension, - pub(super) value: Float, -} - -impl Object for Scalar { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(if let VariableType::Scalar(name) = ty { - name.as_str() == Object::::type_name(self).as_ref() - } else { - false - }) - } - - fn type_name(&self) -> Cow<'static, str> { - get_dimension_name(&self.dimension) - } - - fn format( - &self, - log: &mut dyn RuntimeLog, - span: &S, - f: &mut dyn fmt::Write, - style: Style, - precision: Option, - ) -> OperatorResult { - // This just takes a reference to the unit name, so it's pretty cheap. I don't mind if it's not always used. - // In the rare case that a unit name is generated and memory is allocated on the heap, well we're clearly - // not about to format a number, so it's clear that we're going to use this. - let unit_name = BASE_UNITS - .get(&self.dimension) - .cloned() - .unwrap_or_else(|| format_dimension(&self.dimension)); - - match (style, precision, self.is_number()) { - (Style::Default, None, true) => { - write!(f, "{}", self.value).unwrap_formatting_result(span) - } - (Style::Default, None, false) => { - write!(f, "{} {unit_name}", self.value).unwrap_formatting_result(span) - } - (Style::Default, Some(precision), true) => { - write!(f, "{:.1$}", self.value, precision as usize).unwrap_formatting_result(span) - } - (Style::Default, Some(precision), false) => { - write!(f, "{:.1$} {unit_name}", self.value, precision as usize) - .unwrap_formatting_result(span) - } - (Style::Debug, None, true) => { - write!(f, "{}", self.value).unwrap_formatting_result(span) - } - (Style::Debug, None, false) => { - write!(f, "{} {unit_name}", self.value).unwrap_formatting_result(span) - } - (Style::Debug, Some(precision), true) => { - write!(f, "{:.1$}", self.value, precision as usize).unwrap_formatting_result(span) - } - (Style::Debug, Some(precision), false) => { - write!(f, "{:.1$} {unit_name}", self.value, precision as usize) - .unwrap_formatting_result(span) - } - (Style::Octal, _, true) => { - if precision.is_some() { - log.push(LogMessage::FormatIntegerPrecision(span.clone())); - } - write!(f, "{:o}", self.value.into_inner() as usize).unwrap_formatting_result(span) - } - (Style::Octal, _, false) => { - if precision.is_some() { - log.push(LogMessage::FormatIntegerPrecision(span.clone())); - } - write!(f, "{:o} {unit_name}", self.value.into_inner() as usize) - .unwrap_formatting_result(span) - } - (Style::Hex, _, true) => { - if precision.is_some() { - log.push(LogMessage::FormatIntegerPrecision(span.clone())); - } - write!(f, "{:x}", self.value.into_inner() as usize).unwrap_formatting_result(span) - } - (Style::Hex, _, false) => { - if precision.is_some() { - log.push(LogMessage::FormatIntegerPrecision(span.clone())); - } - write!(f, "{:x} {unit_name}", self.value.into_inner() as usize) - .unwrap_formatting_result(span) - } - (Style::CapitalizedHex, _, true) => { - if precision.is_some() { - log.push(LogMessage::FormatIntegerPrecision(span.clone())); - } - write!(f, "{:X}", self.value.into_inner() as usize).unwrap_formatting_result(span) - } - (Style::CapitalizedHex, _, false) => { - if precision.is_some() { - log.push(LogMessage::FormatIntegerPrecision(span.clone())); - } - write!(f, "{:X} {unit_name}", self.value.into_inner() as usize) - .unwrap_formatting_result(span) - } - (Style::Exponent, None, true) => { - write!(f, "{:e}", self.value.into_inner() as usize).unwrap_formatting_result(span) - } - (Style::Exponent, None, false) => { - write!(f, "{:e} {unit_name}", self.value.into_inner() as usize) - .unwrap_formatting_result(span) - } - (Style::Exponent, Some(precision), true) => { - write!(f, "{:.1$e}", self.value.into_inner(), precision as usize) - .unwrap_formatting_result(span) - } - (Style::Exponent, Some(precision), false) => write!( - f, - "{:.1$e} {unit_name}", - self.value.into_inner(), - precision as usize - ) - .unwrap_formatting_result(span), - (Style::CapitalizedExponent, None, true) => { - write!(f, "{:E}", self.value.into_inner()).unwrap_formatting_result(span) - } - (Style::CapitalizedExponent, None, false) => { - write!(f, "{:E} {unit_name}", self.value.into_inner()) - .unwrap_formatting_result(span) - } - (Style::CapitalizedExponent, Some(precision), true) => { - write!(f, "{:.1$E}", self.value.into_inner(), precision as usize) - .unwrap_formatting_result(span) - } - (Style::CapitalizedExponent, Some(precision), false) => write!( - f, - "{:.1$E} {unit_name}", - self.value.into_inner(), - precision as usize - ) - .unwrap_formatting_result(span), - } - } - - fn addition( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = self.unpack_for_addition_or_subtraction(span, rhs)?; - - let value = Float::new(*self.value + *rhs.value).unwrap_not_nan(span)?; - - Ok(Self { - value, - ..self.clone() - } - .into()) - } - fn subtraction( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = self.unpack_for_addition_or_subtraction(span, rhs)?; - - let value = Float::new(*self.value - *rhs.value).unwrap_not_nan(span)?; - - Ok(Self { - value, - ..self.clone() - } - .into()) - } - fn multiply( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = rhs.downcast_ref::(span)?; - self.multiply_by_scalar(span, rhs).map(|rhs| rhs.into()) - } - fn divide( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = rhs.downcast_ref::(span)?; - self.divide_by_measurement(span, rhs).map(|rhs| rhs.into()) - } - fn unary_plus(&self, _log: &mut dyn RuntimeLog, _span: &S) -> OperatorResult> { - Ok(self.clone().into()) - } - fn unary_minus(&self, _log: &mut dyn RuntimeLog, _span: &S) -> OperatorResult> { - Ok(Self { - value: -self.value, - ..self.clone() - } - .into()) - } - fn cmp( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult { - let rhs = rhs.downcast_ref::(span)?; - if self.dimension == rhs.dimension { - Ok(std::cmp::Ord::cmp(&self.value, &rhs.value)) - } else { - Err(Failure::ExpectedGot( - span.clone(), - Object::::type_name(self), - Object::::type_name(rhs), - )) - } - } - fn method_call( - &self, - context: &mut ExecutionContext, - span: &S, - attribute: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - match attribute.as_str() { - "to_number" => |_context: &mut ExecutionContext, - span: &S, - ty: SString| - -> OperatorResult> { - self.convert_to_number(span, &ty.as_str(span)?) - } - .auto_call(context, span, arguments, expressions), - "abs" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - Ok(Self { - value: Float::new(self.value.abs()).unwrap_not_nan(span)?, - ..self.clone() - } - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "clamp" => |_context: &mut ExecutionContext, - span: &S, - min: Value, - max: Value| - -> OperatorResult> { - let min = self.unpack_for_addition_or_subtraction(span, &min)?; - let max = self.unpack_for_addition_or_subtraction(span, &max)?; - - Ok(Self { - value: self.value.clamp(min.value, max.value), - ..self.clone() - } - .into()) - } - .auto_call(context, span, arguments, expressions), - "copysign" => |_context: &mut ExecutionContext, - span: &S, - sign: Number| - -> OperatorResult> { - let sign = sign.to_index(); - - Ok(Self { - value: Float::new(self.value.copysign(sign as RawFloat)) - .unwrap_not_nan(span)?, - ..self.clone() - } - .into()) - } - .auto_call(context, span, arguments, expressions), - "hypot" => |_context: &mut ExecutionContext, - span: &S, - other: Value| - -> OperatorResult> { - let other = self.unpack_for_addition_or_subtraction(span, &other)?; - - Ok(Self { - value: Float::new(self.value.hypot(*other.value)).unwrap_not_nan(span)?, - ..self.clone() - } - .into()) - } - .auto_call(context, span, arguments, expressions), - "is_finite" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(self.value.is_finite().into()) - } - .auto_call(context, span, arguments, expressions) - } - "is_infinite" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(self.value.is_infinite().into()) - } - .auto_call(context, span, arguments, expressions) - } - "is_normal" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(self.value.is_normal().into()) - } - .auto_call(context, span, arguments, expressions) - } - "cbrt" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - Ok(Self { - dimension: self.dimension / 3, - value: Float::new(self.value.cbrt()).unwrap_not_nan(span)?, - } - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "pow" => |_context: &mut ExecutionContext, - _span: &S, - exponent: Scalar| - -> OperatorResult> { - let exponent = exponent.to_number(span)?; - - self.check_is_zero_dimension(span)?; - - Ok(Self { - dimension: self.dimension, - value: Float::new(self.value.powf(exponent.into_inner())) - .unwrap_not_nan(span)?, - } - .into()) - } - .auto_call(context, span, arguments, expressions), - "powi" => |_context: &mut ExecutionContext, - _span: &S, - exponent: Number| - -> OperatorResult> { - let exponent = exponent.to_index() as i8; - - Ok(Self { - dimension: self.dimension * exponent, - value: Float::new(self.value.powi(exponent as i32)).unwrap_not_nan(span)?, - } - .into()) - } - .auto_call(context, span, arguments, expressions), - "sqrt" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Self { - dimension: self.dimension / 2, - value: Float::new(self.value.sqrt()).unwrap_not_nan(span)?, - } - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "is_sign_negative" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(self.value.is_sign_negative().into()) - } - .auto_call(context, span, arguments, expressions) - } - "is_sign_positive" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(self.value.is_sign_positive().into()) - } - .auto_call(context, span, arguments, expressions) - } - "recip" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Self { - dimension: -self.dimension, - value: Float::new(1.0 / self.value.into_inner()).unwrap_not_nan(span)?, - } - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "round" => |_context: &mut ExecutionContext, - span: &S, - unit: Value| - -> OperatorResult> { - let conversion_factor = Self::get_operation_conversion_factor(span, unit)?; - - let value = conversion_factor.convert_from_measurement_to_number(span, self)?; - let value = Float::new(value.round()).unwrap_not_nan(span)?; - - Ok(Self { - dimension: self.dimension, - value: conversion_factor.convert_to_base_unit(value), - } - .into()) - } - .auto_call_optional(context, span, arguments, expressions), - "trunc" => |_context: &mut ExecutionContext, - span: &S, - unit: Value| - -> OperatorResult> { - let conversion_factor = Self::get_operation_conversion_factor(span, unit)?; - - let value = conversion_factor.convert_from_measurement_to_number(span, self)?; - let value = Float::new(value.trunc()).unwrap_not_nan(span)?; - - Ok(Self { - dimension: self.dimension, - value: conversion_factor.convert_to_base_unit(value), - } - .into()) - } - .auto_call_optional(context, span, arguments, expressions), - "fract" => |_context: &mut ExecutionContext, - span: &S, - unit: Value| - -> OperatorResult> { - let conversion_factor = Self::get_operation_conversion_factor(span, unit)?; - - let value = conversion_factor.convert_from_measurement_to_number(span, self)?; - let value = Float::new(value.fract()).unwrap_not_nan(span)?; - - Ok(Self { - dimension: self.dimension, - value: conversion_factor.convert_to_base_unit(value), - } - .into()) - } - .auto_call_optional(context, span, arguments, expressions), - "floor" => |_context: &mut ExecutionContext, - span: &S, - unit: Value| - -> OperatorResult> { - let conversion_factor = Self::get_operation_conversion_factor(span, unit)?; - - let value = conversion_factor.convert_from_measurement_to_number(span, self)?; - let value = Float::new(value.floor()).unwrap_not_nan(span)?; - - Ok(Self { - dimension: self.dimension, - value: conversion_factor.convert_to_base_unit(value), - } - .into()) - } - .auto_call_optional(context, span, arguments, expressions), - "ceil" => |_context: &mut ExecutionContext, - span: &S, - unit: Value| - -> OperatorResult> { - let conversion_factor = Self::get_operation_conversion_factor(span, unit)?; - - let value = conversion_factor.convert_from_measurement_to_number(span, self)?; - let value = Float::new(value.ceil()).unwrap_not_nan(span)?; - - Ok(Self { - dimension: self.dimension, - value: conversion_factor.convert_to_base_unit(value), - } - .into()) - } - .auto_call_optional(context, span, arguments, expressions), - "max" => |_context: &mut ExecutionContext, - _span: &S, - other: Value| - -> OperatorResult> { - let other = self.unpack_for_addition_or_subtraction(span, &other)?; - Ok(Float::new(*self.value.max(other.value)) - .unwrap_not_nan(span)? - .into()) - } - .auto_call(context, span, arguments, expressions), - "min" => |_context: &mut ExecutionContext, - _span: &S, - other: Value| - -> OperatorResult> { - let other = self.unpack_for_addition_or_subtraction(span, &other)?; - Ok(Float::new(*self.value.min(other.value)) - .unwrap_not_nan(span)? - .into()) - } - .auto_call(context, span, arguments, expressions), - "mul_add" => |context: &mut ExecutionContext, - span: &S, - a: Value, - b: Value| - -> OperatorResult> { - let multiply_result = self.multiply(context.log, span, &b)?; - let add_result = multiply_result.addition(context.log, span, &a)?; - - Ok(add_result) - } - .auto_call(context, span, arguments, expressions), - "signum" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - Float::new(self.value.signum()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "acos" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_inverse_trig_compatible(span)?; - Float::new((self.value * consts::PI).acos()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "acosh" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_inverse_trig_compatible(span)?; - Float::new((self.value * consts::PI).acosh()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "asin" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_inverse_trig_compatible(span)?; - Float::new((self.value * consts::PI).asin()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "asinh" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_inverse_trig_compatible(span)?; - Float::new((self.value * consts::PI).asinh()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "atan" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_inverse_trig_compatible(span)?; - Float::new((self.value * consts::PI).atan()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "atanh" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_inverse_trig_compatible(span)?; - Float::new((self.value * consts::PI).atanh()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "cos" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_trig_compatible(span)?; - Float::new((self.value * consts::PI).cos()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "cosh" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_trig_compatible(span)?; - Float::new((self.value * consts::PI).cosh()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "sin" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_trig_compatible(span)?; - Float::new((self.value * consts::PI).sin()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "sin_cos" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - self.check_trig_compatible(span)?; - let (sin, cos) = (self.value * consts::PI).sin_cos(); - - Ok(Vector2 { - dimension: Dimension::zero(), - value: NVector::>::new(cos, sin), - } - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "sinh" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_trig_compatible(span)?; - Float::new((self.value * consts::PI).sinh()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "tan" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_trig_compatible(span)?; - Float::new((self.value * consts::PI).tan()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "tanh" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - self.check_trig_compatible(span)?; - Float::new((self.value * consts::PI).tanh()) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - // TODO we need functions to convert zero dimensional types to: Angles, Constitute Concentration, Information, Solid Angle, and Temperature. - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } - - fn export( - &self, - _log: &mut dyn RuntimeLog, - _span: &S, - ) -> OperatorResult { - Ok(SerializableValue::Scalar(self.clone())) - } -} - -impl NamedObject for Scalar { - fn static_type_name() -> &'static str { - "Scalar" - } -} - -impl Scalar { - fn multiply_by_scalar(&self, span: &S, rhs: &Self) -> OperatorResult { - let value = Float::new(*self.value * *rhs.value).unwrap_not_nan(span)?; - let dimension = self.dimension + rhs.dimension; - - Ok(Self { dimension, value }) - } - - fn divide_by_measurement(&self, span: &S, rhs: &Self) -> OperatorResult { - let value = Float::new(*self.value / *rhs.value).unwrap_not_nan(span)?; - let dimension = self.dimension - rhs.dimension; - - Ok(Self { dimension, value }) - } - - fn check_is_zero_dimension(&self, span: &S) -> OperatorResult { - if self.dimension.is_zero_dimension() { - Ok(()) - } else { - Err(Failure::ExpectedZeroDimension(span.clone())) - } - } - - fn check_inverse_trig_compatible(&self, span: &S) -> OperatorResult { - if self.dimension.is_zero_dimension() { - Ok(()) - } else { - Err(Failure::InverseTrigIncompatible(span.clone())) - } - } - - fn check_trig_compatible(&self, span: &S) -> OperatorResult { - if self.dimension.is_zero_dimension() && self.dimension.ratio_type_hint.is_angle() { - Ok(()) - } else { - Err(Failure::TrigIncompatible(span.clone())) - } - } - - fn is_number(&self) -> bool { - self.dimension.is_zero_dimension() && self.dimension.ratio_type_hint.0 == 0 - } - - fn unpack_for_addition_or_subtraction<'b, S: Span>( - &'b self, - span: &S, - rhs: &'b Value, - ) -> OperatorResult { - if let Value::Scalar(rhs) = rhs { - if self.dimension == rhs.dimension { - Ok(rhs) - } else { - Err(Failure::ExpectedGot( - span.clone(), - >::type_name(self), - >::type_name(rhs), - )) - } - } else { - Err(Failure::ExpectedGot( - span.clone(), - >::type_name(self), - rhs.type_name(), - )) - } - } - - // pub fn convert_from_number( - // span: &S, - // value: Number, - // ty: SString, - // ) -> OperatorResult> { - // if let Some(conversion_factor) = CONVERSION_FACTORS.get(ty.as_str()) { - // let value = conversion_factor.convert_to_base_unit(value); - // let dimension = conversion_factor.dimension; - - // Ok(Self { dimension, value }.into()) - // } else { - // Err(Failure::UnknownUnitType( - // span.clone(), - // ty.into_string().into(), - // )) - // } - // } - - pub fn convert_to_number(&self, span: &S, ty: &str) -> OperatorResult> { - if let Some(conversion_factor) = CONVERSION_FACTORS.get(ty) { - if self.dimension == conversion_factor.dimension { - Ok(conversion_factor.convert_from_base_unit(self.value).into()) - } else { - Err(Failure::DimensionalMissmatch( - span.clone(), - Object::::type_name(self), - ty.to_string().into(), - )) - } - } else { - Err(Failure::UnknownUnitType( - span.clone(), - ty.to_string().into(), - )) - } - } - - pub fn to_number(&self, span: &S) -> OperatorResult { - self.check_is_zero_dimension(span)?; - if self.dimension.ratio_type_hint.0 == 0 { - // It's just a number/ratio, we can use that. - Ok(self.value) - } else { - Err(Failure::ExpectedGot( - span.clone(), - "Number".into(), - Object::::type_name(self), - )) - } - } - - pub fn from_number(value: Float) -> Self { - let dimension = Dimension { - length: 0, - mass: 0, - time: 0, - electric_current: 0, - thermodynamic_temprature: 0, - amount_of_substance: 0, - luminous_intensity: 0, - ratio_type_hint: RatioTypeHint(0), - }; - - Self { dimension, value } - } - - pub fn from_parsed_raw(measurement: &parsing::Scalar) -> OperatorResult { - if let Some(conversion_factor) = CONVERSION_FACTORS.get(measurement.ty.as_str()) { - let value = unwrap_float(measurement.number.get_span().clone(), &measurement.number)?; - let value = conversion_factor.convert_to_base_unit(value); - - let dimension = conversion_factor.dimension; - - Ok(Self { dimension, value }) - } else { - Err(Failure::UnknownUnitType( - measurement.ty.clone(), - measurement.ty.to_string().into(), - )) - } - } - - pub fn from_parsed(measurement: &parsing::Scalar) -> OperatorResult> { - Self::from_parsed_raw(measurement).map(|measurement| measurement.into()) - } - - pub fn get_conversion_factor( - keyboard_friendly_abbreviation: &str, - ) -> Option<&'static ConversionFactor> { - CONVERSION_FACTORS.get(keyboard_friendly_abbreviation) - } - - fn get_operation_conversion_factor( - span: &S, - unit: Value, - ) -> OperatorResult { - let unit_specification = unit - .downcast_optional::(span)? - .map(|s| s.to_string(span)); - - Ok(match unit_specification { - Some(unit_specification) => { - let unit_specification = unit_specification?; - Self::get_conversion_factor(unit_specification.as_ref()).ok_or( - Failure::UnknownUnitType(span.clone(), unit_specification.into()), - )? - } - None => Self::get_conversion_factor("").unwrap(), - }) - } -} - -impl TryFrom> for Scalar -where - D: UomDimension + ?Sized, - D::L: ToInt, - D::M: ToInt, - D::T: ToInt, - D::I: ToInt, - D::Th: ToInt, - D::N: ToInt, - D::J: ToInt, - U: Units + ?Sized, -{ - type Error = common_data_types::FloatIsNan; - - fn try_from(value: Quantity) -> std::result::Result { - // Due to language limitations in Rust, we are incapable of getting ratio type hints. - // https://stackoverflow.com/questions/30274091/is-it-possible-to-check-if-an-object-implements-a-trait-at-runtime - - let length = D::L::INT; - let mass = D::M::INT; - let time = D::T::INT; - let electric_current = D::I::INT; - let thermodynamic_temprature = D::Th::INT; - let amount_of_substance = D::N::INT; - let luminous_intensity = D::J::INT; - - let dimension = Dimension { - length, - mass, - time, - electric_current, - thermodynamic_temprature, - amount_of_substance, - luminous_intensity, - ratio_type_hint: RatioTypeHint::default(), - }; - - let value = Float::new(value.value)?; - - Ok(Self { dimension, value }) - } -} - -impl TryInto> for Scalar -where - D: UomDimension + ?Sized, - D::L: ToInt, - D::M: ToInt, - D::T: ToInt, - D::I: ToInt, - D::Th: ToInt, - D::N: ToInt, - D::J: ToInt, - U: Units + ?Sized, -{ - type Error = (); - - fn try_into(self) -> std::result::Result, Self::Error> { - // Due to language limitations in Rust, we are incapable of getting ratio type hints. - // https://stackoverflow.com/questions/30274091/is-it-possible-to-check-if-an-object-implements-a-trait-at-runtime - - if D::L::INT == self.dimension.length - && D::M::INT == self.dimension.mass - && D::T::INT == self.dimension.time - && D::I::INT == self.dimension.electric_current - && D::Th::INT == self.dimension.thermodynamic_temprature - && D::N::INT == self.dimension.amount_of_substance - && D::J::INT == self.dimension.luminous_intensity - { - Ok(Quantity { - dimension: std::marker::PhantomData, - units: std::marker::PhantomData, - value: self.value.into_inner(), - }) - } else { - // We are not the same measurement type. - Err(()) - } - } -} - -impl FromStr for Scalar { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let (_leftover, measurement) = parsing::Scalar::parse(ImString::from(s)) - .map_err(|error| anyhow::anyhow!("Failed to parse measurement: {}", error))?; - let measurement = Self::from_parsed_raw(&measurement) - .map_err(|failure| anyhow::anyhow!("{}", failure))?; - - Ok(measurement) - } -} - -impl From for Scalar { - fn from(value: Float) -> Self { - Self { - dimension: Dimension { - length: 0, - mass: 0, - time: 0, - electric_current: 0, - thermodynamic_temprature: 0, - amount_of_substance: 0, - luminous_intensity: 0, - ratio_type_hint: RatioTypeHint::default(), - }, - value, - } - } -} - -impl Serialize for Scalar { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - if self.is_number() { - serializer.serialize_f64(self.value.into_inner()) - } else { - let unit_name = BASE_UNITS - .get(&self.dimension) - .cloned() - .unwrap_or_else(|| format_dimension(&self.dimension)); - - serializer.serialize_str(&format!("{} {}", self.value, unit_name)) - } - } -} - -impl<'de> Deserialize<'de> for Scalar { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_any(ScalarVisitor) - } -} - -struct ScalarVisitor; - -impl<'de> Visitor<'de> for ScalarVisitor { - type Value = Scalar; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a measurement or number") - } - - fn visit_i64(self, v: i64) -> Result - where - E: de::Error, - { - self.visit_f64(v as f64) - } - - fn visit_u64(self, v: u64) -> Result - where - E: de::Error, - { - self.visit_f64(v as f64) - } - - fn visit_f64(self, v: f64) -> Result - where - E: de::Error, - { - let v = Float::new(v).map_err(|e| E::custom(e))?; - Ok(Scalar::from_number(v)) - } - - fn visit_str(self, v: &str) -> Result - where - E: de::Error, - { - Scalar::from_str(v).map_err(|e| E::custom(e)) - } -} - -#[cfg(test)] -mod test { - use uom::si::f64 as si; - - use crate::script::{ - execution::{expressions::run_expression, ExecutionContext}, - logging::StandardLog, - Runtime, - }; - - use super::*; - - use uom::si::length::meter; - - #[test] - fn addition() { - let mut log = StandardLog; - - let a = si::Length::new::(3.0); - let b = si::Length::new::(2.0); - let value_a: Value<&str> = Scalar::try_from(a).unwrap().into(); - let value_b: Value<&str> = Scalar::try_from(b).unwrap().into(); - assert_eq!( - value_a.addition(&mut log, &"span", &value_b), - Ok(Scalar::try_from(a + b).unwrap().into()) - ); - } - - #[test] - fn subtraction() { - let mut log = StandardLog; - - let a = si::Length::new::(3.0); - let b = si::Length::new::(2.0); - let value_a: Value<&str> = Scalar::try_from(a).unwrap().into(); - let value_b: Value<&str> = Scalar::try_from(b).unwrap().into(); - assert_eq!( - value_a.subtraction(&mut log, &"span", &value_b), - Ok(Scalar::try_from(a - b).unwrap().into()) - ); - } - - #[test] - fn multiplication() { - let mut log = StandardLog; - - let a = si::Length::new::(3.0); - let b = si::Length::new::(2.0); - let c = a * b; - let value_a: Value<&str> = Scalar::try_from(a).unwrap().into(); - let value_b: Value<&str> = Scalar::try_from(b).unwrap().into(); - let value_c: Value<&str> = Scalar::try_from(c).unwrap().into(); - assert_eq!( - value_a.clone().multiply(&mut log, &"span", &value_b), - Ok(Scalar::try_from(a * b).unwrap().into()) - ); - assert_eq!( - value_a.multiply(&mut log, &"span", &value_c), - Ok(Scalar::try_from(a * c).unwrap().into()) - ); - } - - #[test] - fn division() { - let mut log = StandardLog; - - let a = si::Length::new::(3.0); - let b = si::Length::new::(2.0); - let value_a: Value<&str> = Scalar::try_from(a).unwrap().into(); - let value_b: Value<&str> = Scalar::try_from(b).unwrap().into(); - - assert_eq!( - value_a.clone().divide(&mut log, &"span", &value_b), - Ok(Scalar::try_from(a / b).unwrap().into()) - ); - - let c: si::Area = a * b; - let value_c: Value<&str> = Scalar::try_from(c).unwrap().into(); - assert_eq!( - value_c.divide(&mut log, &"span", &value_a), - Ok(Scalar::try_from(c / a).unwrap().into()) - ); - } - - #[test] - fn comparisions() { - let mut log = StandardLog; - - let a = si::Length::new::(3.0); - let b = si::Length::new::(2.0); - let value_a: Value<&str> = Scalar::try_from(a).unwrap().into(); - let value_b: Value<&str> = Scalar::try_from(b).unwrap().into(); - assert_eq!( - value_a.cmp(&mut log, &"span", &value_b), - Ok(Ordering::Greater) - ); - assert_eq!( - value_a.cmp(&mut log, &"span", &value_a), - Ok(Ordering::Equal) - ); - assert_eq!(value_b.cmp(&mut log, &"span", &value_a), Ok(Ordering::Less)); - } - - #[test] - fn conversions() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("1m + 100cm == 2m").unwrap().1), - Ok(true.into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("2m * 2m == 4m^2").unwrap().1), - Ok(true.into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("(2m).to_number(\"cm\") == 200") - .unwrap() - .1 - ), - { Ok(true.into()) } - ); - }); - } -} diff --git a/src/script/execution/types/math/transform.rs b/src/script/execution/types/math/transform.rs deleted file mode 100644 index 94e3de5..0000000 --- a/src/script/execution/types/math/transform.rs +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::borrow::Cow; - -use common_data_types::{Dimension, RawFloat}; -use enum_downcast::AsVariant; -use nalgebra::{ - allocator::Allocator, Const, DefaultAllocator, Dim, DimName, DimNameDiff, DimNameSub, OPoint, - U1, -}; - -use crate::script::{ - execution::{ - types::{ - function::{AutoCall, IntoBuiltinFunction}, - NamedObject, Object, OperatorResult, Value, - }, - ExecutionContext, Failure, - }, - logging::RuntimeLog, - parsing::{Expression, VariableType}, - Span, -}; - -use super::{ - get_dimension_name, - vector::{LengthVector, NVector, Vector}, - Angle, Vector2, -}; - -pub type Transform = nalgebra::OMatrix; - -pub type Transform2D = Transform>; -pub type Transform3D = Transform>; - -pub fn register_globals(context: &mut ExecutionContext) { - context.stack.new_variable_str( - "new_transform2D", - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Transform2D::identity().into()) - } - .into_builtin_function() - .into(), - ); - context.stack.new_variable_str( - "new_transform3D", - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Transform3D::identity().into()) - } - .into_builtin_function() - .into(), - ); -} - -impl Object for Transform -where - D: DimName + DimNameSub, - S: Span, - DefaultAllocator: Allocator - + Allocator>::Output, >::Output> - + Allocator - + Allocator>::Output>, - Self: NamedObject - + TransformPoint<>::Output> - + Rotate - + CustomMultiply<>::Output>, - Vector: Into>, - Vector<>::Output>: NamedObject + Into>, - Value: From - + AsVariant>::Output>> - + TryInto>::Output>> - + TryInto>::Output>>, -{ - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(if let VariableType::Transform(dimension) = ty { - *dimension as usize == D::USIZE - } else { - false - }) - } - - fn type_name(&self) -> Cow<'static, str> { - Self::static_type_name().into() - } - - fn unary_minus(&self, _log: &mut dyn RuntimeLog, _span: &S) -> OperatorResult> { - Ok((-self.clone()).into()) - } - fn addition( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let translation = - Vector::>::from_value_ref(span, rhs, Dimension::length())?; - let translation = &translation.value; - let translation = Self::new_translation(translation); - - Ok((self * translation).into()) - } - fn subtraction( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let translation = - Vector::>::from_value_ref(span, rhs, Dimension::length())?; - - let translation = &translation.value; - let translation = Self::new_translation(&-translation); - - Ok((self * translation).into()) - } - fn multiply( - &self, - log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - self.custom_multiply(log, span, rhs) - } - fn method_call( - &self, - context: &mut ExecutionContext, - span: &S, - attribute: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - match attribute.as_str() { - "apply_to_vector" => |_context: &mut ExecutionContext, - _span: &S, - vector: LengthVector>| - -> OperatorResult> { - let value = self.transform_point(&vector.value); - Ok(Vector::> { - dimension: Dimension::length(), - value, - } - .into()) - } - .auto_call(context, span, arguments, expressions), - "rotate" => self.rotate(context, span, arguments, expressions), - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } -} - -impl NamedObject for Transform2D { - fn static_type_name() -> &'static str { - "Transform2D" - } -} - -impl NamedObject for Transform3D { - fn static_type_name() -> &'static str { - "Transform3D" - } -} - -trait Rotate { - fn rotate( - &self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult>; -} - -impl Rotate for Transform2D { - fn rotate( - &self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - |_context: &mut ExecutionContext, - _span: &S, - angle: Angle| - -> OperatorResult> { - let angle = angle.value; - let angle = Self::new_rotation(*angle); - - Ok((self * angle).into()) - } - .auto_call(context, span, arguments, expressions) - } -} - -impl Rotate for Transform3D { - fn rotate( - &self, - context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - |_context: &mut ExecutionContext, - span: &S, - axis: Vector>, - angle: Angle| - -> OperatorResult> { - axis.check_dimension(span, Dimension::zero())?; - - let angle = angle.value; - let rotation = Self::new_rotation(*angle * axis.value); - - Ok((self * rotation).into()) - } - .auto_call(context, span, arguments, expressions) - } -} - -trait CustomMultiply -where - D: Dim, -{ - fn custom_multiply( - &self, - log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult>; -} - -impl CustomMultiply> for Transform2D { - fn custom_multiply( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let scale = rhs.downcast_ref::(span)?; - if scale.dimension.is_zero_dimension() { - let scale = Self::new_nonuniform_scaling(&scale.value); - Ok((self * scale).into()) - } else { - Err(Failure::ExpectedGot( - span.clone(), - "Number".into(), - get_dimension_name(&scale.dimension), - )) - } - } -} - -impl CustomMultiply> for Transform3D { - fn custom_multiply( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - match rhs { - Value::Vector3(scale) => { - if scale.dimension.is_zero_dimension() { - let scale = Self::new_nonuniform_scaling(&scale.value); - Ok((self * scale).into()) - } else { - Err(Failure::ExpectedGot( - span.clone(), - "Number".into(), - get_dimension_name(&scale.dimension), - )) - } - } - Value::Quaternion(quat) => Ok((self * quat.to_homogeneous()).into()), - _ => Err(Failure::ExpectedGot( - span.clone(), - "Scalar Vector3 or Quaternion".into(), - Object::type_name(rhs), - )), - } - } -} - -trait TransformPoint -where - DefaultAllocator: Allocator, -{ - fn transform_point(&self, pt: &NVector) -> NVector; -} - -impl TransformPoint> for Transform2D -where - DefaultAllocator: Allocator> + Allocator, Const<2>>, -{ - fn transform_point(&self, pt: &NVector>) -> NVector> { - let point = OPoint { coords: *pt }; - let point = self.transform_point(&point); - point.coords - } -} - -impl TransformPoint> for Transform3D -where - DefaultAllocator: Allocator> + Allocator, Const<3>>, -{ - fn transform_point(&self, pt: &NVector>) -> NVector> { - let point = OPoint { coords: *pt }; - let point = self.transform_point(&point); - point.coords - } -} - -#[cfg(test)] -mod test { - use crate::script::{ - execution::{expressions::run_expression, types::math::vector::NVector}, - Runtime, - }; - - use super::*; - - #[test] - fn translate() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression( - context, - &Expression::parse( - "(new_transform2D() + vec2(1m, 2m)).apply_to_vector(vec2(0m, 0m))" - ) - .unwrap() - .1 - ), - Ok(Vector { - dimension: Dimension::length(), - value: NVector::>::new(1.0, 2.0), - } - .into()) - ); - - assert_eq!( - run_expression( - context, - &Expression::parse( - "(new_transform2D() - vec2(1m, 2m)).apply_to_vector(vec2(0m, 0m))" - ) - .unwrap() - .1 - ), - Ok(Vector { - dimension: Dimension::length(), - value: NVector::>::new(-1.0, -2.0), - } - .into()) - ); - - assert_eq!( - run_expression( - context, - &Expression::parse( - "(new_transform2D() + vec2(1m, 2m)).apply_to_vector(vec2(1m, 2m))" - ) - .unwrap() - .1 - ), - Ok(Vector { - dimension: Dimension::length(), - value: NVector::>::new(2.0, 4.0), - } - .into()) - ); - - assert_eq!( - run_expression( - context, - &Expression::parse( - "(new_transform2D() - vec2(1m, 2m)).apply_to_vector(vec2(2m, 3m))" - ) - .unwrap() - .1 - ), - Ok(Vector { - dimension: Dimension::length(), - value: NVector::>::new(1.0, 1.0), - } - .into()) - ); - }); - } - - #[test] - fn scale() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression( - context, - &Expression::parse( - "(new_transform2D() * vec2(1, 2)).apply_to_vector(vec2(2m, 2m))" - ) - .unwrap() - .1 - ), - Ok(Vector { - dimension: Dimension::length(), - value: NVector::>::new(2.0, 4.0), - } - .into()) - ); - }); - } - - #[test] - fn rotate_2d() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let result = run_expression( - context, - &Expression::parse( - "(new_transform2D().rotate(90deg)).apply_to_vector(vec2(1m, 0m))", - ) - .unwrap() - .1, - ) - .unwrap(); - let vector = result.downcast::>>(&"").unwrap(); - - assert!((vector.value - NVector::>::new(0.0, 1.0)).norm() < 0.01); - }); - } - - #[test] - fn rotate_3d() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let result = run_expression( - context, - & Expression::parse( - "(new_transform3D().rotate(vec3(0, 0, 1), 90deg)).apply_to_vector(vec3(1m, 0m, 0m))", - ) - .unwrap() - .1, - ) - .unwrap(); - let vector = result.downcast::>>(&"").unwrap(); - - assert!((vector.value - NVector::>::new(0.0, 1.0, 0.0)).norm() < 0.01); - }); - } -} diff --git a/src/script/execution/types/math/vector.rs b/src/script/execution/types/math/vector.rs deleted file mode 100644 index 4bb7fd4..0000000 --- a/src/script/execution/types/math/vector.rs +++ /dev/null @@ -1,1127 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::borrow::Cow; - -use arrayvec::ArrayVec; -use common_data_types::{consts, Dimension, Float, RawFloat}; -use enum_downcast::{AsVariant, EnumDowncast, IntoVariant}; -use fj_math::Scalar as FornjotScalar; -use nalgebra::{ - allocator::Allocator, base::dimension::Const, DefaultAllocator, DimName, ToTypenum, -}; - -use crate::script::{ - execution::{ - types::{ - function::{AutoCall, BuiltinFunctionRef}, - number::UnwrapNotNan, - BuiltinFunction, NamedObject, Object, OperatorResult, TypedObject, Value, - }, - ExecutionContext, Failure, - }, - logging::RuntimeLog, - parsing::{Expression, VariableType}, - Span, -}; - -use super::{get_dimension_name, CheckNan, ConvertUnit, Number, Scalar}; - -pub(super) type NVector = nalgebra::OVector; - -pub type Vector2 = Vector>; -pub type Vector3 = Vector>; -pub type Vector4 = Vector>; - -pub type LengthVector2 = LengthVector>; -pub type LengthVector3 = LengthVector>; - -macro_rules! define_fixed_dimension_vector { - ($name:ident, $dimension_name:literal, $dimension:expr) => { - #[derive(Debug, Clone, PartialEq)] - pub struct $name - where - DefaultAllocator: Allocator, - { - pub value: NVector, - } - - impl From<$name> for Vector - where - DefaultAllocator: Allocator, - { - fn from(value: $name) -> Vector { - Vector { - dimension: $dimension, - value: value.value, - } - } - } - - impl From<$name> for Value - where - DefaultAllocator: Allocator, - Value: From>, - { - fn from(value: $name) -> Value { - Value::from(Vector::from(value)) - } - } - - impl TryFrom> for $name - where - DefaultAllocator: Allocator, - Value: IntoVariant>, - { - type Error = (); - - fn try_from(value: Value) -> Result { - let vector: Vector = value.enum_downcast().map_err(|_| ())?; - Self::try_from(vector).map_err(|_| ()) - } - } - - impl TryFrom> for $name - where - DefaultAllocator: Allocator, - { - type Error = Vector; - - fn try_from(value: Vector) -> Result { - if value.dimension == $dimension { - Ok(Self { value: value.value }) - } else { - Err(value) - } - } - } - - impl<'a, D: DimName> TryFrom<&'a Vector> for $name - where - DefaultAllocator: Allocator, - { - type Error = &'a Vector; - - fn try_from(value: &'a Vector) -> Result { - if value.dimension == $dimension { - Ok(Self { - value: value.value.clone(), - }) - } else { - Err(value) - } - } - } - - impl TypedObject for $name - where - DefaultAllocator: Allocator, - { - fn get_type() -> VariableType { - VariableType::Vector(D::USIZE as u8, S::from_str($dimension_name)) - } - } - - impl NamedObject for $name - where - Vector: NamedObject, - DefaultAllocator: Allocator, - { - fn static_type_name() -> &'static str { - Vector::::static_type_name() - } - } - }; -} - -define_fixed_dimension_vector!(LengthVector, "Length", Dimension::length()); - -impl LengthVector -where - DefaultAllocator: Allocator, -{ - fn as_fornjot_scale_array( - &self, - context: &ExecutionContext, - ) -> [Float; FD] { - let mut number_array = context - .global_resources - .fornjot_unit_conversion_factor - .convert_from_vector_to_iter_without_dimension_check(&self.value); - - std::array::from_fn(|_| number_array.next().unwrap()) - } - - pub fn as_fornjot_scale_nalgebra_vector( - &self, - context: &ExecutionContext, - ) -> NVector { - let components: [_; FD] = self.as_fornjot_scale_array(context).map(|c| c.into_inner()); - NVector::from_row_slice(&components) - } - - pub fn as_fornjot_vector( - &self, - context: &ExecutionContext, - ) -> fj_math::Vector - where - Const: ToTypenum, - { - let components = self - .as_fornjot_scale_array(context) - .map(|c| FornjotScalar::from_f64(c.into_inner())); - - fj_math::Vector { components } - } - - pub fn as_fornjot_point( - &self, - context: &ExecutionContext, - ) -> fj_math::Point - where - Const: ToTypenum, - { - fj_math::Point { - coords: self.as_fornjot_vector(context), - } - } -} - -pub fn register_globals(context: &mut ExecutionContext) { - fn build_constructor() -> BuiltinFunctionRef - where - DefaultAllocator: Allocator, - Value: From>, - { - let closure: Box> = Box::new( - |context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression]| { - let splat_arguments = arguments.to_vec(); - - match Vector::::splat.auto_call(context, span, splat_arguments, expressions) { - Ok(vector) => Ok(vector), - Err(Failure::FunctionCall(_call_failure)) => { - Vector::::from_components(context, span, arguments, expressions) - } - Err(failure) => Err(failure), - } - }, - ); - BuiltinFunctionRef::from(closure) - } - - context - .stack - .new_variable_str("vec2", build_constructor::>().into()); - context - .stack - .new_variable_str("vec3", build_constructor::>().into()); - context - .stack - .new_variable_str("vec4", build_constructor::>().into()); -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Vector -where - DefaultAllocator: Allocator, -{ - pub(super) dimension: Dimension, - pub(super) value: NVector, -} - -impl Object for Vector -where - S: Span, - DefaultAllocator: Allocator, - Vector: NamedObject + Into>, - Value: AsVariant> + TryInto>, - NVector: Copy, -{ - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(if let VariableType::Vector(dimension, name) = ty { - *dimension as usize == D::USIZE - && name.as_str() == Object::::type_name(self).as_ref() - } else { - false - }) - } - - fn type_name(&self) -> Cow<'static, str> { - get_dimension_name(&self.dimension) - } - - // TODO add formatting support. - - fn index( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - index: Value, - ) -> OperatorResult> { - let index = index.downcast::(span)?; - let index = Number::try_from(index.clone()).map_err(|_| { - Failure::ExpectedGot( - span.clone(), - Number::static_type_name().into(), - >::type_name(&index), - ) - })?; - let index = index.to_index(); - - if let Some(component) = self.value.get(index as usize).copied() { - Ok(Float::new(component).unwrap().into()) - } else { - Err(Failure::IndexOutOfRange(span.clone(), index)) - } - } - - fn attribute( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - attribute: &S, - ) -> OperatorResult> { - let swizzle = attribute.as_str(); - let len = swizzle.len(); - let mut chars = swizzle.chars(); - - match len { - 1 => { - let value = self.index_by_character(span, chars.next().unwrap())?; - Ok(Scalar { - dimension: self.dimension, - value, - } - .into()) - } - 2 => { - let x = self - .index_by_character(span, chars.next().unwrap())? - .into_inner(); - let y = self - .index_by_character(span, chars.next().unwrap())? - .into_inner(); - - Ok(Vector::> { - dimension: self.dimension, - value: NVector::>::new(x, y), - } - .into()) - } - 3 => { - let x = self - .index_by_character(span, chars.next().unwrap())? - .into_inner(); - let y = self - .index_by_character(span, chars.next().unwrap())? - .into_inner(); - let z = self - .index_by_character(span, chars.next().unwrap())? - .into_inner(); - - Ok(Vector::> { - dimension: self.dimension, - value: NVector::>::new(x, y, z), - } - .into()) - } - 4 => { - let x = self - .index_by_character(span, chars.next().unwrap())? - .into_inner(); - let y = self - .index_by_character(span, chars.next().unwrap())? - .into_inner(); - let z = self - .index_by_character(span, chars.next().unwrap())? - .into_inner(); - let w = self - .index_by_character(span, chars.next().unwrap())? - .into_inner(); - - Ok(Vector::> { - dimension: self.dimension, - value: NVector::>::new(x, y, z, w), - } - .into()) - } - _ => Err(Failure::SwizzleTooLong(span.clone(), len)), - } - } - - fn addition( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = self.unpack_for_addition_or_subtraction(span, rhs)?; - - let value = self.value + rhs.value; - value.check_nan(span)?; - - Ok(Self { - dimension: self.dimension, - value, - } - .into()) - } - fn subtraction( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let rhs = self.unpack_for_addition_or_subtraction(span, rhs)?; - - let value = self.value - rhs.value; - value.check_nan(span)?; - - Ok(Self { - dimension: self.dimension, - value, - } - .into()) - } - fn multiply( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let scalar = rhs.downcast_ref::(span)?; - - let dimension = self.dimension + scalar.dimension; - let value = self.value * scalar.value.into_inner(); - value.check_nan(span)?; - - Ok(Self { dimension, value }.into()) - } - fn divide( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let scalar = rhs.downcast_ref::(span)?; - - let dimension = self.dimension - scalar.dimension; - let value = self.value / scalar.value.into_inner(); - value.check_nan(span)?; - - Ok(Self { dimension, value }.into()) - } - fn method_call( - &self, - context: &mut ExecutionContext, - span: &S, - attribute: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - match attribute.as_str() { - "dot" => |_context: &mut ExecutionContext, - span: &S, - rhs: Vector| - -> OperatorResult> { - self.validate_dimensions_match(span, &rhs)?; - - Ok(Scalar { - dimension: Dimension::zero(), - value: Float::new(self.value.dot(&rhs.value)).unwrap_not_nan(span)?, - } - .into()) - } - .auto_call(context, span, arguments, expressions), - "cross" => |_context: &mut ExecutionContext, - span: &S, - rhs: Vector| - -> OperatorResult> { - self.validate_dimensions_match(span, &rhs)?; - let value = self.value.cross(&rhs.value); - value.check_nan(span)?; - - Ok(Self { - dimension: self.dimension, - value, - } - .into()) - } - .auto_call(context, span, arguments, expressions), - "abs" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Self { - dimension: self.dimension, - value: self.value.abs(), - } - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "add_scalar" => |_context: &mut ExecutionContext, - span: &S, - scalar: Scalar| - -> OperatorResult> { - if self.dimension == scalar.dimension { - Ok(Self { - dimension: self.dimension, - value: self.value.add_scalar(*scalar.value), - } - .into()) - } else { - Err(Failure::ExpectedGot( - span.clone(), - >::type_name(self), - Object::::type_name(&scalar), - )) - } - } - .auto_call(context, span, arguments, expressions), - "sub_scalar" => |_context: &mut ExecutionContext, - span: &S, - scalar: Scalar| - -> OperatorResult> { - if self.dimension == scalar.dimension { - Ok(Self { - dimension: self.dimension, - value: self.value.add_scalar(-*scalar.value), - } - .into()) - } else { - Err(Failure::ExpectedGot( - span.clone(), - >::type_name(self), - Object::::type_name(&scalar), - )) - } - } - .auto_call(context, span, arguments, expressions), - "angle" => |_context: &mut ExecutionContext, - span: &S, - rhs: Vector| - -> OperatorResult> { - // In Radians - let value = self.value.angle(&rhs.value) / consts::PI; - let value = Float::new(value).unwrap_not_nan(span)?; - let dimension = Dimension::angle(); - - Ok(Scalar { dimension, value }.into()) - } - .auto_call(context, span, arguments, expressions), - "norm" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - Ok(Scalar { - dimension: self.dimension, - value: Float::new(self.value.norm()).unwrap_not_nan(span)?, - } - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "norm_squared" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - Ok(Scalar { - dimension: self.dimension, - value: Float::new(self.value.norm_squared()).unwrap_not_nan(span)?, - } - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "normalize" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - let value = self.value.normalize(); - value.check_nan(span)?; - - Ok(Self { - dimension: Dimension::zero(), - value, - } - .into()) - } - .auto_call(context, span, arguments, expressions) - } - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } -} - -impl NamedObject for Vector> { - fn static_type_name() -> &'static str { - "Vector2" - } -} - -impl NamedObject for Vector> { - fn static_type_name() -> &'static str { - "Vector3" - } -} - -impl NamedObject for Vector> { - fn static_type_name() -> &'static str { - "Vector4" - } -} - -impl Vector -where - DefaultAllocator: Allocator, -{ - pub(super) fn check_dimension( - &self, - span: &S, - dimension: Dimension, - ) -> OperatorResult { - if self.dimension == dimension { - Ok(()) - } else { - Err(Failure::ExpectedGot( - span.clone(), - get_dimension_name(&dimension), - get_dimension_name(&self.dimension), - )) - } - } - - pub(super) fn from_value_ref<'b, S: Span>( - span: &S, - value: &'b Value, - dimension: Dimension, - ) -> OperatorResult - where - Self: NamedObject, - Value: AsVariant, - { - let value = value.downcast_ref::(span)?; - value.check_dimension(span, dimension)?; - - Ok(value) - } - fn validate_dimensions_match(&self, span: &S, rhs: &Self) -> OperatorResult - where - Vector: NamedObject + Object, - { - if self.dimension == rhs.dimension { - Ok(()) - } else { - Err(Failure::ExpectedGot( - span.clone(), - >::type_name(self), - >::type_name(rhs), - )) - } - } - - fn unpack_for_addition_or_subtraction<'b, S: Span>( - &'b self, - span: &S, - rhs: &'b Value, - ) -> OperatorResult - where - Vector: NamedObject + Object, - Value: AsVariant>, - { - let rhs = rhs.downcast_ref::>(span)?; - self.validate_dimensions_match(span, rhs)?; - - Ok(rhs) - } - - fn index_by_character(&self, span: &S, c: char) -> OperatorResult { - let index = match c { - 'x' | 'r' => 0, - 'y' | 'g' => 1, - 'z' | 'b' => 2, - 'w' | 'a' => 3, - _ => return Err(Failure::CharacterNotInVector(span.clone(), c)), - }; - - if let Some(component) = self.value.get(index).copied() { - Ok(Float::new(component).unwrap()) - } else { - Err(Failure::CharacterNotInVector(span.clone(), c)) - } - } - - fn splat( - _context: &mut ExecutionContext, - _span: &S, - splat_value: Scalar, - ) -> OperatorResult> - where - Value: From, - { - let dimension = splat_value.dimension; - let value = NVector::repeat(*splat_value.value); - - Ok(Vector { dimension, value }.into()) - } - - fn from_components( - _context: &mut ExecutionContext, - span: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> - where - Value: From, - { - let mut expression_iter = expressions.iter(); - let mut argument_iter = arguments.into_iter(); - - let mut components = ArrayVec::::new(); - - let first_argument = argument_iter - .next() - .ok_or(Failure::MissingArguments(span.clone()))?; - let first_span = expression_iter.next().unwrap(); - - let first_argument: Scalar = first_argument.downcast(first_span.get_span())?; - - let dimension = first_argument.dimension; - components.push(*first_argument.value); - - for _ in 1..D::USIZE { - if let Some(value) = argument_iter.next() { - let value: Scalar = value.downcast(expression_iter.next().unwrap().get_span())?; - - if value.dimension == dimension { - components.push(*value.value); - } else { - return Err(Failure::ExpectedGot( - span.clone(), - get_dimension_name(&dimension), - get_dimension_name(&value.dimension), - )); - } - } else { - return Err(Failure::MissingArguments(span.clone())); - } - } - - if let Some(extra_expression) = expression_iter.next() { - Err(Failure::TooManyArguments( - extra_expression.get_span().clone(), - )) - } else { - let components = components.into_iter(); - Ok(Self { - dimension, - value: NVector::::from_iterator(components), - } - .into()) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use common_data_types::{Dimension, RatioTypeHint, RawFloat}; - - use crate::script::{ - execution::{expressions::run_expression, ExecutionContext}, - parsing::Expression, - Runtime, - }; - - #[test] - fn construct_vec2() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("vec2(1)").unwrap().1), - Ok(Vector { - dimension: Dimension::zero(), - value: NVector::>::new(1.0, 1.0), - } - .into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec2(1, 2)").unwrap().1), - Ok(Vector { - dimension: Dimension::zero(), - value: NVector::>::new(1.0, 2.0), - } - .into()) - ); - }); - } - - #[test] - fn construct_vec3() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("vec3(1)").unwrap().1), - Ok(Vector { - dimension: Dimension { - length: 0, - mass: 0, - time: 0, - electric_current: 0, - thermodynamic_temprature: 0, - amount_of_substance: 0, - luminous_intensity: 0, - ratio_type_hint: RatioTypeHint(0), - }, - value: NVector::>::new(1.0, 1.0, 1.0), - } - .into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec3(1, 2, 3)").unwrap().1), - Ok(Vector { - dimension: Dimension { - length: 0, - mass: 0, - time: 0, - electric_current: 0, - thermodynamic_temprature: 0, - amount_of_substance: 0, - luminous_intensity: 0, - ratio_type_hint: RatioTypeHint(0), - }, - value: NVector::>::new(1.0, 2.0, 3.0), - } - .into()) - ); - }); - } - - #[test] - fn construct_vec4() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("vec4(1)").unwrap().1), - Ok(Vector { - dimension: Dimension { - length: 0, - mass: 0, - time: 0, - electric_current: 0, - thermodynamic_temprature: 0, - amount_of_substance: 0, - luminous_intensity: 0, - ratio_type_hint: RatioTypeHint(0), - }, - value: NVector::>::new(1.0, 1.0, 1.0, 1.0), - } - .into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec4(1, 2, 3, 4)").unwrap().1), - Ok(Vector { - dimension: Dimension { - length: 0, - mass: 0, - time: 0, - electric_current: 0, - thermodynamic_temprature: 0, - amount_of_substance: 0, - luminous_intensity: 0, - ratio_type_hint: RatioTypeHint(0), - }, - value: NVector::>::new(1.0, 2.0, 3.0, 4.0), - } - .into()) - ); - }); - } - - #[test] - fn to_fornjot_vector() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("vec2(1mm, 2mm)").unwrap().1) - .unwrap() - .downcast::>>(&"") - .map(|v| -> LengthVector2 { v.try_into().unwrap() }) - .unwrap() - .as_fornjot_vector(context), - fj_math::Vector::<2> { - components: [1.0.into(), 2.0.into()] - } - ); - }); - } - #[test] - fn to_fornjot_point() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("vec2(1mm, 2mm)").unwrap().1) - .unwrap() - .downcast::>>(&"") - .map(|v| -> LengthVector2 { v.try_into().unwrap() }) - .unwrap() - .as_fornjot_point(context), - fj_math::Point::<2> { - coords: fj_math::Vector::<2> { - components: [1.0.into(), 2.0.into()] - } - } - ); - }); - } - - #[test] - fn index() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("vec2(1, 2)[0]").unwrap().1), - Ok(Scalar::from_number(Float::new(1.0).unwrap()).into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec2(1, 2)[1]").unwrap().1), - Ok(Scalar::from_number(Float::new(2.0).unwrap()).into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec2(1, 2)[2]").unwrap().1), - Err(Failure::IndexOutOfRange("vec2", 2)) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec2(1, 2)[-1]").unwrap().1), - Err(Failure::IndexOutOfRange("vec2", -1)) - ); - }); - } - - #[test] - fn swizzle() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("vec4(1, 2, 3, 4).x").unwrap().1), - Ok(Float::new(1.0).unwrap().into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec4(1, 2, 3, 4).y").unwrap().1), - Ok(Float::new(2.0).unwrap().into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec4(1, 2, 3, 4).z").unwrap().1), - Ok(Float::new(3.0).unwrap().into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec4(1, 2, 3, 4).w").unwrap().1), - Ok(Float::new(4.0).unwrap().into()) - ); - - assert_eq!( - run_expression(context, &Expression::parse("vec4(1, 2, 3, 4).r").unwrap().1), - Ok(Float::new(1.0).unwrap().into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec4(1, 2, 3, 4).g").unwrap().1), - Ok(Float::new(2.0).unwrap().into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec4(1, 2, 3, 4).b").unwrap().1), - Ok(Float::new(3.0).unwrap().into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec4(1, 2, 3, 4).a").unwrap().1), - Ok(Float::new(4.0).unwrap().into()) - ); - - assert_eq!( - run_expression( - context, - &Expression::parse("vec4(1, 2, 3, 4).xy").unwrap().1 - ), - Ok(Vector { - dimension: Dimension { - length: 0, - mass: 0, - time: 0, - electric_current: 0, - thermodynamic_temprature: 0, - amount_of_substance: 0, - luminous_intensity: 0, - ratio_type_hint: RatioTypeHint(0), - }, - value: NVector::>::new(1.0, 2.0), - } - .into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("vec4(1, 2, 3, 4).xyz").unwrap().1 - ), - Ok(Vector { - dimension: Dimension { - length: 0, - mass: 0, - time: 0, - electric_current: 0, - thermodynamic_temprature: 0, - amount_of_substance: 0, - luminous_intensity: 0, - ratio_type_hint: RatioTypeHint(0), - }, - value: NVector::>::new(1.0, 2.0, 3.0,), - } - .into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("vec4(1, 2, 3, 4).xyzw").unwrap().1 - ), - Ok(Vector { - dimension: Dimension { - length: 0, - mass: 0, - time: 0, - electric_current: 0, - thermodynamic_temprature: 0, - amount_of_substance: 0, - luminous_intensity: 0, - ratio_type_hint: RatioTypeHint(0), - }, - value: NVector::>::new(1.0, 2.0, 3.0, 4.0,), - } - .into()) - ); - }); - } - - #[test] - fn addition() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression( - context, - &Expression::parse("vec2(1, 2) + vec2(3, 4)").unwrap().1 - ), - Ok(Vector { - dimension: Dimension::zero(), - value: NVector::>::new(4.0, 6.0), - } - .into()) - ); - }); - } - - #[test] - fn subtraction() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression( - context, - &Expression::parse("vec2(1, 2) - vec2(3, 4)").unwrap().1 - ), - Ok(Vector { - dimension: Dimension::zero(), - value: NVector::>::new(-2.0, -2.0), - } - .into()) - ); - }); - } - - #[test] - fn scalar_multiplication() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("vec2(1, 2) * 4").unwrap().1), - Ok(Vector { - dimension: Dimension::zero(), - value: NVector::>::new(4.0, 8.0), - } - .into()) - ); - }); - } - - #[test] - fn scalar_division() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression(context, &Expression::parse("vec2(1, 2) / 2").unwrap().1), - Ok(Vector { - dimension: Dimension::zero(), - value: NVector::>::new(0.5, 1.0), - } - .into()) - ); - assert_eq!( - run_expression(context, &Expression::parse("vec2(1, 2) / 0").unwrap().1), - Ok(Vector { - dimension: Dimension::zero(), - value: NVector::>::new(RawFloat::INFINITY, RawFloat::INFINITY,), - } - .into()) - ); - }); - } - - #[test] - fn nan_check() { - assert_eq!(NVector::>::new(1.0, 1.0).check_nan(&"nan"), Ok(())); - assert_eq!( - NVector::>::new(RawFloat::NAN, 1.0).check_nan(&"nan"), - Err(Failure::ResultIsNan("nan")), - ); - assert_eq!( - NVector::>::new(1.0, RawFloat::NAN).check_nan(&"nan"), - Err(Failure::ResultIsNan("nan")), - ); - - assert_eq!( - NVector::>::new(1.0, 1.0, 1.0).check_nan(&"nan"), - Ok(()) - ); - assert_eq!( - NVector::>::new(RawFloat::NAN, 1.0, 1.0).check_nan(&"nan"), - Err(Failure::ResultIsNan("nan")), - ); - assert_eq!( - NVector::>::new(1.0, RawFloat::NAN, 1.0).check_nan(&"nan"), - Err(Failure::ResultIsNan("nan")), - ); - assert_eq!( - NVector::>::new(1.0, 1.0, RawFloat::NAN).check_nan(&"nan"), - Err(Failure::ResultIsNan("nan")), - ); - } - - #[test] - fn angle() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression( - context, - &Expression::parse("vec2(0, 1).angle(vec2(1, 0))").unwrap().1 - ), - Ok(Scalar { - dimension: Dimension::angle(), - value: Float::new(0.5).unwrap(), - } - .into()) - ); - }); - } -} diff --git a/src/script/execution/types/mod.rs b/src/script/execution/types/mod.rs deleted file mode 100644 index 0c629c2..0000000 --- a/src/script/execution/types/mod.rs +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::{borrow::Cow, cmp::Ordering, fmt::Write}; - -use enum_dispatch::enum_dispatch; -use enum_downcast::{AsVariant, EnumDowncast}; - -use crate::script::{ - logging::RuntimeLog, - parsing::{Expression, Litteral, VariableType}, - Span, -}; - -use super::{ExecutionContext, Failure}; - -mod none; -pub use none::NoneType; - -mod default; -pub use default::DefaultValue; - -mod boolean; -pub use boolean::Boolean; - -mod number; - -pub mod function; -pub use function::{BuiltinFunction, UserFunction}; - -mod structures; -pub use structures::{StructDefinition, Structure}; - -mod list; -pub use list::List; - -mod string; -pub use string::{ - formatting::{Style, UnwrapFormattingResult}, - SString, -}; - -mod math; -use self::function::BuiltinFunctionRef; -pub use self::math::{ - print_all_supported_units, Length, LengthVector2, Quaternion, Scalar, Transform2D, Transform3D, - Vector2, Vector3, Vector4, -}; - -mod range; -pub use range::Range; - -mod closure; -pub use closure::Closure; - -mod serializable; -pub use serializable::SerializableValue; - -mod modeling; -pub use modeling::{ - curve::Curve, cycle::Cycle, face::Face, half_edge::HalfEdge, region::Region, shell::Shell, - sketch::Sketch, solid::Solid, surface::Surface, vertex::Vertex, -}; - -pub fn register_globals(context: &mut ExecutionContext<'_, S>) { - math::register_globals(context); - modeling::register_globals(context); -} - -// TODO this should probably be moved to failure_message.rs -pub type OperatorResult = std::result::Result>; - -fn unsupported_operation_message + ?Sized>( - object: &O, - span: &S, - operation_name: &'static str, -) -> OperatorResult { - Err(Failure::UnsupportedOperation( - span.clone(), - object.type_name(), - operation_name, - )) -} - -pub trait TypedObject { - fn get_type() -> VariableType; -} - -pub trait NamedObject { - fn static_type_name() -> &'static str; -} - -#[enum_dispatch] -pub trait Object: NamedObject { - fn matches_type( - &self, - ty: &VariableType, - log: &mut dyn RuntimeLog, - variable_name_span: &S, - ) -> OperatorResult; - - fn format( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - _f: &mut dyn Write, - _style: Style, - _precision: Option, - ) -> OperatorResult { - unsupported_operation_message(self, span, "format") - } - - fn type_name(&self) -> Cow<'static, str> { - Self::static_type_name().into() - } - - fn and( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - _rhs: &Value, - ) -> OperatorResult> { - unsupported_operation_message(self, span, "and") - } - fn or( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - _rhs: &Value, - ) -> OperatorResult> { - unsupported_operation_message(self, span, "or") - } - fn cmp( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - _rhs: &Value, - ) -> OperatorResult { - unsupported_operation_message(self, span, "compare") - } - fn eq(&self, log: &mut dyn RuntimeLog, span: &S, rhs: &Value) -> OperatorResult { - Ok(matches!(self.cmp(log, span, rhs)?, Ordering::Equal)) - } - fn addition( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - _rhs: &Value, - ) -> OperatorResult> { - unsupported_operation_message(self, span, "addition") - } - fn subtraction( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - _rhs: &Value, - ) -> OperatorResult> { - unsupported_operation_message(self, span, "subtraction") - } - fn multiply( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - _rhs: &Value, - ) -> OperatorResult> { - unsupported_operation_message(self, span, "multiply") - } - fn divide( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - _rhs: &Value, - ) -> OperatorResult> { - unsupported_operation_message(self, span, "divide") - } - fn attribute( - &self, - _log: &mut dyn RuntimeLog, - _span: &S, - attribute: &S, - ) -> OperatorResult> { - Err(Failure::UnknownAttribute(attribute.clone())) - } - fn call( - &self, - _context: &mut ExecutionContext, - span: &S, - _arguments: Vec>, - _spans: &[Expression], - ) -> OperatorResult> { - unsupported_operation_message(self, span, "call") - } - fn method_call( - &self, - _context: &mut ExecutionContext, - _span: &S, - attribute: &S, - _arguments: Vec>, - _spans: &[Expression], - ) -> OperatorResult> { - Err(Failure::UnknownAttribute(attribute.clone())) - } - fn index( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - _index: Value, - ) -> OperatorResult> { - unsupported_operation_message(self, span, "index") - } - fn iterate( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - ) -> OperatorResult>>> { - unsupported_operation_message(self, span, "iterate") - } - fn unary_plus(&self, _log: &mut dyn RuntimeLog, span: &S) -> OperatorResult> { - unsupported_operation_message(self, span, "unary plus") - } - fn unary_minus(&self, _log: &mut dyn RuntimeLog, span: &S) -> OperatorResult> { - unsupported_operation_message(self, span, "unary minus") - } - fn unary_logical_not( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - ) -> OperatorResult> { - unsupported_operation_message(self, span, "unary logical not") - } - - fn export( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - ) -> OperatorResult { - unsupported_operation_message(self, span, "export") - } -} - -trait UnwrapBorrowFailure { - fn unwrap_borrow_failure(self, span: &S) -> Result>; -} - -impl UnwrapBorrowFailure for Result { - fn unwrap_borrow_failure(self, span: &S) -> Result> { - self.map_err(|_error| Failure::CannotBorrowImmutably(span.clone(), span.clone())) - } -} - -impl UnwrapBorrowFailure for Result { - fn unwrap_borrow_failure(self, span: &S) -> Result> { - self.map_err(|_error| Failure::CannotBorrowMutably(span.clone(), span.clone())) - } -} - -#[enum_dispatch(Object, ObjectClone, FormatArgument)] -#[derive(Debug, PartialEq, EnumDowncast, Clone)] -pub enum Value { - NoneType, - Default(DefaultValue), - Boolean, - BuiltinFunction(BuiltinFunctionRef), - UserFunction(UserFunction), - Structure(Structure), - StructDefinition(StructDefinition), - List(List), - String(SString), - Range(Range), - Closure(Closure), - Scalar, - Vector2(Vector2), - Vector3(Vector3), - Vector4(Vector4), - Transform2D, - Transform3D, - Quaternion, - Cycle, - Region, - Sketch, - Surface, - Solid, - Shell, - Face, - Curve, - HalfEdge, - Vertex, -} - -impl NamedObject for Value { - fn static_type_name() -> &'static str { - "Value" - } -} - -impl Value { - pub fn downcast_ref(&self, span: &S) -> OperatorResult - where - T: NamedObject, - Self: AsVariant, - { - if let Some(value) = self.enum_downcast_ref() { - Ok(value) - } else { - Err(Failure::ExpectedGot( - span.clone(), - T::static_type_name().into(), - self.type_name(), - )) - } - } - - pub fn downcast(self, span: &S) -> OperatorResult - where - T: NamedObject, - Self: TryInto, - { - let type_name = self.type_name(); - - match self.try_into() { - Ok(value) => Ok(value), - Err(_original) => Err(Failure::ExpectedGot( - span.clone(), - T::static_type_name().into(), - type_name, - )), - } - } - - pub fn downcast_optional(self, span: &S) -> OperatorResult> - where - T: NamedObject, - Self: TryInto, - { - match self { - Self::NoneType(_) => Ok(None), - this => Ok(Some(this.downcast::(span)?)), - } - } - - pub fn from_litteral( - context: &mut ExecutionContext, - value: &Litteral, - ) -> OperatorResult { - match value { - Litteral::Scalar(scalar) => Scalar::from_parsed(scalar), - Litteral::String(string) => SString::from_parsed(string), - Litteral::List(list) => List::from_parsed(context, list), - Litteral::Boolean(_span, value) => Ok(Self::Boolean(*value)), - Litteral::Default(_span) => Ok(DefaultValue.into()), - Litteral::Closure(closure) => Ok(Closure::from(closure).into()), - } - } -} diff --git a/src/script/execution/types/modeling/curve.rs b/src/script/execution/types/modeling/curve.rs deleted file mode 100644 index e226777..0000000 --- a/src/script/execution/types/modeling/curve.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use fj_core::{storage::Handle, topology::Curve as FornjotCurve}; - -use crate::script::{ - execution::{ - types::{Object, OperatorResult}, - ExecutionContext, - }, - logging::RuntimeLog, - parsing::VariableType, - Span, -}; - -use super::handle_wrapper; - -pub fn register_globals(_context: &mut ExecutionContext) { - // TODO we should have the power to build faces from surfaces and regions. -} - -#[derive(Clone)] -pub struct Curve { - pub handle: Handle, -} - -impl Object for Curve { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Face)) - } -} - -handle_wrapper!(Curve, FornjotCurve); diff --git a/src/script/execution/types/modeling/cycle.rs b/src/script/execution/types/modeling/cycle.rs deleted file mode 100644 index baba2d3..0000000 --- a/src/script/execution/types/modeling/cycle.rs +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::ops::Deref; - -use crate::script::{ - execution::{ - types::{ - function::{AutoCall, IntoBuiltinFunction}, - Object, OperatorResult, Value, - }, - ExecutionContext, Failure, - }, - logging::RuntimeLog, - parsing::VariableType, - Span, -}; - -use fj_core::{ - operations::{build::BuildCycle, insert::Insert, reverse::Reverse}, - storage::Handle, - topology::Cycle as FornjotCycle, -}; - -use super::{ - handle_wrapper, - structs::{Circle, Polygon, Segments}, -}; - -pub fn register_globals(context: &mut ExecutionContext) { - context.stack.new_variable_str( - "new_cycle", - (|context: &mut ExecutionContext, span: &S, configuration: Value| match configuration - { - Value::Structure(configuration) => match configuration.name() { - "Circle" => { - let circle = Circle::unpack_struct(span, configuration)?; - - let circle = FornjotCycle::circle( - circle.center.as_fornjot_point(context), - circle.radius.as_fornjot_scalar(context), - circle.surface.handle, - &mut context.global_resources.fornjot_core, - ); - let circle = circle.insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - Ok(Cycle { handle: circle }.into()) - } - "Polygon" => { - let polygon = Polygon::unpack_struct(span, configuration)?; - - let polygone = FornjotCycle::polygon( - polygon.points(context, span)?, - polygon.surface.handle, - &mut context.global_resources.fornjot_core, - ); - let polygon = polygone.insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - Ok(Cycle { handle: polygon }.into()) - } - "Segments" => { - let segments = Segments::unpack_struct(span, configuration)?; - let points = segments.as_polygon(context, span)?; - - let polygone = FornjotCycle::polygon( - points, - segments.surface.handle, - &mut context.global_resources.fornjot_core, - ); - let polygon = polygone.insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - Ok(Cycle { handle: polygon }.into()) - } - _ => Err(Failure::ExpectedGot( - span.clone(), - "Empty, Circle, Polygon, or list of edges".into(), - configuration.name().to_string().into(), - )), - }, - configuration => Err(Failure::ExpectedGot( - span.clone(), - "Empty, Circle, Polygon, or list of edges".into(), - configuration.type_name(), - )), - }) - .into_builtin_function() - .into(), - ) -} - -#[derive(Clone)] -pub struct Cycle { - pub handle: Handle, -} - -impl Object for Cycle { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Cycle)) - } - - fn method_call( - &self, - context: &mut ExecutionContext, - span: &S, - attribute: &S, - arguments: Vec>, - spans: &[crate::script::parsing::Expression], - ) -> OperatorResult> { - match attribute.as_str() { - "reverse" => { - |context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - let reversed = self - .handle - .deref() - .reverse(&mut context.global_resources.fornjot_core); - - let reversed = reversed.insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - Ok(Self::from(reversed).into()) - } - .auto_call(context, span, arguments, spans) - } - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } -} - -handle_wrapper!(Cycle, FornjotCycle); - -#[cfg(test)] -mod test { - use crate::script::{ - execution::{expressions::run_expression, types::Value}, - parsing::Expression, - Runtime, - }; - - use super::*; - - #[test] - fn construct_circle() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert!(matches!( - run_expression( - context, - &Expression::parse( - "new_cycle(Circle { center = vec2(1mm, 2mm), radius = 3mm, surface = global_xy_plane() })" - ) - .unwrap() - .1 - ), - Ok(Value::Cycle(_)) - )); - }); - } - - #[test] - fn construct_polygon() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert!(matches!( - run_expression( - context, - &Expression::parse( - "new_cycle(Polygon { points = [vec2(0m, 0m), vec2(0m, 1m), vec2(1m, 1m), vec2(1m, 0m)], surface = global_xy_plane() })" - ) - .unwrap() - .1 - ), - Ok(Value::Cycle(_)) - )); - }); - } -} diff --git a/src/script/execution/types/modeling/face.rs b/src/script/execution/types/modeling/face.rs deleted file mode 100644 index a47ef05..0000000 --- a/src/script/execution/types/modeling/face.rs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use fj_core::{storage::Handle, topology::Face as FornjotFace}; - -use crate::script::{ - execution::{ - types::{Object, OperatorResult}, - ExecutionContext, - }, - logging::RuntimeLog, - parsing::VariableType, - Span, -}; - -use super::handle_wrapper; - -pub fn register_globals(_context: &mut ExecutionContext) { - // TODO we should have the power to build faces from surfaces and regions. -} - -#[derive(Clone)] -pub struct Face { - pub handle: Handle, -} - -impl Object for Face { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Face)) - } -} - -handle_wrapper!(Face, FornjotFace); - -// TODO test adding duplicate regions to the face (through update and add_regions) -// TODO test updating a region that did not exist in the face diff --git a/src/script/execution/types/modeling/half_edge.rs b/src/script/execution/types/modeling/half_edge.rs deleted file mode 100644 index f62644c..0000000 --- a/src/script/execution/types/modeling/half_edge.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use fj_core::{storage::Handle, topology::HalfEdge as FornjotHalfEdge}; - -use crate::script::{ - execution::{ - types::{Object, OperatorResult}, - ExecutionContext, - }, - logging::RuntimeLog, - parsing::VariableType, - Span, -}; - -use super::handle_wrapper; - -pub fn register_globals(_context: &mut ExecutionContext) { - // TODO we should have the power to build faces from surfaces and regions. -} - -#[derive(Clone)] -pub struct HalfEdge { - pub handle: Handle, -} - -impl Object for HalfEdge { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Face)) - } -} - -handle_wrapper!(HalfEdge, FornjotHalfEdge); diff --git a/src/script/execution/types/modeling/mod.rs b/src/script/execution/types/modeling/mod.rs deleted file mode 100644 index 45f4b70..0000000 --- a/src/script/execution/types/modeling/mod.rs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use crate::script::{execution::ExecutionContext, Span}; - -// TODO I want a box type that can be a square or a rectangle. -pub mod curve; -pub mod cycle; -pub mod face; -pub mod half_edge; -pub mod object_set; -pub mod region; -pub mod shell; -pub mod sketch; -pub mod solid; -mod structs; -pub mod surface; -pub mod vertex; - -pub fn register_globals(context: &mut ExecutionContext<'_, S>) { - structs::register_globals(context); - - cycle::register_globals(context); - face::register_globals(context); - region::register_globals(context); - shell::register_globals(context); - sketch::register_globals(context); - solid::register_globals(context); - surface::register_globals(context); - curve::register_globals(context); - half_edge::register_globals(context); - vertex::register_globals(context); -} - -macro_rules! handle_wrapper { - ($name:ident, $handle:ident) => { - impl From> for crate::script::execution::types::Value { - fn from(handle: Handle<$handle>) -> Self { - $name::from(handle).into() - } - } - - impl std::fmt::Debug for $name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - #[allow(unused)] - use std::ops::Deref; - f.debug_struct(stringify!($name)) - .field("id", &self.handle.id()) - .field("content", &self.handle.deref()) - .finish() - } - } - - impl TryFrom> for Handle<$handle> { - type Error = crate::script::execution::types::Value; - - fn try_from( - value: crate::script::execution::types::Value, - ) -> Result { - use enum_downcast::EnumDowncast; - let value = value.enum_downcast::<$name>()?; - Ok(value.handle.into()) - } - } - - impl From> for $name { - fn from(handle: Handle<$handle>) -> Self { - Self { - handle: handle.into(), - } - } - } - - impl From<$name> for Handle<$handle> { - fn from(val: $name) -> Self { - val.handle.into() - } - } - - impl crate::script::execution::types::TypedObject for $name { - fn get_type() -> VariableType { - VariableType::$name - } - } - - impl crate::script::execution::types::NamedObject for $name { - fn static_type_name() -> &'static str { - stringify!($name) - } - } - - impl PartialEq for $name { - fn eq(&self, other: &Self) -> bool { - self.handle == other.handle - } - } - }; -} - -pub(crate) use handle_wrapper; diff --git a/src/script/execution/types/modeling/object_set.rs b/src/script/execution/types/modeling/object_set.rs deleted file mode 100644 index e23ae01..0000000 --- a/src/script/execution/types/modeling/object_set.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use fj_core::{storage::Handle, topology::ObjectSet}; - -use crate::script::{ - execution::{ - types::{List, OperatorResult, Value}, - Failure, - }, - Span, -}; - -impl Value -where - S: Span, -{ - pub fn from_object_set(object_set: &ObjectSet) -> Self - where - Value: From>, - { - List::from(object_set.iter().map(|handle| Value::from(handle.clone()))).into() - } -} - -pub fn check_for_duplicates( - span: &S, - expected_items: usize, - items: impl Iterator, -) -> OperatorResult> -where - S: Span, - T: Eq, -{ - let mut set = Vec::with_capacity(expected_items); - - for (index, item) in items.enumerate() { - if !set.contains(&item) { - set.push(item); - } else { - return Err(Failure::ListContainsDuplicate(span.clone(), index)); - } - } - - Ok(set) -} diff --git a/src/script/execution/types/modeling/region.rs b/src/script/execution/types/modeling/region.rs deleted file mode 100644 index 707e437..0000000 --- a/src/script/execution/types/modeling/region.rs +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::rc::Rc; - -use fj_core::{ - operations::{build::BuildRegion, insert::Insert}, - storage::Handle, - topology::Region as FornjotRegion, -}; - -use crate::script::{ - execution::{ - types::{ - function::IntoBuiltinFunction, List, Object, OperatorResult, StructDefinition, - Structure, - }, - ExecutionContext, Failure, - }, - logging::RuntimeLog, - parsing::{self, MemberVariable, MemberVariableType, VariableType}, - Span, -}; - -use super::{ - cycle::Cycle, - handle_wrapper, - structs::{Circle, Polygon, Segments}, -}; - -pub fn register_globals(context: &mut ExecutionContext) { - context.stack.new_variable_str( - "RawRegion", - StructDefinition { - // TODO replace box leak with lazy static. - definition: Rc::new(parsing::StructDefinition { - name: S::from_str("RawRegion"), - members: vec![ - MemberVariable { - name: S::from_str("exterior"), - ty: MemberVariableType { - ty: VariableType::Cycle, - constraints: None, - default_value: None, - }, - }, - MemberVariable { - name: S::from_str("interiors"), - ty: MemberVariableType { - ty: VariableType::List, - constraints: None, - default_value: None, - }, - }, - ], - }), - } - .into(), - ); - - context.stack.new_variable_str( - "new_region", - (|context: &mut ExecutionContext, span: &S, configuration: Structure| { - match configuration.name() { - "Circle" => { - let circle = Circle::unpack_struct(span, configuration)?; - - let region = Region::from( - FornjotRegion::circle( - circle.center.as_fornjot_point(context), - circle.radius.as_fornjot_scalar(context), - circle.surface.handle, - &mut context.global_resources.fornjot_core, - ) - .insert(&mut context.global_resources.fornjot_core), - ); - - context.unpack_validation_warnings(span); - - Ok(region.into()) - } - "Polygon" => { - let polygon = Polygon::unpack_struct(span, configuration)?; - - let region = Region::from( - FornjotRegion::polygon( - polygon.points(context, span)?, - polygon.surface.handle, - &mut context.global_resources.fornjot_core, - ) - .insert(&mut context.global_resources.fornjot_core), - ); - - context.unpack_validation_warnings(span); - - Ok(region.into()) - } - "Segments" => { - let segments = Segments::unpack_struct(span, configuration)?; - let mut polygon = segments.as_polygon(context, span)?; - - // The last point just returns to the starting point. - // TODO validate that it's actually at the starting point. - polygon.pop(); - - let region = Region::from( - FornjotRegion::polygon( - polygon, - segments.surface.handle, - &mut context.global_resources.fornjot_core, - ) - .insert(&mut context.global_resources.fornjot_core), - ); - context.unpack_validation_warnings(span); - Ok(region.into()) - } - "RawRegion" => { - let region = Region::build_raw(context, span, configuration)?; - Ok(region.into()) - } - // TODO build a region from an SVG file. - _ => Err(Failure::ExpectedGot( - span.clone(), - "Empty, Circle, Polygon, or Segments".into(), - configuration.name().to_string().into(), - )), - } - }) - .into_builtin_function() - .into(), - ) -} - -#[derive(Clone)] -pub struct Region { - pub handle: Handle, -} - -impl Region { - fn build_raw( - context: &mut ExecutionContext, - span: &S, - configuration: Structure, - ) -> OperatorResult { - let mut members = Rc::unwrap_or_clone(configuration.members); - - // We assume that this is a `RawRegion` type. - let exterior_cycle = members - .remove("exterior") - .unwrap() - .downcast::(span)?; - let exterior_cycle = exterior_cycle.handle; - - let interior_cylce_list = members - .remove("interiors") - .unwrap() - .downcast::>(span)?; - - let interior_cycles = interior_cylce_list - .unpack_dynamic_length::(span)? - .map(|cycle| cycle.handle); - - let region = Self::from( - FornjotRegion::new(exterior_cycle, interior_cycles) - .insert(&mut context.global_resources.fornjot_core), - ); - - context.unpack_validation_warnings(span); - - Ok(region) - } -} - -impl Object for Region { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Region)) - } -} - -handle_wrapper!(Region, FornjotRegion); - -#[cfg(test)] -mod test { - use crate::script::{ - execution::{expressions::run_expression, types::Value}, - parsing::Expression, - Runtime, - }; - - use super::*; - - #[test] - fn construct_circle() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert!(matches!( - run_expression( - context, - &Expression::parse( - "new_region(Circle { center = vec2(1mm, 2mm), radius = 3mm, surface = global_xy_plane() })" - ) - .unwrap() - .1 - ), - Ok(Value::Region(_)) - )); - }); - } - - #[test] - fn construct_polygon() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert!(matches!( - run_expression( - context, - &Expression::parse( - "new_region(Polygon { points = [vec2(0m, 0m), vec2(0m, 1m), vec2(1m, 1m), vec2(1m, 0m)], surface = global_xy_plane() })" - ) - .unwrap() - .1 - ), - Ok(Value::Region(_)) - )); - }); - } - - #[test] - fn construct_raw() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert!(matches!( - run_expression( - context, - &Expression::parse( - "new_region(RawRegion { exterior = new_cycle(Circle { center = vec2(1mm, 2mm), radius = 3mm, surface = global_xy_plane() }), - interiors = [new_cycle(Circle { center = vec2(1mm, 2mm), radius = 3mm / 2, surface = global_xy_plane() })] })" - ) - .unwrap() - .1, - ), - Ok(Value::Region(_)) - )); - }); - } - - // TODO validation failure test (Fornjot failure validation should result in a Failure type when constructing an invalid region) -} diff --git a/src/script/execution/types/modeling/shell.rs b/src/script/execution/types/modeling/shell.rs deleted file mode 100644 index 7270e6d..0000000 --- a/src/script/execution/types/modeling/shell.rs +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::ops::Deref; - -use fj_core::{ - operations::{ - holes::{AddHole, HoleLocation}, - insert::Insert, - update::UpdateShell, - }, - storage::Handle, - topology::Shell as FornjotShell, -}; - -use crate::script::{ - execution::{ - types::{ - function::AutoCall, math::LengthVector3, Length, LengthVector2, List, Object, - OperatorResult, Value, - }, - ExecutionContext, Failure, - }, - logging::RuntimeLog, - parsing::{Expression, VariableType}, - Span, -}; - -use super::{face::Face, handle_wrapper, object_set::check_for_duplicates}; - -pub fn register_globals(_context: &mut ExecutionContext) { - // TODO we should have the power to build shells from faces. -} - -#[derive(Clone)] -pub struct Shell { - pub handle: Handle, -} - -impl Object for Shell { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Shell)) - } - - fn attribute( - &self, - _log: &mut dyn RuntimeLog, - _span: &S, - attribute: &S, - ) -> OperatorResult> { - match attribute.as_str() { - "faces" => Ok(Value::from_object_set(self.handle.faces())), - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } - - fn method_call( - &self, - context: &mut ExecutionContext, - span: &S, - attribute: &S, - arguments: Vec>, - spans: &[Expression], - ) -> OperatorResult> { - match attribute.as_str() { - "add_blind_hole" => |context: &mut ExecutionContext, - span: &S, - face: Face, - position: LengthVector2, - radius: Length, - path: LengthVector3| - -> OperatorResult> { - if !self.handle.deref().faces().contains(&face.handle) { - return Err(Failure::FaceNotInShell(span.clone())); - } - - let position = position.as_fornjot_point(context); - let radius = radius.as_fornjot_scalar(context); - let path = path.as_fornjot_vector(context); - - let new_shell = self.handle.deref().add_blind_hole( - HoleLocation { - face: &face.handle, - position, - }, - radius, - path, - &mut context.global_resources.fornjot_core, - ); - - let new_shell = - Self::from(new_shell.insert(&mut context.global_resources.fornjot_core)); - context.unpack_validation_warnings(span); - - Ok(new_shell.into()) - } - .auto_call(context, span, arguments, spans), - "add_through_hole" => |context: &mut ExecutionContext, - span: &S, - front_face: Face, - front_position: LengthVector2, - - back_face: Face, - back_position: LengthVector2, - - radius: Length| - -> OperatorResult> { - let faces = self.handle.deref().faces(); - if !faces.contains(&front_face.handle) || !faces.contains(&front_face.handle) { - return Err(Failure::FaceNotInShell(span.clone())); - } - - let front_position = front_position.as_fornjot_point(context); - let back_position = back_position.as_fornjot_point(context); - - let radius = radius.as_fornjot_scalar(context); - - let new_shell = self.handle.deref().add_through_hole( - [ - HoleLocation { - face: &front_face.handle, - position: front_position, - }, - HoleLocation { - face: &back_face.handle, - position: back_position, - }, - ], - radius, - &mut context.global_resources.fornjot_core, - ); - - let new_shell = - Self::from(new_shell.insert(&mut context.global_resources.fornjot_core)); - context.unpack_validation_warnings(span); - - Ok(new_shell.into()) - } - .auto_call(context, span, arguments, spans), - "update_face" => |context: &mut ExecutionContext, - span: &S, - face: Face, - update: Value| - -> OperatorResult> { - // Update shell will panic if the shell isn't found in the solid, so check that it's in there. - if !self.handle.deref().faces().contains(&face.handle) { - return Err(Failure::FaceNotInShell(span.clone())); - } - - // Due to borrowing issues, we have to run the update call before we go into - // the update function. - let new_faces = update.call(context, span, vec![face.clone().into()], &[])?; - let new_faces = new_faces.downcast::>(span)?; - let num_faces = new_faces.len(span)?; - let new_faces = new_faces - .unpack_dynamic_length::(span)? - .map(|shell| shell.handle); - - // Update shell will panic if we insert a duplicate, so deduplicate it. - let new_faces = check_for_duplicates(span, num_faces, new_faces)?; - - let new_shell = self.handle.deref().update_face( - &face.handle, - |_face, _core| new_faces.into_iter(), - &mut context.global_resources.fornjot_core, - ); - - let new_shell = - Self::from(new_shell.insert(&mut context.global_resources.fornjot_core)); - context.unpack_validation_warnings(span); - - Ok(new_shell.into()) - } - .auto_call(context, span, arguments, spans), - "add_faces" => { - |context: &mut ExecutionContext, - span: &S, - new_faces: List| - -> OperatorResult> { - let num_faces = new_faces.len(span)?; - let new_faces = new_faces - .unpack_dynamic_length::(span)? - .map(|shell| shell.handle); - - // Update shell will panic if we insert a duplicate, so deduplicate it. - let new_faces = check_for_duplicates(span, num_faces, new_faces)?; - - let new_shell = self - .handle - .deref() - .add_faces(new_faces, &mut context.global_resources.fornjot_core); - - let new_shell = - Self::from(new_shell.insert(&mut context.global_resources.fornjot_core)); - context.unpack_validation_warnings(span); - - Ok(new_shell.into()) - } - .auto_call(context, span, arguments, spans) - } - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } -} - -handle_wrapper!(Shell, FornjotShell); - -// TODO test that the hole creating functions correctly prevent non-existing faces from being used. -// TODO test adding duplicate faces to the shell (through update and add_faces) -// TODO test updating a face that did not exist in the shell -// TODO test blind hole -// TODO test through hole diff --git a/src/script/execution/types/modeling/sketch.rs b/src/script/execution/types/modeling/sketch.rs deleted file mode 100644 index 1786094..0000000 --- a/src/script/execution/types/modeling/sketch.rs +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use fj_core::{ - operations::{build::BuildRegion, insert::Insert, sweep::SweepSketch}, - storage::Handle, - topology::{Region as FornjotRegion, Sketch as FornjotSketch}, -}; - -use crate::script::{ - execution::{ - types::{ - function::{AutoCall, IntoBuiltinFunction}, - math::LengthVector3, - Object, OperatorResult, Value, - }, - ExecutionContext, Failure, - }, - logging::RuntimeLog, - parsing::{self, VariableType}, - Span, -}; - -use super::{ - handle_wrapper, - region::Region, - solid::Solid, - structs::{Circle, Polygon}, - surface::Surface, -}; - -pub fn register_globals(context: &mut ExecutionContext) { - context.stack.new_variable_str( - "new_sketch", - (|context: &mut ExecutionContext, - span: &S, - argument1: Value, - argument2: Value| - -> OperatorResult> { - match (argument1, argument2) { - (Value::Surface(surface), Value::List(regions)) => { - let regions = regions - .unpack_dynamic_length::(span)? - .map(|region| region.handle); - let handle = FornjotSketch::new(surface.handle, regions) - .insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - Ok(Sketch { handle }.into()) - } - (Value::Structure(configuration), Value::NoneType(_)) => match configuration.name() - { - "Circle" => { - let circle = Circle::unpack_struct(span, configuration)?; - let region = FornjotRegion::circle( - circle.center.as_fornjot_point(context), - circle.radius.as_fornjot_scalar(context), - circle.surface.handle.clone(), - &mut context.global_resources.fornjot_core, - ) - .insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - let handle = FornjotSketch::new(circle.surface.handle, [region]) - .insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - Ok(Sketch { handle }.into()) - } - "Polygon" => { - let polygon = Polygon::unpack_struct(span, configuration)?; - - let points = polygon.points(context, span)?; - let surface = polygon.surface; - - let polygon = FornjotRegion::polygon( - points, - surface.handle.clone(), - &mut context.global_resources.fornjot_core, - ); - let region = polygon.insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - let handle = FornjotSketch::new(surface.handle, [region]) - .insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - Ok(Sketch { handle }.into()) - } - // TODO sketch from an SVG file. - _ => Err(Failure::ExpectedGot( - span.clone(), - "Empty, Circle, or Polygon".into(), - configuration.name().to_string().into(), - )), - }, - (value_a, _value_b) => Err(Failure::ExpectedGot( - span.clone(), - "Circle, Polygon, or a surface followed by a List of regions".into(), - value_a.type_name(), - )), - } - }) - .into_builtin_function_optional() - .into(), - ) -} - -#[derive(Clone)] -pub struct Sketch { - pub handle: Handle, -} - -impl Object for Sketch { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Sketch)) - } - - fn method_call( - &self, - context: &mut ExecutionContext, - span: &S, - attribute: &S, - arguments: Vec>, - spans: &[parsing::Expression], - ) -> OperatorResult> { - match attribute.as_str() { - "sweep" => |context: &mut ExecutionContext, - span: &S, - surface: Surface, - path: LengthVector3| - -> OperatorResult> { - let surface = surface.handle; - let path = path.as_fornjot_vector(context); - - let solid = self - .handle - .sweep_sketch(surface, path, &mut context.global_resources.fornjot_core) - .insert(&mut context.global_resources.fornjot_core); - context.unpack_validation_warnings(span); - - Ok(Solid::from(solid).into()) - } - .auto_call(context, span, arguments, spans), - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } -} - -handle_wrapper!(Sketch, FornjotSketch); - -#[cfg(test)] -mod test { - use crate::script::{execution::expressions::run_expression, parsing::Expression, Runtime}; - - use super::*; - - #[test] - fn construct_circle() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert!(matches!( - run_expression( - context, - &Expression::parse( - "new_sketch(Circle { center = vec2(1mm, 2mm), radius = 3mm, surface = global_xy_plane() })" - ) - .unwrap() - .1 - ), - Ok(Value::Sketch(_)) - )); - }); - } - - #[test] - fn construct_polygon() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert!(matches!( - run_expression( - context, - &Expression::parse( - "new_sketch(Polygon { points = [vec2(0m, 0m), vec2(0m, 1m), vec2(1m, 1m), vec2(1m, 0m)], surface = global_xy_plane() })" - ) - .unwrap() - .1 - ), - Ok(Value::Sketch(_)) - )); - }); - } - - #[test] - fn construct_from_regions() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert!(matches!( - run_expression( - context, - &Expression::parse( - "new_sketch(global_xy_plane(), [new_region(Circle { center = vec2(1mm, 2mm), radius = 3mm, surface = global_xy_plane() }), -new_region(Circle { center = vec2(4mm, 2mm), radius = 3mm, surface = global_xy_plane() })])" - ) - .unwrap() - .1 - ), - Ok(Value::Sketch(_)) - )); - }); - } - - #[test] - fn sweep() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert!(matches!( - dbg!(run_expression( - context, - &Expression::parse( - "new_sketch(Circle { center = vec2(1mm, 2mm), radius = 3mm, surface = global_xy_plane() }).sweep(global_xz_plane(), vec3(0cm, 1cm, 0cm))" - ) - .unwrap() - .1, - )), - Ok(Value::Solid(_)) - )); - }); - } - - // TODO validation failure test (Fornjot failure validation should result in a Failure type when constructing an invalid region) -} diff --git a/src/script/execution/types/modeling/solid.rs b/src/script/execution/types/modeling/solid.rs deleted file mode 100644 index 3aacde3..0000000 --- a/src/script/execution/types/modeling/solid.rs +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::ops::Deref; - -use fj_core::{ - operations::{insert::Insert, update::UpdateSolid}, - storage::Handle, - topology::Solid as FornjotSolid, -}; - -use crate::script::{ - execution::{ - types::{function::AutoCall, List, Object, OperatorResult, Value}, - ExecutionContext, Failure, - }, - logging::RuntimeLog, - parsing::VariableType, - Span, -}; - -use super::{handle_wrapper, object_set::check_for_duplicates, shell::Shell}; - -pub fn register_globals(_context: &mut ExecutionContext<'_, S>) {} - -#[derive(Clone)] -pub struct Solid { - pub handle: Handle, -} - -impl Object for Solid { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Sketch)) - } - - fn attribute( - &self, - _log: &mut dyn RuntimeLog, - _span: &S, - attribute: &S, - ) -> OperatorResult> { - match attribute.as_str() { - "shells" => Ok(Value::from_object_set(self.handle.shells())), - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } - - fn method_call( - &self, - context: &mut ExecutionContext, - span: &S, - attribute: &S, - arguments: Vec>, - spans: &[crate::script::parsing::Expression], - ) -> OperatorResult> { - match attribute.as_str() { - "update_shell" => |context: &mut ExecutionContext, - span: &S, - shell: Shell, - update: Value| - -> OperatorResult> { - // Update shell will panic if the shell isn't found in the solid, so check that it's in there. - if !self.handle.deref().shells().contains(&shell.handle) { - return Err(Failure::ShellNotInSolid(span.clone())); - } - - // Due to borrowing issues, we have to run the update call before we go into - // the update function. - let new_shells = update.call(context, span, vec![shell.clone().into()], &[])?; - let new_shells = new_shells.downcast::>(span)?; - let num_shells = new_shells.len(span)?; - let new_shells = new_shells - .unpack_dynamic_length::(span)? - .map(|shell| shell.handle); - - // Update shell will panic if we insert a duplicate, so deduplicate it. - let new_shells = check_for_duplicates(span, num_shells, new_shells)?; - - let new_solid = self.handle.deref().update_shell( - &shell.handle, - |_shell, _core| new_shells.into_iter(), - &mut context.global_resources.fornjot_core, - ); - - let new_solid = - Self::from(new_solid.insert(&mut context.global_resources.fornjot_core)); - context.unpack_validation_warnings(span); - - Ok(new_solid.into()) - } - .auto_call(context, span, arguments, spans), - "add_shells" => { - |context: &mut ExecutionContext, - span: &S, - new_shells: List| - -> OperatorResult> { - let num_shells = new_shells.len(span)?; - let new_shells = new_shells - .unpack_dynamic_length::(span)? - .map(|shell| shell.handle); - - // Update shell will panic if we insert a duplicate, so deduplicate it. - let new_shells = check_for_duplicates(span, num_shells, new_shells)?; - - let new_solid = self - .handle - .deref() - .add_shells(new_shells, &mut context.global_resources.fornjot_core); - - let new_solid = - Self::from(new_solid.insert(&mut context.global_resources.fornjot_core)); - context.unpack_validation_warnings(span); - - Ok(new_solid.into()) - } - .auto_call(context, span, arguments, spans) - } - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } -} - -handle_wrapper!(Solid, FornjotSolid); - -// TODO test adding duplicate shells to the solid (through update and add_shells) -// TODO test updating a shell that did not exist in the solid diff --git a/src/script/execution/types/modeling/structs.rs b/src/script/execution/types/modeling/structs.rs deleted file mode 100644 index 934993d..0000000 --- a/src/script/execution/types/modeling/structs.rs +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::rc::Rc; - -use bezier_rs::{Bezier, BezierHandles, TValueType}; -use common_data_types::Float; -use fj_math::Point; -use glam::f64::DVec2; -use macros::Struct; -use parsing::Span; - -use crate::script::{ - execution::{ - types::{ - math::{Angle, ConvertUnit}, - Length, LengthVector2, List, NamedObject, Object, OperatorResult, StructDefinition, - Structure, Vector2, - }, - ExecutionContext, - }, - parsing::{self, MemberVariable, MemberVariableType}, - Failure, -}; - -use super::surface::Surface; - -pub fn register_globals(context: &mut ExecutionContext<'_, S>) { - Circle::define_struct(context); - Polygon::define_struct(context); - - Segments::define_struct(context); - CenterArc::define_struct(context); - Line::define_struct(context); - QuadraticBezier::define_struct(context); - CubicBezier::define_struct(context); -} - -/// A single, perfectly round, and closed circle. -#[derive(Debug, Struct)] -pub struct Circle { - pub center: LengthVector2, - pub radius: Length, - pub surface: Surface, -} - -/// A list of straight segments that may or may not be closed. -/// Some functions expect this to be closed and will give an error if it is not. -#[derive(Debug, Struct)] -pub struct Polygon { - pub points: List, - pub surface: Surface, -} - -impl Polygon { - pub fn points( - &self, - context: &ExecutionContext, - span: &S, - ) -> OperatorResult>> { - let mut fornjot_points = Vec::with_capacity(self.points.len(span)?); - for point in self.points.clone().unpack_dynamic_length::(span)? { - let point = LengthVector2::try_from(point).map_err(|point| { - Failure::ExpectedGot( - span.clone(), - LengthVector2::static_type_name().into(), - >::type_name(&point), - ) - })?; - let point = point.as_fornjot_point(context); - - fornjot_points.push(point); - } - - Ok(fornjot_points) - } -} - -/// Defines a region made of complex segments, such as lines, arcs, and bezier curves. -#[derive(Debug, Struct)] -pub struct Segments { - pub start: LengthVector2, - pub segments: List, - #[default = "1mm"] - pub distance_between_verticies: Length, - pub surface: Surface, -} - -impl Segments { - pub fn as_polygon( - &self, - context: &ExecutionContext, - span: &S, - ) -> OperatorResult>> { - let mut last_point = self.start.clone(); - let mut points = vec![last_point.as_fornjot_point(context)]; - let distance_per_step = self.distance_between_verticies; - - for edge in self.segments.iter(span)? { - let structure = edge.downcast::>(span)?; - - match structure.name() { - "CenterArc" => { - let arc = CenterArc::unpack_struct(span, structure)?; - let direction = if arc.clockwise { - ArcDirection::Clockwise - } else { - ArcDirection::CounterClockwise - }; - let end = arc_to_points(context, distance_per_step, last_point, arc.center, arc.angle, direction, &mut points); - last_point = end; - } - "Line" => { - let line = Line::unpack_struct(span, structure)?; - last_point = line.end; - points.push(last_point.as_fornjot_point(context)); - }, - "QuadraticBezier" => { - let bezier = QuadraticBezier::unpack_struct(span, structure)?; - let start = last_point.value; - let end = bezier.end.value; - last_point = bezier.end.clone(); - let handle = bezier.handle.value; - let bezier = Bezier { - start: DVec2::new(start.x, start.y), - end: DVec2::new(end.x, end.y), - handles: BezierHandles::Quadratic { - handle: DVec2::new(handle.x, handle.y), - }, - }; - beizer_to_points(context, &bezier, distance_per_step, &mut points); - } - "CubicBezier" => { - let bezier = CubicBezier::unpack_struct(span, structure)?; - - let start = last_point.value; - let start_handle = bezier.start_handle.value; - - let end = bezier.end.value; - let end_handle = bezier.end_handle.value; - last_point = bezier.end.clone(); - let bezier = Bezier { - start: DVec2::new(start.x, start.y), - end: DVec2::new(end.x, end.y), - handles: BezierHandles::Cubic { - handle_start: DVec2::new(start_handle.x, start_handle.y), - handle_end: DVec2::new(end_handle.x, end_handle.y) - }, - }; - beizer_to_points(context, &bezier, distance_per_step, &mut points); - } - name => { - return Err(Failure::ExpectedGot( - span.clone(), - "A Circle, Polygon, CenterArc, EndsArc, LengthArc, LinearBezier, QuadraticBezier, or a CubicBezier".into(), - name.to_string().into(), - )) - } - } - } - - Ok(points) - } -} - -fn beizer_to_points( - context: &ExecutionContext, - beizer: &Bezier, - distance_per_step: Length, - points: &mut Vec>, -) { - let length = beizer.length(None); - let steps = (length / distance_per_step.value.into_inner()).ceil() as usize; - - let lookup_table = beizer.compute_lookup_table(Some(steps), Some(TValueType::Euclidean)); - - for vertex in &lookup_table[1..] { - let x = context - .global_resources - .fornjot_unit_conversion_factor - .convert_from_base_unit(Float::new(vertex.x).unwrap()) - .into_inner(); - let y = context - .global_resources - .fornjot_unit_conversion_factor - .convert_from_base_unit(Float::new(vertex.y).unwrap()) - .into_inner(); - - points.push(fj_math::Point::<2> { - coords: fj_math::Vector::<2> { - components: [x.into(), y.into()], - }, - }); - } -} - -#[derive(Debug, Clone, Copy)] -enum ArcDirection { - Clockwise, - CounterClockwise, -} - -fn arc_to_points( - context: &ExecutionContext, - distance_per_step: Length, - start: LengthVector2, - center: LengthVector2, - angle: Angle, - direction: ArcDirection, - points: &mut Vec>, -) -> LengthVector2 { - let distance_per_step = distance_per_step - .as_fornjot_scale_float(context) - .into_inner(); - let start = start.as_fornjot_scale_nalgebra_vector::(context); - let center = center.as_fornjot_scale_nalgebra_vector::(context); - - let center_to_start = start - center; - let radius = center_to_start.norm(); - - let starting_angle = (start.y - center.y).atan2(start.x - center.x); - let angle = angle.value.into_inner(); - - let (sin, cos) = (starting_angle + angle).sin_cos(); - let end = center + nalgebra::Vector2::new(cos, sin) * radius; - - let arch_length = angle * radius; - let steps = (arch_length / distance_per_step).ceil(); - - let angle_direction = if matches!(direction, ArcDirection::Clockwise) { - -1.0 - } else { - 1.0 - }; - - let angle_step = (angle / steps) * angle_direction; - - let steps = steps as usize; - - // We skip the first step because that's already represented with the starting point. - for step_index in 1..steps { - let angle = starting_angle + angle_step * step_index as f64; - - let (sin, cos) = angle.sin_cos(); - let offset = nalgebra::Vector2::new(cos, sin) * radius; - - let new_position = center + offset; - - points.push(fj_math::Point::<2> { - coords: fj_math::Vector::<2> { - components: [new_position.x.into(), new_position.y.into()], - }, - }) - } - - points.push(fj_math::Point::<2> { - coords: fj_math::Vector::<2> { - components: [end.x.into(), end.y.into()], - }, - }); - - LengthVector2 { value: end } -} - -/// An arc defined by a senter point and a radius. -/// The radius is calulated by using the distance from the center point to the starting point/ -/// The starting point is the previous end point. -#[derive(Debug, Struct)] -pub struct CenterArc { - pub center: LengthVector2, - pub angle: Angle, - #[default = "true"] - pub clockwise: bool, -} - -/// A linear line segment. -/// The start point is assumed by the previous end point. -#[derive(Debug, Struct)] -pub struct Line { - pub end: LengthVector2, -} - -/// A quadratic Bezier curve. -/// The start point is assumed by the previous end point. -#[derive(Debug, Struct)] -pub struct QuadraticBezier { - pub end: LengthVector2, - pub handle: LengthVector2, -} - -/// A cubic Bezier curve. -/// The start point is assumed by the previous end point. -#[derive(Debug, Struct)] -pub struct CubicBezier { - pub start_handle: LengthVector2, - pub end: LengthVector2, - pub end_handle: LengthVector2, -} diff --git a/src/script/execution/types/modeling/surface.rs b/src/script/execution/types/modeling/surface.rs deleted file mode 100644 index dd70705..0000000 --- a/src/script/execution/types/modeling/surface.rs +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use fj_core::{storage::Handle, topology::Surface as FornjotSurface}; - -use crate::script::{ - execution::{ - types::{function::IntoBuiltinFunction, Object, OperatorResult, Value}, - ExecutionContext, - }, - logging::RuntimeLog, - parsing::VariableType, - Span, -}; - -use super::handle_wrapper; - -pub fn register_globals(context: &mut ExecutionContext) { - context.stack.new_variable_str( - "sketch_plane", - (|context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Surface::from( - context - .global_resources - .fornjot_core - .layers - .topology - .surfaces - .space_2d(), - ) - .into()) - }) - .into_builtin_function() - .into(), - ); - - context.stack.new_variable_str( - "global_xy_plane", - (|context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Surface::from( - context - .global_resources - .fornjot_core - .layers - .topology - .surfaces - .xy_plane(), - ) - .into()) - }) - .into_builtin_function() - .into(), - ); - - context.stack.new_variable_str( - "global_xz_plane", - (|context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Surface::from( - context - .global_resources - .fornjot_core - .layers - .topology - .surfaces - .xz_plane(), - ) - .into()) - }) - .into_builtin_function() - .into(), - ); - - context.stack.new_variable_str( - "global_yz_plane", - (|context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(Surface::from( - context - .global_resources - .fornjot_core - .layers - .topology - .surfaces - .yz_plane(), - ) - .into()) - }) - .into_builtin_function() - .into(), - ); - - // TODO surface from plane points. -} - -#[derive(Clone)] -pub struct Surface { - pub handle: Handle, -} - -impl Object for Surface { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Surface)) - } -} - -handle_wrapper!(Surface, FornjotSurface); diff --git a/src/script/execution/types/modeling/vertex.rs b/src/script/execution/types/modeling/vertex.rs deleted file mode 100644 index 24392db..0000000 --- a/src/script/execution/types/modeling/vertex.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use fj_core::{storage::Handle, topology::Vertex as FornjotVertex}; - -use crate::script::{ - execution::{ - types::{Object, OperatorResult}, - ExecutionContext, - }, - logging::RuntimeLog, - parsing::VariableType, - Span, -}; - -use super::handle_wrapper; - -pub fn register_globals(_context: &mut ExecutionContext) { - // TODO we should have the power to build faces from surfaces and regions. -} - -#[derive(Clone)] -pub struct Vertex { - pub handle: Handle, -} - -impl Object for Vertex { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Face)) - } -} - -handle_wrapper!(Vertex, FornjotVertex); diff --git a/src/script/execution/types/number.rs b/src/script/execution/types/number.rs deleted file mode 100644 index 224c9dc..0000000 --- a/src/script/execution/types/number.rs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use common_data_types::{Float, FloatIsNan}; - -use crate::script::{execution::Failure, parsing, Span}; - -use super::{OperatorResult, Scalar, Value}; - -pub trait UnwrapNotNan: Sized { - fn unwrap_not_nan(self, span: &S) -> OperatorResult; -} - -pub fn unwrap_float(span: S, number: &parsing::Number) -> OperatorResult { - match number.to_float::() { - Ok(number) => Ok(number), - Err(error) => Err(Failure::NumberConversion(span, error)), - } -} - -impl UnwrapNotNan for std::result::Result { - fn unwrap_not_nan(self, span: &S) -> OperatorResult { - match self { - Ok(number) => Ok(number), - Err(_float_is_nan) => Err(Failure::ResultIsNan(span.clone())), - } - } -} - -impl From for Value { - fn from(value: Float) -> Self { - let measurement: Scalar = value.into(); - measurement.into() - } -} diff --git a/src/script/execution/types/range.rs b/src/script/execution/types/range.rs deleted file mode 100644 index ef58636..0000000 --- a/src/script/execution/types/range.rs +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use common_data_types::Float; - -use crate::script::{ - execution::{expressions::run_trailer, ExecutionContext, Failure}, - logging::RuntimeLog, - parsing::{self, VariableType}, - Span, -}; - -use super::{math::Number, NamedObject, Object, OperatorResult, Value}; - -#[derive(Debug, Clone, PartialEq)] -pub struct Range { - pub lower_bound: Option, - pub upper_bound_is_inclusive: bool, - pub upper_bound: Option, -} - -impl Object for Range { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::Range)) - } - - fn iterate( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - ) -> OperatorResult>>> { - match ( - self.lower_bound.as_ref(), - self.upper_bound.as_ref(), - self.upper_bound_is_inclusive, - ) { - (None, None, false) => Err(Failure::CannotConvertFromTo( - span.clone(), - "..".into(), - "iterator".into(), - )), - (Some(_lower_bound), None, false) => Err(Failure::CannotConvertFromTo( - span.clone(), - "lower_bound..".into(), - "iterator".into(), - )), - (None, Some(_upper_bound), false) => Err(Failure::CannotConvertFromTo( - span.clone(), - "..upper_bound".into(), - "iterator".into(), - )), - (None, Some(_upper_bound), true) => Err(Failure::CannotConvertFromTo( - span.clone(), - "..=upper_bound".into(), - "iterator".into(), - )), - (Some(lower_bound), Some(upper_bound), false) => { - let lower_bound = lower_bound.to_index(); - let upper_bound = upper_bound.to_index(); - Ok(Box::new( - (lower_bound..upper_bound).map(|index| Float::new(index as _).unwrap().into()), - )) - } - (Some(lower_bound), Some(upper_bound), true) => { - let lower_bound = lower_bound.to_index(); - let upper_bound = upper_bound.to_index(); - Ok(Box::new( - (lower_bound..=upper_bound).map(|index| Float::new(index as _).unwrap().into()), - )) - } - (_, None, true) => unreachable!(), // Inclusive ranges without an upper bound are illegal to construct. - } - } -} - -impl Range { - pub fn from_parsed( - context: &mut ExecutionContext, - range: &parsing::Range, - ) -> OperatorResult { - let lower_bound = if let Some(lower_bound) = &range.lower_bound { - let span = lower_bound.get_span(); - Some(run_trailer(context, lower_bound)?.downcast(span)?) - } else { - None - }; - - let upper_bound_is_inclusive = range.upper_bound_is_inclusive; - - let upper_bound = if let Some(upper_bound) = &range.upper_bound { - let span = upper_bound.get_span(); - - Some(run_trailer(context, upper_bound)?.downcast(span)?) - } else { - None - }; - - if upper_bound_is_inclusive && upper_bound.is_none() { - return Err(Failure::MissingUpperBound(range.get_span().clone())); - } - - Ok(Self { - lower_bound, - upper_bound_is_inclusive, - upper_bound, - }) - } -} - -impl NamedObject for Range { - fn static_type_name() -> &'static str { - "Range" - } -} - -#[cfg(test)] -mod test { - use crate::script::Runtime; - - use super::*; - - #[test] - fn iterate() { - let mut runtime = Runtime::default(); - ExecutionContext::create(&mut runtime, |context| { - assert!( - Range::from_parsed(context, &parsing::Range::parse("..").unwrap().1) - .unwrap() - .iterate(context.log, &"span") - .is_err() - ); - - assert!( - Range::from_parsed(context, &parsing::Range::parse("1..").unwrap().1) - .unwrap() - .iterate(context.log, &"span") - .is_err() - ); - - assert!( - Range::from_parsed(context, &parsing::Range::parse("..1").unwrap().1) - .unwrap() - .iterate(context.log, &"span") - .is_err() - ); - assert!( - Range::from_parsed(context, &parsing::Range::parse("..=1").unwrap().1) - .unwrap() - .iterate(context.log, &"span") - .is_err() - ); - assert!( - Range::from_parsed(context, &parsing::Range::parse("..=1").unwrap().1) - .unwrap() - .iterate(context.log, &"span") - .is_err() - ); - assert_eq!( - Range::from_parsed(context, &parsing::Range::parse("1..5").unwrap().1) - .unwrap() - .iterate(context.log, &"span") - .unwrap() - .collect::>(), - [ - Float::new(1.0).unwrap().into(), - Float::new(2.0).unwrap().into(), - Float::new(3.0).unwrap().into(), - Float::new(4.0).unwrap().into(), - ] - ); - assert_eq!( - Range::from_parsed(context, &parsing::Range::parse("1..=5").unwrap().1) - .unwrap() - .iterate(context.log, &"span") - .unwrap() - .collect::>(), - [ - Float::new(1.0).unwrap().into(), - Float::new(2.0).unwrap().into(), - Float::new(3.0).unwrap().into(), - Float::new(4.0).unwrap().into(), - Float::new(5.0).unwrap().into(), - ] - ); - }); - } -} diff --git a/src/script/execution/types/serializable.rs b/src/script/execution/types/serializable.rs deleted file mode 100644 index 8a6adaa..0000000 --- a/src/script/execution/types/serializable.rs +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::{borrow::Cow, collections::HashMap}; - -use serde::{Deserialize, Serialize}; - -use crate::script::{ - execution::{types::StructDefinition, validate_assignment_type, ExecutionContext, Failure}, - logging::RuntimeLog, - parsing::VariableType, - Span, -}; - -use super::{DefaultValue, List, Object, OperatorResult, SString, Scalar, Structure, Value}; - -#[derive(Debug, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "snake_case")] -pub enum SerializableValue { - #[serde(skip_serializing)] - Default, - #[serde(untagged)] - Boolean(bool), - #[serde(untagged)] - Struct { - #[serde(rename = "type")] - ty: String, - members: HashMap, - }, - #[serde(untagged)] - List(Vec), - #[serde(untagged)] - Scalar(Scalar), - #[serde(untagged)] - String(String), -} - -impl SerializableValue { - pub fn into_value_without_type_check( - self, - context: &mut ExecutionContext, - span: &S, - ) -> OperatorResult> { - match self { - Self::Default => Ok(DefaultValue.into()), - Self::Boolean(value) => Ok((value).into()), - Self::Struct { - ty, - members: mut values, - } => { - let initalizer = context - .stack - .get_variable_str(span, ty.as_ref())? - .downcast_ref::>(span)? - .definition - .clone(); - - let mut failures = Vec::new(); - let mut table = HashMap::with_capacity(initalizer.members.len()); - - for member in initalizer.members.iter() { - if let Some(value) = values.remove(member.name.as_str()) { - match value.into_value(context, span, &member.ty.ty) { - Ok(value) => { - match validate_assignment_type(context, member, span, value, span) { - Ok(value) => { - table.insert(member.name.to_string(), value); - } - Err(failure) => failures.push(failure), - } - } - Err(failure) => { - failures.push(failure); - } - } - } else { - failures.push(Failure::StructMissingAssignment( - span.clone(), - member.name.clone(), - )); - } - } - - if failures.is_empty() { - Ok(Structure::new(initalizer, table).into()) - } else { - Err(Failure::StructConstruction(span.clone(), failures)) - } - } - Self::List(values) => { - let mut collected_values: Vec> = Vec::with_capacity(values.len()); - - for value in values { - let value = value.into_value_without_type_check(context, span)?; - collected_values.push(value); - } - - Ok(List::from(collected_values).into()) - } - Self::String(value) => Ok(SString::from(value).into()), - Self::Scalar(measurement) => Ok(measurement.into()), - } - } - - pub fn into_value( - self, - context: &mut ExecutionContext, - span: &S, - ty: &VariableType, - ) -> OperatorResult> { - if self.matches_type(ty, context.log, span)? { - self.into_value_without_type_check(context, span) - } else { - Err(Failure::CannotConvertFromTo( - span.clone(), - self.type_name(), - ty.name(), - )) - } - } - - fn matches_type( - &self, - ty: &VariableType, - log: &mut dyn RuntimeLog, - variable_name_span: &S, - ) -> OperatorResult { - Ok(match (self, ty) { - (Self::Boolean(_), VariableType::Boolean) => true, - (Self::Struct { ty: s_ty, .. }, VariableType::Struct(v_ty)) => v_ty.as_str() == s_ty, - (Self::List(_), VariableType::List) => true, - (Self::String(_), VariableType::String) => true, - (Self::Scalar(measurement), ty) => { - measurement.matches_type(ty, log, variable_name_span)? - } - (Self::Default, _) => true, - _ => false, - }) - } - - fn type_name(&self) -> Cow<'static, str> { - match self { - SerializableValue::Default => "Default".into(), - SerializableValue::Boolean(_) => "Boolean".into(), - SerializableValue::Struct { ty, members: _ } => format!("struct {}", ty).into(), - SerializableValue::List(_) => "List".into(), - SerializableValue::String(_) => "String".into(), - SerializableValue::Scalar(measurement) => { - >::type_name(measurement) - } - } - } -} - -#[cfg(test)] -mod test { - use common_data_types::Float; - use uom::si::{f64::Length, length::millimeter}; - - use crate::script::{ - execution::{ - expressions::run_expression, - types::{Object, SString, Scalar}, - Module, - }, - parsing::Expression, - Runtime, - }; - - use super::*; - - #[test] - fn deserialize_boolean() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - serde_yaml::from_str::("true") - .unwrap() - .into_value(context, &"", &VariableType::Boolean), - Ok(true.into()) - ); - assert_eq!( - serde_yaml::from_str::("false") - .unwrap() - .into_value(context, &"", &VariableType::Boolean), - Ok(false.into()) - ); - }); - } - - #[test] - fn serialize_boolean() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let boolean: Value<&'static str> = true.into(); - assert_eq!( - boolean.export(context.log, &""), - Ok(SerializableValue::Boolean(true)) - ); - - let boolean: Value<&'static str> = false.into(); - assert_eq!( - boolean.export(context.log, &""), - Ok(SerializableValue::Boolean(false)) - ); - }); - } - - #[test] - fn deserialize_number() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - serde_yaml::from_str::("42") - .unwrap() - .into_value(context, &"", &VariableType::Scalar("Number")), - Ok(Float::new(42.0).unwrap().into()) - ); - }); - } - - #[test] - fn serialize_number() { - let value = SerializableValue::Scalar(Scalar::from(Float::new(42.0).unwrap())); - - assert_eq!(serde_yaml::to_string(&value).unwrap(), "42.0\n"); - } - - #[test] - fn deserialize_struct() { - let mut log = Vec::new(); - - let module = Module::load( - &mut log, - "test_module.ccm", - r#"struct MyStruct { value: Number = 1 }"#, - ) - .unwrap(); - - assert!(log.is_empty()); - - ExecutionContext::create(&mut Runtime::from(module), |context| { - let struct_def = r#" - type: MyStruct - members: - value: 42"#; - - let structure = serde_yaml::from_str::(struct_def) - .unwrap() - .into_value(context, &"", &VariableType::Struct("MyStruct")); - - assert_eq!( - structure.unwrap(), - run_expression( - context, - &Expression::parse("MyStruct { value = 42 }").unwrap().1 - ) - .unwrap() - ); - - let struct_def = r#" - type: MyStruct - members: - value: default"#; - - let structure = serde_yaml::from_str::(struct_def) - .unwrap() - .into_value(context, &"", &VariableType::Struct("MyStruct")); - - assert_eq!( - structure.unwrap(), - run_expression( - context, - &Expression::parse("MyStruct { ..default }").unwrap().1 - ) - .unwrap() - ); - - assert!(serde_yaml::from_str::(struct_def) - .unwrap() - .into_value(context, &"", &VariableType::Struct("OtherStruct")) - .is_err()); - - let struct_def = r#" - type: MyStruct - members: - wrong_value: 42"#; - assert!(serde_yaml::from_str::(struct_def) - .unwrap() - .into_value(context, &"", &VariableType::Struct("MyStruct")) - .is_err()); - - let struct_def = r#" - type: MyStruct - members: - value: true"#; - assert!(serde_yaml::from_str::(struct_def) - .unwrap() - .into_value(context, &"", &VariableType::Struct("MyStruct")) - .is_err()); - }); - } - - #[test] - fn serialize_struct() { - let mut log = Vec::new(); - - let module = Module::load( - &mut log, - "test_module.ccm", - r#"struct MyStruct { value: Number }"#, - ) - .unwrap(); - - assert!(log.is_empty()); - - ExecutionContext::create(&mut Runtime::from(module), |context| { - let value: Value<&'static str> = run_expression( - context, - &Expression::parse("MyStruct { value = 42 }").unwrap().1, - ) - .unwrap(); - - assert_eq!( - value.export(context.log, &""), - Ok(SerializableValue::Struct { - ty: "MyStruct".to_string(), - members: HashMap::from([( - "value".to_string(), - SerializableValue::Scalar(Scalar::from(Float::new(42.0).unwrap())) - )]) - }) - ); - }); - } - - #[test] - fn deserialize_list() { - let list_def = r#"[1, true, "some text"]"#; - - ExecutionContext::create(&mut Runtime::default(), |context| { - let list = serde_yaml::from_str::(list_def).unwrap(); - - let list = list.into_value(context, &"", &VariableType::List); - - assert_eq!( - list.unwrap(), - run_expression( - context, - &Expression::parse("[1, true, \"some text\"]").unwrap().1 - ) - .unwrap() - ); - }); - } - - #[test] - fn serialize_list() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let value: Value<&'static str> = List::from([ - Float::new(1.0).unwrap().into(), - true.into(), - SString::from("some text".to_string()).into(), - ]) - .into(); - assert_eq!( - value.export(context.log, &""), - Ok(SerializableValue::List(vec![ - SerializableValue::Scalar(Scalar::from(Float::new(1.0).unwrap())), - SerializableValue::Boolean(true), - SerializableValue::String("some text".to_string()) - ])) - ); - }); - } - - #[test] - fn deserialize_string() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - serde_yaml::from_str::("text") - .unwrap() - .into_value(context, &"", &VariableType::String), - Ok(SString::from("text").into()) - ); - }); - } - - #[test] - fn serialize_string() { - ExecutionContext::create(&mut Runtime::default(), |context| { - let value: Value<&'static str> = SString::from("This is a test".to_string()).into(); - assert_eq!( - value.export(context.log, &""), - Ok(SerializableValue::String("This is a test".to_string())) - ); - }); - } - - #[test] - fn deserialize_measurement() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - serde_yaml::from_str::("42mm") - .unwrap() - .into_value(context, &"", &VariableType::Scalar("Length")), - Ok(Scalar::try_from(Length::new::(42.0)) - .unwrap() - .into()) - ); - }); - } - - #[test] - fn serialize_measurement() { - let value = - SerializableValue::Scalar(Scalar::try_from(Length::new::(42.0)).unwrap()); - - assert_eq!(serde_yaml::to_string(&value).unwrap(), "0.042 m\n"); - } - - #[test] - fn deserialize_default() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - serde_yaml::from_str::("default") - .unwrap() - .into_value(context, &"", &VariableType::Scalar("Length")), - Ok(DefaultValue.into()) - ); - }); - } - - #[test] - fn serialize_default() { - let value = SerializableValue::Default; - - // It's supposed to fail. - assert!(serde_yaml::to_string(&value).is_err()); - } -} diff --git a/src/script/execution/types/string/formatting.rs b/src/script/execution/types/string/formatting.rs deleted file mode 100644 index f3e9a5a..0000000 --- a/src/script/execution/types/string/formatting.rs +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::fmt::Write; - -use nom::{ - branch::alt, - bytes::complete::{tag, take_while1}, - character::complete::char as nom_char, - combinator::{all_consuming, cut, map, opt, success, value, verify}, - error::context, - multi::{fold_many0, many0}, - sequence::{delimited, pair, preceded, terminated}, - IResult, -}; - -use crate::script::{ - execution::{ - types::{ - math::Number, unsupported_operation_message, NamedObject, Object, OperatorResult, Value, - }, - Failure, - }, - logging::RuntimeLog, - Scalar, Span, -}; - -pub type VResult = IResult>; - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum Style { - Default, - Debug, - Octal, - Hex, - CapitalizedHex, - Exponent, - CapitalizedExponent, -} - -impl Style { - fn parse(input: &str) -> VResult<&str, Self> { - alt(( - value(Self::Debug, nom_char('?')), - value(Self::Octal, nom_char('o')), - value(Self::Hex, nom_char('x')), - value(Self::CapitalizedHex, nom_char('X')), - value(Self::Exponent, nom_char('e')), - value(Self::CapitalizedExponent, nom_char('E')), - ))(input) - } -} - -impl UnsupportedMessage for Style { - fn unsupported_message>( - &self, - object: &O, - span: &S, - ) -> OperatorResult { - match self { - Self::Default => { - unsupported_operation_message::(object, span, "format with default style") - } - Self::Debug => { - unsupported_operation_message::(object, span, "format with debug style") - } - Self::Octal => { - unsupported_operation_message::(object, span, "format with octal style") - } - Self::Hex => { - unsupported_operation_message::(object, span, "format with hex style") - } - Self::CapitalizedHex => unsupported_operation_message::( - object, - span, - "format with capitalized hex style", - ), - Self::Exponent => unsupported_operation_message::( - object, - span, - "format with exponent style", - ), - Self::CapitalizedExponent => unsupported_operation_message::( - object, - span, - "format with capitalized exponent style", - ), - } - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -enum Precision { - Default, - Inline(u8), - Referenced(u8), -} - -impl Precision { - fn parse(input: &str) -> VResult<&str, Self> { - preceded( - nom_char('.'), - alt(( - map(terminated(number, nom_char('$')), Self::Referenced), - map(number, Self::Inline), - )), - )(input) - } -} - -// We don't directly pass the precision information to objects when formatting, rather -// we dereference it (when applicable) before hand and pass them the final result. -impl UnsupportedMessage for Option { - fn unsupported_message>( - &self, - object: &O, - span: &S, - ) -> OperatorResult { - match self { - None => unsupported_operation_message::( - object, - span, - "format with default precision", - ), - Some(_) => { - unsupported_operation_message::(object, span, "format with precision") - } - } - } -} - -#[derive(Debug, PartialEq)] -struct Parameter { - index: Option, - style: Style, - precision: Precision, -} - -impl Parameter { - fn parse(input: &str) -> VResult<&str, Self> { - delimited( - nom_char('{'), - cut(map( - pair( - opt(number), - alt(( - preceded( - nom_char(':'), - pair( - alt((Style::parse, success(Style::Default))), - alt((Precision::parse, success(Precision::Default))), - ), - ), - success((Style::Default, Precision::Default)), - )), - ), - |(index, (style, precision))| Parameter { - index, - style, - precision, - }, - )), - cut(context("Expected closing `}` for parameter", nom_char('}'))), - )(input) - } -} - -#[derive(Debug, PartialEq)] -enum Component { - Litteral(String), - Parameter(Parameter), -} - -impl Component { - fn parse(input: &str) -> VResult<&str, Self> { - alt((Self::parse_litteral, map(Parameter::parse, Self::Parameter)))(input) - } - - fn parse_litteral(input: &str) -> VResult<&str, Self> { - map( - verify( - fold_many0( - alt((take_while1(|c| c != '{'), tag("{{"), tag("}}"))), - String::default, - |mut accum, item: &str| { - match item { - "{{" => accum.push('{'), - "}}" => accum.push('}'), - _ => { - accum.push_str(item); - } - } - - accum - }, - ), - |output: &str| !output.is_empty(), - ), - Self::Litteral, - )(input) - } -} - -#[derive(Debug, PartialEq)] -pub struct Format { - components: Vec, -} - -impl Format { - pub fn parse(input: &str) -> VResult<&str, Self> { - map(all_consuming(many0(Component::parse)), |components| Self { - components, - })(input) - } - - pub fn format( - &self, - log: &mut dyn RuntimeLog, - span: &S, - f: &mut dyn Write, - arguments: &[Value], - ) -> OperatorResult { - let mut next_argument_index = 0; - - fn get_precision( - span: &S, - - precision: &Precision, - arguments: &[Value], - ) -> OperatorResult> { - match precision { - Precision::Default => Ok(None), - Precision::Inline(precision) => Ok(Some(*precision)), - Precision::Referenced(index) => { - if let Some(argument) = arguments.get(*index as usize) { - let precision = argument.downcast_ref::(span)?; - let precision = Number::try_from(precision).map_err(|_| { - Failure::ExpectedGot( - span.clone(), - Number::static_type_name().into(), - >::type_name(precision), - ) - })?; - let precision = precision.to_index(); - - if precision.is_positive() { - Ok(Some(precision as u8)) - } else { - Err(Failure::InvalidPrecision(span.clone(), precision)) - } - } else { - Err(Failure::FormatArgumentIndexOutOfRange( - span.clone(), - *index as isize, - )) - } - } - } - } - - for component in self.components.iter() { - match component { - Component::Litteral(text) => { - write!(f, "{}", text).unwrap_formatting_result(span)? - } - Component::Parameter(Parameter { - index: None, - style, - precision, - }) => { - let precision = get_precision(span, precision, arguments)?; - let argument_index = next_argument_index; - next_argument_index += 1; - - if let Some(argument) = arguments.get(argument_index) { - argument.format(log, span, f, *style, precision)?; - } else { - return Err(Failure::FormatArgumentIndexOutOfRange( - span.clone(), - argument_index as isize, - )); - } - } - Component::Parameter(Parameter { - index: Some(index), - style, - precision, - }) => { - let precision = get_precision(span, precision, arguments)?; - let argument_index = *index as usize; - - if let Some(argument) = arguments.get(argument_index) { - argument.format(log, span, f, *style, precision)?; - } else { - return Err(Failure::FormatArgumentIndexOutOfRange( - span.clone(), - argument_index as isize, - )); - } - } - } - } - - Ok(()) - } -} - -fn number(input: &str) -> VResult<&str, u8> { - map(take_while1(|c| "0123456789".contains(c)), |digits: &str| { - digits.parse::().unwrap() - })(input) -} - -pub trait UnwrapFormattingResult { - fn unwrap_formatting_result(self, span: &S) -> OperatorResult; -} - -impl UnwrapFormattingResult for std::result::Result { - fn unwrap_formatting_result(self, span: &S) -> OperatorResult { - match self { - Ok(result) => Ok(result), - Err(error) => Err(Failure::Formatting(span.clone(), error)), - } - } -} - -pub trait UnsupportedMessage { - fn unsupported_message>( - &self, - object: &O, - span: &S, - ) -> OperatorResult; -} - -#[cfg(test)] -mod test { - use crate::script::logging::StandardLog; - use common_data_types::Float; - - use super::*; - - #[test] - fn litterals() { - assert_eq!( - Component::parse_litteral("1234"), - Ok(("", Component::Litteral("1234".into()))) - ); - - assert_eq!( - Component::parse_litteral("1234{{}"), - Ok(("", Component::Litteral("1234{}".into()))) - ); - } - - #[test] - fn parameters() { - assert_eq!( - Parameter::parse("{}"), - Ok(( - "", - Parameter { - index: None, - style: Style::Default, - precision: Precision::Default - } - )) - ); - assert_eq!( - Parameter::parse("{1}"), - Ok(( - "", - Parameter { - index: Some(1), - style: Style::Default, - precision: Precision::Default - } - )) - ); - assert_eq!( - Parameter::parse("{:?}"), - Ok(( - "", - Parameter { - index: None, - style: Style::Debug, - precision: Precision::Default - } - )) - ); - assert_eq!( - Parameter::parse("{1:?}"), - Ok(( - "", - Parameter { - index: Some(1), - style: Style::Debug, - precision: Precision::Default - } - )) - ); - assert_eq!( - Parameter::parse("{:.5}"), - Ok(( - "", - Parameter { - index: None, - style: Style::Default, - precision: Precision::Inline(5) - } - )) - ); - assert_eq!( - Parameter::parse("{:.5$}"), - Ok(( - "", - Parameter { - index: None, - style: Style::Default, - precision: Precision::Referenced(5) - } - )) - ); - } - - #[test] - fn litterals_and_parameters() { - assert_eq!(Format::parse(""), Ok(("", Format { components: vec![] }))); - - assert_eq!( - Format::parse(" {{}}"), - Ok(( - "", - Format { - components: vec![Component::Litteral(" {}".into())] - } - )) - ); - - assert_eq!( - Format::parse("{}"), - Ok(( - "", - Format { - components: vec![Component::Parameter(Parameter { - index: None, - style: Style::Default, - precision: Precision::Default - })] - } - )) - ); - assert_eq!( - Format::parse(" {}"), - Ok(( - "", - Format { - components: vec![ - Component::Litteral(" ".into()), - Component::Parameter(Parameter { - index: None, - style: Style::Default, - precision: Precision::Default - }) - ] - } - )) - ); - assert_eq!( - Format::parse(" {{}}{}"), - Ok(( - "", - Format { - components: vec![ - Component::Litteral(" {}".into()), - Component::Parameter(Parameter { - index: None, - style: Style::Default, - precision: Precision::Default - }) - ] - } - )) - ); - } - - #[test] - fn do_format() { - let mut log = StandardLog; - - let mut formatted = String::default(); - Format::parse("Test {}") - .unwrap() - .1 - .format::<&str>( - &mut log, - &"scope", - &mut formatted, - &[Float::new(42.24).unwrap().into()], - ) - .unwrap(); - assert_eq!(formatted, "Test 42.24"); - - let mut formatted = String::default(); - Format::parse("Test {1} {0}") - .unwrap() - .1 - .format::<&str>( - &mut log, - &"scope", - &mut formatted, - &[ - Float::new(1.0).unwrap().into(), - Float::new(2.0).unwrap().into(), - ], - ) - .unwrap(); - assert_eq!(formatted, "Test 2 1"); - } -} diff --git a/src/script/execution/types/string/mod.rs b/src/script/execution/types/string/mod.rs deleted file mode 100644 index ebe357e..0000000 --- a/src/script/execution/types/string/mod.rs +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use common_data_types::Float; -use imstr::ImString; -use std::{cell::RefCell, cmp::Ordering, fmt::Write}; - -use crate::script::{ - execution::{ExecutionContext, Failure}, - logging::RuntimeLog, - parsing::{self, Expression, VariableType}, - Span, -}; - -use super::{ - function::AutoCall, math::Number, number::UnwrapNotNan, serializable::SerializableValue, - NamedObject, NoneType, Object, OperatorResult, Range, UnwrapBorrowFailure, Value, -}; - -pub mod formatting; -use formatting::{Style, UnsupportedMessage, UnwrapFormattingResult}; - -static ESCAPE_SEQUENCES: &[(&str, &str)] = &[("\\\"", "\""), ("\\n", "\n"), ("\\\\", "\\")]; - -#[derive(Debug, Clone, PartialEq)] -pub struct SString { - string: RefCell, -} - -impl Object for SString { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(matches!(ty, VariableType::String)) - } - - fn format( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - f: &mut dyn Write, - style: Style, - precision: Option, - ) -> OperatorResult { - let string = self.string.try_borrow().unwrap_borrow_failure(span)?; - - match (style, precision) { - (Style::Default, None) => write!(f, "{}", string).unwrap_formatting_result(span), - (Style::Debug, None) => { - let mut sequence_iter = ESCAPE_SEQUENCES.iter(); - let (replace, find) = sequence_iter.next().unwrap(); // Should never fail since we static initalized that array. - - let mut to_print = string.replace(find, replace); - - for (replace, find) in sequence_iter { - to_print = to_print.replace(find, replace); - } - - write!(f, "\"{}\"", to_print).unwrap_formatting_result(span) - } - (_, None) => style.unsupported_message(self, span), - (Style::Default | Style::Debug, _) => style.unsupported_message(self, span), - _ => { - style.unsupported_message(self, span).ok(); - precision.unsupported_message(self, span) - } - } - } - - fn index( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - index: Value, - ) -> OperatorResult> { - let range = index.downcast::(span)?; - let string = self.string.try_borrow().unwrap_borrow_failure(span)?; - - // TODO could we keep an immutable reference to the original string to avoid a copy? - let slice = match ( - range.lower_bound, - range.upper_bound, - range.upper_bound_is_inclusive, - ) { - (None, None, false) => string.get(..).ok_or((None, None)), - (Some(lower_bound), None, false) => { - let signed_lower_bound = lower_bound.to_index(); - let lower_bound = self.internalize_index(span, signed_lower_bound)?; - string - .get(lower_bound..) - .ok_or((Some(signed_lower_bound), None)) - } - (None, Some(upper_bound), false) => { - let signed_upper_bound = upper_bound.to_index(); - let upper_bound = self.internalize_index(span, signed_upper_bound)?; - string - .get(..upper_bound) - .ok_or((None, Some(signed_upper_bound))) - } - (None, Some(upper_bound), true) => { - let signed_upper_bound = upper_bound.to_index(); - let upper_bound = self.internalize_index(span, signed_upper_bound)?; - string - .get(..=upper_bound) - .ok_or((None, Some(signed_upper_bound))) - } - (Some(lower_bound), Some(upper_bound), false) => { - let signed_lower_bound = lower_bound.to_index(); - let lower_bound = self.internalize_index(span, signed_lower_bound)?; - let signed_upper_bound = upper_bound.to_index(); - let upper_bound = self.internalize_index(span, signed_upper_bound)?; - string - .get(lower_bound..upper_bound) - .ok_or((Some(signed_lower_bound), Some(signed_upper_bound))) - } - (Some(lower_bound), Some(upper_bound), true) => { - let signed_lower_bound = lower_bound.to_index(); - let lower_bound = self.internalize_index(span, signed_lower_bound)?; - let signed_upper_bound = upper_bound.to_index(); - let upper_bound = self.internalize_index(span, signed_upper_bound)?; - string - .get(lower_bound..=upper_bound) - .ok_or((Some(signed_lower_bound), Some(signed_upper_bound))) - } - (_, None, true) => unreachable!(), // Inclusive ranges without an upper bound are illegal to construct. - }; - - // TODO List has an identical error handling. We should probably move this to a common library. - let range_type = if range.upper_bound_is_inclusive { - "..=" - } else { - ".." - }; - - slice - .map(|slice| Self::from(slice.to_string()).into()) - .map_err(|(lower_bound, upper_bound)| { - Failure::SliceOutOfRange(span.clone(), lower_bound, range_type, upper_bound) - }) - } - - fn cmp( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult { - let rhs = rhs.downcast_ref::(span)?; - - Ok(self.string.cmp(&rhs.string)) - } - - fn addition( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - rhs: &Value, - ) -> OperatorResult> { - let mut string = self - .string - .try_borrow() - .unwrap_borrow_failure(span)? - .clone() - .into_std_string(); - - match rhs { - Value::String(rhs) => { - string.push_str(&rhs.as_str(span)?); - - Ok(Self::from(string).into()) - } - Value::Scalar(rhs) => { - // convert numbers to strings. - let rhs = rhs.to_number(span)?; - - string += &format!("{}", rhs.into_inner()); - - Ok(Self::from(string).into()) - } - _ => Err(Failure::ExpectedGot( - span.clone(), - "string or number".into(), - rhs.type_name(), - )), - } - } - - fn method_call( - &self, - context: &mut ExecutionContext, - span: &S, - attribute: &S, - arguments: Vec>, - expressions: &[Expression], - ) -> OperatorResult> { - match attribute.as_str() { - "insert" => |_context: &mut ExecutionContext, - span: &S, - index: Number, - text: SString| - -> OperatorResult> { - let index = index.to_index(); - let index = self.internalize_index(span, index)?; - - self.string - .try_borrow_mut() - .unwrap_borrow_failure(span)? - .insert_str(index, &text.as_str(span)?); - Ok(NoneType.into()) - } - .auto_call(context, span, arguments, expressions), // insert_str - "is_empty" => { - |_context: &mut ExecutionContext, _span: &S| -> OperatorResult> { - Ok(self - .string - .try_borrow() - .unwrap_borrow_failure(span)? - .is_empty() - .into()) - } - .auto_call(context, span, arguments, expressions) - } - "len" => { - |_context: &mut ExecutionContext, span: &S| -> OperatorResult> { - Float::new(self.string.try_borrow().unwrap_borrow_failure(span)?.len() as f64) - .unwrap_not_nan(span) - .map(|n| n.into()) - } - .auto_call(context, span, arguments, expressions) - } - "format" => { - let string = self.string.try_borrow().unwrap_borrow_failure(span)?; - match formatting::Format::parse(string.as_str()) { - Ok((_, format)) => { - let mut output = String::new(); - format.format(context.log, span, &mut output, &arguments)?; - - Ok(Self::from(output).into()) - } - Err(_error) => { - // TODO Better context would be appreciated here. - Err(Failure::ParseFormatter(span.clone())) - } - } - } - // "lines" => todo!(), // TODO when we have iterators. - _ => Err(Failure::UnknownAttribute(attribute.clone())), - } - } - - fn export( - &self, - _log: &mut dyn RuntimeLog, - span: &S, - ) -> OperatorResult { - let string = self.to_string(span)?; - Ok(SerializableValue::String(string)) - } -} - -pub struct StrRef<'a> { - reference: std::cell::Ref<'a, ImString>, -} - -impl<'a> std::ops::Deref for StrRef<'a> { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.reference.as_str() - } -} - -impl SString { - pub fn from_parsed(parsed: &parsing::PString) -> OperatorResult> { - let mut sequence_iter = ESCAPE_SEQUENCES.iter(); - let (find, replace) = sequence_iter.next().unwrap(); // Should never fail since we static initalized that array. - - let mut string = parsed.value.as_str().replace(find, replace); - - for (find, replace) in sequence_iter { - string = string.replace(find, replace); - } - - let string = RefCell::new(string.into()); - Ok(Self { string }.into()) - } - - pub fn as_str<'a, S: Span>(&'a self, span: &S) -> OperatorResult> { - let reference = self.string.try_borrow().unwrap_borrow_failure(span)?; - let string_reference = StrRef { reference }; - - Ok(string_reference) - } - - pub fn to_string(&self, span: &S) -> OperatorResult { - Ok(self - .string - .try_borrow() - .unwrap_borrow_failure(span)? - .clone() - .into_std_string()) - } - - fn internalize_index(&self, span: &S, index: isize) -> OperatorResult { - let string = self.string.try_borrow().unwrap_borrow_failure(span)?; - - let new_index = if index >= 0 { - Ok(index as usize) - } else if let Some(index) = string.len().checked_sub(index.unsigned_abs()) { - Ok(index) - } else { - Err(Failure::IndexOutOfRange(span.clone(), index)) - }?; - - if new_index >= string.len() { - Err(Failure::IndexOutOfRange(span.clone(), index)) - } else if string.is_char_boundary(new_index) { - Ok(new_index) - } else if new_index < string.len() { - Err(Failure::InvalidCharIndex(span.clone(), new_index as isize)) - } else { - Err(Failure::IndexOutOfRange(span.clone(), new_index as isize)) - } - } -} - -impl NamedObject for SString { - fn static_type_name() -> &'static str { - "String" - } -} - -impl From for SString -where - S: Into, -{ - fn from(value: S) -> Self { - Self { - string: RefCell::new(value.into()), - } - } -} - -#[cfg(test)] -mod test { - use crate::script::{ - execution::{expressions::run_expression, ExecutionContext}, - Runtime, - }; - - use super::*; - - #[test] - fn string_concat() { - ExecutionContext::create(&mut Runtime::default(), |context| { - assert_eq!( - run_expression( - context, - &Expression::parse("\"test\" + \"test\" == \"testtest\"") - .unwrap() - .1 - ), - Ok(true.into()) - ); - - assert_eq!( - run_expression( - context, - &Expression::parse("\"test\" + 5 == \"test5\"").unwrap().1 - ), - Ok(true.into()) - ); - }); - } -} diff --git a/src/script/execution/types/structures.rs b/src/script/execution/types/structures.rs deleted file mode 100644 index 1b46df6..0000000 --- a/src/script/execution/types/structures.rs +++ /dev/null @@ -1,536 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::{collections::HashMap, rc::Rc}; - -use crate::script::{ - execution::{ - expressions::{run_expression, run_trailer}, - validate_assignment_type, ExecutionContext, Failure, - }, - logging::RuntimeLog, - parsing::{self, StructInitialization, Trailer, VariableType}, - Span, -}; - -use super::{serializable::SerializableValue, NamedObject, Object, OperatorResult, Value}; - -#[derive(Debug, PartialEq, Clone)] -pub struct Structure { - ty: Rc>, - pub members: Rc>>, -} - -impl Structure { - pub fn name(&self) -> &str { - self.ty.name.as_str() - } -} - -impl Object for Structure { - fn matches_type( - &self, - ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - Ok(if let VariableType::Struct(type_name) = ty { - type_name.as_str() == self.ty.name.as_str() - } else { - false - }) - } - - fn attribute( - &self, - _log: &mut dyn RuntimeLog, - _span: &S, - name: &S, - ) -> OperatorResult> { - let key = name.as_str(); - - if let Some(value) = self.members.get(key) { - Ok(value.clone()) - } else { - Err(Failure::UnknownAttribute(name.clone())) - } - } - - fn export( - &self, - log: &mut dyn RuntimeLog, - span: &S, - ) -> OperatorResult { - let mut members = HashMap::with_capacity(self.members.len()); - - for (key, item) in self.members.iter() { - let serializable = item.export(log, span)?; - members.insert(key.clone(), serializable); - } - - Ok(SerializableValue::Struct { - ty: self.ty.name.to_string(), - members, - }) - } -} - -impl NamedObject for Structure { - fn static_type_name() -> &'static str { - "struct" - } -} - -impl Structure { - pub fn new(ty: Rc>, table: HashMap>) -> Self { - Self { - ty, - members: Rc::new(table), - } - } - - #[cfg(test)] - pub fn from_array( - ty: Rc>, - values: [(String, Value); N], - ) -> Self { - Self { - ty, - members: Rc::new(HashMap::from(values)), - } - } - - pub fn initalization( - context: &mut ExecutionContext, - struct_definition: &Trailer, - initalization: &StructInitialization, - ) -> OperatorResult> { - enum Inheritance<'a, S: Span> { - Structure(&'a Structure), - Default, - None, - } - - fn build_struct( - context: &mut ExecutionContext, - struct_definition: &Rc>, - initalization: &StructInitialization, - inheritance: Inheritance, - ) -> OperatorResult> { - // We're going to run through this whole struct and collect all the errors at once. - let mut failures = Vec::new(); - - let mut members = HashMap::with_capacity(struct_definition.members.len()); - - for member in struct_definition.members.iter() { - let name = member.name.as_str(); - - let assignment = - initalization - .assignments - .iter() - .find_map(|(assignment_name, assignment)| { - if assignment_name.as_str() == name { - Some(assignment) - } else { - None - } - }); - - let value = if let Some(expression) = assignment { - match run_expression(context, expression) { - Ok(value) => match validate_assignment_type( - context, - member, - expression.get_span(), - value, - expression.get_span(), - ) { - Ok(value) => value, - Err(failure) => { - failures.push(failure); - continue; - } - }, - Err(failure) => { - failures.push(failure); - continue; - } - } - } else { - match inheritance { - Inheritance::Structure(inheritance) => { - if let Some(value) = inheritance.members.get(member.name.as_str()) { - value.clone() - } else { - failures.push(Failure::StructMissingAssignment( - initalization.get_span().clone(), - member.name.clone(), - )); - continue; - } - } - Inheritance::Default => { - if let Some(default) = &member.ty.default_value { - match Value::from_litteral(context, default) { - Ok(value) => value, - Err(failure) => { - failures.push(failure); - continue; - } - } - } else { - failures.push(Failure::NoDefault( - initalization.get_span().clone(), - member.name.clone(), - )); - continue; - } - } - Inheritance::None => { - failures.push(Failure::StructMissingAssignment( - initalization.get_span().clone(), - member.name.clone(), - )); - continue; - } - } - }; - - members.insert(member.name.to_string(), value); - } - - // Make sure there aren't extra assignments. - for (name, _expression) in initalization.assignments.iter() { - if !struct_definition - .members - .iter() - .any(|member| member.name.as_str() == name.as_str()) - { - failures.push(Failure::StructExcessAssignment(name.clone())); - } - } - - if failures.is_empty() { - Ok(Structure { - ty: Rc::clone(struct_definition), - members: Rc::new(members), - } - .into()) - } else { - Err(Failure::StructConstruction( - initalization.get_span().clone(), - failures, - )) - } - } - - let struct_evaluated_definition = run_trailer(context, struct_definition)? - .downcast::>(struct_definition.get_span())? - .definition; - - // Figure out our inheritance, if any. - if let Some(trailer) = &initalization.inheritance { - let trailer_result = run_trailer(context, trailer)?; - - match trailer_result { - Value::Structure(structure) => { - if structure.ty == struct_evaluated_definition { - build_struct( - context, - &struct_evaluated_definition, - initalization, - Inheritance::Structure(&structure), - ) - } else { - return Err(Failure::StructWrongInheritanceType( - trailer.get_span().clone(), - struct_evaluated_definition.name.clone(), - structure.ty.name.clone(), - )); - } - } - Value::Default(_span) => build_struct( - context, - &struct_evaluated_definition, - initalization, - Inheritance::Default, - ), - _ => { - return Err(Failure::ExpectedGot( - trailer.get_span().clone(), - "Struct or Default".into(), - trailer_result.type_name(), - )); - } - } - } else { - build_struct( - context, - &struct_evaluated_definition, - initalization, - Inheritance::None, - ) - } - } -} - -#[derive(Clone)] -pub struct StructDefinition { - pub definition: Rc>, -} - -impl Object for StructDefinition { - fn matches_type( - &self, - _ty: &VariableType, - _log: &mut dyn RuntimeLog, - _variable_name_span: &S, - ) -> OperatorResult { - // You cannot assign struct definitions to variables or pass them between functions. - Ok(false) - } -} - -impl std::fmt::Debug for StructDefinition { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("StructDefinition").finish() - } -} - -impl PartialEq for StructDefinition { - fn eq(&self, _other: &Self) -> bool { - false - } -} - -impl NamedObject for StructDefinition { - fn static_type_name() -> &'static str { - "StructDefinition" - } -} - -#[cfg(test)] -mod test { - use crate::script::{ - execution::{expressions::run_expression, ExecutionContext, Module}, - parsing::Expression, - Runtime, - }; - use common_data_types::Float; - - use super::*; - - #[test] - fn struct_initalization() { - let mut log = Vec::new(); - - let module = Module::load( - &mut log, - "test_module.ccm", - r#"struct EmptyStruct {} - struct DefaultStruct { value: Number = 42 } - struct TwoPartStruct { value: Number = 1, other: Number = 2 }"#, - ) - .unwrap(); - - assert!(log.is_empty()); - - let mut runtime = Runtime::from(module); - ExecutionContext::create(&mut runtime, |context| { - let two_part_struct = context - .stack - .get_variable(&"TwoPartStruct") - .unwrap() - .downcast_ref::>(&"") - .unwrap() - .definition - .clone(); - let empty_struct = context - .stack - .get_variable(&"EmptyStruct") - .unwrap() - .downcast_ref::>(&"") - .unwrap() - .definition - .clone(); - let default_struct = context - .stack - .get_variable(&"DefaultStruct") - .unwrap() - .downcast_ref::>(&"") - .unwrap() - .definition - .clone(); - - context.stack.new_variable( - &"two_part_struct", - Structure::from_array( - two_part_struct.clone(), - [ - ("value".into(), Float::new(3.0).unwrap().into()), - ("other".into(), Float::new(4.0).unwrap().into()), - ], - ) - .into(), - ); - - assert_eq!( - run_expression(context, &Expression::parse("EmptyStruct {}").unwrap().1), - Ok(Structure::from_array(empty_struct, []).into()) - ); - - assert_eq!( - run_expression( - context, - &Expression::parse("DefaultStruct { value = 24 }").unwrap().1 - ), - Ok(Structure::from_array( - default_struct.clone(), - [("value".into(), Float::new(24.0).unwrap().into())] - ) - .into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("DefaultStruct { ..default }").unwrap().1 - ), - Ok(Structure::from_array( - default_struct.clone(), - [("value".into(), Float::new(42.0).unwrap().into())] - ) - .into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("DefaultStruct { value = default }") - .unwrap() - .1 - ), - Ok(Structure::from_array( - default_struct, - [("value".into(), Float::new(42.0).unwrap().into())] - ) - .into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("TwoPartStruct { ..default }").unwrap().1 - ), - Ok(Structure::from_array( - two_part_struct.clone(), - [ - ("value".into(), Float::new(1.0).unwrap().into()), - ("other".into(), Float::new(2.0).unwrap().into()) - ] - ) - .into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("TwoPartStruct { value = 3, ..default }") - .unwrap() - .1 - ), - Ok(Structure::from_array( - two_part_struct.clone(), - [ - ("value".into(), Float::new(3.0).unwrap().into()), - ("other".into(), Float::new(2.0).unwrap().into()) - ] - ) - .into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("TwoPartStruct { ..two_part_struct }") - .unwrap() - .1 - ), - Ok(Structure::from_array( - two_part_struct.clone(), - [ - ("value".into(), Float::new(3.0).unwrap().into()), - ("other".into(), Float::new(4.0).unwrap().into()) - ] - ) - .into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("TwoPartStruct { value = 5, ..two_part_struct }") - .unwrap() - .1 - ), - Ok(Structure::from_array( - two_part_struct, - [ - ("value".into(), Float::new(5.0).unwrap().into()), - ("other".into(), Float::new(4.0).unwrap().into()) - ] - ) - .into()) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("TwoPartStruct { ..EmptyStruct {} }") - .unwrap() - .1 - ), - Err(Failure::StructWrongInheritanceType( - "EmptyStruct", - "TwoPartStruct", - "EmptyStruct" - )) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("EmptyStruct { bogus = 24 }").unwrap().1 - ), - Err(Failure::StructConstruction( - "{", - vec![Failure::StructExcessAssignment("bogus")] - )) - ); - assert_eq!( - run_expression( - context, - &Expression::parse("DefaultStruct { value = false }") - .unwrap() - .1 - ), - Err(Failure::StructConstruction( - "{", - vec![Failure::ExpectedGot( - "false", - "Number".into(), - "Boolean".into() - )] - )), - ); - }); - } -} diff --git a/src/script/logging.rs b/src/script/logging.rs deleted file mode 100644 index b801bca..0000000 --- a/src/script/logging.rs +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::error::Error; - -use fj_core::{validation::ValidationError, Core}; - -use super::{ - execution::{ - types::{function::IntoBuiltinFunction, NoneType, OperatorResult, SString, Value}, - ExecutionContext, Failure, GlobalResources, - }, - Span, -}; - -pub fn register_globals(context: &mut ExecutionContext) { - context.stack.new_variable_str( - "print", - (|context: &mut ExecutionContext, - span: &S, - text: SString| - -> OperatorResult> { - let text = text.to_string(span)?; - context - .log - .push(LogMessage::UserMessage(span.clone(), text)); - - Ok(NoneType.into()) - }) - .into_builtin_function() - .into(), - ); - - context.stack.new_variable_str( - "warn", - (|context: &mut ExecutionContext, - span: &S, - text: SString| - -> OperatorResult> { - let text = text.to_string(span)?; - context - .log - .push(LogMessage::UserWarning(span.clone(), text)); - - Ok(NoneType.into()) - }) - .into_builtin_function() - .into(), - ); - - context.stack.new_variable_str( - "error", - (|_context: &mut ExecutionContext, - span: &S, - text: SString| - -> OperatorResult> { - let text = text.to_string(span)?; - - Err(Failure::User(span.clone(), text)) - }) - .into_builtin_function() - .into(), - ); -} - -pub trait RuntimeLog { - fn push(&mut self, message: LogMessage); -} - -#[derive(Debug)] -pub enum LogMessage { - UserMessage(S, String), - UserWarning(S, String), - FormatIntegerPrecision(S), - ModelValidation(S, Vec), -} - -impl LogMessage { - pub fn log_level(&self) -> LogLevel { - match self { - Self::UserMessage(_, _) => LogLevel::Info, - Self::UserWarning(_, _) => LogLevel::Warning, - Self::FormatIntegerPrecision(_) => LogLevel::Warning, - Self::ModelValidation(_, _) => LogLevel::Warning, - } - } -} - -impl std::fmt::Display for LogMessage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::UserMessage(span, message) => write!(f, "{}: {}", span.format_span(), message), - Self::UserWarning(span, message) => write!(f, "{}: {}", span.format_span(), message), - Self::FormatIntegerPrecision(span) => { - write!( - f, - "{}: Integer formats such as Octal and Hex ignore precision", - span.format_span() - ) - } - Self::ModelValidation(span, error_map) => { - writeln!(f, "{}: Model Validation errors:", span.format_span())?; - - for error in error_map.iter() { - writeln!(f, "\t{}", error)?; - - let mut source = error.source(); - while let Some(error) = source { - writeln!(f, "\n\tCaused by:\n\t{}", error)?; - source = error.source(); - } - } - - Ok(()) - } - } - } -} - -pub enum LogLevel { - Info, - Warning, -} - -pub trait UnpackValidationWarnings { - fn unpack_validation_warnings(&mut self, span: &S, log: &mut dyn RuntimeLog); -} - -impl UnpackValidationWarnings for Core { - fn unpack_validation_warnings(&mut self, span: &S, log: &mut dyn RuntimeLog) { - let errors = self.layers.validation.take_errors(); - - if let Err(errors) = errors { - log.push(LogMessage::ModelValidation(span.clone(), errors.0)); - } - } -} - -impl UnpackValidationWarnings for GlobalResources { - fn unpack_validation_warnings(&mut self, span: &S, log: &mut dyn RuntimeLog) { - self.fornjot_core.unpack_validation_warnings(span, log) - } -} - -pub struct StandardLog; - -impl StandardLog { - pub fn global() -> &'static mut Self { - static mut STANDARD_LOG: StandardLog = StandardLog; - - // SAFETY: We contain no data and are never modified, so we can safely have multiple mutable references. - unsafe { &mut STANDARD_LOG } - } -} - -impl RuntimeLog for StandardLog { - fn push(&mut self, message: LogMessage) { - match message.log_level() { - LogLevel::Info => log::info!("{}", message), - LogLevel::Warning => log::warn!("{}", message), - } - } -} diff --git a/src/script/mod.rs b/src/script/mod.rs deleted file mode 100644 index a375a60..0000000 --- a/src/script/mod.rs +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use anyhow::{bail, Result}; - -mod parsing; -pub use parsing::Span; - -use crate::script::{ - execution::{ - types::{Solid, UserFunction}, - Module, - }, - parsing::FunctionSignature, -}; - -pub use crate::script::execution::types::{Scalar, SerializableValue}; - -mod execution; -pub use execution::{print_all_supported_units, Failure}; -use execution::{ - types::{Object, Sketch}, - GlobalResources, -}; - -use self::execution::{ExecutionContext, Stack}; - -pub mod logging; - -#[derive(Default)] -pub struct Runtime { - pub global_resources: GlobalResources, - pub stack: Stack, - pub root_span: S, -} - -impl From> for Runtime { - fn from(module: Module) -> Self { - let root_span = module.get_span().clone(); - let stack = Stack::new(module); - - Runtime { - global_resources: GlobalResources::default(), - stack, - root_span, - } - } -} - -impl Runtime { - pub fn load(file_name: impl Into, code: S) -> Result { - let mut validation_log = Vec::new(); - - let file_name = file_name.into(); - let module = Module::load(&mut validation_log, file_name, code)?; - - if !validation_log.is_empty() { - // TODO there's got to be a better way to present validation errors. - bail!("Module failed validation: {:?}", validation_log); - } - - Ok(Self::from(module)) - } - - pub fn run_sketch( - &mut self, - name: &str, - arguments: Vec, - ) -> std::result::Result<(), Failure> { - let root_span = self.root_span.clone(); - ExecutionContext::create(self, |context| { - let mut argument_values = Vec::with_capacity(arguments.len()); - for argument in arguments { - let value = argument.into_value_without_type_check(context, &root_span)?; - argument_values.push(value); - } - - let sketch = context.stack.get_variable_str(&root_span, name)?; - - let sketch = sketch.downcast_ref::>(&root_span)?.clone(); - - if matches!( - sketch.source.signature, - FunctionSignature::Sketch { arguments: _ } - ) { - // TODO attaching a span to a user function would be useful for debug purposes. - let result = sketch.call(context, &root_span, argument_values, &[])?; - - result.downcast::(&root_span)?; - - log::warn!( - "Sketches currently cannot be serialized, so no output will be provied." - ); - - Ok(()) - } else { - Err(Failure::ExpectedGot( - root_span.clone(), - "sketch".into(), - sketch.source.signature.to_string().into(), - )) - } - }) - } - - pub fn run_solid( - &mut self, - name: &str, - arguments: Vec, - ) -> std::result::Result> { - let root_span = self.root_span.clone(); - ExecutionContext::create(self, |context| { - let mut argument_values = Vec::with_capacity(arguments.len()); - for argument in arguments { - let value = argument.into_value_without_type_check(context, &root_span)?; - argument_values.push(value); - } - - let solid = context.stack.get_variable_str(&root_span, name)?; - - let solid = solid.downcast_ref::>(&root_span)?.clone(); - - if matches!( - solid.source.signature, - FunctionSignature::Solid { arguments: _ } - ) { - // TODO attaching a span to a user function would be useful for debug purposes. - let result = solid.call(context, &root_span, argument_values, &[])?; - - let solid = result.downcast::(&root_span)?; - Ok(solid) - } else { - Err(Failure::ExpectedGot( - root_span.clone(), - "solid".into(), - solid.source.signature.to_string().into(), - )) - } - }) - } - - pub fn run_task( - &mut self, - name: &str, - arguments: Vec, - ) -> std::result::Result> { - let root_span = self.root_span.clone(); - ExecutionContext::create(self, |context| { - let mut argument_values = Vec::with_capacity(arguments.len()); - for argument in arguments { - let value = argument.into_value_without_type_check(context, &root_span)?; - argument_values.push(value); - } - - let task = context.stack.get_variable_str(&root_span, name)?; - - let task = task.downcast_ref::>(&root_span)?.clone(); - - if matches!( - task.source.signature, - FunctionSignature::Task { - return_type: _, - arguments: _ - } - ) { - // TODO attaching a span to a user function would be useful for debug purposes. - let result = task.call(context, &root_span, argument_values, &[])?; - - let result = result.export(context.log, &root_span)?; - - Ok(result) - } else { - Err(Failure::ExpectedGot( - root_span.clone(), - "task".into(), - task.source.signature.to_string().into(), - )) - } - }) - } -} - -#[cfg(test)] -mod test { - use std::borrow::Cow; - - use common_data_types::Float; - use uom::si::{ - f64::Length, - length::{meter, millimeter}, - }; - - use crate::script::execution::types::Scalar; - - use super::*; - - #[test] - fn run_sketch() { - let mut runtime = Runtime::load( - "root_module", - "sketch my_sketch(input: Length = 50m) { new_sketch(global_xy_plane(), []) }", - ) - .unwrap(); - - assert!(matches!( - runtime.run_sketch("my_sketch", vec![]), - Err(Failure::MissingArguments(_)) - )); - - runtime - .run_sketch("my_sketch", vec![SerializableValue::Default]) - .unwrap(); - - runtime - .run_sketch( - "my_sketch", - vec![SerializableValue::Scalar( - Scalar::try_from(Length::new::(10.0)).unwrap(), - )], - ) - .unwrap(); - - let mut runtime = - Runtime::load("root_module", "function my_sketch() -> Number { 2 }").unwrap(); - - assert!(matches!( - runtime.run_sketch("my_sketch", vec![]), - Err(Failure::ExpectedGot( - _, - Cow::Borrowed("sketch"), - _, // Cow::Borrowed("function() -> Number") - )) - )); - } - - #[test] - fn run_solid() { - let mut runtime = Runtime::load( - "root_module", - "solid my_solid(input: Length = 1cm) { new_sketch(Circle { center = vec2(0m, 0m), radius = input, surface = global_xy_plane() }).sweep(global_xy_plane(), vec3(0cm, 0cm, 1cm)) }", - ) - .unwrap(); - - assert!(matches!( - runtime.run_solid("my_solid", vec![]), - Err(Failure::MissingArguments(_)) - )); - - runtime - .run_solid("my_solid", vec![SerializableValue::Default]) - .unwrap(); - - runtime - .run_solid( - "my_solid", - vec![SerializableValue::Scalar( - Scalar::try_from(Length::new::(10.0)).unwrap(), - )], - ) - .unwrap(); - - let mut runtime = - Runtime::load("root_module", "function my_solid() -> Number { 2 }").unwrap(); - - assert!(matches!( - runtime.run_solid("my_solid", vec![]), - Err(Failure::ExpectedGot( - _, - Cow::Borrowed("solid"), - _, // Cow::Borrowed("function() -> Number") - )) - )); - } - - #[test] - fn run_task() { - let mut runtime = Runtime::load( - "root_module", - "task my_task(input: Number = 50) -> Number { input }", - ) - .unwrap(); - - assert!(matches!( - runtime.run_task("my_task", vec![]), - Err(Failure::MissingArguments(_)) - )); - - assert_eq!( - runtime.run_task("my_task", vec![SerializableValue::Default]), - Ok(SerializableValue::Scalar(Float::new(50.0).unwrap().into())) - ); - - assert_eq!( - runtime.run_task( - "my_task", - vec![SerializableValue::Scalar(Float::new(22.0).unwrap().into())] - ), - Ok(SerializableValue::Scalar(Float::new(22.0).unwrap().into())) - ); - - let mut runtime = - Runtime::load("root_module", "sketch my_sketch() { new_sketch([]) }").unwrap(); - - assert!(matches!( - runtime.run_task("my_sketch", vec![]), - Err(Failure::ExpectedGot( - _, - Cow::Borrowed("task"), - _, // Cow::Borrowed("sketch()") - )) - )); - } -} diff --git a/src/script/module.rs b/src/script/module.rs deleted file mode 100644 index 2716503..0000000 --- a/src/script/module.rs +++ /dev/null @@ -1,419 +0,0 @@ -use std::collections::HashMap; - -use anyhow::{anyhow, Context, Result}; - -use super::parsing; -use parsing::Span; - -const RESERVED_KEYWORDS: &[&str] = &[ - "let", "import", "extern", "struct", "sketch", "widget", "function", "return", "if", "else", - "match", "for", "while", "loop", "in", "break", "continue", "struct", "true", "false", - "default", -]; - -fn is_reserved_keyword(word: &S) -> Option<&'static str> { - for keyword in RESERVED_KEYWORDS { - if matches!(word.compare(keyword), nom::CompareResult::Ok) { - return Some(keyword); - } - } - - None -} - -#[derive(Debug, Eq, PartialEq)] -enum CallableReference { - Function(usize), - Sketch(usize), - Widget(usize), -} - -#[derive(Debug, Eq, PartialEq)] -pub struct Module { - file_name: String, - root_elements: RootElements, - callables: HashMap, -} - -impl Module { - pub fn load(file_name: impl Into, code: impl Into) -> Result { - let file_name = file_name.into(); - let code = code.into(); - - let root_elements = Self::load_ast(code)?; - - Ok(Self { - file_name, - root_elements, - callables, - }) - } - - fn load_ast(code: S) -> Result> { - let (_, ast) = parsing::FileAST::parse(code) - .map_err(|error| anyhow!("Failed to parse file: {:?}", error))?; - - let mut imports = Vec::new(); - let mut structs = Vec::new(); - let mut functions = Vec::new(); - let mut sketches = Vec::new(); - let mut widgets = Vec::new(); - - for element in ast.root_elements.into_iter() { - match element { - parsing::RootElement::Import(import) => imports.push(import), - parsing::RootElement::Struct(sstruct) => structs.push(sstruct), - parsing::RootElement::Sketch(sketch) => sketches.push(sketch), - parsing::RootElement::Widget(widget) => widgets.push(widget), - parsing::RootElement::Function(function) => functions.push(function), - } - } - - Ok(RootElements { - imports, - structs, - functions, - sketches, - widgets, - }) - } - - /// Validation is checking for errors that don't require the context of execution. - fn validate(&self, log: &mut ValidationLog) { - self.root_elements.validate(log); - } -} - -#[derive(Debug, Eq, PartialEq)] -struct RootElements { - imports: Vec>, - structs: Vec>, - functions: Vec>, - sketches: Vec>, - widgets: Vec>, -} - -impl RootElements { - /// Validation is checking for errors that don't require the context of execution.1 - fn validate(&self, log: &mut ValidationLog) { - // There is no validation to be done for imports. - - for structure in self.structs.iter() { - Self::validate_struct(log, structure); - } - for function in self.functions.iter() { - Self::validate_named_block(log, &function.named_block); - } - for sketch in self.sketches.iter() { - Self::validate_named_block(log, &sketch.named_block); - } - for widget in self.widgets.iter() { - Self::validate_named_block(log, &widget.named_block); - } - } - - fn validate_struct(log: &mut ValidationLog, structure: &parsing::Struct) { - // Name should not be a reserved keyword. - if let Some(keyword) = is_reserved_keyword(&structure.name) { - log.push( - structure.name.clone(), - ValidationMessage::ReservedKeyword(keyword), - ); - } - - // None of the members should have a reserved keyword for a name. - for member in structure.members.iter() { - if let Some(keyword) = is_reserved_keyword(&member.name) { - log.push( - member.name.clone(), - ValidationMessage::ReservedKeyword(keyword), - ); - } - } - } - - fn validate_named_block(log: &mut ValidationLog, block: &parsing::NamedBlock) { - // Name should not be a reserved keyword. - if let Some(keyword) = is_reserved_keyword(&block.name) { - log.push( - block.name.clone(), - ValidationMessage::ReservedKeyword(keyword), - ); - } - - // Parameter names should not be reserved keywords. - for parameter in block.parameters.iter() { - if let Some(keyword) = is_reserved_keyword(¶meter.name) { - log.push( - parameter.name.clone(), - ValidationMessage::ReservedKeyword(keyword), - ); - } - } - - Self::validate_block(log, &block.block); - } - - fn validate_block(log: &mut ValidationLog, block: &parsing::Block) { - let mut core_iter = block.statements.iter().peekable(); - let statement_iter = std::iter::from_fn(|| { - core_iter - .next() - .map(|next| (next, core_iter.peek().is_none())) - }); - - for (statement, is_last) in statement_iter { - // Variables should not contain keywords. - if let Some(parsing::Statement::Assign(assignment)) = statement.get() { - for to_assign in assignment.to_assign.iter() { - for part in to_assign.path.parts.iter() { - if let Some(keyword) = is_reserved_keyword(part) { - log.push(part.clone(), ValidationMessage::ReservedKeyword(keyword)); - } - } - } - } - - // Some statements require that they have a semicolon behind them, unless they are the last line. - // Enforce that. - if let parsing::BlockStatement::Open(statement) = statement { - if !is_last { - if let Some(span) = match statement { - parsing::Statement::Expression(spanable) => Some(spanable.get_span()), - parsing::Statement::Assign(spanable) => Some(spanable.get_span()), - parsing::Statement::Return(spanable) => Some(spanable.get_span()), - parsing::Statement::Break(spanable) => Some(spanable.get_span()), - parsing::Statement::Continue(spanable) => Some(spanable.get_span()), - - _ => None, - } { - log.push(span, ValidationMessage::UnclosedStatement); - } - } - } - } - } -} - -#[derive(Debug, Eq, PartialEq, Default)] -struct ValidationLog { - entries: Vec>, -} - -impl ValidationLog { - fn push(&mut self, span: S, message: ValidationMessage) { - self.entries.push(ValidationEntry { span, message }); - } - - fn containes_error(&self) -> bool { - for entry in self.entries.iter() { - if matches!(entry.message.log_level(), ValidationLogLevel::Error) { - return true; - } - } - - false - } -} - -#[derive(Debug, Eq, PartialEq)] -struct ValidationEntry { - span: S, - message: ValidationMessage, -} - -#[derive(Debug, Eq, PartialEq)] -enum ValidationMessage { - UnclosedStatement, - ReservedKeyword(&'static str), -} - -impl ValidationMessage { - fn log_level(&self) -> ValidationLogLevel { - match self { - Self::UnclosedStatement => ValidationLogLevel::Error, - Self::ReservedKeyword(_) => ValidationLogLevel::Error, - } - } -} - -impl std::fmt::Display for ValidationMessage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ValidationMessage::UnclosedStatement => { - f.write_str("Non-tail expressions must be closed with a semicolon") - } - ValidationMessage::ReservedKeyword(keyword) => { - write!(f, "Keyword `{}` is reserved", keyword) - } - } - } -} - -enum ValidationLogLevel { - Warning, - Error, -} - -#[cfg(test)] -mod test { - use super::*; - - const TEST_AST_CODE: &str = r#" - import path::to::module; - struct MyStruct {} - sketch my_sketch() {} - widget my_widget() {} - function my_function() -> Length {} -"#; - - #[test] - fn load_module() { - assert!(Module::<&str>::load("my_module.ccm", "").is_ok()); - } - - #[test] - fn load_ast() { - let root: RootElements<&str> = Module::load_ast(TEST_AST_CODE).unwrap(); - - assert_eq!( - root.imports, - [parsing::Import { - path: vec!["path", "to", "module"], - external: false - }] - ); - - assert_eq!( - root.structs, - [parsing::Struct { - name: "MyStruct", - members: vec![] - }] - ); - assert_eq!( - root.sketches, - [parsing::Sketch { - named_block: parsing::NamedBlock { - name: "my_sketch", - parameters: vec![], - block: parsing::Block { statements: vec![] } - }, - }] - ); - assert_eq!( - root.widgets, - [parsing::Widget { - named_block: parsing::NamedBlock { - name: "my_widget", - parameters: vec![], - block: parsing::Block { statements: vec![] } - }, - }] - ); - assert_eq!( - root.functions, - [parsing::Function { - named_block: parsing::NamedBlock { - name: "my_function", - parameters: vec![], - block: parsing::Block { statements: vec![] } - }, - return_type: parsing::VariableType::Length - }] - ); - } - - fn validate(code: &str) -> ValidationLog<&str> { - let module = Module::<&str>::load("my_module.ccs", code).unwrap(); - - let mut validation_log = ValidationLog::default(); - module.validate(&mut validation_log); - - validation_log - } - - #[test] - fn validate_block_empty() { - assert_eq!(validate("widget MyWidget() {}").entries, []); - } - - #[test] - fn validate_block_tail_expressions() { - assert_eq!(validate("widget MyWidget() { let a = b; }").entries, []); - assert_eq!(validate("widget MyWidget() { let a = b }").entries, []); - assert_eq!( - validate("widget MyWidget() { let a = b; let c = d }").entries, - [] - ); - assert_eq!( - validate("widget MyWidget() { let a = b let c = d }").entries, - [ValidationEntry { - span: "let", - message: ValidationMessage::UnclosedStatement, - }] - ); - } - - #[test] - fn validate_assignment_keyword_resurvation() { - assert_eq!( - validate("widget MyWidget() { let break = b; }").entries, - [ValidationEntry { - span: "break", - message: ValidationMessage::ReservedKeyword("break") - }] - ); - } - - #[test] - fn validate_named_block_keyword_resurvation() { - assert_eq!( - validate("widget break() { }").entries, - [ValidationEntry { - span: "break", - message: ValidationMessage::ReservedKeyword("break") - }] - ); - } - - #[test] - fn validate_parameter_names_are_not_keywords() { - assert_eq!(validate("widget MyWidget() { }").entries, []); - - assert_eq!(validate("widget MyWidget(okay: Length) { }").entries, []); - - assert_eq!( - validate("widget MyWidget(break: Length) { }").entries, - [ValidationEntry { - span: "break", - message: ValidationMessage::ReservedKeyword("break") - }] - ); - } - - #[test] - fn validate_struct_name_not_keyword() { - assert_eq!(validate("struct MyStruct { }").entries, []); - assert_eq!( - validate("struct break { }").entries, - [ValidationEntry { - span: "break", - message: ValidationMessage::ReservedKeyword("break") - }] - ); - } - - #[test] - fn validate_struct_member_not_keyword() { - assert_eq!(validate("struct MyStruct { }").entries, []); - assert_eq!(validate("struct MyStruct { okay: Length }").entries, []); - assert_eq!( - validate("struct MyStruct { break: Length }").entries, - [ValidationEntry { - span: "break", - message: ValidationMessage::ReservedKeyword("break") - }] - ); - } -} diff --git a/src/script/parsing/blocks.rs b/src/script/parsing/blocks.rs deleted file mode 100644 index 1888c3f..0000000 --- a/src/script/parsing/blocks.rs +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::tag, - character::complete::char as nom_char, - combinator::{map, success}, - error::context, - multi::{many0, separated_list0}, - sequence::{delimited, pair, preceded, terminated, tuple}, -}; - -use super::{parse_name, space0, MemberVariable, Span, Statement, VResult, VariableType}; - -#[derive(Debug, Eq, PartialEq)] -pub struct CallableBlock { - pub parameter_span: S, - pub parameters: Vec>, - pub block: Block, -} - -impl CallableBlock { - pub fn parse_with_return_type(input: S) -> VResult)> { - Self::parse_internal( - input, - context( - "Could not parse return type", - preceded(pair(tag("->"), space0), VariableType::parse), - ), - ) - } - - pub fn parse(input: S) -> VResult { - Self::parse_internal(input, success(())).map(|(input, (callable, _))| (input, callable)) - } - - fn parse_internal( - input: S, - return_type_parser: impl FnMut(S) -> VResult, - ) -> VResult { - map( - tuple(( - context( - "Expected parameter list", - terminated( - pair( - preceded(space0, tag("(")), - separated_list0( - nom_char(','), - delimited(space0, MemberVariable::parse, space0), - ), - ), - pair(nom_char(')'), space0), - ), - ), - delimited(space0, return_type_parser, space0), - Block::parse, - )), - |((parameter_span, parameters), return_type, block)| { - ( - Self { - parameter_span, - parameters, - block, - }, - return_type, - ) - }, - )(input) - } -} - -#[derive(Debug, Eq, PartialEq)] -pub struct NamedBlock { - pub name: S, - pub callable: CallableBlock, -} - -impl NamedBlock { - pub fn parse(input: S) -> VResult { - map( - pair(parse_name, CallableBlock::parse), - |(name, callable)| Self { name, callable }, - )(input) - } - - pub fn parse_with_return_type(input: S) -> VResult)> { - map( - pair(parse_name, CallableBlock::parse_with_return_type), - |(name, (callable, return_type))| (Self { name, callable }, return_type), - )(input) - } -} - -#[derive(Debug, Eq, PartialEq)] -pub enum BlockStatement { - Closed(Statement), - Open(Statement), - Blank(S), -} - -impl BlockStatement { - fn parse(input: S) -> VResult { - alt(( - map( - delimited(space0, Statement::parse, pair(space0, nom_char(';'))), - Self::Closed, - ), - map(preceded(space0, Statement::parse), Self::Open), - map(preceded(space0, tag(";")), Self::Blank), - ))(input) - } - - pub fn get(&self) -> Option<&Statement> { - match self { - BlockStatement::Closed(statement) | BlockStatement::Open(statement) => Some(statement), - BlockStatement::Blank(_) => None, - } - } - - pub fn get_span(&self) -> &S { - match self { - BlockStatement::Closed(spanable) => spanable.get_span(), - BlockStatement::Open(spanable) => spanable.get_span(), - BlockStatement::Blank(spanable) => spanable, - } - } -} - -#[derive(Debug, Eq, PartialEq)] -pub struct Block { - pub statements: Vec>, -} - -impl Block { - pub fn parse(input: S) -> VResult { - map( - delimited( - context("Block is missing opening bracket", nom_char('{')), - many0(alt((delimited(space0, BlockStatement::parse, space0),))), - context( - "Block is missing closing bracket", - pair(space0, nom_char('}')), - ), - ), - |statements| Self { statements }, - )(input) - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::{ - member_variable::{ - MemberVariableConstraint, MemberVariableConstraintList, MemberVariableType, - }, - Assign, Assignable, AssignableVariable, Break, Litteral, Loop, Number, Return, Scalar, - }; - - use super::*; - - #[test] - fn block() { - assert_eq!(Block::parse("{}"), Ok(("", Block { statements: vec![] }))); - assert_eq!( - Block::parse("{;}"), - Ok(( - "", - Block { - statements: vec![BlockStatement::Blank(";")], - } - )) - ); - - assert_eq!( - Block::parse("{ break; a = b; }"), - Ok(( - "", - Block { - statements: vec![ - BlockStatement::Closed(Statement::Break(Break { - starting_span: "break", - loop_name: None, - expression: None - })), - BlockStatement::Closed(Statement::Assign(Assign { - starting_span: "a", - is_new: false, - to_assign: Assignable::Variable(AssignableVariable { - name: "a", - ty: None, - }), - statement: Box::new(Statement::parse("b").unwrap().1) - })) - ], - } - )) - ); - - assert_eq!( - Block::parse("{ a = b }"), - Ok(( - "", - Block { - statements: vec![BlockStatement::Open(Statement::Assign(Assign { - starting_span: "a", - is_new: false, - to_assign: Assignable::Variable(AssignableVariable { - name: "a", - ty: None, - }), - statement: Box::new(Statement::parse("b").unwrap().1) - }))], - } - )) - ); - - assert_eq!( - Block::parse("{ break; a = b }"), - Ok(( - "", - Block { - statements: vec![ - BlockStatement::Closed(Statement::Break(Break { - starting_span: "break", - loop_name: None, - expression: None - })), - BlockStatement::Open(Statement::Assign(Assign { - starting_span: "a", - is_new: false, - to_assign: Assignable::Variable(AssignableVariable { - name: "a", - ty: None, - }), - statement: Box::new(Statement::parse("b").unwrap().1) - })) - ], - } - )) - ); - - assert_eq!( - Block::parse("{ loop {} }"), - Ok(( - "", - Block { - statements: vec![BlockStatement::Open(Statement::Loop(Loop { - starting_span: "loop", - name: None, - block: Block { statements: vec![] } - }))], - } - )) - ); - - assert_eq!( - Block::parse("{ loop {} return }"), - Ok(( - "", - Block { - statements: vec![ - BlockStatement::Open(Statement::Loop(Loop { - starting_span: "loop", - name: None, - block: Block { statements: vec![] } - })), - BlockStatement::Open(Statement::Return(Return { - starting_span: "return", - expression: None - })) - ], - } - )) - ); - - assert!(Block::parse("{ break a = b }").is_err()); - assert!(Block::parse("{ break a = b; }").is_err()); - } - - #[test] - fn named_block() { - assert_eq!( - NamedBlock::parse("my_thing() {}"), - Ok(( - "", - NamedBlock { - name: "my_thing", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - } - )) - ); - - assert_eq!( - NamedBlock::parse("my_thing(one: Length) {}"), - Ok(( - "", - NamedBlock { - name: "my_thing", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![MemberVariable { - name: "one", - ty: MemberVariableType { - ty: VariableType::Scalar("Length"), - constraints: None, - default_value: None - } - }], - block: Block { statements: vec![] } - } - } - )) - ); - - assert_eq!( - NamedBlock::parse("my_thing(one: Length, two: Angle = 2) {}"), - Ok(( - "", - NamedBlock { - name: "my_thing", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![ - MemberVariable { - name: "one", - ty: MemberVariableType { - ty: VariableType::Scalar("Length"), - constraints: None, - default_value: None - } - }, - MemberVariable { - name: "two", - ty: MemberVariableType { - ty: VariableType::Scalar("Angle"), - constraints: None, - default_value: Some(Litteral::Scalar(Scalar { - number: Number { - integer: Some("2"), - dot: None, - fractional: None - }, - ty: "" - })) - } - } - ], - block: Block { statements: vec![] } - } - } - )) - ); - - assert_eq!( - NamedBlock::parse("my_thing(one: Length, two: Angle = 2, #[integer] three: Number) {}"), - Ok(( - "", - NamedBlock { - name: "my_thing", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![ - MemberVariable { - name: "one", - ty: MemberVariableType { - ty: VariableType::Scalar("Length"), - constraints: None, - default_value: None - } - }, - MemberVariable { - name: "two", - ty: MemberVariableType { - ty: VariableType::Scalar("Angle"), - constraints: None, - default_value: Some(Litteral::Scalar(Scalar { - number: Number { - integer: Some("2"), - dot: None, - fractional: None - }, - ty: "" - })) - } - }, - MemberVariable { - name: "three", - ty: MemberVariableType { - ty: VariableType::Scalar("Number"), - constraints: Some(MemberVariableConstraintList { - constraints: vec![MemberVariableConstraint::Integer] - }), - default_value: None, - } - } - ], - block: Block { statements: vec![] } - } - } - )) - ); - - assert_eq!( - NamedBlock::parse_with_return_type("my_thing() -> struct T {}"), - Ok(( - "", - ( - NamedBlock { - name: "my_thing", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - }, - }, - VariableType::Struct("T") - ) - )) - ); - } -} diff --git a/src/script/parsing/closure.rs b/src/script/parsing/closure.rs deleted file mode 100644 index b39bef2..0000000 --- a/src/script/parsing/closure.rs +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use nom::{ - branch::alt, - bytes::complete::tag, - character::complete::{char as nom_char, space0}, - combinator::map, - multi::separated_list0, - sequence::{delimited, pair, preceded, terminated}, -}; - -use super::{parse_name, CallableBlock, FunctionSignature, Span, VResult}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum CapturedVariable { - Copy(S), - Reference(S), -} - -impl CapturedVariable { - fn parse(input: S) -> VResult { - alt(( - map( - preceded(pair(nom_char('&'), space0), parse_name), - Self::Reference, - ), - map(parse_name, Self::Copy), - ))(input) - } -} - -#[derive(Debug, Eq, PartialEq)] -pub struct Closure { - pub starting_span: S, - pub captured_variables: Vec>, - pub callable: CallableBlock, - pub signature: FunctionSignature, -} - -impl Closure { - pub fn parse(input: S) -> VResult { - map( - pair( - pair( - terminated(tag("["), space0), - terminated( - separated_list0( - delimited(space0, nom_char(','), space0), - CapturedVariable::parse, - ), - pair(space0, tag("]")), - ), - ), - CallableBlock::parse_with_return_type, - ), - |((starting_span, captured_variables), (callable, return_type))| Self { - signature: FunctionSignature::Function { - return_type: Box::new(return_type), - arguments: callable.parameters.iter().map(|p| p.ty.clone()).collect(), - }, - starting_span, - captured_variables, - callable, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::{Block, VariableType}; - - use super::*; - - #[test] - fn captured_variable() { - assert_eq!( - CapturedVariable::parse("&variable"), - Ok(("", CapturedVariable::Reference("variable"))) - ); - assert_eq!( - CapturedVariable::parse("variable"), - Ok(("", CapturedVariable::Copy("variable"))) - ); - } - - #[test] - fn closure() { - assert_eq!( - Closure::parse("[]() -> Number {}"), - Ok(( - "", - Closure { - starting_span: "[", - captured_variables: vec![], - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - }, - signature: FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Number")), - arguments: vec![] - } - } - )) - ); - - assert_eq!( - Closure::parse("[a]() -> Number {}"), - Ok(( - "", - Closure { - starting_span: "[", - captured_variables: vec![CapturedVariable::Copy("a")], - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - }, - signature: FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Number")), - arguments: vec![] - } - } - )) - ); - - assert_eq!( - Closure::parse("[a, &b]() -> Number {}"), - Ok(( - "", - Closure { - starting_span: "[", - captured_variables: vec![ - CapturedVariable::Copy("a"), - CapturedVariable::Reference("b") - ], - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - }, - signature: FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Number")), - arguments: vec![] - } - } - )) - ); - } -} diff --git a/src/script/parsing/function.rs b/src/script/parsing/function.rs deleted file mode 100644 index 7bbef10..0000000 --- a/src/script/parsing/function.rs +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use nom::{ - combinator::{cut, map}, - sequence::{pair, terminated}, -}; - -use super::{space1, take_keyword, FunctionSignature, NamedBlock, Span, VResult}; - -#[derive(Debug, Eq, PartialEq)] -pub struct Function { - pub starting_span: S, - pub named_block: NamedBlock, - pub signature: FunctionSignature, -} - -impl Function { - pub fn parse_as_function(input: S) -> VResult { - map( - pair( - terminated(take_keyword("function"), space1), - cut(NamedBlock::parse_with_return_type), - ), - |(starting_span, (named_block, return_type))| Self { - starting_span, - signature: FunctionSignature::Function { - return_type: Box::new(return_type), - arguments: named_block - .callable - .parameters - .iter() - .map(|p| p.ty.clone()) - .collect(), - }, - named_block, - }, - )(input) - } - - pub fn parse_as_sketch(input: S) -> VResult { - map( - pair( - terminated(take_keyword("sketch"), space1), - cut(NamedBlock::parse), - ), - |(starting_span, named_block)| Self { - starting_span, - signature: FunctionSignature::Sketch { - arguments: named_block - .callable - .parameters - .iter() - .map(|p| p.ty.clone()) - .collect(), - }, - named_block, - }, - )(input) - } - - pub fn parse_as_solid(input: S) -> VResult { - map( - pair( - terminated(take_keyword("solid"), space1), - cut(NamedBlock::parse), - ), - |(starting_span, named_block)| Self { - starting_span, - signature: FunctionSignature::Solid { - arguments: named_block - .callable - .parameters - .iter() - .map(|p| p.ty.clone()) - .collect(), - }, - named_block, - }, - )(input) - } - - pub fn parse_as_task(input: S) -> VResult { - map( - pair( - terminated(take_keyword("task"), space1), - cut(NamedBlock::parse_with_return_type), - ), - |(starting_span, (named_block, return_type))| Self { - starting_span, - signature: FunctionSignature::Task { - return_type: Box::new(return_type), - arguments: named_block - .callable - .parameters - .iter() - .map(|p| p.ty.clone()) - .collect(), - }, - named_block, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::{blocks::CallableBlock, Block, VariableType}; - - use super::*; - - #[test] - fn function() { - assert!(Function::parse_as_function("function my_function() {}").is_err()); - assert_eq!( - Function::parse_as_function("function my_function() -> Length {}"), - Ok(( - "", - Function { - starting_span: "function", - named_block: NamedBlock { - name: "my_function", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - }, - signature: FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Length")), - arguments: vec![], - } - } - )) - ); - } -} diff --git a/src/script/parsing/list.rs b/src/script/parsing/list.rs deleted file mode 100644 index 4296595..0000000 --- a/src/script/parsing/list.rs +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - bytes::complete::tag, - character::complete::char as nom_char, - combinator::opt, - multi::separated_list0, - sequence::{delimited, pair, terminated}, -}; - -use super::{space0, Expression, Span, VResult}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct List { - pub starting_span: S, - pub expressions: Vec>, -} - -impl List { - pub fn parse(input: S) -> VResult { - let (input, (starting_span, expressions)) = terminated( - pair( - terminated(tag("["), space0), - separated_list0(nom_char(','), delimited(space0, Expression::parse, space0)), - ), - pair(opt(pair(nom_char(','), space0)), nom_char(']')), - )(input)?; - - Ok(( - input, - Self { - starting_span, - expressions, - }, - )) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::{ArithmeticExpression, Comparison, Factor, Term, Trailer}; - - use super::*; - - #[test] - fn list() { - assert_eq!( - List::parse("[]"), - Ok(( - "", - List { - starting_span: "[", - expressions: vec![] - } - )) - ); - assert_eq!( - List::parse("[\n]"), - Ok(( - "", - List { - starting_span: "[", - expressions: vec![] - } - )) - ); - assert_eq!( - List::parse("[one]"), - Ok(( - "", - List { - starting_span: "[", - expressions: vec![Expression::Buffer(Comparison::None( - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable( - "one" - )))) - ))] - } - )) - ); - assert_eq!( - List::parse("[one, two]"), - Ok(( - "", - List { - starting_span: "[", - expressions: vec![ - Expression::Buffer(Comparison::None(ArithmeticExpression::Term( - Term::Trailer(Trailer::None(Factor::Variable("one"),)) - ))), - Expression::Buffer(Comparison::None(ArithmeticExpression::Term( - Term::Trailer(Trailer::None(Factor::Variable("two"))) - ))) - ] - } - )) - ); - assert_eq!( - List::parse("[one, two, three]"), - Ok(( - "", - List { - starting_span: "[", - expressions: vec![ - Expression::Buffer(Comparison::None(ArithmeticExpression::Term( - Term::Trailer(Trailer::None(Factor::Variable("one"),)) - ))), - Expression::Buffer(Comparison::None(ArithmeticExpression::Term( - Term::Trailer(Trailer::None(Factor::Variable("two"),)) - ))), - Expression::Buffer(Comparison::None(ArithmeticExpression::Term( - Term::Trailer(Trailer::None(Factor::Variable("three"),)) - ))) - ] - } - )) - ); - - assert_eq!( - List::parse("[one, two, three, ]"), - Ok(( - "", - List { - starting_span: "[", - expressions: vec![ - Expression::Buffer(Comparison::None(ArithmeticExpression::Term( - Term::Trailer(Trailer::None(Factor::Variable("one"),)) - ))), - Expression::Buffer(Comparison::None(ArithmeticExpression::Term( - Term::Trailer(Trailer::None(Factor::Variable("two"),)) - ))), - Expression::Buffer(Comparison::None(ArithmeticExpression::Term( - Term::Trailer(Trailer::None(Factor::Variable("three"),)) - ))) - ] - } - )) - ); - - assert!(List::parse("[one two]").is_err()); - } -} diff --git a/src/script/parsing/litteral.rs b/src/script/parsing/litteral.rs deleted file mode 100644 index b8b8fbc..0000000 --- a/src/script/parsing/litteral.rs +++ /dev/null @@ -1,156 +0,0 @@ -use std::rc::Rc; - -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{branch::alt, combinator::map}; - -use super::{closure::Closure, take_keyword, List, PString, Scalar, Span, VResult}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum Litteral { - Scalar(Scalar), - String(PString), - List(List), - Boolean(S, bool), - Default(S), - Closure(Rc>), -} - -impl Litteral { - pub fn parse(input: S) -> VResult { - alt(( - map(Scalar::parse, Self::Scalar), - map(PString::parse, Self::String), - map(Closure::parse, |closure| Self::Closure(Rc::new(closure))), - map(List::parse, Self::List), - map(take_keyword("true"), |span| Self::Boolean(span, true)), - map(take_keyword("false"), |span| Self::Boolean(span, false)), - map(take_keyword("default"), Self::Default), - ))(input) - } - - pub fn get_span(&self) -> &S { - match self { - Litteral::Scalar(spanable) => spanable.get_span(), - Litteral::String(spanable) => spanable.get_span(), - Litteral::List(spanable) => spanable.get_span(), - Litteral::Boolean(spanable, _) => spanable, - Litteral::Default(spanable) => spanable, - Litteral::Closure(spanable) => spanable.get_span(), - } - } -} - -#[cfg(test)] -mod test { - use std::rc::Rc; - - use crate::script::parsing::{Block, CallableBlock, FunctionSignature, Number, VariableType}; - - use super::*; - - #[test] - fn litteral() { - // Scalar(Scalar), - assert_eq!( - Litteral::parse("1234.5678m"), - Ok(( - "", - Litteral::Scalar(Scalar { - number: Number { - integer: Some("1234"), - dot: Some("."), - fractional: Some("5678") - }, - ty: "m" - }) - )) - ); - - // Scalar, but it's just a number - assert_eq!( - Litteral::parse("1234.5678"), - Ok(( - "", - Litteral::Scalar(Scalar { - number: Number { - integer: Some("1234"), - dot: Some("."), - fractional: Some("5678") - }, - ty: "", - }) - )) - ); - - // String(PString), - assert_eq!( - Litteral::parse(r#""test""#), - Ok(("", Litteral::String(PString { value: "test" }))) - ); - - // List(List), - assert_eq!( - Litteral::parse("[]"), - Ok(( - "", - Litteral::List(List { - starting_span: "[", - expressions: vec![] - }) - )) - ); - - // Boolean(bool), - assert_eq!( - Litteral::parse("true"), - Ok(("", Litteral::Boolean("true", true))) - ); - assert_eq!( - Litteral::parse("false"), - Ok(("", Litteral::Boolean("false", false))) - ); - - // Default, - assert_eq!( - Litteral::parse("default"), - Ok(("", Litteral::Default("default"))) - ); - - // Closure - assert_eq!( - Litteral::parse("[]() -> Number {}"), - Ok(( - "", - Litteral::Closure(Rc::new(Closure { - starting_span: "[", - captured_variables: vec![], - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - }, - signature: FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Number")), - arguments: vec![] - } - })) - )) - ) - } -} diff --git a/src/script/parsing/member_variable.rs b/src/script/parsing/member_variable.rs deleted file mode 100644 index 2f490c3..0000000 --- a/src/script/parsing/member_variable.rs +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::tag, - character::complete::char as nom_char, - combinator::{cut, map, opt, value}, - error::context, - multi::separated_list0, - sequence::{delimited, pair, preceded, terminated, tuple}, -}; -use std::fmt::Display; - -use super::{ - parse_name, space0, take_keyword, IteratorFormatter, Litteral, Span, VResult, VariableType, -}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum MemberVariableConstraint { - Min(Litteral), - Max(Litteral), - Enum(Vec>), - Integer, - // TODO add the ability to constrain lists to a certain type, or set of types. -} - -impl MemberVariableConstraint { - fn parse(input: S) -> VResult { - alt(( - Self::parametric_constraint("min", map(Litteral::parse, Self::Min)), - Self::parametric_constraint("max", map(Litteral::parse, Self::Max)), - Self::parametric_constraint( - "enum", - map( - separated_list0(nom_char(','), delimited(space0, Litteral::parse, space0)), - Self::Enum, - ), - ), - value(Self::Integer, take_keyword("integer")), - ))(input) - } - - fn parametric_constraint( - name: &'static str, - parameter_parser: impl FnMut(S) -> VResult, - ) -> impl FnMut(S) -> VResult { - preceded( - take_keyword(name), - delimited( - pair( - space0, - context("Expected opening `(` to begin arguments", nom_char('(')), - ), - delimited(space0, parameter_parser, space0), - context("Expected closing `)` to end arguments", nom_char(')')), - ), - ) - } -} - -/// Display a litteral, but is limited and can only display litterals that do not require runtime evaluation. -struct DisplayLitteral<'a, S: Span>(&'a Litteral); - -impl Display for DisplayLitteral<'_, S> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.0 { - Litteral::Scalar(measurement) => write!(f, "{}", measurement.ty.as_str()), - Litteral::String(string) => write!(f, "\"{}\"", string.value.as_str()), - Litteral::List(_list) => write!(f, "[...]"), - Litteral::Boolean(_span, value) => write!(f, "{}", value), - Litteral::Default(_span) => write!(f, "default"), - Litteral::Closure(_closure) => write!(f, "[...](...) -> ? {{...}}"), // TODO we should probably display closures correctly. - } - } -} - -impl Display for MemberVariableConstraint { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - MemberVariableConstraint::Min(min) => write!(f, "min({})", DisplayLitteral(min)), - MemberVariableConstraint::Max(max) => write!(f, "max({})", DisplayLitteral(max)), - MemberVariableConstraint::Enum(enumeration) => { - write!( - f, - "enum({})", - IteratorFormatter(enumeration.iter().map(|l| DisplayLitteral(l))) - ) - } - MemberVariableConstraint::Integer => write!(f, "Integer"), - } - } -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct MemberVariableConstraintList { - pub constraints: Vec>, -} - -impl MemberVariableConstraintList { - fn parse(input: S) -> VResult { - map( - delimited( - pair(tag("#["), space0), - separated_list0( - nom_char(','), - delimited(space0, MemberVariableConstraint::parse, space0), - ), - pair( - space0, - context("Missing closing `]` for constraint list", cut(tag("]"))), - ), - ), - |constraints| Self { constraints }, - )(input) - } -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct MemberVariableType { - pub ty: VariableType, - pub constraints: Option>, - pub default_value: Option>, -} - -impl MemberVariableType { - pub fn parse(input: S) -> VResult { - map( - tuple(( - opt(terminated(MemberVariableConstraintList::parse, space0)), - VariableType::parse, - opt(preceded( - delimited(space0, nom_char('='), space0), - Litteral::parse, - )), - )), - |(constraints, ty, default_value)| Self { - constraints, - ty, - default_value, - }, - )(input) - } -} - -impl Display for MemberVariableType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match (self.constraints.as_ref(), self.default_value.is_some()) { - (None, false) => write!(f, "{}", self.ty), - (Some(constraints), false) => { - write!( - f, - "#[{}] {}", - IteratorFormatter(constraints.constraints.iter()), - self.ty - ) - } - (None, true) => write!(f, "{} = default", self.ty), - (Some(constraints), true) => write!( - f, - "#[{}] {} = default", - IteratorFormatter(constraints.constraints.iter(),), - self.ty - ), - } - } -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct MemberVariable { - pub name: S, - pub ty: MemberVariableType, -} - -impl MemberVariable { - pub fn parse(input: S) -> VResult { - map( - tuple(( - opt(terminated(MemberVariableConstraintList::parse, space0)), - terminated(parse_name, space0), - preceded(nom_char(':'), preceded(space0, VariableType::parse)), - opt(preceded( - delimited(space0, nom_char('='), space0), - Litteral::parse, - )), - )), - |(constraints, name, ty, default_value)| Self { - ty: MemberVariableType { - constraints, - ty, - default_value, - }, - name, - }, - )(input) - } -} - -impl Display for MemberVariable { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match ( - self.ty.constraints.as_ref(), - self.ty.default_value.is_some(), - ) { - (None, false) => write!(f, "{}: {}", self.name.as_str(), self.ty.ty), - (Some(constraints), false) => { - write!( - f, - "#[{}] {}: {}", - IteratorFormatter(constraints.constraints.iter()), - self.name.as_str(), - self.ty.ty - ) - } - (None, true) => write!(f, "{}: {} = default", self.name.as_str(), self.ty.ty), - (Some(constraints), true) => write!( - f, - "#[{}] {}: {} = default", - IteratorFormatter(constraints.constraints.iter(),), - self.name.as_str(), - self.ty.ty - ), - } - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::{Number, Scalar}; - - use super::*; - - #[test] - fn member_variable_constraint() { - assert_eq!( - MemberVariableConstraint::parse("integer"), - Ok(("", MemberVariableConstraint::Integer)) - ); - - assert_eq!( - MemberVariableConstraint::parse("min(0)"), - Ok(( - "", - MemberVariableConstraint::Min(Litteral::Scalar(Scalar { - number: Number { - integer: Some("0"), - dot: None, - fractional: None - }, - ty: "" - })) - )) - ); - assert_eq!( - MemberVariableConstraint::parse("max(0)"), - Ok(( - "", - MemberVariableConstraint::Max(Litteral::Scalar(Scalar { - number: Number { - integer: Some("0"), - dot: None, - fractional: None - }, - ty: "", - })) - )) - ); - assert_eq!( - MemberVariableConstraint::parse("enum(0, 1, 2)"), - Ok(( - "", - MemberVariableConstraint::Enum(vec![ - Litteral::Scalar(Scalar { - number: Number { - integer: Some("0"), - dot: None, - fractional: None - }, - ty: "" - }), - Litteral::Scalar(Scalar { - number: Number { - integer: Some("1"), - dot: None, - fractional: None - }, - ty: "" - }), - Litteral::Scalar(Scalar { - number: Number { - integer: Some("2"), - dot: None, - fractional: None - }, - ty: "" - }) - ]) - )) - ); - } - - #[test] - fn member_variable_constraint_list() { - assert_eq!( - MemberVariableConstraintList::parse("#[]"), - Ok(( - "", - MemberVariableConstraintList { - constraints: vec![] - } - )) - ); - - assert_eq!( - MemberVariableConstraintList::parse("#[integer]"), - Ok(( - "", - MemberVariableConstraintList { - constraints: vec![MemberVariableConstraint::Integer] - } - )) - ); - - assert_eq!( - MemberVariableConstraintList::parse("#[integer, integer]"), - Ok(( - "", - MemberVariableConstraintList { - constraints: vec![ - MemberVariableConstraint::Integer, - MemberVariableConstraint::Integer - ] - } - )) - ); - - assert_eq!( - MemberVariableConstraintList::parse("#[integer, integer, integer]"), - Ok(( - "", - MemberVariableConstraintList { - constraints: vec![ - MemberVariableConstraint::Integer, - MemberVariableConstraint::Integer, - MemberVariableConstraint::Integer - ] - } - )) - ); - } - - #[test] - fn member_variable() { - assert_eq!( - MemberVariable::parse("variable: Number"), - Ok(( - "", - MemberVariable { - name: "variable", - ty: MemberVariableType { - ty: VariableType::Scalar("Number"), - constraints: None, - default_value: None - }, - } - )) - ); - - assert_eq!( - MemberVariable::parse("variable: Number = 2"), - Ok(( - "", - MemberVariable { - name: "variable", - ty: MemberVariableType { - ty: VariableType::Scalar("Number"), - constraints: None, - default_value: Some(Litteral::Scalar(Scalar { - number: Number { - integer: Some("2"), - dot: None, - fractional: None - }, - ty: "" - })) - }, - } - )) - ); - - assert_eq!( - MemberVariable::parse("#[integer] variable: Number = 2"), - Ok(( - "", - MemberVariable { - name: "variable", - ty: MemberVariableType { - ty: VariableType::Scalar("Number"), - constraints: Some(MemberVariableConstraintList { - constraints: vec![MemberVariableConstraint::Integer] - }), - default_value: Some(Litteral::Scalar(Scalar { - number: Number { - integer: Some("2"), - dot: None, - fractional: None - }, - ty: "" - })) - }, - } - )) - ); - } - - #[test] - fn member_variable_type() { - assert_eq!( - MemberVariableType::parse("Number"), - Ok(( - "", - MemberVariableType { - ty: VariableType::Scalar("Number"), - constraints: None, - default_value: None - }, - )) - ); - - assert_eq!( - MemberVariableType::parse("Number = 2"), - Ok(( - "", - MemberVariableType { - ty: VariableType::Scalar("Number"), - constraints: None, - default_value: Some(Litteral::Scalar(Scalar { - number: Number { - integer: Some("2"), - dot: None, - fractional: None - }, - ty: "" - })) - }, - )) - ); - - assert_eq!( - MemberVariableType::parse("#[integer] Number = 2"), - Ok(( - "", - MemberVariableType { - ty: VariableType::Scalar("Number"), - constraints: Some(MemberVariableConstraintList { - constraints: vec![MemberVariableConstraint::Integer] - }), - default_value: Some(Litteral::Scalar(Scalar { - number: Number { - integer: Some("2"), - dot: None, - fractional: None - }, - ty: "" - })) - }, - )) - ); - } -} diff --git a/src/script/parsing/mod.rs b/src/script/parsing/mod.rs deleted file mode 100644 index 5677899..0000000 --- a/src/script/parsing/mod.rs +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::fmt::Display; - -use nom::{ - branch::alt, - bytes::complete::{tag, take_until, take_while, take_while1}, - character::complete::char as nom_char, - combinator::{recognize, rest, value, verify}, - error::context, - multi::{fold_many0, fold_many1}, - sequence::{delimited, preceded}, - CompareResult, IResult, -}; - -// TODO currently all comments are just discarded. -// I would like to start tracking them for auto-formatting and generation -// of documentation. - -pub type VResult = IResult>; - -mod span; -pub use span::Span; - -mod root; -pub use root::{FileAST, Import, RootElement}; - -mod pstruct; -pub use pstruct::StructDefinition; - -mod function; -pub use function::Function; - -mod member_variable; -pub use member_variable::{MemberVariable, MemberVariableType}; - -mod blocks; -pub use blocks::{Block, BlockStatement, CallableBlock, NamedBlock}; - -mod statements; -pub use statements::{ - ArithmeticExpression, Assign, Assignable, AssignableVariable, Break, Comparison, Continue, - Else, Expression, Factor, For, If, Loop, Match, Return, Statement, Term, Trailer, While, -}; - -mod variable_type; -pub use variable_type::{FunctionSignature, VariableType}; - -mod range; -pub use range::Range; - -mod struct_initalization; -pub use struct_initalization::StructInitialization; - -mod litteral; -pub use litteral::Litteral; - -mod list; -pub use list::List; - -mod string; -pub use string::PString; - -mod number; -pub use number::Number; - -mod scalar; -pub use scalar::Scalar; - -mod closure; -pub use closure::{CapturedVariable, Closure}; - -fn is_digit(c: char) -> bool { - "0123456789".contains(c) -} - -fn parse_integer(input: S) -> VResult { - take_while1(is_digit)(input) -} - -fn parse_name(input: S) -> VResult { - fn is_alpha(c: char) -> bool { - c.is_alphabetic() || matches!(c, '_') - } - - fn is_alphanumeric(c: char) -> bool { - is_alpha(c) || c.is_numeric() - } - - context( - "Failed to parse name", - recognize(preceded(take_while1(is_alpha), take_while(is_alphanumeric))), - )(input) -} - -fn take_keyword(keyword: &'static str) -> impl FnMut(S) -> VResult { - verify(parse_name::, move |name| { - matches!(name.compare(keyword), CompareResult::Ok) && name.input_len() == keyword.len() - }) -} - -fn is_space(c: char) -> bool { - matches!(c, ' ' | '\t' | '\r' | '\n') -} - -fn consume_space_token(input: S) -> VResult { - value( - (), - alt(( - take_while1(is_space), - delimited(tag("//"), take_while(|c| c != '\n'), nom_char('\n')), - delimited(tag("/*"), take_until("*/"), tag("*/")), - preceded(tag("//"), rest), - )), - )(input) -} - -fn space0(input: S) -> VResult { - value((), fold_many0(consume_space_token, || (), |_, _| {}))(input) -} - -fn space1(input: S) -> VResult { - value((), fold_many1(consume_space_token, || (), |_, _| {}))(input) -} - -/// Presents iterators in a comma separated format. -struct IteratorFormatter(pub I) -where - I: Iterator + Clone, - D: Display; - -impl Display for IteratorFormatter -where - I: Iterator + Clone, - D: Display, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut iter = self.0.clone().peekable(); - - loop { - match (iter.next(), iter.peek().is_some()) { - (Some(next), true) => write!(f, "{}, ", next)?, - (Some(next), false) => write!(f, "{}", next)?, - (None, _) => break Ok(()), - } - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn integer() { - assert_eq!(parse_integer("1234"), Ok(("", "1234"))); - assert_eq!(parse_integer("1234.5678"), Ok((".5678", "1234"))); - } - - #[test] - fn name() { - assert_eq!(parse_name("name"), Ok(("", "name"))); - assert_eq!(parse_name("name1"), Ok(("", "name1"))); - assert_eq!(parse_name("name_1"), Ok(("", "name_1"))); - assert_eq!(parse_name("_name"), Ok(("", "_name"))); - assert!(parse_name("1name").is_err()); - } - - #[test] - fn space_and_comments() { - assert_eq!(space0(" \t\r\n"), Ok(("", ()))); - assert_eq!( - space0(" // A line comment. \n after_the_line"), - Ok(("after_the_line", ())) - ); - assert_eq!( - space0(" /* a block comment. */ after_the_comment"), - Ok(("after_the_comment", ())) - ); - - assert_eq!( - space0(" /* a block comment. */ /* another comment */ after_the_comment"), - Ok(("after_the_comment", ())) - ); - assert_eq!(space0(""), Ok(("", ()))); - - assert_eq!(space1(" \t\r\n"), Ok(("", ()))); - assert_eq!( - space1(" // A line comment. \n after_the_line"), - Ok(("after_the_line", ())) - ); - assert_eq!(space1(" // A line comment with no newline"), Ok(("", ()))); - assert_eq!( - space1(" /* a block comment. */ after_the_comment"), - Ok(("after_the_comment", ())) - ); - - assert_eq!( - space1(" /* a block comment. */ /* another comment */ after_the_comment"), - Ok(("after_the_comment", ())) - ); - assert!(space1("").is_err()); - } -} diff --git a/src/script/parsing/number.rs b/src/script/parsing/number.rs deleted file mode 100644 index 0f0e81f..0000000 --- a/src/script/parsing/number.rs +++ /dev/null @@ -1,179 +0,0 @@ -use std::fmt::Display; - -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::take_while1, - combinator::{map, success, verify}, - sequence::{delimited, tuple}, -}; - -use crate::script::parsing::{parse_integer, space0}; - -use super::{Span, VResult}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Number { - pub integer: Option, - pub dot: Option, - pub fractional: Option, -} - -impl Number { - pub fn parse(input: S) -> VResult { - fn take_dot(input: S) -> VResult { - verify(take_while1(|c| c == '.'), |output: &S| { - output.as_str().len() == 1 - })(input) - } - - let (input, (integer, dot, fractional)) = alt(( - tuple(( - map(parse_integer, Some), - map(delimited(space0, take_dot, space0), Some), - map(parse_integer, Some), - )), - tuple(( - success(None), - map(delimited(space0, take_dot, space0), Some), - map(parse_integer, Some), - )), - tuple(( - map(parse_integer, Some), - map(delimited(space0, take_dot, space0), Some), - success(None), - )), - tuple((map(parse_integer, Some), success(None), success(None))), - ))(input)?; - - Ok(( - input, - Self { - integer, - dot, - fractional, - }, - )) - } - - pub fn get_span(&self) -> &S { - // We accept '0.0', '.0', '0.', and '.', but not ''. It should not be possible for this to fail. - self.integer.as_ref().unwrap_or_else(|| { - self.dot - .as_ref() - .unwrap_or_else(|| self.fractional.as_ref().unwrap()) - }) - } - - pub fn to_float

(&self) -> Result - where - P: std::str::FromStr, - { - match (self.integer.as_ref(), self.fractional.as_ref()) { - (None, None) => P::from_str("0.0"), - (Some(integer), None) => P::from_str(&integer.to_string()), - (None, Some(fractional)) => P::from_str(&format!(".{}", fractional.to_string())), - (Some(integer), Some(fractional)) => P::from_str(&format!( - "{}.{}", - integer.to_string(), - fractional.to_string() - )), - } - } -} - -impl Display for Number { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match ( - self.integer.as_ref().map(|s| s.as_str()), - self.fractional.as_ref().map(|s| s.as_str()), - ) { - (None, None) => write!(f, "."), - (Some(integer), None) => write!(f, "{}", integer), - (None, Some(fractional)) => write!(f, ".{}", fractional), - (Some(integer), Some(fractional)) => write!(f, "{}.{}", integer, fractional), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn number() { - assert_eq!( - Number::parse("1234"), - Ok(( - "", - Number { - integer: Some("1234"), - dot: None, - fractional: None - } - )) - ); - assert_eq!( - Number::parse(".1234"), - Ok(( - "", - Number { - integer: None, - dot: Some("."), - fractional: Some("1234") - } - )) - ); - assert_eq!( - Number::parse("1234."), - Ok(( - "", - Number { - integer: Some("1234"), - dot: Some("."), - fractional: None - } - )) - ); - assert_eq!( - Number::parse("1234.5678"), - Ok(( - "", - Number { - integer: Some("1234"), - dot: Some("."), - fractional: Some("5678") - } - )) - ); - assert_eq!( - Number::parse("1234;"), - Ok(( - ";", - Number { - integer: Some("1234"), - dot: None, - fractional: None - } - )) - ); - assert!(Number::parse("").is_err()); - assert!(Number::parse(".").is_err()); - } -} diff --git a/src/script/parsing/pstruct.rs b/src/script/parsing/pstruct.rs deleted file mode 100644 index 73bd29a..0000000 --- a/src/script/parsing/pstruct.rs +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use nom::{ - character::complete::char as nom_char, - combinator::{map, opt}, - multi::separated_list0, - sequence::{delimited, pair, preceded}, -}; - -use super::{parse_name, space0, take_keyword, MemberVariable, Span, VResult}; - -#[derive(Debug, Eq, PartialEq)] -pub struct StructDefinition { - pub name: S, - pub members: Vec>, -} - -impl StructDefinition { - pub fn parse(input: S) -> VResult { - map( - preceded( - take_keyword("struct"), - pair( - delimited(space0, parse_name, space0), - delimited( - pair(nom_char('{'), space0), - separated_list0(nom_char(','), preceded(space0, MemberVariable::parse)), - preceded( - pair(opt(pair(space0, nom_char(','))), space0), - nom_char('}'), - ), - ), - ), - ), - |(name, assignments)| Self { - name, - members: assignments, - }, - )(input) - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::member_variable::{ - MemberVariableConstraint, MemberVariableConstraintList, MemberVariableType, - }; - use crate::script::parsing::{Litteral, VariableType}; - - use super::*; - - #[test] - fn parse_struct() { - assert_eq!( - StructDefinition::parse("struct MyStruct {}"), - Ok(( - "", - StructDefinition { - name: "MyStruct", - members: vec![] - } - )) - ); - - assert_eq!( - StructDefinition::parse("struct MyStruct { a: Length }"), - Ok(( - "", - StructDefinition { - name: "MyStruct", - members: vec![MemberVariable { - name: "a", - ty: MemberVariableType { - ty: VariableType::Scalar("Length"), - constraints: None, - default_value: None - } - }] - } - )) - ); - - assert_eq!( - StructDefinition::parse("struct MyStruct { a: Length, #[integer] b: Angle = true }"), - Ok(( - "", - StructDefinition { - name: "MyStruct", - members: vec![ - MemberVariable { - name: "a", - ty: MemberVariableType { - ty: VariableType::Scalar("Length"), - constraints: None, - default_value: None - } - }, - MemberVariable { - name: "b", - ty: MemberVariableType { - ty: VariableType::Scalar("Angle"), - constraints: Some(MemberVariableConstraintList { - constraints: vec![MemberVariableConstraint::Integer] - }), - default_value: Some(Litteral::Boolean("true", true)) - } - } - ] - } - )) - ); - - assert_eq!( - StructDefinition::parse("struct MyStruct { a: Length, #[integer] b: Angle = true, }"), - Ok(( - "", - StructDefinition { - name: "MyStruct", - members: vec![ - MemberVariable { - name: "a", - ty: MemberVariableType { - ty: VariableType::Scalar("Length"), - constraints: None, - default_value: None - } - }, - MemberVariable { - name: "b", - ty: MemberVariableType { - ty: VariableType::Scalar("Angle"), - constraints: Some(MemberVariableConstraintList { - constraints: vec![MemberVariableConstraint::Integer] - }), - default_value: Some(Litteral::Boolean("true", true)) - } - } - ] - } - )) - ); - } -} diff --git a/src/script/parsing/range.rs b/src/script/parsing/range.rs deleted file mode 100644 index acfb156..0000000 --- a/src/script/parsing/range.rs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::tag, - combinator::{map, opt}, - sequence::{delimited, tuple}, -}; - -use crate::script::parsing::space0; - -use super::{Span, Trailer, VResult}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Range { - pub comparison_operator: S, - pub lower_bound: Option>, - pub upper_bound_is_inclusive: bool, - pub upper_bound: Option>, -} - -impl Range { - pub fn parse(input: S) -> VResult { - enum RangeType { - Exclusive(S), - Inclusive(S), - } - - map( - tuple(( - opt(Trailer::parse), - delimited( - space0, - alt(( - map(tag("..="), RangeType::Inclusive), - map(tag(".."), RangeType::Exclusive), - )), - space0, - ), - opt(Trailer::parse), - )), - |(lower_bound, range_type, upper_bound)| { - let (comparison_operator, upper_bound_is_inclusive) = match range_type { - RangeType::Inclusive(operator) => (operator, true), - RangeType::Exclusive(operator) => (operator, false), - }; - - Self { - comparison_operator, - upper_bound, - upper_bound_is_inclusive, - lower_bound, - } - }, - )(input) - } - - pub fn get_span(&self) -> &S { - self.lower_bound - .as_ref() - .map(|expression| expression.get_span()) - .unwrap_or(&self.comparison_operator) - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::Factor; - - use super::*; - - #[test] - fn range() { - assert_eq!( - Range::parse(".."), - Ok(( - "", - Range { - comparison_operator: "..", - upper_bound: None, - upper_bound_is_inclusive: false, - lower_bound: None, - } - )) - ); - assert_eq!( - Range::parse("..="), - Ok(( - "", - Range { - comparison_operator: "..=", - upper_bound: None, - upper_bound_is_inclusive: true, - lower_bound: None, - } - )) - ); - assert_eq!( - Range::parse("a..b"), - Ok(( - "", - Range { - comparison_operator: "..", - upper_bound: Some(Trailer::None(Factor::Variable("b"))), - lower_bound: Some(Trailer::None(Factor::Variable("a"))), - upper_bound_is_inclusive: false, - } - )) - ); - - assert_eq!(*Range::parse("..").unwrap().1.get_span(), ".."); - assert_eq!(*Range::parse("a..").unwrap().1.get_span(), "a"); - } -} diff --git a/src/script/parsing/root/import.rs b/src/script/parsing/root/import.rs deleted file mode 100644 index 1612eca..0000000 --- a/src/script/parsing/root/import.rs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use nom::{ - bytes::complete::tag, - character::complete::char as nom_char, - combinator::{map, opt}, - multi::separated_list0, - sequence::{delimited, pair, preceded}, -}; - -use crate::script::{ - parsing::{parse_name, space0, take_keyword, VResult}, - Span, -}; - -#[derive(Debug, Eq, PartialEq)] -pub struct Import { - pub path: Vec, - pub external: bool, -} - -impl Import { - pub fn parse(input: S) -> VResult { - map( - preceded( - take_keyword("import"), - delimited( - space0, - pair( - opt(take_keyword("extern")), - separated_list0(tag("::"), delimited(space0, parse_name, space0)), - ), - pair(space0, nom_char(';')), - ), - ), - |(external, path)| Import { - path, - external: external.is_some(), - }, - )(input) - } -} diff --git a/src/script/parsing/root/mod.rs b/src/script/parsing/root/mod.rs deleted file mode 100644 index ebfc055..0000000 --- a/src/script/parsing/root/mod.rs +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use nom::{ - branch::alt, - combinator::{all_consuming, map}, - multi::many0, - sequence::delimited, -}; - -pub use self::import::Import; - -use super::{space0, Function, Span, StructDefinition, VResult}; - -mod import; - -#[derive(Debug, Eq, PartialEq)] -pub struct FileAST { - pub root_elements: Vec>, -} - -impl FileAST { - pub fn parse(input: S) -> VResult { - all_consuming(map( - many0(delimited(space0, RootElement::parse, space0)), - |root_elements| FileAST { root_elements }, - ))(input) - } -} - -#[derive(Debug, Eq, PartialEq)] -pub enum RootElement { - Import(Import), - Struct(StructDefinition), - Function(Function), -} - -impl RootElement { - fn parse(input: S) -> VResult { - alt(( - map(Import::parse, Self::Import), - map(StructDefinition::parse, Self::Struct), - map(Function::parse_as_sketch, Self::Function), - map(Function::parse_as_solid, Self::Function), - map(Function::parse_as_task, Self::Function), - map(Function::parse_as_function, Self::Function), - ))(input) - } -} - -#[cfg(test)] -mod test { - - use crate::script::parsing::{ - blocks::CallableBlock, Block, FunctionSignature, NamedBlock, VariableType, - }; - - use super::*; - - #[test] - fn root_element() { - // Import(Import), - assert_eq!( - RootElement::parse("import path::to::module;"), - Ok(( - "", - RootElement::Import(Import { - path: vec!["path", "to", "module"], - external: false - }) - )) - ); - - // Struct(Struct), - assert_eq!( - RootElement::parse("struct MyStruct {}"), - Ok(( - "", - RootElement::Struct(StructDefinition { - name: "MyStruct", - members: vec![] - }) - )) - ); - - // Sketch(Sketch), - assert_eq!( - RootElement::parse("sketch my_sketch() {}"), - Ok(( - "", - RootElement::Function(Function { - starting_span: "sketch", - named_block: NamedBlock { - name: "my_sketch", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - }, - signature: FunctionSignature::Sketch { arguments: vec![] }, - }) - )) - ); - - // Solid(Solid), - assert_eq!( - RootElement::parse("solid my_solid() {}"), - Ok(( - "", - RootElement::Function(Function { - starting_span: "solid", - named_block: NamedBlock { - name: "my_solid", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - }, - signature: FunctionSignature::Solid { arguments: vec![] }, - }) - )) - ); - - // Function(NamedBlock), - assert_eq!( - RootElement::parse("function my_function() -> Length {}"), - Ok(( - "", - RootElement::Function(Function { - starting_span: "function", - named_block: NamedBlock { - name: "my_function", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - }, - signature: FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Length")), - arguments: vec![] - }, - }) - )) - ); - } - - #[test] - fn import() { - assert_eq!( - Import::parse("import path::to::module;"), - Ok(( - "", - Import { - path: vec!["path", "to", "module"], - external: false - } - )) - ); - assert_eq!( - Import::parse("import extern path::to::module;"), - Ok(( - "", - Import { - path: vec!["path", "to", "module"], - external: true - } - )) - ); - } - - #[test] - fn file_ast() { - assert_eq!( - FileAST::parse( - r#" - /// My Struct - struct MyStruct {} - - /* My Sketch */ - sketch my_sketch() {} - solid my_solid() {} - function my_function() -> Length {} - task my_task() -> Length {} -"# - ), - Ok(( - "", - FileAST { - root_elements: vec![ - RootElement::Struct(StructDefinition { - name: "MyStruct", - members: vec![] - }), - RootElement::Function(Function { - starting_span: "sketch", - named_block: NamedBlock { - name: "my_sketch", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - }, - signature: FunctionSignature::Sketch { arguments: vec![] }, - }), - RootElement::Function(Function { - starting_span: "solid", - named_block: NamedBlock { - name: "my_solid", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - }, - signature: FunctionSignature::Solid { arguments: vec![] }, - }), - RootElement::Function(Function { - starting_span: "function", - named_block: NamedBlock { - name: "my_function", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - }, - signature: FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Length")), - arguments: vec![] - }, - }), - RootElement::Function(Function { - starting_span: "task", - named_block: NamedBlock { - name: "my_task", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - }, - signature: FunctionSignature::Task { - return_type: Box::new(VariableType::Scalar("Length")), - arguments: vec![] - }, - }), - ] - } - )) - ); - } -} diff --git a/src/script/parsing/scalar.rs b/src/script/parsing/scalar.rs deleted file mode 100644 index aedb0a9..0000000 --- a/src/script/parsing/scalar.rs +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::{tag, take_while, take_while1}, - character::complete::char as nom_char, - combinator::{consumed, map, verify}, - multi::fold_many0, - sequence::{delimited, separated_pair}, -}; - -use super::{space0, Number, Span, VResult}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Scalar { - pub number: Number, - pub ty: S, -} - -impl Scalar { - pub fn parse(input: S) -> VResult { - // TODO the unit type parsing is terribly under-tested. - map( - separated_pair(Number::parse, space0, Self::parse_type), - |(number, ty)| Scalar { number, ty }, - )(input) - } - - fn parse_type(input: S) -> VResult { - fn parse_unit(input: S) -> VResult { - verify( - map( - consumed(fold_many0( - alt(( - take_while1(|c: char| { - c.is_alphanumeric() || matches!(c, '^' | '/' | '%' | '-' | '\\') - }), - tag("<-"), - )), - || (), - |_, _| (), - )), - |(span, _)| span, - ), - |input: &S| { - let input = input.as_str(); - // If the unit type exists, it must start with a letter or number. - input.is_empty() - || input - .chars() - .next() - .map(|input| input.is_alphanumeric()) - .unwrap_or(false) - }, - )(input) - } - - fn parse_unit_quoted(input: S) -> VResult { - delimited(nom_char('<'), take_while(|c| c != '>'), nom_char('>'))(input) - } - - fn empty_unit(input: S) -> VResult { - let empty = input.slice(..0); - Ok((input, empty)) - } - - alt((parse_unit_quoted, parse_unit, empty_unit))(input) - } - - pub fn get_span(&self) -> &S { - self.number.get_span() - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parse_type() { - assert_eq!(Scalar::parse_type(""), Ok(("", "cm^3(STP)"))); - assert_eq!(Scalar::parse_type("mm"), Ok(("", "mm"))); - assert_eq!(Scalar::parse_type(""), Ok(("", ""))); - - assert_eq!(Scalar::parse_type("mm <"), Ok((" <", "mm"))); - assert_eq!(Scalar::parse_type("mm.floor()"), Ok((".floor()", "mm"))); - assert_eq!(Scalar::parse_type(".floor()"), Ok((".floor()", ""))); - } - - #[test] - fn measurement() { - assert_eq!( - Scalar::parse("22 m"), - Ok(( - "", - Scalar { - number: Number { - integer: Some("22"), - dot: None, - fractional: None - }, - ty: "m" - } - )) - ); - - assert_eq!( - Scalar::parse("22m"), - Ok(( - "", - Scalar { - number: Number { - integer: Some("22"), - dot: None, - fractional: None - }, - ty: "m" - } - )) - ); - - assert_eq!( - Scalar::parse("22.44m"), - Ok(( - "", - Scalar { - number: Number { - integer: Some("22"), - dot: Some("."), - fractional: Some("44"), - }, - ty: "m" - } - )) - ); - } - - #[test] - fn number() { - assert_eq!( - Scalar::parse("22"), - Ok(( - "", - Scalar { - number: Number { - integer: Some("22"), - dot: None, - fractional: None - }, - ty: "" - } - )) - ); - - assert_eq!( - Scalar::parse("22.44"), - Ok(( - "", - Scalar { - number: Number { - integer: Some("22"), - dot: Some("."), - fractional: Some("44"), - }, - ty: "" - } - )) - ); - - assert_eq!( - Scalar::parse("22.44 <"), - Ok(( - "<", - Scalar { - number: Number { - integer: Some("22"), - dot: Some("."), - fractional: Some("44"), - }, - ty: "" - } - )) - ); - - assert_eq!( - Scalar::parse("22.44.floor()"), - Ok(( - ".floor()", - Scalar { - number: Number { - integer: Some("22"), - dot: Some("."), - fractional: Some("44"), - }, - ty: "" - } - )) - ); - } -} diff --git a/src/script/parsing/span.rs b/src/script/parsing/span.rs deleted file mode 100644 index c9a8e8c..0000000 --- a/src/script/parsing/span.rs +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use std::ops::{RangeFrom, RangeTo}; - -use nom_locate::LocatedSpan; - -pub trait Span: - nom::InputIter - + for<'b> nom::Compare<&'b str> - + nom::InputLength - + nom::InputTake - + nom::InputTakeAtPosition - + nom::Slice> - + nom::Slice> - + nom::Offset - + for<'b> nom::FindSubstring<&'b str> - + Clone - + std::fmt::Debug - + Eq - + PartialEq - + std::hash::Hash - + ToString - + AsStr - + FromStr - + FormatSpan - + 'static -{ - fn chars(&self) -> impl Iterator; -} - -pub trait AsStr { - fn as_str(&self) -> &str; -} - -pub trait FromStr { - fn from_str(string: &'static str) -> Self; -} - -impl AsStr for &'static str { - fn as_str(&self) -> &str { - self - } -} - -impl FromStr for &'static str { - fn from_str(string: &'static str) -> Self { - string - } -} - -impl AsStr for LocatedSpan<&'static str> { - fn as_str(&self) -> &str { - self.fragment() - } -} - -impl FromStr for LocatedSpan<&'static str> { - fn from_str(string: &'static str) -> Self { - LocatedSpan::new(string) - } -} - -impl AsStr for imstr::ImString { - fn as_str(&self) -> &str { - self.as_str() - } -} - -impl FromStr for imstr::ImString { - fn from_str(string: &'static str) -> Self { - imstr::ImString::from(string) - } -} - -impl AsStr for LocatedSpan { - fn as_str(&self) -> &str { - self.fragment().as_str() - } -} - -impl FromStr for LocatedSpan { - fn from_str(string: &'static str) -> Self { - LocatedSpan::new(imstr::ImString::from(string)) - } -} - -impl Span for &'static str { - fn chars(&self) -> impl Iterator { - str::chars(self) - } -} -impl Span for LocatedSpan<&'static str> { - fn chars(&self) -> impl Iterator { - self.fragment().chars() - } -} -impl Span for imstr::ImString { - fn chars(&self) -> impl Iterator { - imstr::ImString::chars(self) - } -} -impl Span for LocatedSpan { - fn chars(&self) -> impl Iterator { - self.fragment().chars() - } -} - -pub trait FormatSpan { - fn format_span(&self) -> String; -} - -impl<'a> FormatSpan for &'a str { - fn format_span(&self) -> String { - format!("`{}`", self) - } -} -impl<'a> FormatSpan for LocatedSpan<&'a str> { - fn format_span(&self) -> String { - format!("[{}:{}]", self.location_line(), self.get_column()) - } -} -impl FormatSpan for imstr::ImString { - fn format_span(&self) -> String { - format!("`{}`", self) - } -} -impl FormatSpan for LocatedSpan { - fn format_span(&self) -> String { - format!("[{}:{}]", self.location_line(), self.get_column()) - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::PString; - - use super::*; - - #[test] - fn span_types() { - assert_eq!( - PString::parse(r#""test""#), - Ok(("", PString { value: "test" })) - ); - - assert_eq!( - PString::parse(LocatedSpan::new(r#""test""#)) - .unwrap() - .1 - .value - .fragment(), - &"test" - ); - } -} diff --git a/src/script/parsing/statements/assign.rs b/src/script/parsing/statements/assign.rs deleted file mode 100644 index c6b70cb..0000000 --- a/src/script/parsing/statements/assign.rs +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use nom::{ - branch::alt, - bytes::complete::tag, - character::complete::char as nom_char, - combinator::{map, opt}, - multi::separated_list0, - sequence::{delimited, pair, preceded, terminated, tuple}, -}; - -use crate::script::{ - parsing::{parse_name, space0, space1, take_keyword, VResult, VariableType}, - Span, -}; - -use super::Statement; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct AssignableVariable { - pub name: S, - pub ty: Option>, -} - -impl AssignableVariable { - fn parse(input: S) -> VResult { - map( - pair( - terminated(parse_name, space0), - opt(preceded(pair(nom_char(':'), space0), VariableType::parse)), - ), - |(name, ty)| Self { name, ty }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.name - } -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum Assignable { - Variable(AssignableVariable), - List(S, Vec>), -} - -pub enum AssignableIter< - 'a, - S: Span + 'a, - A: Iterator>, - B: Iterator>, -> { - A(A), - B(B), -} - -impl< - 'a, - S: Span + 'a, - A: Iterator>, - B: Iterator>, - > std::iter::Iterator for AssignableIter<'a, S, A, B> -{ - type Item = &'a AssignableVariable; - - fn next(&mut self) -> Option { - match self { - AssignableIter::A(iter) => iter.next(), - AssignableIter::B(iter) => iter.next(), - } - } -} - -impl Assignable { - pub(super) fn parse(input: S) -> VResult { - alt(( - map(AssignableVariable::parse, Self::Variable), - map(Self::parse_list, |(starting_span, list)| { - Self::List(starting_span, list) - }), - ))(input) - } - - fn parse_list(input: S) -> VResult>)> { - terminated( - pair( - tag("["), - separated_list0( - delimited(space0, nom_char(','), space0), - AssignableVariable::parse, - ), - ), - nom_char(']'), - )(input) - } - - pub fn iter( - &self, - ) -> AssignableIter< - '_, - S, - std::iter::Once<&AssignableVariable>, - std::slice::Iter>, - > { - match self { - Assignable::Variable(single) => AssignableIter::A(std::iter::once(single)), - Assignable::List(_span, list) => AssignableIter::B(list.iter()), - } - } - - pub fn get_span(&self) -> &S { - match self { - Assignable::Variable(spanable) => spanable.get_span(), - Assignable::List(spanable, _) => spanable, - } - } -} - -#[derive(Debug, Eq, PartialEq)] -pub struct Assign { - pub starting_span: S, - pub is_new: bool, - pub to_assign: Assignable, - pub statement: Box>, -} - -impl Assign { - pub fn parse(input: S) -> VResult { - map( - tuple(( - opt(terminated(take_keyword::("let"), space1)), - Assignable::parse, - preceded(delimited(space0, nom_char('='), space0), Statement::parse), - )), - |(let_keyword, to_assign, statement)| { - let is_new = let_keyword.is_some(); - Self { - starting_span: let_keyword.unwrap_or_else(|| to_assign.get_span().clone()), - is_new, - to_assign, - statement: Box::new(statement), - } - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn assignable_variable() { - assert_eq!( - AssignableVariable::parse("variable"), - Ok(( - "", - AssignableVariable { - name: "variable", - ty: None - } - )) - ); - - assert_eq!( - AssignableVariable::parse("variable: Length"), - Ok(( - "", - AssignableVariable { - name: "variable", - ty: Some(VariableType::Scalar("Length")), - } - )) - ); - } - - #[test] - fn assignable() { - assert_eq!( - Assignable::parse("variable"), - Ok(( - "", - Assignable::Variable(AssignableVariable { - name: "variable", - ty: None - }) - )) - ); - - assert_eq!( - Assignable::parse("[a, b]"), - Ok(( - "", - Assignable::List( - "[", - vec![ - AssignableVariable { - name: "a", - ty: None - }, - AssignableVariable { - name: "b", - ty: None - } - ] - ) - )) - ); - - assert_eq!( - Assignable::parse("[a: Length, b: Angle]"), - Ok(( - "", - Assignable::List( - "[", - vec![ - AssignableVariable { - name: "a", - ty: Some(VariableType::Scalar("Length")) - }, - AssignableVariable { - name: "b", - ty: Some(VariableType::Scalar("Angle")) - } - ] - ) - )) - ); - } - - #[test] - fn statement_assign() { - assert_eq!( - Assign::parse("a = b"), - Ok(( - "", - Assign { - starting_span: "a", - is_new: false, - to_assign: Assignable::Variable(AssignableVariable { - name: "a", - ty: None, - }), - statement: Box::new(Statement::parse("b").unwrap().1) - } - )) - ); - - assert_eq!( - Assign::parse("a: Length = b"), - Ok(( - "", - Assign { - starting_span: "a", - is_new: false, - to_assign: Assignable::Variable(AssignableVariable { - name: "a", - ty: Some(VariableType::Scalar("Length")), - }), - statement: Box::new(Statement::parse("b").unwrap().1) - } - )) - ); - - assert_eq!( - Assign::parse("a = loop {}"), - Ok(( - "", - Assign { - starting_span: "a", - is_new: false, - to_assign: Assignable::Variable(AssignableVariable { - name: "a", - ty: None, - }), - statement: Box::new(Statement::parse("loop {}").unwrap().1), - } - )) - ); - - assert_eq!( - Assign::parse("let a = b"), - Ok(( - "", - Assign { - starting_span: "let", - is_new: true, - to_assign: Assignable::Variable(AssignableVariable { - name: "a", - ty: None, - }), - statement: Box::new(Statement::parse("b").unwrap().1) - } - )) - ); - } -} diff --git a/src/script/parsing/statements/expression/arithmetic.rs b/src/script/parsing/statements/expression/arithmetic.rs deleted file mode 100644 index 9bebe3f..0000000 --- a/src/script/parsing/statements/expression/arithmetic.rs +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - character::complete::char as nom_char, - combinator::{cut, flat_map, map, value}, - error::context, - multi::fold_many0, - sequence::{delimited, pair}, -}; - -use crate::script::{ - parsing::{space0, VResult}, - Span, -}; - -use super::{term::Term, Trailer}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum ArithmeticExpression { - Addition(Box, Term), - Subtraction(Box, Term), - Term(Term), -} - -impl ArithmeticExpression { - pub fn parser( - trailer_parser: fn(S) -> VResult>, - ) -> impl FnMut(S) -> VResult { - #[derive(Clone)] - enum Operator { - Addition, - Subtraction, - } - - alt(( - flat_map( - move |input| Term::parser(trailer_parser, input), - move |first_term| { - fold_many0( - pair( - delimited( - space0, - alt(( - value(Operator::Addition, nom_char('+')), - value(Operator::Subtraction, nom_char('-')), - )), - space0, - ), - context( - "Expected right side term", - cut(move |input| Term::parser(trailer_parser, input)), - ), - ), - move || ArithmeticExpression::Term(first_term.clone()), - |expression, (operator, factor)| match operator { - Operator::Addition => Self::Addition(Box::new(expression), factor), - Operator::Subtraction => { - Self::Subtraction(Box::new(expression), factor) - } - }, - ) - }, - ), - map(move |input| Term::parser(trailer_parser, input), Self::Term), - )) - } - - pub fn get_span(&self) -> &S { - match self { - ArithmeticExpression::Addition(spanable, _) => spanable.get_span(), - ArithmeticExpression::Subtraction(spanable, _) => spanable.get_span(), - ArithmeticExpression::Term(spanable) => spanable.get_span(), - } - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::statements::expression::{factor::Factor, trailer::Trailer}; - - use super::*; - - #[test] - fn arithmetic_expression() { - // Addition(Box, Term), - assert_eq!( - ArithmeticExpression::parser(Trailer::parse)("a + b"), - Ok(( - "", - ArithmeticExpression::Addition( - Box::new(ArithmeticExpression::Term(Term::Trailer(Trailer::None( - Factor::Variable("a") - )))), - Term::Trailer(Trailer::None(Factor::Variable("b"))) - ) - )) - ); - // Subtraction(Box, Term), - assert_eq!( - ArithmeticExpression::parser(Trailer::parse)("a - b"), - Ok(( - "", - ArithmeticExpression::Subtraction( - Box::new(ArithmeticExpression::Term(Term::Trailer(Trailer::None( - Factor::Variable("a") - )))), - Term::Trailer(Trailer::None(Factor::Variable("b"))) - ) - )) - ); - // Term(Term), - assert_eq!( - ArithmeticExpression::parser(Trailer::parse)("a"), - Ok(( - "", - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable("a")))) - )) - ); - - // Order of operation tests. - assert_eq!( - ArithmeticExpression::parser(Trailer::parse)("+a + b"), - Ok(( - "", - ArithmeticExpression::Addition( - Box::new(ArithmeticExpression::Term(Term::Trailer(Trailer::None( - Factor::UnaryPlus(Box::new(Factor::Variable("a"))) - )))), - Term::Trailer(Trailer::None(Factor::Variable("b"))) - ) - )) - ); - } -} diff --git a/src/script/parsing/statements/expression/comparison.rs b/src/script/parsing/statements/expression/comparison.rs deleted file mode 100644 index 0738c20..0000000 --- a/src/script/parsing/statements/expression/comparison.rs +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::tag, - character::complete::char as nom_char, - combinator::{cut, flat_map, map, value}, - error::context, - multi::fold_many0, - sequence::{delimited, pair}, -}; - -use crate::script::{ - parsing::{space0, VResult}, - Span, -}; - -use super::{arithmetic::ArithmeticExpression, Trailer}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum Comparison { - LessThan(Box, ArithmeticExpression), - LessThanEqual(Box, ArithmeticExpression), - Equal(Box, ArithmeticExpression), - GreaterThanEqual(Box, ArithmeticExpression), - GreaterThan(Box, ArithmeticExpression), - None(ArithmeticExpression), -} - -impl Comparison { - pub(super) fn parser( - trailer_parser: fn(S) -> VResult>, - input: S, - ) -> VResult { - #[derive(Clone)] - enum Operator { - LessThan, - LessThanEqual, - Equal, - GreaterThanEqual, - GreaterThan, - } - - alt(( - flat_map( - ArithmeticExpression::parser(trailer_parser), - move |first_expression| { - fold_many0( - pair( - delimited( - space0, - alt(( - value(Operator::LessThanEqual, tag("<=")), - value(Operator::Equal, tag("==")), - value(Operator::GreaterThanEqual, tag(">=")), - value(Operator::LessThan, nom_char('<')), - value(Operator::GreaterThan, nom_char('>')), - )), - space0, - ), - context( - "Expected right side arithmetic expression.", - cut(ArithmeticExpression::parser(trailer_parser)), - ), - ), - move || Comparison::None(first_expression.clone()), - |comparison, (operator, expression)| match operator { - Operator::LessThan => Self::LessThan(Box::new(comparison), expression), - Operator::LessThanEqual => { - Self::LessThanEqual(Box::new(comparison), expression) - } - Operator::Equal => Self::Equal(Box::new(comparison), expression), - Operator::GreaterThanEqual => { - Self::GreaterThanEqual(Box::new(comparison), expression) - } - Operator::GreaterThan => { - Self::GreaterThan(Box::new(comparison), expression) - } - }, - ) - }, - ), - map(ArithmeticExpression::parser(trailer_parser), Self::None), - ))(input) - } - - pub fn get_span(&self) -> &S { - match self { - Comparison::LessThan(spanable, _) => spanable.get_span(), - Comparison::LessThanEqual(spanable, _) => spanable.get_span(), - Comparison::Equal(spanable, _) => spanable.get_span(), - Comparison::GreaterThanEqual(spanable, _) => spanable.get_span(), - Comparison::GreaterThan(spanable, _) => spanable.get_span(), - Comparison::None(spanable) => spanable.get_span(), - } - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::statements::expression::{ - factor::Factor, term::Term, trailer::Trailer, - }; - - use super::*; - - #[test] - fn comparison() { - assert_eq!( - Comparison::parser(Trailer::parse, "a"), - Ok(( - "", - Comparison::None(ArithmeticExpression::Term(Term::Trailer(Trailer::None( - Factor::Variable("a") - )))) - )) - ); - - assert_eq!( - Comparison::parser(Trailer::parse, "a < b"), - Ok(( - "", - Comparison::LessThan( - Box::new(Comparison::None(ArithmeticExpression::Term(Term::Trailer( - Trailer::None(Factor::Variable("a")) - )))), - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable("b")))), - ) - )) - ); - - assert_eq!( - Comparison::parser(Trailer::parse, "a <= b"), - Ok(( - "", - Comparison::LessThanEqual( - Box::new(Comparison::None(ArithmeticExpression::Term(Term::Trailer( - Trailer::None(Factor::Variable("a")) - )))), - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable("b")))), - ) - )) - ); - - assert_eq!( - Comparison::parser(Trailer::parse, "a == b"), - Ok(( - "", - Comparison::Equal( - Box::new(Comparison::None(ArithmeticExpression::Term(Term::Trailer( - Trailer::None(Factor::Variable("a")) - )))), - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable("b")))), - ) - )) - ); - - assert_eq!( - Comparison::parser(Trailer::parse, "a >= b"), - Ok(( - "", - Comparison::GreaterThanEqual( - Box::new(Comparison::None(ArithmeticExpression::Term(Term::Trailer( - Trailer::None(Factor::Variable("a")) - )))), - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable("b")))), - ) - )) - ); - - assert_eq!( - Comparison::parser(Trailer::parse, "a > b"), - Ok(( - "", - Comparison::GreaterThan( - Box::new(Comparison::None(ArithmeticExpression::Term(Term::Trailer( - Trailer::None(Factor::Variable("a")) - )))), - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable("b")))), - ) - )) - ); - } -} diff --git a/src/script/parsing/statements/expression/factor.rs b/src/script/parsing/statements/expression/factor.rs deleted file mode 100644 index be297ff..0000000 --- a/src/script/parsing/statements/expression/factor.rs +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - character::complete::char as nom_char, - combinator::map, - sequence::{delimited, pair, preceded}, -}; - -use crate::script::{ - parsing::{parse_name, space0, Litteral, VResult}, - Span, -}; - -use super::Expression; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum Factor { - Litteral(Litteral), - Variable(S), - Parenthesis(Box>), - UnaryPlus(Box>), - UnaryMinus(Box>), - UnaryLogicalNot(Box>), -} - -impl Factor { - pub fn parse(input: S) -> VResult { - alt(( - map( - preceded(pair(nom_char('+'), space0), Self::parse), - |factor| Self::UnaryPlus(Box::new(factor)), - ), - map( - preceded(pair(nom_char('-'), space0), Self::parse), - |factor| Self::UnaryMinus(Box::new(factor)), - ), - map( - preceded(pair(nom_char('!'), space0), Self::parse), - |factor| Self::UnaryLogicalNot(Box::new(factor)), - ), - map(Litteral::parse, Self::Litteral), - map( - delimited(nom_char('('), Expression::parse, nom_char(')')), - |expression| Self::Parenthesis(Box::new(expression)), - ), - map(parse_name, Self::Variable), - ))(input) - } - - pub fn get_span(&self) -> &S { - match self { - Factor::Litteral(spanable) => spanable.get_span(), - Factor::Variable(spanable) => spanable, - Factor::Parenthesis(spanable) => spanable.get_span(), - Factor::UnaryPlus(spanable) => spanable.get_span(), - Factor::UnaryMinus(spanable) => spanable.get_span(), - Factor::UnaryLogicalNot(spanable) => spanable.get_span(), - } - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::{ - statements::expression::{ - arithmetic::ArithmeticExpression, comparison::Comparison, term::Term, trailer::Trailer, - }, - Number, Scalar, - }; - - use super::*; - - #[test] - fn factor() { - // Const(Litteral), - assert_eq!( - Factor::parse("22"), - Ok(( - "", - Factor::Litteral(Litteral::Scalar(Scalar { - number: Number { - integer: Some("22"), - dot: None, - fractional: None - }, - ty: "" - })) - )) - ); - // Variable(S), - assert_eq!( - Factor::parse("my_variable"), - Ok(("", Factor::Variable("my_variable"))) - ); - // Parenthesis(Box>), - assert_eq!( - Factor::parse("(my_variable)"), - Ok(( - "", - Factor::Parenthesis(Box::new(Expression::Buffer(Comparison::None( - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable( - "my_variable" - )))) - )))) - )) - ); - // UnaryPlus(Term), - assert_eq!( - Factor::parse("+my_variable"), - Ok(( - "", - Factor::UnaryPlus(Box::new(Factor::Variable("my_variable"))) - )) - ); - // UnaryMinus(Term), - assert_eq!( - Factor::parse("-my_variable"), - Ok(( - "", - Factor::UnaryMinus(Box::new(Factor::Variable("my_variable"))) - )) - ); - // UnaryLogicalNot(Term), - assert_eq!( - Factor::parse("!my_variable"), - Ok(( - "", - Factor::UnaryLogicalNot(Box::new(Factor::Variable("my_variable"))) - )) - ); - } -} diff --git a/src/script/parsing/statements/expression/mod.rs b/src/script/parsing/statements/expression/mod.rs deleted file mode 100644 index 4aad8e8..0000000 --- a/src/script/parsing/statements/expression/mod.rs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::tag, - combinator::{cut, flat_map, map, value}, - error::context, - multi::fold_many0, - sequence::{delimited, pair}, -}; - -use crate::script::{ - parsing::{space0, VResult}, - Span, -}; - -pub use self::{ - arithmetic::ArithmeticExpression, comparison::Comparison, factor::Factor, term::Term, - trailer::Trailer, -}; - -mod arithmetic; -mod comparison; -mod factor; -mod term; -mod trailer; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum Expression { - And(Box, Comparison), - Or(Box, Comparison), - Buffer(Comparison), -} - -impl Expression { - pub fn parse(input: S) -> VResult { - Self::parse_impl(Trailer::parse, input) - } - - pub fn parse_no_struct_initalization(input: S) -> VResult { - Self::parse_impl(Trailer::parse_no_struct_initalization, input) - } - - fn parse_impl(trailer_parser: fn(S) -> VResult>, input: S) -> VResult { - #[derive(Clone)] - enum Operator { - And, - Or, - } - - alt(( - flat_map( - |input| Comparison::parser(trailer_parser, input), - move |first_comp| { - fold_many0( - pair( - delimited( - space0, - alt(( - value(Operator::And, tag("&&")), - value(Operator::Or, tag("||")), - )), - space0, - ), - context( - "Expected right side comparison or arithmetic expression", - cut(move |input| Comparison::parser(trailer_parser, input)), - ), - ), - move || Self::Buffer(first_comp.clone()), - |expression, (operator, comparison)| match operator { - Operator::And => Self::And(Box::new(expression), comparison), - Operator::Or => Self::Or(Box::new(expression), comparison), - }, - ) - }, - ), - map( - |input| Comparison::parser(trailer_parser, input), - Self::Buffer, - ), - ))(input) - } - - pub fn get_span(&self) -> &S { - match self { - Expression::And(spanable, _) => spanable.get_span(), - Expression::Or(spanable, _) => spanable.get_span(), - Expression::Buffer(spanable) => spanable.get_span(), - } - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::statements::expression::{ - arithmetic::ArithmeticExpression, factor::Factor, term::Term, trailer::Trailer, - }; - - use super::*; - - #[test] - fn expression() { - assert_eq!( - Expression::parse("a"), - Ok(( - "", - Expression::Buffer(Comparison::None(ArithmeticExpression::Term(Term::Trailer( - Trailer::None(Factor::Variable("a")) - ))),) - )) - ); - - assert_eq!( - Expression::parse("a || b"), - Ok(( - "", - Expression::Or( - Box::new(Expression::Buffer(Comparison::None( - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable( - "a" - )))) - ))), - Comparison::None(ArithmeticExpression::Term(Term::Trailer(Trailer::None( - Factor::Variable("b") - )))) - ) - )) - ); - - assert_eq!( - Expression::parse("a && b"), - Ok(( - "", - Expression::And( - Box::new(Expression::Buffer(Comparison::None( - ArithmeticExpression::Term(Term::Trailer(Trailer::None(Factor::Variable( - "a" - )))) - ))), - Comparison::None(ArithmeticExpression::Term(Term::Trailer(Trailer::None( - Factor::Variable("b") - )))) - ) - )) - ); - } -} diff --git a/src/script/parsing/statements/expression/term.rs b/src/script/parsing/statements/expression/term.rs deleted file mode 100644 index 5db8db3..0000000 --- a/src/script/parsing/statements/expression/term.rs +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - character::complete::char as nom_char, - combinator::{cut, flat_map, map, value}, - error::context, - multi::fold_many0, - sequence::{delimited, pair}, -}; - -use crate::script::{ - parsing::{space0, Range, VResult}, - Span, -}; - -use super::trailer::Trailer; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum Term { - Multiply(Box, Trailer), - Divide(Box, Trailer), - Range(Range), - Trailer(Trailer), -} - -impl Term { - pub fn parser(trailer_parser: fn(S) -> VResult>, input: S) -> VResult { - #[derive(Clone)] - enum Operator { - Multiplication, - Division, - } - - alt(( - map(Range::parse, Self::Range), - flat_map(trailer_parser, move |first_trailer| { - fold_many0( - pair( - delimited( - space0, - alt(( - value(Operator::Multiplication, nom_char('*')), - value(Operator::Division, nom_char('/')), - )), - space0, - ), - context("Expected right side term.", cut(trailer_parser)), - ), - move || Self::Trailer(first_trailer.clone()), - |term, (operator, accessor)| match operator { - Operator::Multiplication => Self::Multiply(Box::new(term), accessor), - Operator::Division => Self::Divide(Box::new(term), accessor), - }, - ) - }), - map(trailer_parser, Self::Trailer), - ))(input) - } - - pub fn get_span(&self) -> &S { - match self { - Term::Multiply(spanable, _) => spanable.get_span(), - Term::Divide(spanable, _) => spanable.get_span(), - Term::Range(spannable) => spannable.get_span(), - Term::Trailer(spanable) => spanable.get_span(), - } - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::statements::expression::factor::Factor; - - use super::*; - - #[test] - fn term() { - // Multiply(Box, Factor), - assert_eq!( - Term::parser(Trailer::parse, "x * y"), - Ok(( - "", - Term::Multiply( - Box::new(Term::Trailer(Trailer::None(Factor::Variable("x")))), - Trailer::None(Factor::Variable("y")) - ) - )) - ); - // Divide(Box, Factor), - assert_eq!( - Term::parser(Trailer::parse, "x / y"), - Ok(( - "", - Term::Divide( - Box::new(Term::Trailer(Trailer::None(Factor::Variable("x")))), - Trailer::None(Factor::Variable("y")) - ) - )) - ); - // Factor(Factor), - assert_eq!( - Term::parser(Trailer::parse, "x"), - Ok(("", Term::Trailer(Trailer::None(Factor::Variable("x"))))) - ); - - // Range(Range), - assert_eq!( - Term::parser(Trailer::parse, "a..b"), - Ok(( - "", - Term::Range(Range { - comparison_operator: "..", - upper_bound: Some(Trailer::None(Factor::Variable("b"))), - lower_bound: Some(Trailer::None(Factor::Variable("a"))), - upper_bound_is_inclusive: false, - }) - )) - ); - assert_eq!( - Term::parser(Trailer::parse, ".."), - Ok(( - "", - Term::Range(Range { - comparison_operator: "..", - upper_bound: None, - lower_bound: None, - upper_bound_is_inclusive: false, - }) - )) - ); - } -} diff --git a/src/script/parsing/statements/expression/trailer.rs b/src/script/parsing/statements/expression/trailer.rs deleted file mode 100644 index 7eab8a0..0000000 --- a/src/script/parsing/statements/expression/trailer.rs +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - character::complete::char as nom_char, - combinator::{flat_map, map}, - multi::{fold_many0, separated_list0}, - sequence::{delimited, pair, preceded}, -}; - -use crate::script::{ - parsing::{parse_name, space0, StructInitialization, VResult}, - Span, -}; - -use super::{factor::Factor, Expression}; - -#[derive(Clone)] -enum Operation { - Attribute(S), - Call(Vec>), - MethodCall(S, Vec>), - StructInitalization(StructInitialization), - Index(Expression), -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum Trailer { - None(Factor), - Attribute(Box>, S), - Call(Box>, Vec>), - MethodCall(Box>, S, Vec>), - StructInitalization(Box>, StructInitialization), - Index(Box>, Box>), -} - -impl Trailer { - fn parse_method_call(input: S) -> VResult> { - map( - pair( - preceded(pair(nom_char('.'), space0), parse_name), - preceded( - space0, - delimited( - nom_char('('), - separated_list0( - nom_char(','), - delimited(space0, Expression::parse, space0), - ), - nom_char(')'), - ), - ), - ), - |(attribute, arguments)| Operation::MethodCall(attribute, arguments), - )(input) - } - - fn parse_attribute_access(input: S) -> VResult> { - map( - preceded(pair(nom_char('.'), space0), parse_name), - Operation::Attribute, - )(input) - } - - fn parse_struct_initalization(input: S) -> VResult> { - map(StructInitialization::parse, Operation::StructInitalization)(input) - } - - fn parse_call(input: S) -> VResult> { - map( - delimited( - nom_char('('), - separated_list0(nom_char(','), delimited(space0, Expression::parse, space0)), - nom_char(')'), - ), - Operation::Call, - )(input) - } - - fn parse_index(input: S) -> VResult> { - map( - delimited( - nom_char('['), - delimited(space0, Expression::parse, space0), - nom_char(']'), - ), - Operation::Index, - )(input) - } - - fn unpack_operation(trailer: Trailer, operation: Operation) -> Self { - match operation { - Operation::Attribute(member_name) => Self::Attribute(Box::new(trailer), member_name), - Operation::Call(arguments) => Self::Call(Box::new(trailer), arguments), - Operation::MethodCall(member_name, arguments) => { - Self::MethodCall(Box::new(trailer), member_name, arguments) - } - Operation::StructInitalization(initalization) => { - Self::StructInitalization(Box::new(trailer), initalization) - } - Operation::Index(indexer) => Self::Index(Box::new(trailer), Box::new(indexer)), - } - } - - pub fn parse(input: S) -> VResult { - alt(( - flat_map(Factor::parse, |first_factor| { - fold_many0( - delimited( - space0, - alt(( - Self::parse_method_call, - Self::parse_attribute_access, - Self::parse_struct_initalization, - Self::parse_call, - Self::parse_index, - )), - space0, - ), - move || Self::None(first_factor.clone()), - Self::unpack_operation, - ) - }), - map(Factor::parse, Self::None), - ))(input) - } - - pub fn parse_no_struct_initalization(input: S) -> VResult { - alt(( - flat_map(Factor::parse, |first_factor| { - fold_many0( - delimited( - space0, - alt(( - Self::parse_method_call, - Self::parse_attribute_access, - Self::parse_call, - Self::parse_index, - )), - space0, - ), - move || Self::None(first_factor.clone()), - Self::unpack_operation, - ) - }), - map(Factor::parse, Self::None), - ))(input) - } - - pub fn get_span(&self) -> &S { - match self { - Trailer::None(spanable) => spanable.get_span(), - Trailer::Attribute(spanable, _) => spanable.get_span(), - Trailer::Call(spanable, _) => spanable.get_span(), - Trailer::Index(spanable, _) => spanable.get_span(), - Trailer::StructInitalization(spanable, _) => spanable.get_span(), - Trailer::MethodCall(spanable, _, _) => spanable.get_span(), - } - } -} - -#[cfg(test)] -mod test { - use crate::script::parsing::Litteral; - - use super::*; - - #[test] - fn trailer_call() { - assert_eq!( - Trailer::parse("my_function()"), - Ok(( - "", - Trailer::Call( - Box::new(Trailer::None(Factor::Variable("my_function"))), - vec![] - ) - )) - ); - - assert_eq!( - Trailer::parse("my_function(a)"), - Ok(( - "", - Trailer::Call( - Box::new(Trailer::None(Factor::Variable("my_function"))), - vec![Expression::parse("a").unwrap().1] - ) - )) - ); - - assert_eq!( - Trailer::parse("my_function(a, b)"), - Ok(( - "", - Trailer::Call( - Box::new(Trailer::None(Factor::Variable("my_function"))), - vec![ - Expression::parse("a").unwrap().1, - Expression::parse("b").unwrap().1 - ] - ) - )) - ); - } - - #[test] - fn trailer_attribute() { - assert_eq!( - Trailer::parse("variable"), - Ok(("", Trailer::None(Factor::Variable("variable")))) - ); - - assert_eq!( - Trailer::parse("variable.access"), - Ok(( - "", - Trailer::Attribute( - Box::new(Trailer::None(Factor::Variable("variable"))), - "access" - ) - )) - ); - - assert_eq!( - Trailer::parse("variable.access.sub_access"), - Ok(( - "", - Trailer::Attribute( - Box::new(Trailer::Attribute( - Box::new(Trailer::None(Factor::Variable("variable"))), - "access" - )), - "sub_access" - ) - )) - ); - } - - #[test] - fn trailer_index() { - assert_eq!( - Trailer::parse("x[y]"), - Ok(( - "", - Trailer::Index( - Box::new(Trailer::None(Factor::Variable("x"))), - Box::new(Expression::parse("y").unwrap().1) - ) - )) - ); - - assert_eq!( - Trailer::parse("x[y][z]"), - Ok(( - "", - Trailer::Index( - Box::new(Trailer::Index( - Box::new(Trailer::None(Factor::Variable("x"))), - Box::new(Expression::parse("y").unwrap().1) - )), - Box::new(Expression::parse("z").unwrap().1) - ) - )) - ); - } - - #[test] - fn trailer_method_call() { - assert_eq!( - Trailer::parse("test.sub_call()"), - Ok(( - "", - Trailer::MethodCall( - Box::new(Trailer::None(Factor::Variable("test"))), - "sub_call", - vec![] - ), - )) - ); - - assert_eq!( - Trailer::parse("test.sub_call(a, b)"), - Ok(( - "", - Trailer::MethodCall( - Box::new(Trailer::None(Factor::Variable("test"))), - "sub_call", - vec![ - Expression::parse("a").unwrap().1, - Expression::parse("b").unwrap().1 - ] - ), - )) - ); - - assert_eq!( - Trailer::parse("test.sub_call().returned()"), - Ok(( - "", - Trailer::MethodCall( - Box::new(Trailer::MethodCall( - Box::new(Trailer::None(Factor::Variable("test"))), - "sub_call", - vec![] - )), - "returned", - vec![] - ) - )) - ); - } - - #[test] - fn trailer_struct_initalization() { - assert_eq!( - Trailer::parse("MyStruct { ..default }"), - Ok(( - "", - Trailer::StructInitalization( - Box::new(Trailer::None(Factor::Variable("MyStruct"))), - StructInitialization { - starting_span: "{", - assignments: vec![], - inheritance: Some(Box::new(Trailer::None(Factor::Litteral( - Litteral::Default("default") - )))) - } - ) - )) - ); - // StructInitalization(StructInitalization), - assert_eq!( - Trailer::parse("MyStruct {}"), - Ok(( - "", - Trailer::StructInitalization( - Box::new(Trailer::None(Factor::Variable("MyStruct"))), - StructInitialization { - starting_span: "{", - assignments: vec![], - inheritance: None - } - ) - )) - ); - assert_eq!( - Trailer::parse("MyStruct { a = b, }"), - Ok(( - "", - Trailer::StructInitalization( - Box::new(Trailer::None(Factor::Variable("MyStruct"))), - StructInitialization { - starting_span: "{", - assignments: vec![("a", Expression::parse("b").unwrap().1),], - inheritance: None - } - ) - )) - ); - assert_eq!( - Trailer::parse("MyStruct { a = b, c = d }"), - Ok(( - "", - Trailer::StructInitalization( - Box::new(Trailer::None(Factor::Variable("MyStruct"))), - StructInitialization { - starting_span: "{", - assignments: vec![ - ("a", Expression::parse("b").unwrap().1), - ("c", Expression::parse("d").unwrap().1) - ], - inheritance: None - } - ) - )) - ); - assert_eq!( - Trailer::parse("MyStruct { a = b, c = d, ..default }"), - Ok(( - "", - Trailer::StructInitalization( - Box::new(Trailer::None(Factor::Variable("MyStruct"))), - StructInitialization { - starting_span: "{", - assignments: vec![ - ("a", Expression::parse("b").unwrap().1), - ("c", Expression::parse("d").unwrap().1) - ], - inheritance: Some(Box::new(Trailer::None(Factor::Litteral( - Litteral::Default("default") - )))) - } - ) - )) - ); - } -} diff --git a/src/script/parsing/statements/mod.rs b/src/script/parsing/statements/mod.rs deleted file mode 100644 index d3c3798..0000000 --- a/src/script/parsing/statements/mod.rs +++ /dev/null @@ -1,259 +0,0 @@ -use std::rc::Rc; - -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{branch::alt, combinator::map, error::context}; - -pub use self::{ - assign::{Assign, Assignable, AssignableVariable}, - expression::{ArithmeticExpression, Comparison, Expression, Factor, Term, Trailer}, - s_break::Break, - s_continue::Continue, - s_for::For, - s_if::{Else, If}, - s_loop::Loop, - s_match::Match, - s_return::Return, - s_while::While, -}; - -use super::{Function, Span, StructDefinition, VResult}; - -mod assign; -mod expression; -mod s_break; -mod s_continue; -mod s_for; -mod s_if; -mod s_loop; -mod s_match; -mod s_return; -mod s_while; - -#[derive(Debug, Eq, PartialEq)] -pub enum Statement { - Expression(Expression), - Assign(Assign), - Return(Return), - If(If), - Match(Match), - For(For), - While(While), - Loop(Loop), - Break(Break), - Continue(Continue), - DefineFunction(Rc>), - DefineStruct(Rc>), -} - -impl Statement { - pub fn parse(input: S) -> VResult { - context( - "Failed to parse statement", - alt(( - map(Assign::parse, Self::Assign), - map(Return::parse, Self::Return), - map(If::parse, Self::If), - map(Match::parse, Self::Match), - map(For::parse, Self::For), - map(While::parse, Self::While), - map(Loop::parse, Self::Loop), - map(Break::parse, Self::Break), - map(Continue::parse, Self::Continue), - map(Function::parse_as_function, |function| { - Self::DefineFunction(Rc::new(function)) - }), - map(StructDefinition::parse, |structure| { - Self::DefineStruct(Rc::new(structure)) - }), - map(Expression::parse, Self::Expression), - )), - )(input) - } - - pub fn get_span(&self) -> &S { - match self { - Statement::Expression(spanable) => spanable.get_span(), - Statement::Assign(spanable) => spanable.get_span(), - Statement::Return(spanable) => spanable.get_span(), - Statement::If(spanable) => spanable.get_span(), - Statement::Match(spanable) => spanable.get_span(), - Statement::For(spanable) => spanable.get_span(), - Statement::While(spanable) => spanable.get_span(), - Statement::Loop(spanable) => spanable.get_span(), - Statement::Break(spanable) => spanable.get_span(), - Statement::Continue(spanable) => spanable.get_span(), - Statement::DefineFunction(spanable) => spanable.get_span(), - Statement::DefineStruct(spanable) => &spanable.name, - } - } -} - -#[cfg(test)] -mod test { - use std::rc::Rc; - - use crate::script::parsing::{ - blocks::CallableBlock, - statements::assign::{Assignable, AssignableVariable}, - Block, FunctionSignature, NamedBlock, VariableType, - }; - - use super::*; - - #[test] - fn statement() { - assert_eq!( - Statement::parse("a = b"), - Ok(( - "", - Statement::Assign(Assign { - starting_span: "a", - is_new: false, - to_assign: Assignable::Variable(AssignableVariable { - name: "a", - ty: None, - }), - statement: Box::new(Statement::parse("b").unwrap().1) - }) - )) - ); - - assert_eq!( - Statement::parse("return"), - Ok(( - "", - Statement::Return(Return { - starting_span: "return", - expression: None - }) - )) - ); - - assert_eq!( - Statement::parse("match a {}"), - Ok(( - "", - Statement::Match(Match { - starting_span: "match", - expression: Expression::parse("a").unwrap().1, - branches: vec![] - }) - )) - ); - - assert_eq!( - Statement::parse("for a in b {}"), - Ok(( - "", - Statement::For(For { - starting_span: "for", - name: None, - variable_assignment: Assignable::parse("a").unwrap().1, - iterator_expression: Expression::parse("b").unwrap().1, - block: Block { statements: vec![] } - }) - )) - ); - - assert_eq!( - Statement::parse("while a {}"), - Ok(( - "", - Statement::While(While { - starting_span: "while", - name: None, - expression: Expression::parse("a").unwrap().1, - block: Block { statements: vec![] } - }) - )) - ); - - assert_eq!( - Statement::parse("loop {}"), - Ok(( - "", - Statement::Loop(Loop { - starting_span: "loop", - name: None, - block: Block { statements: vec![] } - }) - )) - ); - - assert_eq!( - Statement::parse("break"), - Ok(( - "", - Statement::Break(Break { - starting_span: "break", - loop_name: None, - expression: None - }) - )) - ); - - assert_eq!( - Statement::parse("continue"), - Ok(( - "", - Statement::Continue(Continue { - starting_span: "continue", - - loop_name: None - }) - )) - ); - - assert_eq!( - Statement::parse("function my_function() -> Number {}"), - Ok(( - "", - Statement::DefineFunction( - Function { - starting_span: "function", - named_block: NamedBlock { - name: "my_function", - callable: CallableBlock { - parameter_span: "(", - parameters: vec![], - block: Block { statements: vec![] } - } - }, - signature: FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Number")), - arguments: vec![] - } - } - .into() - ) - )) - ); - - assert_eq!( - Statement::parse("struct MyStruct {}"), - Ok(( - "", - Statement::DefineStruct(Rc::new(StructDefinition { - name: "MyStruct", - members: vec![] - })) - )) - ); - } -} diff --git a/src/script/parsing/statements/s_break.rs b/src/script/parsing/statements/s_break.rs deleted file mode 100644 index e39bb6a..0000000 --- a/src/script/parsing/statements/s_break.rs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - character::complete::char as nom_char, - combinator::{map, opt}, - sequence::{pair, preceded}, -}; - -use crate::script::{ - parsing::{parse_name, space0, space1, take_keyword, Expression, VResult}, - Span, -}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Break { - pub starting_span: S, - pub loop_name: Option, - pub expression: Option>, -} - -impl Break { - pub fn parse(input: S) -> VResult { - map( - pair( - take_keyword("break"), - pair( - opt(preceded(pair(space0, nom_char('\'')), parse_name)), - opt(preceded(space1, Expression::parse)), - ), - ), - |(starting_span, (loop_name, expression))| Break { - starting_span, - loop_name, - expression, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn statement_break() { - assert_eq!( - Break::parse("break"), - Ok(( - "", - Break { - starting_span: "break", - loop_name: None, - expression: None - } - )) - ); - - assert_eq!( - Break::parse("break a"), - Ok(( - "", - Break { - starting_span: "break", - loop_name: None, - expression: Some(Expression::parse("a").unwrap().1) - } - )) - ); - - assert_eq!( - Break::parse("break 'my_loop"), - Ok(( - "", - Break { - starting_span: "break", - loop_name: Some("my_loop"), - expression: None - } - )) - ); - - assert_eq!( - Break::parse("break 'my_loop a"), - Ok(( - "", - Break { - starting_span: "break", - loop_name: Some("my_loop"), - expression: Some(Expression::parse("a").unwrap().1) - } - )) - ); - } -} diff --git a/src/script/parsing/statements/s_continue.rs b/src/script/parsing/statements/s_continue.rs deleted file mode 100644 index a765be3..0000000 --- a/src/script/parsing/statements/s_continue.rs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - character::complete::char as nom_char, - combinator::{map, opt}, - sequence::{pair, preceded}, -}; - -use crate::script::{ - parsing::{parse_name, space0, take_keyword, VResult}, - Span, -}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Continue { - pub starting_span: S, - pub loop_name: Option, -} - -impl Continue { - pub fn parse(input: S) -> VResult { - map( - pair( - take_keyword("continue"), - opt(preceded(pair(space0, nom_char('\'')), parse_name)), - ), - |(starting_span, loop_name)| Continue { - starting_span, - loop_name, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn statement_continue() { - assert_eq!( - Continue::parse("continue"), - Ok(( - "", - Continue { - starting_span: "continue", - - loop_name: None - } - )) - ); - assert_eq!( - Continue::parse("continue 'my_loop"), - Ok(( - "", - Continue { - starting_span: "continue", - loop_name: Some("my_loop") - } - )) - ); - } -} diff --git a/src/script/parsing/statements/s_for.rs b/src/script/parsing/statements/s_for.rs deleted file mode 100644 index 1f1f0b6..0000000 --- a/src/script/parsing/statements/s_for.rs +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - character::complete::char as nom_char, - combinator::{cut, map, opt}, - error::context, - sequence::{delimited, pair, tuple}, -}; - -use crate::script::{ - parsing::{parse_name, space0, take_keyword, Block, Expression, VResult}, - Span, -}; - -use super::assign::Assignable; - -#[derive(Debug, Eq, PartialEq)] -pub struct For { - pub starting_span: S, - pub name: Option, - pub variable_assignment: Assignable, - pub iterator_expression: Expression, - pub block: Block, -} - -impl For { - pub fn parse(input: S) -> VResult { - map( - tuple(( - opt(delimited( - nom_char('\''), - parse_name, - pair(nom_char(':'), space0), - )), - pair( - take_keyword("for"), - cut(tuple(( - delimited( - space0, - context("Missing variable assignment", Assignable::parse), - space0, - ), - context("Missing `in` keyword", take_keyword("in")), - delimited( - space0, - context( - "Missing iterator expression", - Expression::parse_no_struct_initalization, - ), - space0, - ), - Block::parse, - ))), - ), - )), - |(name, (starting_span, (variable_assignment, _in, iterator_expression, block)))| { - Self { - starting_span, - name, - variable_assignment, - iterator_expression, - block, - } - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn statement_for() { - assert_eq!( - For::parse("for a in b {}"), - Ok(( - "", - For { - starting_span: "for", - name: None, - variable_assignment: Assignable::parse("a").unwrap().1, - iterator_expression: Expression::parse("b").unwrap().1, - block: Block { statements: vec![] } - } - )) - ); - - assert_eq!( - For::parse("'my_for_loop: for a in b {}"), - Ok(( - "", - For { - starting_span: "for", - name: Some("my_for_loop"), - variable_assignment: Assignable::parse("a").unwrap().1, - iterator_expression: Expression::parse("b").unwrap().1, - block: Block { statements: vec![] } - } - )) - ); - } -} diff --git a/src/script/parsing/statements/s_if.rs b/src/script/parsing/statements/s_if.rs deleted file mode 100644 index 34b7ebf..0000000 --- a/src/script/parsing/statements/s_if.rs +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ - -use nom::{ - branch::alt, - combinator::{cut, map, opt}, - error::context, - sequence::{delimited, pair, preceded, terminated, tuple}, -}; - -use crate::script::{ - parsing::{space0, space1, take_keyword, Block, Expression, VResult}, - Span, -}; - -#[derive(Debug, Eq, PartialEq)] -pub struct If { - pub starting_span: S, - pub expression: Expression, - pub block: Block, - pub else_statement: Option>, -} - -#[derive(Debug, Eq, PartialEq)] -pub enum Else { - Else(Block), - IfElse(Box>), -} - -impl If { - pub fn parse(input: S) -> VResult { - map( - pair( - terminated(take_keyword("if"), space1), - tuple(( - context( - "An expression is required for the if condition", - Expression::parse_no_struct_initalization, - ), - delimited( - space0, - context("Expected a code block for if statement", Block::parse), - space0, - ), - opt(preceded( - take_keyword("else"), - context( - "Expected a code block or another if statement after `else` keyword", - cut(alt(( - map(preceded(space0, Block::parse), |block| Else::Else(block)), - map(preceded(space1, Self::parse), |if_statement| { - Else::IfElse(Box::new(if_statement)) - }), - ))), - ), - )), - )), - ), - |(starting_span, (expression, block, else_statement))| Self { - starting_span, - expression, - block, - else_statement, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn statement_if() { - assert_eq!( - If::parse("if a {}"), - Ok(( - "", - If { - starting_span: "if", - expression: Expression::parse("a").unwrap().1, - block: Block { statements: vec![] }, - else_statement: None - } - )) - ); - - assert_eq!( - If::parse("if a {} else {}"), - Ok(( - "", - If { - starting_span: "if", - expression: Expression::parse("a").unwrap().1, - block: Block { statements: vec![] }, - else_statement: Some(Else::Else(Block { statements: vec![] })) - } - )) - ); - - assert_eq!( - If::parse("if a {} else if b {}"), - Ok(( - "", - If { - starting_span: "if", - expression: Expression::parse("a").unwrap().1, - block: Block { statements: vec![] }, - else_statement: Some(Else::IfElse(Box::new(If { - starting_span: "if", - expression: Expression::parse("b").unwrap().1, - block: Block { statements: vec![] }, - else_statement: None, - }))) - } - )) - ); - - assert!(If::parse("ifa {}").is_err()); - assert_eq!(If::parse("if a {} elseif").unwrap().0, "elseif"); - assert!(If::parse("if a {} else = 0").is_err()); - assert_eq!(If::parse("if a {} var = 0").unwrap().0, "var = 0"); - } -} diff --git a/src/script/parsing/statements/s_loop.rs b/src/script/parsing/statements/s_loop.rs deleted file mode 100644 index 2ff20a8..0000000 --- a/src/script/parsing/statements/s_loop.rs +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - character::complete::char as nom_char, - combinator::{cut, map, opt}, - error::context, - sequence::{delimited, pair, preceded, tuple}, -}; - -use crate::script::{ - parsing::{parse_name, space0, take_keyword, Block, VResult}, - Span, -}; - -#[derive(Debug, Eq, PartialEq)] -pub struct Loop { - pub starting_span: S, - pub name: Option, - pub block: Block, -} - -impl Loop { - pub fn parse(input: S) -> VResult { - map( - tuple(( - opt(delimited( - nom_char('\''), - parse_name, - pair(nom_char(':'), space0), - )), - pair( - take_keyword("loop"), - cut(preceded( - space0, - context("Loop is missing its block", Block::parse), - )), - ), - )), - |(name, (starting_span, block))| Self { - starting_span, - name, - block, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn statement_loop() { - assert_eq!( - Loop::parse("loop {}"), - Ok(( - "", - Loop { - starting_span: "loop", - name: None, - block: Block { statements: vec![] } - } - )) - ); - - assert_eq!( - Loop::parse("'my_loop: loop {}"), - Ok(( - "", - Loop { - starting_span: "loop", - name: Some("my_loop"), - block: Block { statements: vec![] } - } - )) - ); - } -} diff --git a/src/script/parsing/statements/s_match.rs b/src/script/parsing/statements/s_match.rs deleted file mode 100644 index 3631600..0000000 --- a/src/script/parsing/statements/s_match.rs +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::tag, - character::complete::char as nom_char, - combinator::map, - error::context, - multi::separated_list0, - sequence::{delimited, pair, separated_pair, terminated}, -}; - -use crate::script::{ - parsing::{space0, space1, take_keyword, Block, BlockStatement, Expression, Litteral, VResult}, - Span, -}; - -use super::Statement; - -#[derive(Debug, Eq, PartialEq)] -pub struct Match { - pub starting_span: S, - pub expression: Expression, - pub branches: Vec>, -} - -impl Match { - pub fn parse(input: S) -> VResult { - map( - pair( - terminated(take_keyword("match"), space1), - pair( - Expression::parse_no_struct_initalization, - delimited( - pair(space0, nom_char('{')), - separated_list0( - nom_char(','), - delimited(space0, MatchBranch::parse, space0), - ), - pair(space0, nom_char('}')), - ), - ), - ), - |(starting_span, (expression, branches))| Self { - starting_span, - expression, - branches, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[derive(Debug, Eq, PartialEq)] -pub struct MatchBranch { - pub litteral: Litteral, - pub block: Block, -} - -impl MatchBranch { - fn parse(input: S) -> VResult { - map( - separated_pair( - context("Match branch must start with a litteral", Litteral::parse), - delimited( - space0, - context("Match branch missing `=>` token", tag("=>")), - space0, - ), - alt(( - Block::parse, - map(Expression::parse, |expression| Block { - statements: vec![BlockStatement::Open(Statement::Expression(expression))], - }), - )), - ), - |(litteral, block)| Self { litteral, block }, - )(input) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn match_branch() { - assert_eq!( - MatchBranch::parse("1 => {}"), - Ok(( - "", - MatchBranch { - litteral: Litteral::parse("1").unwrap().1, - block: Block { statements: vec![] } - } - )) - ); - - assert_eq!( - MatchBranch::parse("1 => false"), - Ok(( - "", - MatchBranch { - litteral: Litteral::parse("1").unwrap().1, - block: Block { - statements: vec![BlockStatement::Open(Statement::Expression( - Expression::parse("false").unwrap().1 - ))] - } - } - )) - ); - } - - #[test] - fn statement_match() { - assert_eq!( - Match::parse("match a {}"), - Ok(( - "", - Match { - starting_span: "match", - expression: Expression::parse("a").unwrap().1, - branches: vec![] - } - )) - ); - - assert_eq!( - Match::parse("match a { 1 => {} }"), - Ok(( - "", - Match { - starting_span: "match", - expression: Expression::parse("a").unwrap().1, - branches: vec![MatchBranch { - litteral: Litteral::parse("1").unwrap().1, - block: Block { statements: vec![] } - }] - } - )) - ); - - assert_eq!( - Match::parse("match a { 1 => {}, 2 => {}}"), - Ok(( - "", - Match { - starting_span: "match", - expression: Expression::parse("a").unwrap().1, - branches: vec![ - MatchBranch { - litteral: Litteral::parse("1").unwrap().1, - block: Block { statements: vec![] } - }, - MatchBranch { - litteral: Litteral::parse("2").unwrap().1, - block: Block { statements: vec![] } - } - ] - } - )) - ); - - assert!(Match::parse("match a { not a branch }").is_err(),); - } -} diff --git a/src/script/parsing/statements/s_return.rs b/src/script/parsing/statements/s_return.rs deleted file mode 100644 index a0e6bfd..0000000 --- a/src/script/parsing/statements/s_return.rs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - combinator::{map, opt}, - sequence::{pair, preceded}, -}; - -use crate::script::{ - parsing::{space1, take_keyword, Expression, VResult}, - Span, -}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Return { - pub starting_span: S, - pub expression: Option>, -} - -impl Return { - pub fn parse(input: S) -> VResult { - map( - pair( - take_keyword("return"), - opt(preceded(space1, Expression::parse)), - ), - |(starting_span, expression)| Self { - starting_span, - expression, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn statement_return() { - assert_eq!( - Return::parse("return"), - Ok(( - "", - Return { - starting_span: "return", - expression: None - } - )) - ); - - assert_eq!( - Return::parse("return a"), - Ok(( - "", - Return { - starting_span: "return", - expression: Some(Expression::parse("a").unwrap().1) - } - )) - ); - assert!(Return::parse("returna").is_err()); - } -} diff --git a/src/script/parsing/statements/s_while.rs b/src/script/parsing/statements/s_while.rs deleted file mode 100644 index 88a48ad..0000000 --- a/src/script/parsing/statements/s_while.rs +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - character::complete::char as nom_char, - combinator::{cut, map, opt}, - error::context, - sequence::{delimited, pair, tuple}, -}; - -use crate::script::{ - parsing::{parse_name, space0, take_keyword, Block, Expression, VResult}, - Span, -}; - -#[derive(Debug, Eq, PartialEq)] -pub struct While { - pub starting_span: S, - pub name: Option, - pub expression: Expression, - pub block: Block, -} - -impl While { - pub fn parse(input: S) -> VResult { - map( - tuple(( - opt(delimited( - nom_char('\''), - parse_name, - pair(nom_char(':'), space0), - )), - pair( - take_keyword("while"), - cut(pair( - delimited( - space0, - context( - "Missing while loop condition expression", - Expression::parse_no_struct_initalization, - ), - space0, - ), - context("While loop is missing block", Block::parse), - )), - ), - )), - |(name, (starting_span, (expression, block)))| Self { - starting_span, - name, - expression, - block, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn statement_while() { - assert_eq!( - While::parse("while a {}"), - Ok(( - "", - While { - starting_span: "while", - name: None, - expression: Expression::parse("a").unwrap().1, - block: Block { statements: vec![] } - } - )) - ); - - assert_eq!( - While::parse("'my_while_loop: while a {}"), - Ok(( - "", - While { - starting_span: "while", - name: Some("my_while_loop"), - expression: Expression::parse("a").unwrap().1, - block: Block { statements: vec![] } - } - )) - ); - } -} diff --git a/src/script/parsing/string.rs b/src/script/parsing/string.rs deleted file mode 100644 index c605b10..0000000 --- a/src/script/parsing/string.rs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::{escaped, take_till, take_while1}, - character::complete::{char as nom_char, one_of}, - sequence::delimited, -}; - -use super::{Span, VResult}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct PString { - pub value: S, -} - -impl PString { - pub fn parse(input: S) -> VResult { - let (input, value) = delimited( - nom_char('"'), - alt(( - escaped( - take_while1(|c| !matches!(c, '\\' | '"')), - '\\', - one_of(r#""n\"#), - ), - take_till(|c| c == '"'), - )), - nom_char('"'), - )(input)?; - - Ok((input, PString { value })) - } - - pub fn get_span(&self) -> &S { - &self.value - } -} - -impl ToString for PString { - fn to_string(&self) -> String { - let mut char_iterator = self.value.chars().peekable(); - let iterator = std::iter::from_fn(|| { - char_iterator - .next() - .map(|next| (next, char_iterator.peek().copied())) - }); - let mut string = String::default(); - - for (current, next) in iterator { - if current == '\\' { - match next { - Some('"') => string.push('"'), - Some('\n') => string.push('\n'), - Some('\\') => string.push('\\'), - _ => {} // Shouldn't be possible to get an unrecognized char. This could happen for a downslash at the end of the string though. - } - } else { - string.push(current); - } - } - - string - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_string() { - assert_eq!( - PString::parse(r#""test""#), - Ok(("", PString { value: "test" })) - ); - assert_eq!( - PString::parse(r#"" \n \\ \" ""#), - Ok(( - "", - PString { - value: r#" \n \\ \" "#, - } - )) - ); - assert_eq!(PString::parse("\"\""), Ok(("", PString { value: "" }))); - } -} diff --git a/src/script/parsing/struct_initalization.rs b/src/script/parsing/struct_initalization.rs deleted file mode 100644 index 8931e86..0000000 --- a/src/script/parsing/struct_initalization.rs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use nom::{ - branch::alt, - bytes::complete::tag, - character::complete::char as nom_char, - combinator::{map, success, value}, - error::context, - multi::separated_list0, - sequence::{delimited, pair, preceded, separated_pair, terminated}, -}; - -use super::{parse_name, space0, Expression, Span, Trailer, VResult}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct StructInitialization { - pub starting_span: S, - pub assignments: Vec<(S, Expression)>, - pub inheritance: Option>>, -} - -impl StructInitialization { - pub fn parse(input: S) -> VResult { - map( - terminated( - pair( - terminated(context("Missing opening bracket", tag("{")), space0), - alt(( - pair( - success(vec![]), - map( - preceded( - terminated(tag(".."), space0), - map(Trailer::parse, Box::new), - ), - Some, - ), - ), - pair( - separated_list0( - nom_char(','), - delimited( - space0, - separated_pair( - parse_name, - delimited(space0, nom_char('='), space0), - Expression::parse, - ), - space0, - ), - ), - alt(( - map( - preceded( - pair( - pair(space0, nom_char(',')), - delimited(space0, tag(".."), space0), - ), - map(Trailer::parse, Box::new), - ), - Some, - ), - value(None, delimited(space0, nom_char(','), space0)), - success(None), - )), - ), - )), - ), - pair(space0, context("Missing closing bracket", nom_char('}'))), - ), - |(starting_span, (assignments, inheritance))| Self { - starting_span, - assignments, - inheritance, - }, - )(input) - } - - pub fn get_span(&self) -> &S { - &self.starting_span - } -} - -// The functionality of struct initalization is done in the tests for trailers. diff --git a/src/script/parsing/variable_type.rs b/src/script/parsing/variable_type.rs deleted file mode 100644 index 28af6c2..0000000 --- a/src/script/parsing/variable_type.rs +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright 2024 James Carl - * AGPL-3.0-only or AGPL-3.0-or-later - * - * This file is part of Command Cad. - * - * Command CAD is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -use std::{borrow::Cow, fmt::Display}; - -use nom::{ - branch::alt, - bytes::complete::tag, - character::complete::char as nom_char, - combinator::map, - error::context, - multi::many0, - sequence::{delimited, pair, preceded, terminated}, -}; - -use crate::script::parsing::IteratorFormatter; - -use super::{parse_name, space0, take_keyword, MemberVariableType, Span, VResult}; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum FunctionSignature { - Function { - return_type: Box>, - arguments: Vec>, - }, - Task { - return_type: Box>, - arguments: Vec>, - }, - Sketch { - arguments: Vec>, - }, - Solid { - arguments: Vec>, - }, -} - -impl FunctionSignature { - fn parse(input: S) -> VResult { - alt(( - Self::parse_private_function, - Self::parse_task, - Self::parse_sketch, - Self::parse_solid, - ))(input) - } - - fn parse_private_function(input: S) -> VResult { - map( - preceded( - pair(tag("function"), space0), - pair( - terminated(Self::parse_argument_list, space0), - Self::parse_return_type, - ), - ), - |(arguments, return_type)| Self::Function { - arguments, - return_type: Box::new(return_type), - }, - )(input) - } - fn parse_task(input: S) -> VResult { - map( - preceded( - pair(tag("task"), space0), - pair( - terminated(Self::parse_argument_list, space0), - Self::parse_return_type, - ), - ), - |(arguments, return_type)| Self::Task { - arguments, - return_type: Box::new(return_type), - }, - )(input) - } - fn parse_sketch(input: S) -> VResult { - map( - preceded( - pair(tag("sketch"), space0), - terminated(Self::parse_argument_list, space0), - ), - |arguments| Self::Sketch { arguments }, - )(input) - } - fn parse_solid(input: S) -> VResult { - map( - preceded( - pair(tag("solid"), space0), - terminated(Self::parse_argument_list, space0), - ), - |arguments| Self::Sketch { arguments }, - )(input) - } - - fn parse_argument_list(input: S) -> VResult>> { - delimited( - nom_char('('), - many0(MemberVariableType::parse), - nom_char(')'), - )(input) - } - - fn parse_return_type(input: S) -> VResult> { - preceded(pair(tag("->"), space0), VariableType::parse)(input) - } -} - -impl Display for FunctionSignature { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - FunctionSignature::Function { - return_type, - arguments, - } => write!( - f, - "function({}) -> {}", - IteratorFormatter(arguments.iter()), - return_type - ), - FunctionSignature::Task { - return_type, - arguments, - } => write!( - f, - "task({}) -> {}", - IteratorFormatter(arguments.iter()), - return_type - ), - FunctionSignature::Sketch { arguments } => { - write!(f, "sketch({})", IteratorFormatter(arguments.iter())) - } - FunctionSignature::Solid { arguments } => { - write!(f, "solid({})", IteratorFormatter(arguments.iter())) - } - } - } -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum VariableType { - String, - List, - Boolean, - Range, - Struct(S), - Scalar(S), - Vector(u8, S), - Transform(u8), - Quaternion, - Cycle, - Region, - Sketch, - Surface, - Solid, - Shell, - Face, - Curve, - HalfEdge, - Vertex, - Function(FunctionSignature), -} - -impl VariableType { - pub(super) fn parse(input: S) -> VResult { - context( - "Invalid type", - alt(( - alt(( - map(tag("String"), |_| Self::String), - map(tag("List"), |_| Self::List), - map(tag("Boolean"), |_| Self::Boolean), - map(tag("Range"), |_| Self::Range), - map(tag("Cycle"), |_| Self::Cycle), - map(tag("Region"), |_| Self::Region), - map(tag("Sketch"), |_| Self::Sketch), - map(tag("Surface"), |_| Self::Surface), - map(tag("Solid"), |_| Self::Solid), - map(tag("Shell"), |_| Self::Shell), - map(tag("Face"), |_| Self::Face), - map(tag("Curve"), |_| Self::Curve), - map(tag("HalfEdge"), |_| Self::HalfEdge), - map(tag("Vertex"), |_| Self::Vertex), - )), - map( - preceded(pair(take_keyword("struct"), space0), parse_name), - Self::Struct, - ), - map( - preceded( - pair(take_keyword("Vector2"), space0), - delimited( - pair(nom_char('<'), space0), - parse_name, - pair(space0, nom_char('>')), - ), - ), - |name| Self::Vector(2, name), - ), - map( - preceded( - pair(take_keyword("Vector3"), space0), - delimited( - pair(nom_char('<'), space0), - parse_name, - pair(space0, nom_char('>')), - ), - ), - |name| Self::Vector(3, name), - ), - map( - preceded( - pair(take_keyword("Vector4"), space0), - delimited( - pair(nom_char('<'), space0), - parse_name, - pair(space0, nom_char('>')), - ), - ), - |name| Self::Vector(4, name), - ), - map(tag("Transform2D"), |_| Self::Transform(2)), - map(tag("Transform3D"), |_| Self::Transform(3)), - map(tag("Quaternion"), |_| Self::Quaternion), - map(FunctionSignature::parse, Self::Function), - map(parse_name, Self::Scalar), - )), - )(input) - } - - pub fn name(&self) -> Cow<'static, str> { - match self { - Self::String => "String".into(), - Self::List => "List".into(), - Self::Boolean => "Boolean".into(), - Self::Range => "Range".into(), - Self::Struct(name) => format!("struct {}", name.as_str()).into(), - Self::Scalar(name) => name.to_string().into(), - Self::Vector(dimension, name) => { - format!("Vector{}<{}>", dimension, name.as_str()).into() - } - Self::Transform(2) => "Transform2D".into(), - Self::Transform(3) => "Transform3D".into(), - Self::Transform(_) => unreachable!(), - Self::Quaternion => "Quaternion".into(), - Self::Cycle => "Cycle".into(), - Self::Region => "Region".into(), - Self::Sketch => "Sketch".into(), - Self::Surface => "Surface".into(), - Self::Solid => "Solid".into(), - Self::Shell => "Shell".into(), - Self::Face => "Face".into(), - Self::Curve => "Curve".into(), - Self::HalfEdge => "HalfEdge".into(), - Self::Vertex => "Vertex".into(), - Self::Function(function) => format!("{}", function).into(), - } - } -} - -impl Display for VariableType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::String => write!(f, "String"), - Self::List => write!(f, "List"), - Self::Boolean => write!(f, "Boolean"), - Self::Range => write!(f, "Range"), - Self::Struct(name) => write!(f, "struct {}", name.as_str()), - Self::Scalar(name) => write!(f, "{}", name.as_str()), - Self::Vector(dimension, name) => { - write!(f, "Vector{}<{}>", dimension, name.as_str()) - } - Self::Transform(2) => write!(f, "Transform2D"), - Self::Transform(3) => write!(f, "Transform3D"), - Self::Transform(_) => unreachable!(), - Self::Quaternion => write!(f, "Quaternion"), - Self::Cycle => write!(f, "Cycle"), - Self::Region => write!(f, "Region"), - Self::Sketch => write!(f, "Sketch"), - Self::Surface => write!(f, "Surface"), - Self::Solid => write!(f, "Solid"), - Self::Shell => write!(f, "Shell"), - Self::Face => write!(f, "Face"), - Self::Curve => write!(f, "Curve"), - Self::HalfEdge => write!(f, "HalfEdge"), - Self::Vertex => write!(f, "Vertex"), - Self::Function(function) => write!(f, "{}", function), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn variable_type() { - assert_eq!( - VariableType::parse("Length"), - Ok(("", VariableType::Scalar("Length"))) - ); - assert_eq!( - VariableType::parse("Angle"), - Ok(("", VariableType::Scalar("Angle"))) - ); - assert_eq!( - VariableType::parse("Vector2"), - Ok(("", VariableType::Vector(2, "Length"))) - ); - assert_eq!( - VariableType::parse("Vector3"), - Ok(("", VariableType::Vector(3, "Length"))) - ); - assert_eq!( - VariableType::parse("Vector4"), - Ok(("", VariableType::Vector(4, "Length"))) - ); - assert_eq!(VariableType::parse("List"), Ok(("", VariableType::List))); - assert_eq!( - VariableType::parse("Boolean"), - Ok(("", VariableType::Boolean)) - ); - assert_eq!( - VariableType::parse("struct MyStruct"), - Ok(("", VariableType::Struct("MyStruct"))) - ); - - assert_eq!( - VariableType::parse("function() -> Number"), - Ok(( - "", - VariableType::Function(FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Number")), - arguments: vec![] - }) - )) - ); - - assert_eq!( - VariableType::parse("function() -> Number"), - Ok(( - "", - VariableType::Function(FunctionSignature::Function { - return_type: Box::new(VariableType::Scalar("Number")), - arguments: vec![] - }) - )) - ); - } -} diff --git a/topiary/command_cad_model.scm b/topiary/command_cad_model.scm new file mode 100644 index 0000000..776966a --- /dev/null +++ b/topiary/command_cad_model.scm @@ -0,0 +1,74 @@ +; These should not be formatted. +[ + (comment) + (string) +] @leaf + +; Surround spaces +[ + "else" + "for" + "if" + "let" + "in" + "*" + "/" + "//" + "+" + "-" + "<<" + ">>" + "&" + "|" + "^" + ">" + ">=" + "==" + "<=" + "<" + "!=" + "&&" + "||" + "->" + "=" + "&=" + "|=" + "^=" + "&&=" + "||=" + "^^=" + "+=" + "-=" + "**=" + "*=" + "//=" + "/=" + "<<=" + ">>=" + (path) +] @prepend_space @append_space + +; Allow blank lines before. +[ + (statement) + (comment) +] @allow_blank_line_before + +; Newlines go after statements. +( + (statement) @append_spaced_softline + . + (comment)* @do_nothing +) +(comment) @append_hardline @prepend_space +("," @prepend_antispace @append_space) +(struct_definition ")" @prepend_antispace) + +(procedural_block + . + "{" @append_spaced_softline @append_indent_start + + + "}" @prepend_indent_end + . +) diff --git a/topiary/language.ncl b/topiary/language.ncl new file mode 100644 index 0000000..f1a1403 --- /dev/null +++ b/topiary/language.ncl @@ -0,0 +1,12 @@ +{ + languages = { + command_cad_model = { + extensions = ["ccm"], + grammar = { + git = "https://github.com/IamTheCarl/CommandCAD.git", + rev = "eb24b9bc749c4a494a71cdeb3e4345f446c05be8", + subdir = "crates/tree-sitter-command-cad-model" + }, + } + }, +} \ No newline at end of file diff --git a/topiary/shell.nix b/topiary/shell.nix new file mode 100644 index 0000000..5e003d9 --- /dev/null +++ b/topiary/shell.nix @@ -0,0 +1,27 @@ +{ pkgs ? import { } }: +let + topiary = pkgs.rustPlatform.buildRustPackage rec { + pname = "topiary-cli"; + version = "0.5.1"; + + src = pkgs.fetchCrate { + inherit pname version; + sha256 = "sha256-D8P4LHq8DxDH2o2aWJjDmMnx+aDlcb0zjeVcUry3jrs="; + }; + + cargoSha256 = "sha256-5c88uUZ3USeRQ6QY/Qfax5UbLsAX6xswybLPhybCJko="; + + # Checks require network access to download languages. + doCheck = false; + }; +in +pkgs.mkShell { + buildInputs = [ + topiary + ]; + + # TODO make this point to the directory with our language query. + shellHook = '' + # export TOPIARTY_LANGUAGE_DIR= + ''; +} diff --git a/topiary/test.ccm b/topiary/test.ccm new file mode 100644 index 0000000..6e811d2 --- /dev/null +++ b/topiary/test.ccm @@ -0,0 +1,22 @@ +# This is a comment +()[] -> std.None { + let value = 5i; # This is some text. + let value = 3i; + + let some_text = "This is some text my man."; + + if this { + # True + } else { + # False + } + + # This is some other text. + let value = 1i + 2i * 3i; + + for i in list { } + + value = 3i; + + let my_function = (a: std.Length, b: std.Length)[value, value2] -> std.None { }; +} diff --git a/tree-sitter-command-cad-model/.editorconfig b/tree-sitter-command-cad-model/.editorconfig new file mode 100644 index 0000000..65330c4 --- /dev/null +++ b/tree-sitter-command-cad-model/.editorconfig @@ -0,0 +1,46 @@ +root = true + +[*] +charset = utf-8 + +[*.{json,toml,yml,gyp}] +indent_style = space +indent_size = 2 + +[*.js] +indent_style = space +indent_size = 2 + +[*.scm] +indent_style = space +indent_size = 2 + +[*.{c,cc,h}] +indent_style = space +indent_size = 4 + +[*.rs] +indent_style = space +indent_size = 4 + +[*.{py,pyi}] +indent_style = space +indent_size = 4 + +[*.swift] +indent_style = space +indent_size = 4 + +[*.go] +indent_style = tab +indent_size = 8 + +[Makefile] +indent_style = tab +indent_size = 8 + +[parser.c] +indent_size = 2 + +[{alloc,array,parser}.h] +indent_size = 2 diff --git a/tree-sitter-command-cad-model/.gitattributes b/tree-sitter-command-cad-model/.gitattributes new file mode 100644 index 0000000..7e2cae0 --- /dev/null +++ b/tree-sitter-command-cad-model/.gitattributes @@ -0,0 +1,37 @@ +* text=auto eol=lf + +# Generated source files +src/*.json linguist-generated +src/parser.c linguist-generated +src/tree_sitter/* linguist-generated + +# C bindings +bindings/c/* linguist-generated +CMakeLists.txt linguist-generated +Makefile linguist-generated + +# Rust bindings +bindings/rust/* linguist-generated +Cargo.toml linguist-generated +Cargo.lock linguist-generated + +# Node.js bindings +bindings/node/* linguist-generated +binding.gyp linguist-generated +package.json linguist-generated +package-lock.json linguist-generated + +# Python bindings +bindings/python/** linguist-generated +setup.py linguist-generated +pyproject.toml linguist-generated + +# Go bindings +bindings/go/* linguist-generated +go.mod linguist-generated +go.sum linguist-generated + +# Swift bindings +bindings/swift/** linguist-generated +Package.swift linguist-generated +Package.resolved linguist-generated diff --git a/tree-sitter-command-cad-model/.gitignore b/tree-sitter-command-cad-model/.gitignore new file mode 100644 index 0000000..308fcab --- /dev/null +++ b/tree-sitter-command-cad-model/.gitignore @@ -0,0 +1,40 @@ +# Rust artifacts +target/ + +# Node artifacts +build/ +prebuilds/ +node_modules/ + +# Swift artifacts +.build/ + +# Go artifacts +_obj/ + +# Python artifacts +.venv/ +dist/ +*.egg-info +*.whl + +# C artifacts +*.a +*.so +*.so.* +*.dylib +*.dll +*.pc + +# Example dirs +/examples/*/ + +# Grammar volatiles +*.wasm +*.obj +*.o + +# Archives +*.tar.gz +*.tgz +*.zip diff --git a/tree-sitter-command-cad-model/CMakeLists.txt b/tree-sitter-command-cad-model/CMakeLists.txt new file mode 100644 index 0000000..618c40e --- /dev/null +++ b/tree-sitter-command-cad-model/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.13) + +project(tree-sitter-command_cad_model + VERSION "0.1.0" + DESCRIPTION "Scripts for creating models in Command CAD" + HOMEPAGE_URL "https://github.com/iamthecarl/commandcad" + LANGUAGES C) + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) +option(TREE_SITTER_REUSE_ALLOCATOR "Reuse the library allocator" OFF) + +set(TREE_SITTER_ABI_VERSION 14 CACHE STRING "Tree-sitter ABI version") +if(NOT ${TREE_SITTER_ABI_VERSION} MATCHES "^[0-9]+$") + unset(TREE_SITTER_ABI_VERSION CACHE) + message(FATAL_ERROR "TREE_SITTER_ABI_VERSION must be an integer") +endif() + +find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI") + +add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" + COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json + --abi=${TREE_SITTER_ABI_VERSION} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Generating parser.c") + +add_library(tree-sitter-command_cad_model src/parser.c) +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c) + target_sources(tree-sitter-command_cad_model PRIVATE src/scanner.c) +endif() +target_include_directories(tree-sitter-command_cad_model PRIVATE src) + +target_compile_definitions(tree-sitter-command_cad_model PRIVATE + $<$:TREE_SITTER_REUSE_ALLOCATOR> + $<$:TREE_SITTER_DEBUG>) + +set_target_properties(tree-sitter-command_cad_model + PROPERTIES + C_STANDARD 11 + POSITION_INDEPENDENT_CODE ON + SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}" + DEFINE_SYMBOL "") + +configure_file(bindings/c/tree-sitter-command_cad_model.pc.in + "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-command_cad_model.pc" @ONLY) + +include(GNUInstallDirs) + +install(FILES bindings/c/tree-sitter-command_cad_model.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/tree_sitter") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-command_cad_model.pc" + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig") +install(TARGETS tree-sitter-command_cad_model + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") + +add_custom_target(ts-test "${TREE_SITTER_CLI}" test + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "tree-sitter test") diff --git a/tree-sitter-command-cad-model/Cargo.toml b/tree-sitter-command-cad-model/Cargo.toml new file mode 100644 index 0000000..541f846 --- /dev/null +++ b/tree-sitter-command-cad-model/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "tree-sitter-command-cad-model" +description = "CommandCadModel grammar for tree-sitter" +version = "0.0.1" +license = "MIT" +readme = "README.md" +keywords = ["incremental", "parsing", "tree-sitter", "command-cad-model"] +categories = ["parsing", "text-editors"] +repository = "https://github.com/tree-sitter/tree-sitter-command-cad-model" +edition = "2021" +autoexamples = false + +build = "bindings/rust/build.rs" +include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"] + +[lib] +path = "bindings/rust/lib.rs" + +[dependencies] +tree-sitter = ">=0.22.6" + +[build-dependencies] +cc = "1.0.87" diff --git a/tree-sitter-command-cad-model/Makefile b/tree-sitter-command-cad-model/Makefile new file mode 100644 index 0000000..3497397 --- /dev/null +++ b/tree-sitter-command-cad-model/Makefile @@ -0,0 +1,94 @@ +ifeq ($(OS),Windows_NT) +$(error Windows is not supported) +endif + +LANGUAGE_NAME := tree-sitter-command_cad_model +HOMEPAGE_URL := https://github.com/iamthecarl/commandcad +VERSION := 0.1.0 + +# repository +SRC_DIR := src + +TS ?= tree-sitter + +# install directory layout +PREFIX ?= /usr/local +INCLUDEDIR ?= $(PREFIX)/include +LIBDIR ?= $(PREFIX)/lib +PCLIBDIR ?= $(LIBDIR)/pkgconfig + +# source/object files +PARSER := $(SRC_DIR)/parser.c +EXTRAS := $(filter-out $(PARSER),$(wildcard $(SRC_DIR)/*.c)) +OBJS := $(patsubst %.c,%.o,$(PARSER) $(EXTRAS)) + +# flags +ARFLAGS ?= rcs +override CFLAGS += -I$(SRC_DIR) -std=c11 -fPIC + +# ABI versioning +SONAME_MAJOR = $(shell sed -n 's/\#define LANGUAGE_VERSION //p' $(PARSER)) +SONAME_MINOR = $(word 1,$(subst ., ,$(VERSION))) + +# OS-specific bits +ifeq ($(shell uname),Darwin) + SOEXT = dylib + SOEXTVER_MAJOR = $(SONAME_MAJOR).$(SOEXT) + SOEXTVER = $(SONAME_MAJOR).$(SONAME_MINOR).$(SOEXT) + LINKSHARED = -dynamiclib -Wl,-install_name,$(LIBDIR)/lib$(LANGUAGE_NAME).$(SOEXTVER),-rpath,@executable_path/../Frameworks +else + SOEXT = so + SOEXTVER_MAJOR = $(SOEXT).$(SONAME_MAJOR) + SOEXTVER = $(SOEXT).$(SONAME_MAJOR).$(SONAME_MINOR) + LINKSHARED = -shared -Wl,-soname,lib$(LANGUAGE_NAME).$(SOEXTVER) +endif +ifneq ($(filter $(shell uname),FreeBSD NetBSD DragonFly),) + PCLIBDIR := $(PREFIX)/libdata/pkgconfig +endif + +all: lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) $(LANGUAGE_NAME).pc + +lib$(LANGUAGE_NAME).a: $(OBJS) + $(AR) $(ARFLAGS) $@ $^ + +lib$(LANGUAGE_NAME).$(SOEXT): $(OBJS) + $(CC) $(LDFLAGS) $(LINKSHARED) $^ $(LDLIBS) -o $@ +ifneq ($(STRIP),) + $(STRIP) $@ +endif + +$(LANGUAGE_NAME).pc: bindings/c/$(LANGUAGE_NAME).pc.in + sed -e 's|@PROJECT_VERSION@|$(VERSION)|' \ + -e 's|@CMAKE_INSTALL_LIBDIR@|$(LIBDIR:$(PREFIX)/%=%)|' \ + -e 's|@CMAKE_INSTALL_INCLUDEDIR@|$(INCLUDEDIR:$(PREFIX)/%=%)|' \ + -e 's|@PROJECT_DESCRIPTION@|$(DESCRIPTION)|' \ + -e 's|@PROJECT_HOMEPAGE_URL@|$(HOMEPAGE_URL)|' \ + -e 's|@CMAKE_INSTALL_PREFIX@|$(PREFIX)|' $< > $@ + +$(PARSER): $(SRC_DIR)/grammar.json + $(TS) generate $^ + +install: all + install -d '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter '$(DESTDIR)$(PCLIBDIR)' '$(DESTDIR)$(LIBDIR)' + install -m644 bindings/c/$(LANGUAGE_NAME).h '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h + install -m644 $(LANGUAGE_NAME).pc '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc + install -m644 lib$(LANGUAGE_NAME).a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a + install -m755 lib$(LANGUAGE_NAME).$(SOEXT) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) + ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) + ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) + +uninstall: + $(RM) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a \ + '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) \ + '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) \ + '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) \ + '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h \ + '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc + +clean: + $(RM) $(OBJS) $(LANGUAGE_NAME).pc lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) + +test: + $(TS) test + +.PHONY: all install uninstall clean test diff --git a/tree-sitter-command-cad-model/Package.swift b/tree-sitter-command-cad-model/Package.swift new file mode 100644 index 0000000..47882c2 --- /dev/null +++ b/tree-sitter-command-cad-model/Package.swift @@ -0,0 +1,37 @@ +// swift-tools-version:5.3 +import PackageDescription + +let package = Package( + name: "TreeSitterCommandCadModel", + products: [ + .library(name: "TreeSitterCommandCadModel", targets: ["TreeSitterCommandCadModel"]), + ], + dependencies: [ + .package(url: "https://github.com/ChimeHQ/SwiftTreeSitter", from: "0.8.0"), + ], + targets: [ + .target( + name: "TreeSitterCommandCadModel", + dependencies: [], + path: ".", + sources: [ + "src/parser.c", + // NOTE: if your language has an external scanner, add it here. + ], + resources: [ + .copy("queries") + ], + publicHeadersPath: "bindings/swift", + cSettings: [.headerSearchPath("src")] + ), + .testTarget( + name: "TreeSitterCommandCadModelTests", + dependencies: [ + "SwiftTreeSitter", + "TreeSitterCommandCadModel", + ], + path: "bindings/swift/TreeSitterCommandCadModelTests" + ) + ], + cLanguageStandard: .c11 +) diff --git a/tree-sitter-command-cad-model/binding.gyp b/tree-sitter-command-cad-model/binding.gyp new file mode 100644 index 0000000..5fc9b2c --- /dev/null +++ b/tree-sitter-command-cad-model/binding.gyp @@ -0,0 +1,30 @@ +{ + "targets": [ + { + "target_name": "tree_sitter_command_cad_model_binding", + "dependencies": [ + " + +typedef struct TSLanguage TSLanguage; + +extern "C" TSLanguage *tree_sitter_command_cad_model(); + +// "tree-sitter", "language" hashed with BLAKE2 +const napi_type_tag LANGUAGE_TYPE_TAG = { + 0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16 +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + exports["name"] = Napi::String::New(env, "command_cad_model"); + auto language = Napi::External::New(env, tree_sitter_command_cad_model()); + language.TypeTag(&LANGUAGE_TYPE_TAG); + exports["language"] = language; + return exports; +} + +NODE_API_MODULE(tree_sitter_command_cad_model_binding, Init) diff --git a/tree-sitter-command-cad-model/bindings/node/index.d.ts b/tree-sitter-command-cad-model/bindings/node/index.d.ts new file mode 100644 index 0000000..efe259e --- /dev/null +++ b/tree-sitter-command-cad-model/bindings/node/index.d.ts @@ -0,0 +1,28 @@ +type BaseNode = { + type: string; + named: boolean; +}; + +type ChildNode = { + multiple: boolean; + required: boolean; + types: BaseNode[]; +}; + +type NodeInfo = + | (BaseNode & { + subtypes: BaseNode[]; + }) + | (BaseNode & { + fields: { [name: string]: ChildNode }; + children: ChildNode[]; + }); + +type Language = { + name: string; + language: unknown; + nodeTypeInfo: NodeInfo[]; +}; + +declare const language: Language; +export = language; diff --git a/tree-sitter-command-cad-model/bindings/node/index.js b/tree-sitter-command-cad-model/bindings/node/index.js new file mode 100644 index 0000000..6657bcf --- /dev/null +++ b/tree-sitter-command-cad-model/bindings/node/index.js @@ -0,0 +1,7 @@ +const root = require("path").join(__dirname, "..", ".."); + +module.exports = require("node-gyp-build")(root); + +try { + module.exports.nodeTypeInfo = require("../../src/node-types.json"); +} catch (_) {} diff --git a/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/__init__.py b/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/__init__.py new file mode 100644 index 0000000..f447501 --- /dev/null +++ b/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/__init__.py @@ -0,0 +1,5 @@ +"CommandCadModel grammar for tree-sitter" + +from ._binding import language + +__all__ = ["language"] diff --git a/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/__init__.pyi b/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/__init__.pyi new file mode 100644 index 0000000..5416666 --- /dev/null +++ b/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/__init__.pyi @@ -0,0 +1 @@ +def language() -> int: ... diff --git a/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/binding.c b/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/binding.c new file mode 100644 index 0000000..2fdeac5 --- /dev/null +++ b/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/binding.c @@ -0,0 +1,27 @@ +#include + +typedef struct TSLanguage TSLanguage; + +TSLanguage *tree_sitter_command_cad_model(void); + +static PyObject* _binding_language(PyObject *self, PyObject *args) { + return PyLong_FromVoidPtr(tree_sitter_command_cad_model()); +} + +static PyMethodDef methods[] = { + {"language", _binding_language, METH_NOARGS, + "Get the tree-sitter language for this grammar."}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "_binding", + .m_doc = NULL, + .m_size = -1, + .m_methods = methods +}; + +PyMODINIT_FUNC PyInit__binding(void) { + return PyModule_Create(&module); +} diff --git a/src/script/parsing/statements/expression/define_function.rs b/tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/py.typed similarity index 100% rename from src/script/parsing/statements/expression/define_function.rs rename to tree-sitter-command-cad-model/bindings/python/tree_sitter_command_cad_model/py.typed diff --git a/tree-sitter-command-cad-model/bindings/rust/build.rs b/tree-sitter-command-cad-model/bindings/rust/build.rs new file mode 100644 index 0000000..fe987ec --- /dev/null +++ b/tree-sitter-command-cad-model/bindings/rust/build.rs @@ -0,0 +1,22 @@ +fn main() { + let src_dir = std::path::Path::new("src"); + + let mut c_config = cc::Build::new(); + c_config.std("c11").include(src_dir); + + #[cfg(target_env = "msvc")] + c_config.flag("-utf-8"); + + let parser_path = src_dir.join("parser.c"); + c_config.file(&parser_path); + println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); + + // NOTE: if your language uses an external scanner, uncomment this block: + /* + let scanner_path = src_dir.join("scanner.c"); + c_config.file(&scanner_path); + println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); + */ + + c_config.compile("tree-sitter-command_cad_model"); +} diff --git a/tree-sitter-command-cad-model/bindings/rust/lib.rs b/tree-sitter-command-cad-model/bindings/rust/lib.rs new file mode 100644 index 0000000..79eb826 --- /dev/null +++ b/tree-sitter-command-cad-model/bindings/rust/lib.rs @@ -0,0 +1,54 @@ +//! This crate provides CommandCadModel language support for the [tree-sitter][] parsing library. +//! +//! Typically, you will use the [language][language func] function to add this language to a +//! tree-sitter [Parser][], and then use the parser to parse some code: +//! +//! ``` +//! let code = r#" +//! "#; +//! let mut parser = tree_sitter::Parser::new(); +//! parser.set_language(&tree_sitter_command_cad_model::language()).expect("Error loading CommandCadModel grammar"); +//! let tree = parser.parse(code, None).unwrap(); +//! assert!(!tree.root_node().has_error()); +//! ``` +//! +//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html +//! [language func]: fn.language.html +//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html +//! [tree-sitter]: https://tree-sitter.github.io/ + +use tree_sitter::Language; + +extern "C" { + fn tree_sitter_command_cad_model() -> Language; +} + +/// Get the tree-sitter [Language][] for this grammar. +/// +/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html +pub fn language() -> Language { + unsafe { tree_sitter_command_cad_model() } +} + +/// The content of the [`node-types.json`][] file for this grammar. +/// +/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types +pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); + +// Uncomment these to include any queries that this grammar contains + +// pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm"); +// pub const INJECTIONS_QUERY: &str = include_str!("../../queries/injections.scm"); +// pub const LOCALS_QUERY: &str = include_str!("../../queries/locals.scm"); +// pub const TAGS_QUERY: &str = include_str!("../../queries/tags.scm"); + +#[cfg(test)] +mod tests { + #[test] + fn test_can_load_grammar() { + let mut parser = tree_sitter::Parser::new(); + parser + .set_language(&super::language()) + .expect("Error loading CommandCadModel grammar"); + } +} diff --git a/tree-sitter-command-cad-model/bindings/swift/TreeSitterCommandCadModel/command_cad_model.h b/tree-sitter-command-cad-model/bindings/swift/TreeSitterCommandCadModel/command_cad_model.h new file mode 100644 index 0000000..a9a91cd --- /dev/null +++ b/tree-sitter-command-cad-model/bindings/swift/TreeSitterCommandCadModel/command_cad_model.h @@ -0,0 +1,16 @@ +#ifndef TREE_SITTER_COMMAND_CAD_MODEL_H_ +#define TREE_SITTER_COMMAND_CAD_MODEL_H_ + +typedef struct TSLanguage TSLanguage; + +#ifdef __cplusplus +extern "C" { +#endif + +const TSLanguage *tree_sitter_command_cad_model(void); + +#ifdef __cplusplus +} +#endif + +#endif // TREE_SITTER_COMMAND_CAD_MODEL_H_ diff --git a/tree-sitter-command-cad-model/go.mod b/tree-sitter-command-cad-model/go.mod new file mode 100644 index 0000000..2943667 --- /dev/null +++ b/tree-sitter-command-cad-model/go.mod @@ -0,0 +1,5 @@ +module github.com/iamthecarl/commandcad + +go 1.22 + +require github.com/tree-sitter/go-tree-sitter v0.24.0 diff --git a/tree-sitter-command-cad-model/grammar.js b/tree-sitter-command-cad-model/grammar.js new file mode 100644 index 0000000..a8f9bf8 --- /dev/null +++ b/tree-sitter-command-cad-model/grammar.js @@ -0,0 +1,246 @@ +/** + * @file Scripts for creating models in Command CAD + * @author James Carl + * @license AGPL + */ + +/// +// @ts-check + +const PREC = { + struct_member: 18, + unit: 17, + member_access: 16, + method_call: 15, + function_call: 14, + field: 13, + unary: 12, + exponential: 11, + multiplicative: 10, + additive: 9, + shift: 8, + bitand: 7, + bitxor: 6, + bitor: 5, + comparative: 4, + and: 3, + or: 2, + xor: 1, + assign: 0, + closure: -1, + struct_def: -2, +}; + +const unary_operator_table = ['-', '+', '!']; +const constraint_set_unary_operator_table = ['-', '+']; + +function make_unary_expression(table, expression) { + return choice(...table.map((operator) => seq( + seq(field('op', operator), expression), + ))); +} + +const binary_operator_table = [ + [PREC.exponential, '**'], + + [PREC.multiplicative, '*'], + [PREC.multiplicative, '/'], + + [PREC.additive, '+'], + [PREC.additive, '-'], + + [PREC.shift, '<<'], + [PREC.shift, '>>'], + + [PREC.bitand, '&'], + [PREC.bitor, '|'], + [PREC.bitxor, '^'], + + [PREC.comparative, '>'], + [PREC.comparative, '>='], + [PREC.comparative, '=='], + [PREC.comparative, '<='], + [PREC.comparative, '<'], + [PREC.comparative, '!='], + + [PREC.and, '&&'], + [PREC.or, '||'], + [PREC.xor, '^^'], +]; + +const constraint_set_binary_operator_table = [ + [PREC.multiplicative, '*'], + [PREC.multiplicative, '/'], + + [PREC.additive, '+'], + [PREC.additive, '-'], +]; + +function make_binary_expression(table, expression) { + return choice(...table.map(([precedence, operator]) => prec.left(precedence, seq( + field('a', expression), + field('op', operator), + field('b', expression), + )))); +} + +module.exports = grammar({ + name: "command_cad_model", + extras: $ => [$.comment, $._whitespace], + word: $ => $.identifier, + rules: { + source_file: $ => $.expression, + + comment: $ => token(seq('#', /.*/)), + _whitespace: _ => /\s/, + identifier: _ => /[a-zA-Z_][a-zA-Z0-9_]*/, + string: _ => /\"(\\\"|[^\"])*\"/, + self: _ => 'self', + + base_ten: _ => /[0-9]+/, + octal: _ => seq(token.immediate(/0o/), /[0-9]+/), + hex: _ => seq(token.immediate(/0x/), /[0-9a-fA-F]+/), + binary: _ => seq(token.immediate(/0b/), /[01]+/), + integer: $ => choice($.base_ten, $.octal, $.hex, $.binary), + signed_integer: $ => seq(field('value', $.integer), token.immediate(/[iI]/)), + unsigned_integer: $ => seq(field('value', $.integer), token.immediate(/[uU]/)), + + number: _ => /[0-9]+/, + unit_quote: _ => /'(\\'|[^'])*'/, + + _float: $ => prec.right(choice( + seq(field('whole', $.number), '.', field('fractional', $.number)), + seq(field('whole', $.number)), + )), + + _unit: $ => choice($.identifier, $.unit_quote), + + scalar: $ => prec.left(PREC.unit, seq($._float, field('unit', optional($._unit)))), + vector2: $ => seq('<(', field('x', $.expression), ',', field('y', $.expression), ')>'), + vector3: $ => seq('<(', field('x', $.expression), ',', field('y', $.expression), ',', field('z', $.expression), ')>'), + vector4: $ => seq('<(', field('x', $.expression), ',', field('y', $.expression), ',', field('z', $.expression), ',', field('w', $.expression), ')>'), + + true: $ => 'true', + false: $ => 'false', + boolean: $ => choice($.true, $.false), + + function_call: $ => seq( + prec.left(PREC.function_call, seq( + field('to_call', $.expression), field("argument", $.dictionary_construction), + )) + ), + + method_call: $ => seq( + prec.left(PREC.method_call, seq( + field('self_dictionary', $.expression), '::', field('to_call', $.identifier), field("argument", $.dictionary_construction) + )) + ), + + expression: $ => choice( + $.parenthesis, + $.signed_integer, + $.unsigned_integer, + $.scalar, + $.vector2, + $.vector3, + $.vector4, + $.boolean, + $.string, + $.self, + $.identifier, + $.member_access, + $.list, + $.if, + $.struct_definition, + $.dictionary_construction, + $.closure_definition, + $.unary_expression, + $.binary_expression, + $.function_call, + $.method_call, + $.constraint_set, + $.let_in + ), + unary_expression: $=> make_unary_expression(unary_operator_table, $.expression), + binary_expression: $ => make_binary_expression(binary_operator_table, $.expression), + + if: $ => seq('if', field('condition', $.expression), 'then ', field('on_true', $.expression), 'else', field('on_false', $.expression)), + + let_in: $ => seq('let', field('assignment', repeat($.let_in_assignment)), 'in', field('expression', $.expression)), + let_in_assignment: $ => seq(field('ident', $.identifier), '=', field('value', $.expression), ';'), + + member_access: $ => prec.left(PREC.member_access, seq( + field('base', $.expression), seq('.', field('member', $.identifier)) + )), + + declaration_type: $ => seq(':', $.expression), + + parenthesis: $ => seq('(', $.expression, ')'), + list: $ => seq( + '[', + repeat(seq($.expression, ',')), + optional($.expression), + ']' + ), + + varadic_dots: $ => '...', + + struct_member: $ => prec.left(PREC.struct_member, seq(field('name', $.identifier), $.declaration_type, optional(seq('=', field('default', $.expression))))), + _struct_final_element: $ => choice( + seq($.struct_member), + seq($.varadic_dots, optional(',')) + ), + struct_definition: $ => prec.left(PREC.struct_def, seq('(', + seq( + optional(field('members', repeat1(seq($.struct_member, ',')))), + field('final_element', optional($._struct_final_element)), + ), + ')')), + + dictionary_member_assignment: $ => seq(field('name', $.identifier), '=', field('assignment', $.expression)), + dictionary_construction: $ => seq('(', + field('assignments', + optional(seq( + $.dictionary_member_assignment, + repeat(seq(',', $.dictionary_member_assignment)), + optional(',') + )), + ), + ')' + ), + + closure_definition: $ => prec.left(PREC.closure, seq( + field('argument', $.struct_definition), + '->', + field('result', $.expression), + ':', + field('expression', $.expression), + )), + + _constraint_set_relation: $ => choice('>', '>=', '==', '<=', '<', '!='), + constraint_set_fields: $ => seq(repeat(seq($.identifier, ',')), $.identifier), + constraint_set: $ => seq( + '<<<', + field('variables', $.constraint_set_fields), ':', + field('lhs', $.constraint_set_expression), + field('relation', $._constraint_set_relation), + field('rhs', $.constraint_set_expression), + '>>>'), + constraint_set_expression: $ => choice( + $.constraint_set_parenthesis, + $.scalar, + $.identifier, + $.constraint_set_unary_expression, + $.constraint_set_binary_expression, + $.constraint_set_method_call, + ), + constraint_set_parenthesis: $ => seq('(', $.constraint_set_expression, ')'), + constraint_set_unary_expression: $ => make_unary_expression(constraint_set_unary_operator_table, $.constraint_set_expression), + constraint_set_binary_expression: $ => make_binary_expression(constraint_set_binary_operator_table, $.constraint_set_expression), + constraint_set_method_call: $ => seq( + prec.left(PREC.method_call, seq( + field('self_dictionary', $.constraint_set_expression), '::', field('to_call', $.identifier), '(', field("argument", $.constraint_set_expression), ')' + )) + ), + } +}); diff --git a/tree-sitter-command-cad-model/package.json b/tree-sitter-command-cad-model/package.json new file mode 100644 index 0000000..f4932da --- /dev/null +++ b/tree-sitter-command-cad-model/package.json @@ -0,0 +1,53 @@ +{ + "name": "tree-sitter-command-cad-model", + "version": "0.0.1", + "description": "CommandCadModel grammar for tree-sitter", + "repository": "github:tree-sitter/tree-sitter-command-cad-model", + "license": "MIT", + "main": "bindings/node", + "types": "bindings/node", + "keywords": [ + "incremental", + "parsing", + "tree-sitter", + "command_cad_model" + ], + "files": [ + "grammar.js", + "binding.gyp", + "prebuilds/**", + "bindings/node/*", + "queries/*", + "src/**" + ], + "dependencies": { + "node-addon-api": "^7.1.0", + "node-gyp-build": "^4.8.0" + }, + "devDependencies": { + "prebuildify": "^6.0.0", + "tree-sitter-cli": "^0.22.6" + }, + "peerDependencies": { + "tree-sitter": "^0.21.0" + }, + "peerDependenciesMeta": { + "tree-sitter": { + "optional": true + } + }, + "scripts": { + "install": "node-gyp-build", + "prebuildify": "prebuildify --napi --strip", + "build": "tree-sitter generate --no-bindings", + "build-wasm": "tree-sitter build --wasm", + "test": "tree-sitter test", + "parse": "tree-sitter parse" + }, + "tree-sitter": [ + { + "scope": "source.command_cad_model", + "injection-regex": "^command_cad_model$" + } + ] +} diff --git a/tree-sitter-command-cad-model/pyproject.toml b/tree-sitter-command-cad-model/pyproject.toml new file mode 100644 index 0000000..705de88 --- /dev/null +++ b/tree-sitter-command-cad-model/pyproject.toml @@ -0,0 +1,29 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "tree-sitter-command-cad-model" +description = "Scripts for creating models in Command CAD" +version = "0.1.0" +keywords = ["incremental", "parsing", "tree-sitter", "command-cad-model"] +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Software Development :: Compilers", + "Topic :: Text Processing :: Linguistic", + "Typing :: Typed", +] +authors = [{ name = "James Carl" }] +requires-python = ">=3.9" +license.text = "AGPL" +readme = "README.md" + +[project.urls] +Homepage = "https://github.com/iamthecarl/commandcad" + +[project.optional-dependencies] +core = ["tree-sitter~=0.22"] + +[tool.cibuildwheel] +build = "cp39-*" +build-frontend = "build" diff --git a/tree-sitter-command-cad-model/setup.py b/tree-sitter-command-cad-model/setup.py new file mode 100644 index 0000000..cd3a3da --- /dev/null +++ b/tree-sitter-command-cad-model/setup.py @@ -0,0 +1,62 @@ +from os.path import isdir, join +from platform import system + +from setuptools import Extension, find_packages, setup +from setuptools.command.build import build +from wheel.bdist_wheel import bdist_wheel + + +class Build(build): + def run(self): + if isdir("queries"): + dest = join(self.build_lib, "tree_sitter_command_cad_model", "queries") + self.copy_tree("queries", dest) + super().run() + + +class BdistWheel(bdist_wheel): + def get_tag(self): + python, abi, platform = super().get_tag() + if python.startswith("cp"): + python, abi = "cp39", "abi3" + return python, abi, platform + + +setup( + packages=find_packages("bindings/python"), + package_dir={"": "bindings/python"}, + package_data={ + "tree_sitter_command_cad_model": ["*.pyi", "py.typed"], + "tree_sitter_command_cad_model.queries": ["*.scm"], + }, + ext_package="tree_sitter_command_cad_model", + ext_modules=[ + Extension( + name="_binding", + sources=[ + "bindings/python/tree_sitter_command_cad_model/binding.c", + "src/parser.c", + # NOTE: if your language uses an external scanner, add it here. + ], + extra_compile_args=[ + "-std=c11", + "-fvisibility=hidden", + ] if system() != "Windows" else [ + "/std:c11", + "/utf-8", + ], + define_macros=[ + ("Py_LIMITED_API", "0x03090000"), + ("PY_SSIZE_T_CLEAN", None), + ("TREE_SITTER_HIDE_SYMBOLS", None), + ], + include_dirs=["src"], + py_limited_api=True, + ) + ], + cmdclass={ + "build": Build, + "bdist_wheel": BdistWheel + }, + zip_safe=False +) diff --git a/tree-sitter-command-cad-model/src/grammar.json b/tree-sitter-command-cad-model/src/grammar.json new file mode 100644 index 0000000..f9cd538 --- /dev/null +++ b/tree-sitter-command-cad-model/src/grammar.json @@ -0,0 +1,2168 @@ +{ + "$schema": "https://tree-sitter.github.io/tree-sitter/assets/schemas/grammar.schema.json", + "name": "command_cad_model", + "word": "identifier", + "rules": { + "source_file": { + "type": "SYMBOL", + "name": "expression" + }, + "comment": { + "type": "TOKEN", + "content": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "#" + }, + { + "type": "PATTERN", + "value": ".*" + } + ] + } + }, + "_whitespace": { + "type": "PATTERN", + "value": "\\s" + }, + "identifier": { + "type": "PATTERN", + "value": "[a-zA-Z_][a-zA-Z0-9_]*" + }, + "string": { + "type": "PATTERN", + "value": "\\\"(\\\\\\\"|[^\\\"])*\\\"" + }, + "self": { + "type": "STRING", + "value": "self" + }, + "base_ten": { + "type": "PATTERN", + "value": "[0-9]+" + }, + "octal": { + "type": "SEQ", + "members": [ + { + "type": "IMMEDIATE_TOKEN", + "content": { + "type": "PATTERN", + "value": "0o" + } + }, + { + "type": "PATTERN", + "value": "[0-9]+" + } + ] + }, + "hex": { + "type": "SEQ", + "members": [ + { + "type": "IMMEDIATE_TOKEN", + "content": { + "type": "PATTERN", + "value": "0x" + } + }, + { + "type": "PATTERN", + "value": "[0-9a-fA-F]+" + } + ] + }, + "binary": { + "type": "SEQ", + "members": [ + { + "type": "IMMEDIATE_TOKEN", + "content": { + "type": "PATTERN", + "value": "0b" + } + }, + { + "type": "PATTERN", + "value": "[01]+" + } + ] + }, + "integer": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "base_ten" + }, + { + "type": "SYMBOL", + "name": "octal" + }, + { + "type": "SYMBOL", + "name": "hex" + }, + { + "type": "SYMBOL", + "name": "binary" + } + ] + }, + "signed_integer": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "value", + "content": { + "type": "SYMBOL", + "name": "integer" + } + }, + { + "type": "IMMEDIATE_TOKEN", + "content": { + "type": "PATTERN", + "value": "[iI]" + } + } + ] + }, + "unsigned_integer": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "value", + "content": { + "type": "SYMBOL", + "name": "integer" + } + }, + { + "type": "IMMEDIATE_TOKEN", + "content": { + "type": "PATTERN", + "value": "[uU]" + } + } + ] + }, + "number": { + "type": "PATTERN", + "value": "[0-9]+" + }, + "unit_quote": { + "type": "PATTERN", + "value": "'(\\\\'|[^'])*'" + }, + "_float": { + "type": "PREC_RIGHT", + "value": 0, + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "whole", + "content": { + "type": "SYMBOL", + "name": "number" + } + }, + { + "type": "STRING", + "value": "." + }, + { + "type": "FIELD", + "name": "fractional", + "content": { + "type": "SYMBOL", + "name": "number" + } + } + ] + }, + { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "whole", + "content": { + "type": "SYMBOL", + "name": "number" + } + } + ] + } + ] + } + }, + "_unit": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "identifier" + }, + { + "type": "SYMBOL", + "name": "unit_quote" + } + ] + }, + "scalar": { + "type": "PREC_LEFT", + "value": 17, + "content": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "_float" + }, + { + "type": "FIELD", + "name": "unit", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "_unit" + }, + { + "type": "BLANK" + } + ] + } + } + ] + } + }, + "vector2": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "<(" + }, + { + "type": "FIELD", + "name": "x", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": "," + }, + { + "type": "FIELD", + "name": "y", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": ")>" + } + ] + }, + "vector3": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "<(" + }, + { + "type": "FIELD", + "name": "x", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": "," + }, + { + "type": "FIELD", + "name": "y", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": "," + }, + { + "type": "FIELD", + "name": "z", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": ")>" + } + ] + }, + "vector4": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "<(" + }, + { + "type": "FIELD", + "name": "x", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": "," + }, + { + "type": "FIELD", + "name": "y", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": "," + }, + { + "type": "FIELD", + "name": "z", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": "," + }, + { + "type": "FIELD", + "name": "w", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": ")>" + } + ] + }, + "true": { + "type": "STRING", + "value": "true" + }, + "false": { + "type": "STRING", + "value": "false" + }, + "boolean": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "true" + }, + { + "type": "SYMBOL", + "name": "false" + } + ] + }, + "function_call": { + "type": "SEQ", + "members": [ + { + "type": "PREC_LEFT", + "value": 14, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "to_call", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "argument", + "content": { + "type": "SYMBOL", + "name": "dictionary_construction" + } + } + ] + } + } + ] + }, + "method_call": { + "type": "SEQ", + "members": [ + { + "type": "PREC_LEFT", + "value": 15, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "self_dictionary", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": "::" + }, + { + "type": "FIELD", + "name": "to_call", + "content": { + "type": "SYMBOL", + "name": "identifier" + } + }, + { + "type": "FIELD", + "name": "argument", + "content": { + "type": "SYMBOL", + "name": "dictionary_construction" + } + } + ] + } + } + ] + }, + "expression": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "parenthesis" + }, + { + "type": "SYMBOL", + "name": "signed_integer" + }, + { + "type": "SYMBOL", + "name": "unsigned_integer" + }, + { + "type": "SYMBOL", + "name": "scalar" + }, + { + "type": "SYMBOL", + "name": "vector2" + }, + { + "type": "SYMBOL", + "name": "vector3" + }, + { + "type": "SYMBOL", + "name": "vector4" + }, + { + "type": "SYMBOL", + "name": "boolean" + }, + { + "type": "SYMBOL", + "name": "string" + }, + { + "type": "SYMBOL", + "name": "self" + }, + { + "type": "SYMBOL", + "name": "identifier" + }, + { + "type": "SYMBOL", + "name": "member_access" + }, + { + "type": "SYMBOL", + "name": "list" + }, + { + "type": "SYMBOL", + "name": "if" + }, + { + "type": "SYMBOL", + "name": "struct_definition" + }, + { + "type": "SYMBOL", + "name": "dictionary_construction" + }, + { + "type": "SYMBOL", + "name": "closure_definition" + }, + { + "type": "SYMBOL", + "name": "unary_expression" + }, + { + "type": "SYMBOL", + "name": "binary_expression" + }, + { + "type": "SYMBOL", + "name": "function_call" + }, + { + "type": "SYMBOL", + "name": "method_call" + }, + { + "type": "SYMBOL", + "name": "constraint_set" + }, + { + "type": "SYMBOL", + "name": "let_in" + } + ] + }, + "unary_expression": { + "type": "CHOICE", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "-" + } + }, + { + "type": "SYMBOL", + "name": "expression" + } + ] + } + ] + }, + { + "type": "SEQ", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "+" + } + }, + { + "type": "SYMBOL", + "name": "expression" + } + ] + } + ] + }, + { + "type": "SEQ", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "!" + } + }, + { + "type": "SYMBOL", + "name": "expression" + } + ] + } + ] + } + ] + }, + "binary_expression": { + "type": "CHOICE", + "members": [ + { + "type": "PREC_LEFT", + "value": 11, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "**" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 10, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "*" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 10, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "/" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 9, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "+" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 9, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "-" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 8, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "<<" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 8, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": ">>" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 7, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "&" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 5, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "|" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 6, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "^" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 4, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": ">" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 4, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": ">=" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 4, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "==" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 4, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "<=" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 4, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "<" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 4, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "!=" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 3, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "&&" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 2, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "||" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 1, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "^^" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + } + ] + }, + "if": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "if" + }, + { + "type": "FIELD", + "name": "condition", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": "then " + }, + { + "type": "FIELD", + "name": "on_true", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": "else" + }, + { + "type": "FIELD", + "name": "on_false", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + }, + "let_in": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "let" + }, + { + "type": "FIELD", + "name": "assignment", + "content": { + "type": "REPEAT", + "content": { + "type": "SYMBOL", + "name": "let_in_assignment" + } + } + }, + { + "type": "STRING", + "value": "in" + }, + { + "type": "FIELD", + "name": "expression", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + }, + "let_in_assignment": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "ident", + "content": { + "type": "SYMBOL", + "name": "identifier" + } + }, + { + "type": "STRING", + "value": "=" + }, + { + "type": "FIELD", + "name": "value", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": ";" + } + ] + }, + "member_access": { + "type": "PREC_LEFT", + "value": 16, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "base", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "." + }, + { + "type": "FIELD", + "name": "member", + "content": { + "type": "SYMBOL", + "name": "identifier" + } + } + ] + } + ] + } + }, + "declaration_type": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": ":" + }, + { + "type": "SYMBOL", + "name": "expression" + } + ] + }, + "parenthesis": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "(" + }, + { + "type": "SYMBOL", + "name": "expression" + }, + { + "type": "STRING", + "value": ")" + } + ] + }, + "list": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "[" + }, + { + "type": "REPEAT", + "content": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "expression" + }, + { + "type": "STRING", + "value": "," + } + ] + } + }, + { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "expression" + }, + { + "type": "BLANK" + } + ] + }, + { + "type": "STRING", + "value": "]" + } + ] + }, + "varadic_dots": { + "type": "STRING", + "value": "..." + }, + "struct_member": { + "type": "PREC_LEFT", + "value": 18, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "name", + "content": { + "type": "SYMBOL", + "name": "identifier" + } + }, + { + "type": "SYMBOL", + "name": "declaration_type" + }, + { + "type": "CHOICE", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "=" + }, + { + "type": "FIELD", + "name": "default", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + }, + { + "type": "BLANK" + } + ] + } + ] + } + }, + "_struct_final_element": { + "type": "CHOICE", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "struct_member" + } + ] + }, + { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "varadic_dots" + }, + { + "type": "CHOICE", + "members": [ + { + "type": "STRING", + "value": "," + }, + { + "type": "BLANK" + } + ] + } + ] + } + ] + }, + "struct_definition": { + "type": "PREC_LEFT", + "value": -2, + "content": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "(" + }, + { + "type": "SEQ", + "members": [ + { + "type": "CHOICE", + "members": [ + { + "type": "FIELD", + "name": "members", + "content": { + "type": "REPEAT1", + "content": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "struct_member" + }, + { + "type": "STRING", + "value": "," + } + ] + } + } + }, + { + "type": "BLANK" + } + ] + }, + { + "type": "FIELD", + "name": "final_element", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "_struct_final_element" + }, + { + "type": "BLANK" + } + ] + } + } + ] + }, + { + "type": "STRING", + "value": ")" + } + ] + } + }, + "dictionary_member_assignment": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "name", + "content": { + "type": "SYMBOL", + "name": "identifier" + } + }, + { + "type": "STRING", + "value": "=" + }, + { + "type": "FIELD", + "name": "assignment", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + }, + "dictionary_construction": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "(" + }, + { + "type": "FIELD", + "name": "assignments", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "dictionary_member_assignment" + }, + { + "type": "REPEAT", + "content": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "," + }, + { + "type": "SYMBOL", + "name": "dictionary_member_assignment" + } + ] + } + }, + { + "type": "CHOICE", + "members": [ + { + "type": "STRING", + "value": "," + }, + { + "type": "BLANK" + } + ] + } + ] + }, + { + "type": "BLANK" + } + ] + } + }, + { + "type": "STRING", + "value": ")" + } + ] + }, + "closure_definition": { + "type": "PREC_LEFT", + "value": -1, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "argument", + "content": { + "type": "SYMBOL", + "name": "struct_definition" + } + }, + { + "type": "STRING", + "value": "->" + }, + { + "type": "FIELD", + "name": "result", + "content": { + "type": "SYMBOL", + "name": "expression" + } + }, + { + "type": "STRING", + "value": ":" + }, + { + "type": "FIELD", + "name": "expression", + "content": { + "type": "SYMBOL", + "name": "expression" + } + } + ] + } + }, + "_constraint_set_relation": { + "type": "CHOICE", + "members": [ + { + "type": "STRING", + "value": ">" + }, + { + "type": "STRING", + "value": ">=" + }, + { + "type": "STRING", + "value": "==" + }, + { + "type": "STRING", + "value": "<=" + }, + { + "type": "STRING", + "value": "<" + }, + { + "type": "STRING", + "value": "!=" + } + ] + }, + "constraint_set_fields": { + "type": "SEQ", + "members": [ + { + "type": "REPEAT", + "content": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "identifier" + }, + { + "type": "STRING", + "value": "," + } + ] + } + }, + { + "type": "SYMBOL", + "name": "identifier" + } + ] + }, + "constraint_set": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "<<<" + }, + { + "type": "FIELD", + "name": "variables", + "content": { + "type": "SYMBOL", + "name": "constraint_set_fields" + } + }, + { + "type": "STRING", + "value": ":" + }, + { + "type": "FIELD", + "name": "lhs", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + }, + { + "type": "FIELD", + "name": "relation", + "content": { + "type": "SYMBOL", + "name": "_constraint_set_relation" + } + }, + { + "type": "FIELD", + "name": "rhs", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + }, + { + "type": "STRING", + "value": ">>>" + } + ] + }, + "constraint_set_expression": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "constraint_set_parenthesis" + }, + { + "type": "SYMBOL", + "name": "scalar" + }, + { + "type": "SYMBOL", + "name": "identifier" + }, + { + "type": "SYMBOL", + "name": "constraint_set_unary_expression" + }, + { + "type": "SYMBOL", + "name": "constraint_set_binary_expression" + }, + { + "type": "SYMBOL", + "name": "constraint_set_method_call" + } + ] + }, + "constraint_set_parenthesis": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "(" + }, + { + "type": "SYMBOL", + "name": "constraint_set_expression" + }, + { + "type": "STRING", + "value": ")" + } + ] + }, + "constraint_set_unary_expression": { + "type": "CHOICE", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "-" + } + }, + { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + ] + } + ] + }, + { + "type": "SEQ", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "+" + } + }, + { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + ] + } + ] + } + ] + }, + "constraint_set_binary_expression": { + "type": "CHOICE", + "members": [ + { + "type": "PREC_LEFT", + "value": 10, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "*" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 10, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "/" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 9, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "+" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + } + ] + } + }, + { + "type": "PREC_LEFT", + "value": 9, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "a", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + }, + { + "type": "FIELD", + "name": "op", + "content": { + "type": "STRING", + "value": "-" + } + }, + { + "type": "FIELD", + "name": "b", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + } + ] + } + } + ] + }, + "constraint_set_method_call": { + "type": "SEQ", + "members": [ + { + "type": "PREC_LEFT", + "value": 15, + "content": { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "self_dictionary", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + }, + { + "type": "STRING", + "value": "::" + }, + { + "type": "FIELD", + "name": "to_call", + "content": { + "type": "SYMBOL", + "name": "identifier" + } + }, + { + "type": "STRING", + "value": "(" + }, + { + "type": "FIELD", + "name": "argument", + "content": { + "type": "SYMBOL", + "name": "constraint_set_expression" + } + }, + { + "type": "STRING", + "value": ")" + } + ] + } + } + ] + } + }, + "extras": [ + { + "type": "SYMBOL", + "name": "comment" + }, + { + "type": "SYMBOL", + "name": "_whitespace" + } + ], + "conflicts": [], + "precedences": [], + "externals": [], + "inline": [], + "supertypes": [], + "reserved": {} +} \ No newline at end of file diff --git a/tree-sitter-command-cad-model/src/node-types.json b/tree-sitter-command-cad-model/src/node-types.json new file mode 100644 index 0000000..ce4d974 --- /dev/null +++ b/tree-sitter-command-cad-model/src/node-types.json @@ -0,0 +1,1329 @@ +[ + { + "type": "base_ten", + "named": true, + "fields": {} + }, + { + "type": "binary", + "named": true, + "fields": {} + }, + { + "type": "binary_expression", + "named": true, + "fields": { + "a": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "b": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "op": { + "multiple": false, + "required": true, + "types": [ + { + "type": "!=", + "named": false + }, + { + "type": "&", + "named": false + }, + { + "type": "&&", + "named": false + }, + { + "type": "*", + "named": false + }, + { + "type": "**", + "named": false + }, + { + "type": "+", + "named": false + }, + { + "type": "-", + "named": false + }, + { + "type": "/", + "named": false + }, + { + "type": "<", + "named": false + }, + { + "type": "<<", + "named": false + }, + { + "type": "<=", + "named": false + }, + { + "type": "==", + "named": false + }, + { + "type": ">", + "named": false + }, + { + "type": ">=", + "named": false + }, + { + "type": ">>", + "named": false + }, + { + "type": "^", + "named": false + }, + { + "type": "^^", + "named": false + }, + { + "type": "|", + "named": false + }, + { + "type": "||", + "named": false + } + ] + } + } + }, + { + "type": "boolean", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "false", + "named": true + }, + { + "type": "true", + "named": true + } + ] + } + }, + { + "type": "closure_definition", + "named": true, + "fields": { + "argument": { + "multiple": false, + "required": true, + "types": [ + { + "type": "struct_definition", + "named": true + } + ] + }, + "expression": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "result": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "constraint_set", + "named": true, + "fields": { + "lhs": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_expression", + "named": true + } + ] + }, + "relation": { + "multiple": false, + "required": true, + "types": [ + { + "type": "!=", + "named": false + }, + { + "type": "<", + "named": false + }, + { + "type": "<=", + "named": false + }, + { + "type": "==", + "named": false + }, + { + "type": ">", + "named": false + }, + { + "type": ">=", + "named": false + } + ] + }, + "rhs": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_expression", + "named": true + } + ] + }, + "variables": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_fields", + "named": true + } + ] + } + } + }, + { + "type": "constraint_set_binary_expression", + "named": true, + "fields": { + "a": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_expression", + "named": true + } + ] + }, + "b": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_expression", + "named": true + } + ] + }, + "op": { + "multiple": false, + "required": true, + "types": [ + { + "type": "*", + "named": false + }, + { + "type": "+", + "named": false + }, + { + "type": "-", + "named": false + }, + { + "type": "/", + "named": false + } + ] + } + } + }, + { + "type": "constraint_set_expression", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_binary_expression", + "named": true + }, + { + "type": "constraint_set_method_call", + "named": true + }, + { + "type": "constraint_set_parenthesis", + "named": true + }, + { + "type": "constraint_set_unary_expression", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "scalar", + "named": true + } + ] + } + }, + { + "type": "constraint_set_fields", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + }, + { + "type": "constraint_set_method_call", + "named": true, + "fields": { + "argument": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_expression", + "named": true + } + ] + }, + "self_dictionary": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_expression", + "named": true + } + ] + }, + "to_call": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + } + }, + { + "type": "constraint_set_parenthesis", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_expression", + "named": true + } + ] + } + }, + { + "type": "constraint_set_unary_expression", + "named": true, + "fields": { + "op": { + "multiple": false, + "required": true, + "types": [ + { + "type": "+", + "named": false + }, + { + "type": "-", + "named": false + } + ] + } + }, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "constraint_set_expression", + "named": true + } + ] + } + }, + { + "type": "declaration_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "dictionary_construction", + "named": true, + "fields": { + "assignments": { + "multiple": true, + "required": false, + "types": [ + { + "type": ",", + "named": false + }, + { + "type": "dictionary_member_assignment", + "named": true + } + ] + } + } + }, + { + "type": "dictionary_member_assignment", + "named": true, + "fields": { + "assignment": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + } + }, + { + "type": "expression", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "binary_expression", + "named": true + }, + { + "type": "boolean", + "named": true + }, + { + "type": "closure_definition", + "named": true + }, + { + "type": "constraint_set", + "named": true + }, + { + "type": "dictionary_construction", + "named": true + }, + { + "type": "function_call", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "if", + "named": true + }, + { + "type": "let_in", + "named": true + }, + { + "type": "list", + "named": true + }, + { + "type": "member_access", + "named": true + }, + { + "type": "method_call", + "named": true + }, + { + "type": "parenthesis", + "named": true + }, + { + "type": "scalar", + "named": true + }, + { + "type": "self", + "named": true + }, + { + "type": "signed_integer", + "named": true + }, + { + "type": "string", + "named": true + }, + { + "type": "struct_definition", + "named": true + }, + { + "type": "unary_expression", + "named": true + }, + { + "type": "unsigned_integer", + "named": true + }, + { + "type": "vector2", + "named": true + }, + { + "type": "vector3", + "named": true + }, + { + "type": "vector4", + "named": true + } + ] + } + }, + { + "type": "function_call", + "named": true, + "fields": { + "argument": { + "multiple": false, + "required": true, + "types": [ + { + "type": "dictionary_construction", + "named": true + } + ] + }, + "to_call": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "hex", + "named": true, + "fields": {} + }, + { + "type": "if", + "named": true, + "fields": { + "condition": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "on_false": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "on_true": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "integer", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "base_ten", + "named": true + }, + { + "type": "binary", + "named": true + }, + { + "type": "hex", + "named": true + }, + { + "type": "octal", + "named": true + } + ] + } + }, + { + "type": "let_in", + "named": true, + "fields": { + "assignment": { + "multiple": true, + "required": false, + "types": [ + { + "type": "let_in_assignment", + "named": true + } + ] + }, + "expression": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "let_in_assignment", + "named": true, + "fields": { + "ident": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "list", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "member_access", + "named": true, + "fields": { + "base": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "member": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + } + }, + { + "type": "method_call", + "named": true, + "fields": { + "argument": { + "multiple": false, + "required": true, + "types": [ + { + "type": "dictionary_construction", + "named": true + } + ] + }, + "self_dictionary": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "to_call": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + } + }, + { + "type": "number", + "named": true, + "fields": {} + }, + { + "type": "octal", + "named": true, + "fields": {} + }, + { + "type": "parenthesis", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "scalar", + "named": true, + "fields": { + "fractional": { + "multiple": false, + "required": false, + "types": [ + { + "type": "number", + "named": true + } + ] + }, + "unit": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "unit_quote", + "named": true + } + ] + }, + "whole": { + "multiple": false, + "required": true, + "types": [ + { + "type": "number", + "named": true + } + ] + } + } + }, + { + "type": "signed_integer", + "named": true, + "fields": { + "value": { + "multiple": false, + "required": true, + "types": [ + { + "type": "integer", + "named": true + } + ] + } + } + }, + { + "type": "source_file", + "named": true, + "root": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "struct_definition", + "named": true, + "fields": { + "final_element": { + "multiple": true, + "required": false, + "types": [ + { + "type": ",", + "named": false + }, + { + "type": "struct_member", + "named": true + }, + { + "type": "varadic_dots", + "named": true + } + ] + }, + "members": { + "multiple": true, + "required": false, + "types": [ + { + "type": ",", + "named": false + }, + { + "type": "struct_member", + "named": true + } + ] + } + } + }, + { + "type": "struct_member", + "named": true, + "fields": { + "default": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "declaration_type", + "named": true + } + ] + } + }, + { + "type": "unary_expression", + "named": true, + "fields": { + "op": { + "multiple": false, + "required": true, + "types": [ + { + "type": "!", + "named": false + }, + { + "type": "+", + "named": false + }, + { + "type": "-", + "named": false + } + ] + } + }, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "unsigned_integer", + "named": true, + "fields": { + "value": { + "multiple": false, + "required": true, + "types": [ + { + "type": "integer", + "named": true + } + ] + } + } + }, + { + "type": "vector2", + "named": true, + "fields": { + "x": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "y": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "vector3", + "named": true, + "fields": { + "x": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "y": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "z": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "vector4", + "named": true, + "fields": { + "w": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "x": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "y": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "z": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "!", + "named": false + }, + { + "type": "!=", + "named": false + }, + { + "type": "&", + "named": false + }, + { + "type": "&&", + "named": false + }, + { + "type": "(", + "named": false + }, + { + "type": ")", + "named": false + }, + { + "type": ")>", + "named": false + }, + { + "type": "*", + "named": false + }, + { + "type": "**", + "named": false + }, + { + "type": "+", + "named": false + }, + { + "type": ",", + "named": false + }, + { + "type": "-", + "named": false + }, + { + "type": "->", + "named": false + }, + { + "type": ".", + "named": false + }, + { + "type": "/", + "named": false + }, + { + "type": ":", + "named": false + }, + { + "type": "::", + "named": false + }, + { + "type": ";", + "named": false + }, + { + "type": "<", + "named": false + }, + { + "type": "<(", + "named": false + }, + { + "type": "<<", + "named": false + }, + { + "type": "<<<", + "named": false + }, + { + "type": "<=", + "named": false + }, + { + "type": "=", + "named": false + }, + { + "type": "==", + "named": false + }, + { + "type": ">", + "named": false + }, + { + "type": ">=", + "named": false + }, + { + "type": ">>", + "named": false + }, + { + "type": ">>>", + "named": false + }, + { + "type": "[", + "named": false + }, + { + "type": "]", + "named": false + }, + { + "type": "^", + "named": false + }, + { + "type": "^^", + "named": false + }, + { + "type": "comment", + "named": true, + "extra": true + }, + { + "type": "else", + "named": false + }, + { + "type": "false", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "if", + "named": false + }, + { + "type": "in", + "named": false + }, + { + "type": "let", + "named": false + }, + { + "type": "self", + "named": true + }, + { + "type": "string", + "named": true + }, + { + "type": "then ", + "named": false + }, + { + "type": "true", + "named": true + }, + { + "type": "unit_quote", + "named": true + }, + { + "type": "varadic_dots", + "named": true + }, + { + "type": "|", + "named": false + }, + { + "type": "||", + "named": false + } +] \ No newline at end of file diff --git a/tree-sitter-command-cad-model/src/parser.c b/tree-sitter-command-cad-model/src/parser.c new file mode 100644 index 0000000..919ee9f --- /dev/null +++ b/tree-sitter-command-cad-model/src/parser.c @@ -0,0 +1,15488 @@ +/* Automatically @generated by tree-sitter v0.25.10 */ + +#include "tree_sitter/parser.h" + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#define LANGUAGE_VERSION 15 +#define STATE_COUNT 322 +#define LARGE_STATE_COUNT 4 +#define SYMBOL_COUNT 105 +#define ALIAS_COUNT 0 +#define TOKEN_COUNT 58 +#define EXTERNAL_TOKEN_COUNT 0 +#define FIELD_COUNT 32 +#define MAX_ALIAS_SEQUENCE_LENGTH 9 +#define MAX_RESERVED_WORD_SET_SIZE 0 +#define PRODUCTION_ID_COUNT 30 +#define SUPERTYPE_COUNT 0 + +enum ts_symbol_identifiers { + sym_identifier = 1, + sym_comment = 2, + sym__whitespace = 3, + sym_string = 4, + sym_self = 5, + aux_sym_base_ten_token1 = 6, + aux_sym_octal_token1 = 7, + aux_sym_hex_token1 = 8, + aux_sym_hex_token2 = 9, + aux_sym_binary_token1 = 10, + aux_sym_binary_token2 = 11, + aux_sym_signed_integer_token1 = 12, + aux_sym_unsigned_integer_token1 = 13, + sym_unit_quote = 14, + anon_sym_DOT = 15, + anon_sym_LT_LPAREN = 16, + anon_sym_COMMA = 17, + anon_sym_RPAREN_GT = 18, + sym_true = 19, + sym_false = 20, + anon_sym_COLON_COLON = 21, + anon_sym_DASH = 22, + anon_sym_PLUS = 23, + anon_sym_BANG = 24, + anon_sym_STAR_STAR = 25, + anon_sym_STAR = 26, + anon_sym_SLASH = 27, + anon_sym_LT_LT = 28, + anon_sym_GT_GT = 29, + anon_sym_AMP = 30, + anon_sym_PIPE = 31, + anon_sym_CARET = 32, + anon_sym_GT = 33, + anon_sym_GT_EQ = 34, + anon_sym_EQ_EQ = 35, + anon_sym_LT_EQ = 36, + anon_sym_LT = 37, + anon_sym_BANG_EQ = 38, + anon_sym_AMP_AMP = 39, + anon_sym_PIPE_PIPE = 40, + anon_sym_CARET_CARET = 41, + anon_sym_if = 42, + anon_sym_then = 43, + anon_sym_else = 44, + anon_sym_let = 45, + anon_sym_in = 46, + anon_sym_EQ = 47, + anon_sym_SEMI = 48, + anon_sym_COLON = 49, + anon_sym_LPAREN = 50, + anon_sym_RPAREN = 51, + anon_sym_LBRACK = 52, + anon_sym_RBRACK = 53, + sym_varadic_dots = 54, + anon_sym_DASH_GT = 55, + anon_sym_LT_LT_LT = 56, + anon_sym_GT_GT_GT = 57, + sym_source_file = 58, + sym_base_ten = 59, + sym_octal = 60, + sym_hex = 61, + sym_binary = 62, + sym_integer = 63, + sym_signed_integer = 64, + sym_unsigned_integer = 65, + sym_number = 66, + sym__float = 67, + sym__unit = 68, + sym_scalar = 69, + sym_vector2 = 70, + sym_vector3 = 71, + sym_vector4 = 72, + sym_boolean = 73, + sym_function_call = 74, + sym_method_call = 75, + sym_expression = 76, + sym_unary_expression = 77, + sym_binary_expression = 78, + sym_if = 79, + sym_let_in = 80, + sym_let_in_assignment = 81, + sym_member_access = 82, + sym_declaration_type = 83, + sym_parenthesis = 84, + sym_list = 85, + sym_struct_member = 86, + sym__struct_final_element = 87, + sym_struct_definition = 88, + sym_dictionary_member_assignment = 89, + sym_dictionary_construction = 90, + sym_closure_definition = 91, + sym__constraint_set_relation = 92, + sym_constraint_set_fields = 93, + sym_constraint_set = 94, + sym_constraint_set_expression = 95, + sym_constraint_set_parenthesis = 96, + sym_constraint_set_unary_expression = 97, + sym_constraint_set_binary_expression = 98, + sym_constraint_set_method_call = 99, + aux_sym_let_in_repeat1 = 100, + aux_sym_list_repeat1 = 101, + aux_sym_struct_definition_repeat1 = 102, + aux_sym_dictionary_construction_repeat1 = 103, + aux_sym_constraint_set_fields_repeat1 = 104, +}; + +static const char * const ts_symbol_names[] = { + [ts_builtin_sym_end] = "end", + [sym_identifier] = "identifier", + [sym_comment] = "comment", + [sym__whitespace] = "_whitespace", + [sym_string] = "string", + [sym_self] = "self", + [aux_sym_base_ten_token1] = "base_ten_token1", + [aux_sym_octal_token1] = "octal_token1", + [aux_sym_hex_token1] = "hex_token1", + [aux_sym_hex_token2] = "hex_token2", + [aux_sym_binary_token1] = "binary_token1", + [aux_sym_binary_token2] = "binary_token2", + [aux_sym_signed_integer_token1] = "signed_integer_token1", + [aux_sym_unsigned_integer_token1] = "unsigned_integer_token1", + [sym_unit_quote] = "unit_quote", + [anon_sym_DOT] = ".", + [anon_sym_LT_LPAREN] = "<(", + [anon_sym_COMMA] = ",", + [anon_sym_RPAREN_GT] = ")>", + [sym_true] = "true", + [sym_false] = "false", + [anon_sym_COLON_COLON] = "::", + [anon_sym_DASH] = "-", + [anon_sym_PLUS] = "+", + [anon_sym_BANG] = "!", + [anon_sym_STAR_STAR] = "**", + [anon_sym_STAR] = "*", + [anon_sym_SLASH] = "/", + [anon_sym_LT_LT] = "<<", + [anon_sym_GT_GT] = ">>", + [anon_sym_AMP] = "&", + [anon_sym_PIPE] = "|", + [anon_sym_CARET] = "^", + [anon_sym_GT] = ">", + [anon_sym_GT_EQ] = ">=", + [anon_sym_EQ_EQ] = "==", + [anon_sym_LT_EQ] = "<=", + [anon_sym_LT] = "<", + [anon_sym_BANG_EQ] = "!=", + [anon_sym_AMP_AMP] = "&&", + [anon_sym_PIPE_PIPE] = "||", + [anon_sym_CARET_CARET] = "^^", + [anon_sym_if] = "if", + [anon_sym_then] = "then ", + [anon_sym_else] = "else", + [anon_sym_let] = "let", + [anon_sym_in] = "in", + [anon_sym_EQ] = "=", + [anon_sym_SEMI] = ";", + [anon_sym_COLON] = ":", + [anon_sym_LPAREN] = "(", + [anon_sym_RPAREN] = ")", + [anon_sym_LBRACK] = "[", + [anon_sym_RBRACK] = "]", + [sym_varadic_dots] = "varadic_dots", + [anon_sym_DASH_GT] = "->", + [anon_sym_LT_LT_LT] = "<<<", + [anon_sym_GT_GT_GT] = ">>>", + [sym_source_file] = "source_file", + [sym_base_ten] = "base_ten", + [sym_octal] = "octal", + [sym_hex] = "hex", + [sym_binary] = "binary", + [sym_integer] = "integer", + [sym_signed_integer] = "signed_integer", + [sym_unsigned_integer] = "unsigned_integer", + [sym_number] = "number", + [sym__float] = "_float", + [sym__unit] = "_unit", + [sym_scalar] = "scalar", + [sym_vector2] = "vector2", + [sym_vector3] = "vector3", + [sym_vector4] = "vector4", + [sym_boolean] = "boolean", + [sym_function_call] = "function_call", + [sym_method_call] = "method_call", + [sym_expression] = "expression", + [sym_unary_expression] = "unary_expression", + [sym_binary_expression] = "binary_expression", + [sym_if] = "if", + [sym_let_in] = "let_in", + [sym_let_in_assignment] = "let_in_assignment", + [sym_member_access] = "member_access", + [sym_declaration_type] = "declaration_type", + [sym_parenthesis] = "parenthesis", + [sym_list] = "list", + [sym_struct_member] = "struct_member", + [sym__struct_final_element] = "_struct_final_element", + [sym_struct_definition] = "struct_definition", + [sym_dictionary_member_assignment] = "dictionary_member_assignment", + [sym_dictionary_construction] = "dictionary_construction", + [sym_closure_definition] = "closure_definition", + [sym__constraint_set_relation] = "_constraint_set_relation", + [sym_constraint_set_fields] = "constraint_set_fields", + [sym_constraint_set] = "constraint_set", + [sym_constraint_set_expression] = "constraint_set_expression", + [sym_constraint_set_parenthesis] = "constraint_set_parenthesis", + [sym_constraint_set_unary_expression] = "constraint_set_unary_expression", + [sym_constraint_set_binary_expression] = "constraint_set_binary_expression", + [sym_constraint_set_method_call] = "constraint_set_method_call", + [aux_sym_let_in_repeat1] = "let_in_repeat1", + [aux_sym_list_repeat1] = "list_repeat1", + [aux_sym_struct_definition_repeat1] = "struct_definition_repeat1", + [aux_sym_dictionary_construction_repeat1] = "dictionary_construction_repeat1", + [aux_sym_constraint_set_fields_repeat1] = "constraint_set_fields_repeat1", +}; + +static const TSSymbol ts_symbol_map[] = { + [ts_builtin_sym_end] = ts_builtin_sym_end, + [sym_identifier] = sym_identifier, + [sym_comment] = sym_comment, + [sym__whitespace] = sym__whitespace, + [sym_string] = sym_string, + [sym_self] = sym_self, + [aux_sym_base_ten_token1] = aux_sym_base_ten_token1, + [aux_sym_octal_token1] = aux_sym_octal_token1, + [aux_sym_hex_token1] = aux_sym_hex_token1, + [aux_sym_hex_token2] = aux_sym_hex_token2, + [aux_sym_binary_token1] = aux_sym_binary_token1, + [aux_sym_binary_token2] = aux_sym_binary_token2, + [aux_sym_signed_integer_token1] = aux_sym_signed_integer_token1, + [aux_sym_unsigned_integer_token1] = aux_sym_unsigned_integer_token1, + [sym_unit_quote] = sym_unit_quote, + [anon_sym_DOT] = anon_sym_DOT, + [anon_sym_LT_LPAREN] = anon_sym_LT_LPAREN, + [anon_sym_COMMA] = anon_sym_COMMA, + [anon_sym_RPAREN_GT] = anon_sym_RPAREN_GT, + [sym_true] = sym_true, + [sym_false] = sym_false, + [anon_sym_COLON_COLON] = anon_sym_COLON_COLON, + [anon_sym_DASH] = anon_sym_DASH, + [anon_sym_PLUS] = anon_sym_PLUS, + [anon_sym_BANG] = anon_sym_BANG, + [anon_sym_STAR_STAR] = anon_sym_STAR_STAR, + [anon_sym_STAR] = anon_sym_STAR, + [anon_sym_SLASH] = anon_sym_SLASH, + [anon_sym_LT_LT] = anon_sym_LT_LT, + [anon_sym_GT_GT] = anon_sym_GT_GT, + [anon_sym_AMP] = anon_sym_AMP, + [anon_sym_PIPE] = anon_sym_PIPE, + [anon_sym_CARET] = anon_sym_CARET, + [anon_sym_GT] = anon_sym_GT, + [anon_sym_GT_EQ] = anon_sym_GT_EQ, + [anon_sym_EQ_EQ] = anon_sym_EQ_EQ, + [anon_sym_LT_EQ] = anon_sym_LT_EQ, + [anon_sym_LT] = anon_sym_LT, + [anon_sym_BANG_EQ] = anon_sym_BANG_EQ, + [anon_sym_AMP_AMP] = anon_sym_AMP_AMP, + [anon_sym_PIPE_PIPE] = anon_sym_PIPE_PIPE, + [anon_sym_CARET_CARET] = anon_sym_CARET_CARET, + [anon_sym_if] = anon_sym_if, + [anon_sym_then] = anon_sym_then, + [anon_sym_else] = anon_sym_else, + [anon_sym_let] = anon_sym_let, + [anon_sym_in] = anon_sym_in, + [anon_sym_EQ] = anon_sym_EQ, + [anon_sym_SEMI] = anon_sym_SEMI, + [anon_sym_COLON] = anon_sym_COLON, + [anon_sym_LPAREN] = anon_sym_LPAREN, + [anon_sym_RPAREN] = anon_sym_RPAREN, + [anon_sym_LBRACK] = anon_sym_LBRACK, + [anon_sym_RBRACK] = anon_sym_RBRACK, + [sym_varadic_dots] = sym_varadic_dots, + [anon_sym_DASH_GT] = anon_sym_DASH_GT, + [anon_sym_LT_LT_LT] = anon_sym_LT_LT_LT, + [anon_sym_GT_GT_GT] = anon_sym_GT_GT_GT, + [sym_source_file] = sym_source_file, + [sym_base_ten] = sym_base_ten, + [sym_octal] = sym_octal, + [sym_hex] = sym_hex, + [sym_binary] = sym_binary, + [sym_integer] = sym_integer, + [sym_signed_integer] = sym_signed_integer, + [sym_unsigned_integer] = sym_unsigned_integer, + [sym_number] = sym_number, + [sym__float] = sym__float, + [sym__unit] = sym__unit, + [sym_scalar] = sym_scalar, + [sym_vector2] = sym_vector2, + [sym_vector3] = sym_vector3, + [sym_vector4] = sym_vector4, + [sym_boolean] = sym_boolean, + [sym_function_call] = sym_function_call, + [sym_method_call] = sym_method_call, + [sym_expression] = sym_expression, + [sym_unary_expression] = sym_unary_expression, + [sym_binary_expression] = sym_binary_expression, + [sym_if] = sym_if, + [sym_let_in] = sym_let_in, + [sym_let_in_assignment] = sym_let_in_assignment, + [sym_member_access] = sym_member_access, + [sym_declaration_type] = sym_declaration_type, + [sym_parenthesis] = sym_parenthesis, + [sym_list] = sym_list, + [sym_struct_member] = sym_struct_member, + [sym__struct_final_element] = sym__struct_final_element, + [sym_struct_definition] = sym_struct_definition, + [sym_dictionary_member_assignment] = sym_dictionary_member_assignment, + [sym_dictionary_construction] = sym_dictionary_construction, + [sym_closure_definition] = sym_closure_definition, + [sym__constraint_set_relation] = sym__constraint_set_relation, + [sym_constraint_set_fields] = sym_constraint_set_fields, + [sym_constraint_set] = sym_constraint_set, + [sym_constraint_set_expression] = sym_constraint_set_expression, + [sym_constraint_set_parenthesis] = sym_constraint_set_parenthesis, + [sym_constraint_set_unary_expression] = sym_constraint_set_unary_expression, + [sym_constraint_set_binary_expression] = sym_constraint_set_binary_expression, + [sym_constraint_set_method_call] = sym_constraint_set_method_call, + [aux_sym_let_in_repeat1] = aux_sym_let_in_repeat1, + [aux_sym_list_repeat1] = aux_sym_list_repeat1, + [aux_sym_struct_definition_repeat1] = aux_sym_struct_definition_repeat1, + [aux_sym_dictionary_construction_repeat1] = aux_sym_dictionary_construction_repeat1, + [aux_sym_constraint_set_fields_repeat1] = aux_sym_constraint_set_fields_repeat1, +}; + +static const TSSymbolMetadata ts_symbol_metadata[] = { + [ts_builtin_sym_end] = { + .visible = false, + .named = true, + }, + [sym_identifier] = { + .visible = true, + .named = true, + }, + [sym_comment] = { + .visible = true, + .named = true, + }, + [sym__whitespace] = { + .visible = false, + .named = true, + }, + [sym_string] = { + .visible = true, + .named = true, + }, + [sym_self] = { + .visible = true, + .named = true, + }, + [aux_sym_base_ten_token1] = { + .visible = false, + .named = false, + }, + [aux_sym_octal_token1] = { + .visible = false, + .named = false, + }, + [aux_sym_hex_token1] = { + .visible = false, + .named = false, + }, + [aux_sym_hex_token2] = { + .visible = false, + .named = false, + }, + [aux_sym_binary_token1] = { + .visible = false, + .named = false, + }, + [aux_sym_binary_token2] = { + .visible = false, + .named = false, + }, + [aux_sym_signed_integer_token1] = { + .visible = false, + .named = false, + }, + [aux_sym_unsigned_integer_token1] = { + .visible = false, + .named = false, + }, + [sym_unit_quote] = { + .visible = true, + .named = true, + }, + [anon_sym_DOT] = { + .visible = true, + .named = false, + }, + [anon_sym_LT_LPAREN] = { + .visible = true, + .named = false, + }, + [anon_sym_COMMA] = { + .visible = true, + .named = false, + }, + [anon_sym_RPAREN_GT] = { + .visible = true, + .named = false, + }, + [sym_true] = { + .visible = true, + .named = true, + }, + [sym_false] = { + .visible = true, + .named = true, + }, + [anon_sym_COLON_COLON] = { + .visible = true, + .named = false, + }, + [anon_sym_DASH] = { + .visible = true, + .named = false, + }, + [anon_sym_PLUS] = { + .visible = true, + .named = false, + }, + [anon_sym_BANG] = { + .visible = true, + .named = false, + }, + [anon_sym_STAR_STAR] = { + .visible = true, + .named = false, + }, + [anon_sym_STAR] = { + .visible = true, + .named = false, + }, + [anon_sym_SLASH] = { + .visible = true, + .named = false, + }, + [anon_sym_LT_LT] = { + .visible = true, + .named = false, + }, + [anon_sym_GT_GT] = { + .visible = true, + .named = false, + }, + [anon_sym_AMP] = { + .visible = true, + .named = false, + }, + [anon_sym_PIPE] = { + .visible = true, + .named = false, + }, + [anon_sym_CARET] = { + .visible = true, + .named = false, + }, + [anon_sym_GT] = { + .visible = true, + .named = false, + }, + [anon_sym_GT_EQ] = { + .visible = true, + .named = false, + }, + [anon_sym_EQ_EQ] = { + .visible = true, + .named = false, + }, + [anon_sym_LT_EQ] = { + .visible = true, + .named = false, + }, + [anon_sym_LT] = { + .visible = true, + .named = false, + }, + [anon_sym_BANG_EQ] = { + .visible = true, + .named = false, + }, + [anon_sym_AMP_AMP] = { + .visible = true, + .named = false, + }, + [anon_sym_PIPE_PIPE] = { + .visible = true, + .named = false, + }, + [anon_sym_CARET_CARET] = { + .visible = true, + .named = false, + }, + [anon_sym_if] = { + .visible = true, + .named = false, + }, + [anon_sym_then] = { + .visible = true, + .named = false, + }, + [anon_sym_else] = { + .visible = true, + .named = false, + }, + [anon_sym_let] = { + .visible = true, + .named = false, + }, + [anon_sym_in] = { + .visible = true, + .named = false, + }, + [anon_sym_EQ] = { + .visible = true, + .named = false, + }, + [anon_sym_SEMI] = { + .visible = true, + .named = false, + }, + [anon_sym_COLON] = { + .visible = true, + .named = false, + }, + [anon_sym_LPAREN] = { + .visible = true, + .named = false, + }, + [anon_sym_RPAREN] = { + .visible = true, + .named = false, + }, + [anon_sym_LBRACK] = { + .visible = true, + .named = false, + }, + [anon_sym_RBRACK] = { + .visible = true, + .named = false, + }, + [sym_varadic_dots] = { + .visible = true, + .named = true, + }, + [anon_sym_DASH_GT] = { + .visible = true, + .named = false, + }, + [anon_sym_LT_LT_LT] = { + .visible = true, + .named = false, + }, + [anon_sym_GT_GT_GT] = { + .visible = true, + .named = false, + }, + [sym_source_file] = { + .visible = true, + .named = true, + }, + [sym_base_ten] = { + .visible = true, + .named = true, + }, + [sym_octal] = { + .visible = true, + .named = true, + }, + [sym_hex] = { + .visible = true, + .named = true, + }, + [sym_binary] = { + .visible = true, + .named = true, + }, + [sym_integer] = { + .visible = true, + .named = true, + }, + [sym_signed_integer] = { + .visible = true, + .named = true, + }, + [sym_unsigned_integer] = { + .visible = true, + .named = true, + }, + [sym_number] = { + .visible = true, + .named = true, + }, + [sym__float] = { + .visible = false, + .named = true, + }, + [sym__unit] = { + .visible = false, + .named = true, + }, + [sym_scalar] = { + .visible = true, + .named = true, + }, + [sym_vector2] = { + .visible = true, + .named = true, + }, + [sym_vector3] = { + .visible = true, + .named = true, + }, + [sym_vector4] = { + .visible = true, + .named = true, + }, + [sym_boolean] = { + .visible = true, + .named = true, + }, + [sym_function_call] = { + .visible = true, + .named = true, + }, + [sym_method_call] = { + .visible = true, + .named = true, + }, + [sym_expression] = { + .visible = true, + .named = true, + }, + [sym_unary_expression] = { + .visible = true, + .named = true, + }, + [sym_binary_expression] = { + .visible = true, + .named = true, + }, + [sym_if] = { + .visible = true, + .named = true, + }, + [sym_let_in] = { + .visible = true, + .named = true, + }, + [sym_let_in_assignment] = { + .visible = true, + .named = true, + }, + [sym_member_access] = { + .visible = true, + .named = true, + }, + [sym_declaration_type] = { + .visible = true, + .named = true, + }, + [sym_parenthesis] = { + .visible = true, + .named = true, + }, + [sym_list] = { + .visible = true, + .named = true, + }, + [sym_struct_member] = { + .visible = true, + .named = true, + }, + [sym__struct_final_element] = { + .visible = false, + .named = true, + }, + [sym_struct_definition] = { + .visible = true, + .named = true, + }, + [sym_dictionary_member_assignment] = { + .visible = true, + .named = true, + }, + [sym_dictionary_construction] = { + .visible = true, + .named = true, + }, + [sym_closure_definition] = { + .visible = true, + .named = true, + }, + [sym__constraint_set_relation] = { + .visible = false, + .named = true, + }, + [sym_constraint_set_fields] = { + .visible = true, + .named = true, + }, + [sym_constraint_set] = { + .visible = true, + .named = true, + }, + [sym_constraint_set_expression] = { + .visible = true, + .named = true, + }, + [sym_constraint_set_parenthesis] = { + .visible = true, + .named = true, + }, + [sym_constraint_set_unary_expression] = { + .visible = true, + .named = true, + }, + [sym_constraint_set_binary_expression] = { + .visible = true, + .named = true, + }, + [sym_constraint_set_method_call] = { + .visible = true, + .named = true, + }, + [aux_sym_let_in_repeat1] = { + .visible = false, + .named = false, + }, + [aux_sym_list_repeat1] = { + .visible = false, + .named = false, + }, + [aux_sym_struct_definition_repeat1] = { + .visible = false, + .named = false, + }, + [aux_sym_dictionary_construction_repeat1] = { + .visible = false, + .named = false, + }, + [aux_sym_constraint_set_fields_repeat1] = { + .visible = false, + .named = false, + }, +}; + +enum ts_field_identifiers { + field_a = 1, + field_argument = 2, + field_assignment = 3, + field_assignments = 4, + field_b = 5, + field_base = 6, + field_condition = 7, + field_default = 8, + field_expression = 9, + field_final_element = 10, + field_fractional = 11, + field_ident = 12, + field_lhs = 13, + field_member = 14, + field_members = 15, + field_name = 16, + field_on_false = 17, + field_on_true = 18, + field_op = 19, + field_relation = 20, + field_result = 21, + field_rhs = 22, + field_self_dictionary = 23, + field_to_call = 24, + field_unit = 25, + field_value = 26, + field_variables = 27, + field_w = 28, + field_whole = 29, + field_x = 30, + field_y = 31, + field_z = 32, +}; + +static const char * const ts_field_names[] = { + [0] = NULL, + [field_a] = "a", + [field_argument] = "argument", + [field_assignment] = "assignment", + [field_assignments] = "assignments", + [field_b] = "b", + [field_base] = "base", + [field_condition] = "condition", + [field_default] = "default", + [field_expression] = "expression", + [field_final_element] = "final_element", + [field_fractional] = "fractional", + [field_ident] = "ident", + [field_lhs] = "lhs", + [field_member] = "member", + [field_members] = "members", + [field_name] = "name", + [field_on_false] = "on_false", + [field_on_true] = "on_true", + [field_op] = "op", + [field_relation] = "relation", + [field_result] = "result", + [field_rhs] = "rhs", + [field_self_dictionary] = "self_dictionary", + [field_to_call] = "to_call", + [field_unit] = "unit", + [field_value] = "value", + [field_variables] = "variables", + [field_w] = "w", + [field_whole] = "whole", + [field_x] = "x", + [field_y] = "y", + [field_z] = "z", +}; + +static const TSMapSlice ts_field_map_slices[PRODUCTION_ID_COUNT] = { + [1] = {.index = 0, .length = 1}, + [2] = {.index = 1, .length = 2}, + [3] = {.index = 3, .length = 1}, + [4] = {.index = 4, .length = 1}, + [5] = {.index = 5, .length = 3}, + [6] = {.index = 8, .length = 2}, + [7] = {.index = 10, .length = 1}, + [8] = {.index = 11, .length = 1}, + [9] = {.index = 12, .length = 1}, + [10] = {.index = 13, .length = 1}, + [11] = {.index = 14, .length = 1}, + [12] = {.index = 15, .length = 2}, + [13] = {.index = 17, .length = 2}, + [14] = {.index = 19, .length = 3}, + [15] = {.index = 22, .length = 2}, + [16] = {.index = 24, .length = 2}, + [17] = {.index = 26, .length = 2}, + [18] = {.index = 28, .length = 2}, + [19] = {.index = 30, .length = 3}, + [20] = {.index = 33, .length = 2}, + [21] = {.index = 35, .length = 2}, + [22] = {.index = 37, .length = 2}, + [23] = {.index = 39, .length = 3}, + [24] = {.index = 42, .length = 3}, + [25] = {.index = 45, .length = 3}, + [26] = {.index = 48, .length = 3}, + [27] = {.index = 51, .length = 4}, + [28] = {.index = 55, .length = 4}, + [29] = {.index = 59, .length = 3}, +}; + +static const TSFieldMapEntry ts_field_map_entries[] = { + [0] = + {field_whole, 0}, + [1] = + {field_fractional, 0, .inherited = true}, + {field_whole, 0, .inherited = true}, + [3] = + {field_op, 0}, + [4] = + {field_value, 0}, + [5] = + {field_fractional, 0, .inherited = true}, + {field_unit, 1}, + {field_whole, 0, .inherited = true}, + [8] = + {field_argument, 1}, + {field_to_call, 0}, + [10] = + {field_expression, 2}, + [11] = + {field_name, 0}, + [12] = + {field_final_element, 1}, + [13] = + {field_assignments, 1}, + [14] = + {field_members, 1}, + [15] = + {field_fractional, 2}, + {field_whole, 0}, + [17] = + {field_base, 0}, + {field_member, 2}, + [19] = + {field_a, 0}, + {field_b, 2}, + {field_op, 1}, + [22] = + {field_assignment, 1}, + {field_expression, 3}, + [24] = + {field_assignment, 2}, + {field_name, 0}, + [26] = + {field_assignments, 1}, + {field_assignments, 2}, + [28] = + {field_final_element, 2}, + {field_members, 1}, + [30] = + {field_argument, 3}, + {field_self_dictionary, 0}, + {field_to_call, 2}, + [33] = + {field_x, 1}, + {field_y, 3}, + [35] = + {field_ident, 0}, + {field_value, 2}, + [37] = + {field_default, 3}, + {field_name, 0}, + [39] = + {field_assignments, 1}, + {field_assignments, 2}, + {field_assignments, 3}, + [42] = + {field_argument, 0}, + {field_expression, 4}, + {field_result, 2}, + [45] = + {field_condition, 1}, + {field_on_false, 5}, + {field_on_true, 3}, + [48] = + {field_x, 1}, + {field_y, 3}, + {field_z, 5}, + [51] = + {field_lhs, 3}, + {field_relation, 4}, + {field_rhs, 5}, + {field_variables, 1}, + [55] = + {field_w, 7}, + {field_x, 1}, + {field_y, 3}, + {field_z, 5}, + [59] = + {field_argument, 4}, + {field_self_dictionary, 0}, + {field_to_call, 2}, +}; + +static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT][MAX_ALIAS_SEQUENCE_LENGTH] = { + [0] = {0}, +}; + +static const uint16_t ts_non_terminal_alias_map[] = { + 0, +}; + +static const TSStateId ts_primary_state_ids[STATE_COUNT] = { + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 2, + [4] = 4, + [5] = 5, + [6] = 5, + [7] = 4, + [8] = 8, + [9] = 9, + [10] = 10, + [11] = 11, + [12] = 12, + [13] = 13, + [14] = 14, + [15] = 9, + [16] = 16, + [17] = 17, + [18] = 18, + [19] = 19, + [20] = 20, + [21] = 21, + [22] = 22, + [23] = 23, + [24] = 24, + [25] = 25, + [26] = 26, + [27] = 27, + [28] = 28, + [29] = 29, + [30] = 30, + [31] = 31, + [32] = 32, + [33] = 33, + [34] = 34, + [35] = 35, + [36] = 10, + [37] = 11, + [38] = 12, + [39] = 13, + [40] = 14, + [41] = 16, + [42] = 17, + [43] = 18, + [44] = 19, + [45] = 24, + [46] = 46, + [47] = 31, + [48] = 33, + [49] = 34, + [50] = 35, + [51] = 10, + [52] = 11, + [53] = 12, + [54] = 13, + [55] = 14, + [56] = 9, + [57] = 16, + [58] = 17, + [59] = 18, + [60] = 19, + [61] = 24, + [62] = 29, + [63] = 31, + [64] = 34, + [65] = 33, + [66] = 35, + [67] = 20, + [68] = 21, + [69] = 22, + [70] = 30, + [71] = 32, + [72] = 20, + [73] = 22, + [74] = 46, + [75] = 27, + [76] = 27, + [77] = 29, + [78] = 78, + [79] = 79, + [80] = 80, + [81] = 81, + [82] = 82, + [83] = 83, + [84] = 84, + [85] = 85, + [86] = 86, + [87] = 87, + [88] = 88, + [89] = 89, + [90] = 90, + [91] = 91, + [92] = 92, + [93] = 93, + [94] = 94, + [95] = 95, + [96] = 96, + [97] = 97, + [98] = 98, + [99] = 99, + [100] = 100, + [101] = 101, + [102] = 102, + [103] = 103, + [104] = 104, + [105] = 105, + [106] = 106, + [107] = 107, + [108] = 108, + [109] = 109, + [110] = 110, + [111] = 111, + [112] = 112, + [113] = 113, + [114] = 114, + [115] = 115, + [116] = 116, + [117] = 117, + [118] = 118, + [119] = 119, + [120] = 120, + [121] = 121, + [122] = 122, + [123] = 123, + [124] = 79, + [125] = 78, + [126] = 79, + [127] = 127, + [128] = 81, + [129] = 80, + [130] = 78, + [131] = 131, + [132] = 82, + [133] = 133, + [134] = 98, + [135] = 99, + [136] = 100, + [137] = 101, + [138] = 102, + [139] = 104, + [140] = 140, + [141] = 80, + [142] = 114, + [143] = 115, + [144] = 133, + [145] = 112, + [146] = 122, + [147] = 147, + [148] = 83, + [149] = 97, + [150] = 150, + [151] = 151, + [152] = 140, + [153] = 84, + [154] = 154, + [155] = 85, + [156] = 82, + [157] = 81, + [158] = 86, + [159] = 87, + [160] = 116, + [161] = 154, + [162] = 92, + [163] = 94, + [164] = 95, + [165] = 96, + [166] = 150, + [167] = 167, + [168] = 120, + [169] = 121, + [170] = 110, + [171] = 171, + [172] = 90, + [173] = 116, + [174] = 89, + [175] = 175, + [176] = 93, + [177] = 123, + [178] = 178, + [179] = 91, + [180] = 92, + [181] = 94, + [182] = 95, + [183] = 96, + [184] = 97, + [185] = 98, + [186] = 99, + [187] = 100, + [188] = 101, + [189] = 102, + [190] = 103, + [191] = 104, + [192] = 105, + [193] = 167, + [194] = 107, + [195] = 108, + [196] = 196, + [197] = 111, + [198] = 113, + [199] = 114, + [200] = 115, + [201] = 117, + [202] = 118, + [203] = 119, + [204] = 88, + [205] = 205, + [206] = 206, + [207] = 122, + [208] = 175, + [209] = 178, + [210] = 167, + [211] = 211, + [212] = 178, + [213] = 211, + [214] = 109, + [215] = 106, + [216] = 112, + [217] = 217, + [218] = 171, + [219] = 171, + [220] = 217, + [221] = 221, + [222] = 78, + [223] = 80, + [224] = 82, + [225] = 225, + [226] = 226, + [227] = 227, + [228] = 228, + [229] = 229, + [230] = 229, + [231] = 231, + [232] = 232, + [233] = 233, + [234] = 234, + [235] = 226, + [236] = 232, + [237] = 237, + [238] = 81, + [239] = 239, + [240] = 228, + [241] = 234, + [242] = 242, + [243] = 242, + [244] = 244, + [245] = 88, + [246] = 246, + [247] = 246, + [248] = 244, + [249] = 249, + [250] = 250, + [251] = 251, + [252] = 252, + [253] = 252, + [254] = 251, + [255] = 255, + [256] = 256, + [257] = 257, + [258] = 256, + [259] = 257, + [260] = 257, + [261] = 256, + [262] = 262, + [263] = 263, + [264] = 264, + [265] = 265, + [266] = 266, + [267] = 267, + [268] = 267, + [269] = 269, + [270] = 270, + [271] = 269, + [272] = 266, + [273] = 273, + [274] = 265, + [275] = 273, + [276] = 270, + [277] = 277, + [278] = 278, + [279] = 279, + [280] = 280, + [281] = 281, + [282] = 282, + [283] = 283, + [284] = 284, + [285] = 285, + [286] = 286, + [287] = 287, + [288] = 279, + [289] = 289, + [290] = 290, + [291] = 279, + [292] = 279, + [293] = 293, + [294] = 294, + [295] = 295, + [296] = 296, + [297] = 281, + [298] = 295, + [299] = 299, + [300] = 300, + [301] = 301, + [302] = 302, + [303] = 303, + [304] = 304, + [305] = 305, + [306] = 306, + [307] = 307, + [308] = 308, + [309] = 302, + [310] = 310, + [311] = 311, + [312] = 312, + [313] = 313, + [314] = 310, + [315] = 300, + [316] = 312, + [317] = 317, + [318] = 318, + [319] = 319, + [320] = 301, + [321] = 321, +}; + +static bool ts_lex(TSLexer *lexer, TSStateId state) { + START_LEXER(); + eof = lexer->eof(lexer); + switch (state) { + case 0: + if (eof) ADVANCE(23); + ADVANCE_MAP( + '!', 54, + '"', 5, + '#', 24, + '&', 62, + '\'', 7, + '(', 82, + ')', 83, + '*', 57, + '+', 52, + ',', 47, + '-', 51, + '.', 45, + '/', 58, + '0', 33, + '1', 35, + ':', 81, + ';', 80, + '<', 72, + '=', 79, + '>', 66, + '[', 84, + ']', 85, + '^', 64, + 't', 28, + '|', 63, + ); + if (('\t' <= lookahead && lookahead <= '\r') || + lookahead == ' ') ADVANCE(25); + if (('2' <= lookahead && lookahead <= '9')) ADVANCE(36); + if (('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 1: + ADVANCE_MAP( + '!', 53, + '"', 5, + '#', 24, + '(', 82, + ')', 83, + '*', 56, + '+', 52, + '-', 50, + '.', 10, + '/', 58, + '0', 34, + ':', 12, + '<', 9, + '>', 18, + '[', 84, + ']', 85, + ); + if (('\t' <= lookahead && lookahead <= '\r') || + lookahead == ' ') ADVANCE(25); + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(36); + if (('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 2: + ADVANCE_MAP( + '!', 14, + '#', 24, + '&', 62, + '\'', 7, + '(', 82, + ')', 17, + '*', 57, + '+', 52, + ',', 47, + '-', 51, + '.', 44, + '/', 58, + ':', 12, + '<', 72, + '=', 15, + '>', 67, + '^', 64, + '|', 63, + ); + if (('\t' <= lookahead && lookahead <= '\r') || + lookahead == ' ') ADVANCE(25); + if (('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 3: + ADVANCE_MAP( + '!', 14, + '#', 24, + '\'', 7, + '*', 56, + '+', 52, + '-', 50, + '.', 44, + '/', 58, + ':', 12, + '<', 73, + '=', 15, + '>', 65, + ); + if (('\t' <= lookahead && lookahead <= '\r') || + lookahead == ' ') ADVANCE(25); + if (('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 4: + ADVANCE_MAP( + '!', 14, + '#', 24, + ')', 83, + '*', 56, + '+', 52, + '-', 50, + '/', 58, + ':', 12, + '<', 73, + '=', 15, + '>', 68, + ); + if (('\t' <= lookahead && lookahead <= '\r') || + lookahead == ' ') ADVANCE(25); + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'F') || + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(39); + END_STATE(); + case 5: + if (lookahead == '"') ADVANCE(31); + if (lookahead == '\\') ADVANCE(6); + if (lookahead != 0) ADVANCE(5); + END_STATE(); + case 6: + if (lookahead == '"') ADVANCE(32); + if (lookahead == '\\') ADVANCE(6); + if (lookahead != 0) ADVANCE(5); + END_STATE(); + case 7: + if (lookahead == '\'') ADVANCE(42); + if (lookahead == '\\') ADVANCE(8); + if (lookahead != 0) ADVANCE(7); + END_STATE(); + case 8: + if (lookahead == '\'') ADVANCE(43); + if (lookahead == '\\') ADVANCE(8); + if (lookahead != 0) ADVANCE(7); + END_STATE(); + case 9: + if (lookahead == '(') ADVANCE(46); + if (lookahead == '<') ADVANCE(13); + END_STATE(); + case 10: + if (lookahead == '.') ADVANCE(11); + END_STATE(); + case 11: + if (lookahead == '.') ADVANCE(86); + END_STATE(); + case 12: + if (lookahead == ':') ADVANCE(49); + END_STATE(); + case 13: + if (lookahead == '<') ADVANCE(88); + END_STATE(); + case 14: + if (lookahead == '=') ADVANCE(74); + END_STATE(); + case 15: + if (lookahead == '=') ADVANCE(70); + END_STATE(); + case 16: + if (lookahead == '>') ADVANCE(89); + END_STATE(); + case 17: + if (lookahead == '>') ADVANCE(48); + END_STATE(); + case 18: + if (lookahead == '>') ADVANCE(16); + END_STATE(); + case 19: + if (eof) ADVANCE(23); + ADVANCE_MAP( + '!', 14, + '#', 24, + '&', 62, + '\'', 7, + '(', 82, + ')', 83, + '*', 57, + '+', 52, + ',', 47, + '-', 51, + '.', 44, + '/', 58, + ':', 81, + ';', 80, + '<', 72, + '=', 79, + '>', 67, + ']', 85, + '^', 64, + 't', 28, + '|', 63, + ); + if (('\t' <= lookahead && lookahead <= '\r') || + lookahead == ' ') ADVANCE(25); + if (('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 20: + if (eof) ADVANCE(23); + ADVANCE_MAP( + '!', 14, + '#', 24, + '&', 62, + '\'', 7, + '(', 82, + ')', 83, + '*', 57, + '+', 52, + ',', 47, + '-', 51, + '.', 44, + '/', 58, + ':', 81, + ';', 80, + '<', 72, + '=', 79, + '>', 67, + ']', 85, + '^', 64, + '|', 63, + '0', 41, + '1', 41, + ); + if (('\t' <= lookahead && lookahead <= '\r') || + lookahead == ' ') ADVANCE(25); + if (('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 21: + if (eof) ADVANCE(23); + ADVANCE_MAP( + '!', 14, + '#', 24, + '&', 62, + '\'', 7, + '(', 82, + ')', 83, + '*', 57, + '+', 52, + ',', 47, + '-', 50, + '.', 44, + '/', 58, + ':', 81, + ';', 80, + '<', 72, + '=', 79, + '>', 66, + ']', 85, + '^', 64, + '|', 63, + ); + if (('\t' <= lookahead && lookahead <= '\r') || + lookahead == ' ') ADVANCE(25); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(36); + if (('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 22: + if (eof) ADVANCE(23); + ADVANCE_MAP( + '!', 14, + '#', 24, + '&', 62, + '(', 82, + ')', 83, + '*', 57, + '+', 52, + ',', 47, + '-', 50, + '.', 44, + '/', 58, + ':', 81, + ';', 80, + '<', 72, + '=', 79, + '>', 66, + ']', 85, + '^', 64, + 't', 28, + '|', 63, + ); + if (('\t' <= lookahead && lookahead <= '\r') || + lookahead == ' ') ADVANCE(25); + if (('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 23: + ACCEPT_TOKEN(ts_builtin_sym_end); + END_STATE(); + case 24: + ACCEPT_TOKEN(sym_comment); + if (lookahead != 0 && + lookahead != '\n') ADVANCE(24); + END_STATE(); + case 25: + ACCEPT_TOKEN(sym__whitespace); + END_STATE(); + case 26: + ACCEPT_TOKEN(sym_identifier); + if (lookahead == ' ') ADVANCE(78); + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 27: + ACCEPT_TOKEN(sym_identifier); + if (lookahead == 'e') ADVANCE(29); + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 28: + ACCEPT_TOKEN(sym_identifier); + if (lookahead == 'h') ADVANCE(27); + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 29: + ACCEPT_TOKEN(sym_identifier); + if (lookahead == 'n') ADVANCE(26); + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 30: + ACCEPT_TOKEN(sym_identifier); + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'Z') || + lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) ADVANCE(30); + END_STATE(); + case 31: + ACCEPT_TOKEN(sym_string); + END_STATE(); + case 32: + ACCEPT_TOKEN(sym_string); + if (lookahead == '"') ADVANCE(31); + if (lookahead == '\\') ADVANCE(6); + if (lookahead != 0) ADVANCE(5); + END_STATE(); + case 33: + ACCEPT_TOKEN(aux_sym_base_ten_token1); + if (lookahead == 'b') ADVANCE(40); + if (lookahead == 'o') ADVANCE(37); + if (lookahead == 'x') ADVANCE(38); + if (lookahead == '0' || + lookahead == '1') ADVANCE(35); + if (('2' <= lookahead && lookahead <= '9')) ADVANCE(36); + END_STATE(); + case 34: + ACCEPT_TOKEN(aux_sym_base_ten_token1); + if (lookahead == 'b') ADVANCE(40); + if (lookahead == 'o') ADVANCE(37); + if (lookahead == 'x') ADVANCE(38); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(36); + END_STATE(); + case 35: + ACCEPT_TOKEN(aux_sym_base_ten_token1); + if (lookahead == '0' || + lookahead == '1') ADVANCE(35); + if (('2' <= lookahead && lookahead <= '9')) ADVANCE(36); + END_STATE(); + case 36: + ACCEPT_TOKEN(aux_sym_base_ten_token1); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(36); + END_STATE(); + case 37: + ACCEPT_TOKEN(aux_sym_octal_token1); + END_STATE(); + case 38: + ACCEPT_TOKEN(aux_sym_hex_token1); + END_STATE(); + case 39: + ACCEPT_TOKEN(aux_sym_hex_token2); + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'F') || + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(39); + END_STATE(); + case 40: + ACCEPT_TOKEN(aux_sym_binary_token1); + END_STATE(); + case 41: + ACCEPT_TOKEN(aux_sym_binary_token2); + if (lookahead == '0' || + lookahead == '1') ADVANCE(41); + END_STATE(); + case 42: + ACCEPT_TOKEN(sym_unit_quote); + END_STATE(); + case 43: + ACCEPT_TOKEN(sym_unit_quote); + if (lookahead == '\'') ADVANCE(42); + if (lookahead == '\\') ADVANCE(8); + if (lookahead != 0) ADVANCE(7); + END_STATE(); + case 44: + ACCEPT_TOKEN(anon_sym_DOT); + END_STATE(); + case 45: + ACCEPT_TOKEN(anon_sym_DOT); + if (lookahead == '.') ADVANCE(11); + END_STATE(); + case 46: + ACCEPT_TOKEN(anon_sym_LT_LPAREN); + END_STATE(); + case 47: + ACCEPT_TOKEN(anon_sym_COMMA); + END_STATE(); + case 48: + ACCEPT_TOKEN(anon_sym_RPAREN_GT); + END_STATE(); + case 49: + ACCEPT_TOKEN(anon_sym_COLON_COLON); + END_STATE(); + case 50: + ACCEPT_TOKEN(anon_sym_DASH); + END_STATE(); + case 51: + ACCEPT_TOKEN(anon_sym_DASH); + if (lookahead == '>') ADVANCE(87); + END_STATE(); + case 52: + ACCEPT_TOKEN(anon_sym_PLUS); + END_STATE(); + case 53: + ACCEPT_TOKEN(anon_sym_BANG); + END_STATE(); + case 54: + ACCEPT_TOKEN(anon_sym_BANG); + if (lookahead == '=') ADVANCE(74); + END_STATE(); + case 55: + ACCEPT_TOKEN(anon_sym_STAR_STAR); + END_STATE(); + case 56: + ACCEPT_TOKEN(anon_sym_STAR); + END_STATE(); + case 57: + ACCEPT_TOKEN(anon_sym_STAR); + if (lookahead == '*') ADVANCE(55); + END_STATE(); + case 58: + ACCEPT_TOKEN(anon_sym_SLASH); + END_STATE(); + case 59: + ACCEPT_TOKEN(anon_sym_LT_LT); + END_STATE(); + case 60: + ACCEPT_TOKEN(anon_sym_GT_GT); + END_STATE(); + case 61: + ACCEPT_TOKEN(anon_sym_GT_GT); + if (lookahead == '>') ADVANCE(89); + END_STATE(); + case 62: + ACCEPT_TOKEN(anon_sym_AMP); + if (lookahead == '&') ADVANCE(75); + END_STATE(); + case 63: + ACCEPT_TOKEN(anon_sym_PIPE); + if (lookahead == '|') ADVANCE(76); + END_STATE(); + case 64: + ACCEPT_TOKEN(anon_sym_CARET); + if (lookahead == '^') ADVANCE(77); + END_STATE(); + case 65: + ACCEPT_TOKEN(anon_sym_GT); + if (lookahead == '=') ADVANCE(69); + END_STATE(); + case 66: + ACCEPT_TOKEN(anon_sym_GT); + if (lookahead == '=') ADVANCE(69); + if (lookahead == '>') ADVANCE(61); + END_STATE(); + case 67: + ACCEPT_TOKEN(anon_sym_GT); + if (lookahead == '=') ADVANCE(69); + if (lookahead == '>') ADVANCE(60); + END_STATE(); + case 68: + ACCEPT_TOKEN(anon_sym_GT); + if (lookahead == '=') ADVANCE(69); + if (lookahead == '>') ADVANCE(16); + END_STATE(); + case 69: + ACCEPT_TOKEN(anon_sym_GT_EQ); + END_STATE(); + case 70: + ACCEPT_TOKEN(anon_sym_EQ_EQ); + END_STATE(); + case 71: + ACCEPT_TOKEN(anon_sym_LT_EQ); + END_STATE(); + case 72: + ACCEPT_TOKEN(anon_sym_LT); + if (lookahead == '<') ADVANCE(59); + if (lookahead == '=') ADVANCE(71); + END_STATE(); + case 73: + ACCEPT_TOKEN(anon_sym_LT); + if (lookahead == '=') ADVANCE(71); + END_STATE(); + case 74: + ACCEPT_TOKEN(anon_sym_BANG_EQ); + END_STATE(); + case 75: + ACCEPT_TOKEN(anon_sym_AMP_AMP); + END_STATE(); + case 76: + ACCEPT_TOKEN(anon_sym_PIPE_PIPE); + END_STATE(); + case 77: + ACCEPT_TOKEN(anon_sym_CARET_CARET); + END_STATE(); + case 78: + ACCEPT_TOKEN(anon_sym_then); + END_STATE(); + case 79: + ACCEPT_TOKEN(anon_sym_EQ); + if (lookahead == '=') ADVANCE(70); + END_STATE(); + case 80: + ACCEPT_TOKEN(anon_sym_SEMI); + END_STATE(); + case 81: + ACCEPT_TOKEN(anon_sym_COLON); + if (lookahead == ':') ADVANCE(49); + END_STATE(); + case 82: + ACCEPT_TOKEN(anon_sym_LPAREN); + END_STATE(); + case 83: + ACCEPT_TOKEN(anon_sym_RPAREN); + END_STATE(); + case 84: + ACCEPT_TOKEN(anon_sym_LBRACK); + END_STATE(); + case 85: + ACCEPT_TOKEN(anon_sym_RBRACK); + END_STATE(); + case 86: + ACCEPT_TOKEN(sym_varadic_dots); + END_STATE(); + case 87: + ACCEPT_TOKEN(anon_sym_DASH_GT); + END_STATE(); + case 88: + ACCEPT_TOKEN(anon_sym_LT_LT_LT); + END_STATE(); + case 89: + ACCEPT_TOKEN(anon_sym_GT_GT_GT); + END_STATE(); + default: + return false; + } +} + +static bool ts_lex_keywords(TSLexer *lexer, TSStateId state) { + START_LEXER(); + eof = lexer->eof(lexer); + switch (state) { + case 0: + ADVANCE_MAP( + 'I', 1, + 'e', 2, + 'f', 3, + 'i', 4, + 'l', 5, + 's', 6, + 't', 7, + 'U', 8, + 'u', 8, + ); + END_STATE(); + case 1: + ACCEPT_TOKEN(aux_sym_signed_integer_token1); + END_STATE(); + case 2: + if (lookahead == 'l') ADVANCE(9); + END_STATE(); + case 3: + if (lookahead == 'a') ADVANCE(10); + END_STATE(); + case 4: + ACCEPT_TOKEN(aux_sym_signed_integer_token1); + if (lookahead == 'f') ADVANCE(11); + if (lookahead == 'n') ADVANCE(12); + END_STATE(); + case 5: + if (lookahead == 'e') ADVANCE(13); + END_STATE(); + case 6: + if (lookahead == 'e') ADVANCE(14); + END_STATE(); + case 7: + if (lookahead == 'r') ADVANCE(15); + END_STATE(); + case 8: + ACCEPT_TOKEN(aux_sym_unsigned_integer_token1); + END_STATE(); + case 9: + if (lookahead == 's') ADVANCE(16); + END_STATE(); + case 10: + if (lookahead == 'l') ADVANCE(17); + END_STATE(); + case 11: + ACCEPT_TOKEN(anon_sym_if); + END_STATE(); + case 12: + ACCEPT_TOKEN(anon_sym_in); + END_STATE(); + case 13: + if (lookahead == 't') ADVANCE(18); + END_STATE(); + case 14: + if (lookahead == 'l') ADVANCE(19); + END_STATE(); + case 15: + if (lookahead == 'u') ADVANCE(20); + END_STATE(); + case 16: + if (lookahead == 'e') ADVANCE(21); + END_STATE(); + case 17: + if (lookahead == 's') ADVANCE(22); + END_STATE(); + case 18: + ACCEPT_TOKEN(anon_sym_let); + END_STATE(); + case 19: + if (lookahead == 'f') ADVANCE(23); + END_STATE(); + case 20: + if (lookahead == 'e') ADVANCE(24); + END_STATE(); + case 21: + ACCEPT_TOKEN(anon_sym_else); + END_STATE(); + case 22: + if (lookahead == 'e') ADVANCE(25); + END_STATE(); + case 23: + ACCEPT_TOKEN(sym_self); + END_STATE(); + case 24: + ACCEPT_TOKEN(sym_true); + END_STATE(); + case 25: + ACCEPT_TOKEN(sym_false); + END_STATE(); + default: + return false; + } +} + +static const TSLexerMode ts_lex_modes[STATE_COUNT] = { + [0] = {.lex_state = 0}, + [1] = {.lex_state = 1}, + [2] = {.lex_state = 1}, + [3] = {.lex_state = 1}, + [4] = {.lex_state = 1}, + [5] = {.lex_state = 1}, + [6] = {.lex_state = 1}, + [7] = {.lex_state = 1}, + [8] = {.lex_state = 1}, + [9] = {.lex_state = 1}, + [10] = {.lex_state = 1}, + [11] = {.lex_state = 1}, + [12] = {.lex_state = 1}, + [13] = {.lex_state = 1}, + [14] = {.lex_state = 1}, + [15] = {.lex_state = 1}, + [16] = {.lex_state = 1}, + [17] = {.lex_state = 1}, + [18] = {.lex_state = 1}, + [19] = {.lex_state = 1}, + [20] = {.lex_state = 1}, + [21] = {.lex_state = 1}, + [22] = {.lex_state = 1}, + [23] = {.lex_state = 1}, + [24] = {.lex_state = 1}, + [25] = {.lex_state = 1}, + [26] = {.lex_state = 1}, + [27] = {.lex_state = 1}, + [28] = {.lex_state = 1}, + [29] = {.lex_state = 1}, + [30] = {.lex_state = 1}, + [31] = {.lex_state = 1}, + [32] = {.lex_state = 1}, + [33] = {.lex_state = 1}, + [34] = {.lex_state = 1}, + [35] = {.lex_state = 1}, + [36] = {.lex_state = 1}, + [37] = {.lex_state = 1}, + [38] = {.lex_state = 1}, + [39] = {.lex_state = 1}, + [40] = {.lex_state = 1}, + [41] = {.lex_state = 1}, + [42] = {.lex_state = 1}, + [43] = {.lex_state = 1}, + [44] = {.lex_state = 1}, + [45] = {.lex_state = 1}, + [46] = {.lex_state = 1}, + [47] = {.lex_state = 1}, + [48] = {.lex_state = 1}, + [49] = {.lex_state = 1}, + [50] = {.lex_state = 1}, + [51] = {.lex_state = 1}, + [52] = {.lex_state = 1}, + [53] = {.lex_state = 1}, + [54] = {.lex_state = 1}, + [55] = {.lex_state = 1}, + [56] = {.lex_state = 1}, + [57] = {.lex_state = 1}, + [58] = {.lex_state = 1}, + [59] = {.lex_state = 1}, + [60] = {.lex_state = 1}, + [61] = {.lex_state = 1}, + [62] = {.lex_state = 1}, + [63] = {.lex_state = 1}, + [64] = {.lex_state = 1}, + [65] = {.lex_state = 1}, + [66] = {.lex_state = 1}, + [67] = {.lex_state = 1}, + [68] = {.lex_state = 1}, + [69] = {.lex_state = 1}, + [70] = {.lex_state = 1}, + [71] = {.lex_state = 1}, + [72] = {.lex_state = 1}, + [73] = {.lex_state = 1}, + [74] = {.lex_state = 1}, + [75] = {.lex_state = 1}, + [76] = {.lex_state = 1}, + [77] = {.lex_state = 1}, + [78] = {.lex_state = 21}, + [79] = {.lex_state = 20}, + [80] = {.lex_state = 21}, + [81] = {.lex_state = 21}, + [82] = {.lex_state = 21}, + [83] = {.lex_state = 19}, + [84] = {.lex_state = 19}, + [85] = {.lex_state = 19}, + [86] = {.lex_state = 19}, + [87] = {.lex_state = 19}, + [88] = {.lex_state = 22}, + [89] = {.lex_state = 19}, + [90] = {.lex_state = 19}, + [91] = {.lex_state = 19}, + [92] = {.lex_state = 20}, + [93] = {.lex_state = 19}, + [94] = {.lex_state = 20}, + [95] = {.lex_state = 20}, + [96] = {.lex_state = 20}, + [97] = {.lex_state = 20}, + [98] = {.lex_state = 20}, + [99] = {.lex_state = 20}, + [100] = {.lex_state = 20}, + [101] = {.lex_state = 20}, + [102] = {.lex_state = 20}, + [103] = {.lex_state = 19}, + [104] = {.lex_state = 20}, + [105] = {.lex_state = 19}, + [106] = {.lex_state = 19}, + [107] = {.lex_state = 19}, + [108] = {.lex_state = 19}, + [109] = {.lex_state = 19}, + [110] = {.lex_state = 19}, + [111] = {.lex_state = 19}, + [112] = {.lex_state = 20}, + [113] = {.lex_state = 19}, + [114] = {.lex_state = 20}, + [115] = {.lex_state = 20}, + [116] = {.lex_state = 20}, + [117] = {.lex_state = 19}, + [118] = {.lex_state = 19}, + [119] = {.lex_state = 19}, + [120] = {.lex_state = 19}, + [121] = {.lex_state = 19}, + [122] = {.lex_state = 20}, + [123] = {.lex_state = 19}, + [124] = {.lex_state = 2}, + [125] = {.lex_state = 2}, + [126] = {.lex_state = 19}, + [127] = {.lex_state = 20}, + [128] = {.lex_state = 2}, + [129] = {.lex_state = 2}, + [130] = {.lex_state = 19}, + [131] = {.lex_state = 20}, + [132] = {.lex_state = 2}, + [133] = {.lex_state = 2}, + [134] = {.lex_state = 2}, + [135] = {.lex_state = 2}, + [136] = {.lex_state = 2}, + [137] = {.lex_state = 2}, + [138] = {.lex_state = 2}, + [139] = {.lex_state = 2}, + [140] = {.lex_state = 20}, + [141] = {.lex_state = 19}, + [142] = {.lex_state = 2}, + [143] = {.lex_state = 2}, + [144] = {.lex_state = 2}, + [145] = {.lex_state = 2}, + [146] = {.lex_state = 2}, + [147] = {.lex_state = 20}, + [148] = {.lex_state = 2}, + [149] = {.lex_state = 2}, + [150] = {.lex_state = 20}, + [151] = {.lex_state = 20}, + [152] = {.lex_state = 20}, + [153] = {.lex_state = 2}, + [154] = {.lex_state = 2}, + [155] = {.lex_state = 2}, + [156] = {.lex_state = 19}, + [157] = {.lex_state = 19}, + [158] = {.lex_state = 2}, + [159] = {.lex_state = 2}, + [160] = {.lex_state = 2}, + [161] = {.lex_state = 2}, + [162] = {.lex_state = 2}, + [163] = {.lex_state = 2}, + [164] = {.lex_state = 2}, + [165] = {.lex_state = 2}, + [166] = {.lex_state = 20}, + [167] = {.lex_state = 20}, + [168] = {.lex_state = 2}, + [169] = {.lex_state = 2}, + [170] = {.lex_state = 2}, + [171] = {.lex_state = 19}, + [172] = {.lex_state = 2}, + [173] = {.lex_state = 19}, + [174] = {.lex_state = 2}, + [175] = {.lex_state = 20}, + [176] = {.lex_state = 2}, + [177] = {.lex_state = 2}, + [178] = {.lex_state = 20}, + [179] = {.lex_state = 2}, + [180] = {.lex_state = 19}, + [181] = {.lex_state = 19}, + [182] = {.lex_state = 19}, + [183] = {.lex_state = 19}, + [184] = {.lex_state = 19}, + [185] = {.lex_state = 19}, + [186] = {.lex_state = 19}, + [187] = {.lex_state = 19}, + [188] = {.lex_state = 19}, + [189] = {.lex_state = 19}, + [190] = {.lex_state = 2}, + [191] = {.lex_state = 19}, + [192] = {.lex_state = 2}, + [193] = {.lex_state = 20}, + [194] = {.lex_state = 2}, + [195] = {.lex_state = 2}, + [196] = {.lex_state = 20}, + [197] = {.lex_state = 2}, + [198] = {.lex_state = 2}, + [199] = {.lex_state = 19}, + [200] = {.lex_state = 19}, + [201] = {.lex_state = 2}, + [202] = {.lex_state = 2}, + [203] = {.lex_state = 2}, + [204] = {.lex_state = 2}, + [205] = {.lex_state = 20}, + [206] = {.lex_state = 20}, + [207] = {.lex_state = 19}, + [208] = {.lex_state = 20}, + [209] = {.lex_state = 20}, + [210] = {.lex_state = 20}, + [211] = {.lex_state = 2}, + [212] = {.lex_state = 20}, + [213] = {.lex_state = 2}, + [214] = {.lex_state = 2}, + [215] = {.lex_state = 2}, + [216] = {.lex_state = 19}, + [217] = {.lex_state = 20}, + [218] = {.lex_state = 19}, + [219] = {.lex_state = 19}, + [220] = {.lex_state = 20}, + [221] = {.lex_state = 1}, + [222] = {.lex_state = 3}, + [223] = {.lex_state = 3}, + [224] = {.lex_state = 3}, + [225] = {.lex_state = 21}, + [226] = {.lex_state = 21}, + [227] = {.lex_state = 4}, + [228] = {.lex_state = 21}, + [229] = {.lex_state = 21}, + [230] = {.lex_state = 21}, + [231] = {.lex_state = 4}, + [232] = {.lex_state = 21}, + [233] = {.lex_state = 4}, + [234] = {.lex_state = 21}, + [235] = {.lex_state = 21}, + [236] = {.lex_state = 21}, + [237] = {.lex_state = 4}, + [238] = {.lex_state = 3}, + [239] = {.lex_state = 21}, + [240] = {.lex_state = 21}, + [241] = {.lex_state = 21}, + [242] = {.lex_state = 3}, + [243] = {.lex_state = 3}, + [244] = {.lex_state = 3}, + [245] = {.lex_state = 3}, + [246] = {.lex_state = 3}, + [247] = {.lex_state = 1}, + [248] = {.lex_state = 1}, + [249] = {.lex_state = 0}, + [250] = {.lex_state = 0}, + [251] = {.lex_state = 1}, + [252] = {.lex_state = 1}, + [253] = {.lex_state = 1}, + [254] = {.lex_state = 1}, + [255] = {.lex_state = 1}, + [256] = {.lex_state = 1}, + [257] = {.lex_state = 1}, + [258] = {.lex_state = 1}, + [259] = {.lex_state = 1}, + [260] = {.lex_state = 1}, + [261] = {.lex_state = 1}, + [262] = {.lex_state = 1}, + [263] = {.lex_state = 1}, + [264] = {.lex_state = 0}, + [265] = {.lex_state = 1}, + [266] = {.lex_state = 0}, + [267] = {.lex_state = 1}, + [268] = {.lex_state = 1}, + [269] = {.lex_state = 1}, + [270] = {.lex_state = 1}, + [271] = {.lex_state = 1}, + [272] = {.lex_state = 0}, + [273] = {.lex_state = 0}, + [274] = {.lex_state = 1}, + [275] = {.lex_state = 0}, + [276] = {.lex_state = 1}, + [277] = {.lex_state = 0}, + [278] = {.lex_state = 1}, + [279] = {.lex_state = 21}, + [280] = {.lex_state = 1}, + [281] = {.lex_state = 0}, + [282] = {.lex_state = 1}, + [283] = {.lex_state = 0}, + [284] = {.lex_state = 1}, + [285] = {.lex_state = 1}, + [286] = {.lex_state = 0}, + [287] = {.lex_state = 0}, + [288] = {.lex_state = 21}, + [289] = {.lex_state = 0}, + [290] = {.lex_state = 0}, + [291] = {.lex_state = 21}, + [292] = {.lex_state = 21}, + [293] = {.lex_state = 1}, + [294] = {.lex_state = 0}, + [295] = {.lex_state = 1}, + [296] = {.lex_state = 1}, + [297] = {.lex_state = 0}, + [298] = {.lex_state = 1}, + [299] = {.lex_state = 1}, + [300] = {.lex_state = 0}, + [301] = {.lex_state = 0}, + [302] = {.lex_state = 1}, + [303] = {.lex_state = 4}, + [304] = {.lex_state = 20}, + [305] = {.lex_state = 21}, + [306] = {.lex_state = 1}, + [307] = {.lex_state = 0}, + [308] = {.lex_state = 0}, + [309] = {.lex_state = 1}, + [310] = {.lex_state = 0}, + [311] = {.lex_state = 0}, + [312] = {.lex_state = 1}, + [313] = {.lex_state = 0}, + [314] = {.lex_state = 0}, + [315] = {.lex_state = 0}, + [316] = {.lex_state = 1}, + [317] = {.lex_state = 1}, + [318] = {.lex_state = 0}, + [319] = {.lex_state = 0}, + [320] = {.lex_state = 0}, + [321] = {.lex_state = 0}, +}; + +static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { + [STATE(0)] = { + [ts_builtin_sym_end] = ACTIONS(1), + [sym_identifier] = ACTIONS(1), + [sym_comment] = ACTIONS(3), + [sym__whitespace] = ACTIONS(3), + [sym_string] = ACTIONS(1), + [sym_self] = ACTIONS(1), + [aux_sym_base_ten_token1] = ACTIONS(1), + [aux_sym_octal_token1] = ACTIONS(1), + [aux_sym_hex_token1] = ACTIONS(1), + [aux_sym_binary_token1] = ACTIONS(1), + [aux_sym_binary_token2] = ACTIONS(1), + [aux_sym_signed_integer_token1] = ACTIONS(1), + [aux_sym_unsigned_integer_token1] = ACTIONS(1), + [sym_unit_quote] = ACTIONS(1), + [anon_sym_DOT] = ACTIONS(1), + [anon_sym_COMMA] = ACTIONS(1), + [sym_true] = ACTIONS(1), + [sym_false] = ACTIONS(1), + [anon_sym_COLON_COLON] = ACTIONS(1), + [anon_sym_DASH] = ACTIONS(1), + [anon_sym_PLUS] = ACTIONS(1), + [anon_sym_BANG] = ACTIONS(1), + [anon_sym_STAR_STAR] = ACTIONS(1), + [anon_sym_STAR] = ACTIONS(1), + [anon_sym_SLASH] = ACTIONS(1), + [anon_sym_LT_LT] = ACTIONS(1), + [anon_sym_GT_GT] = ACTIONS(1), + [anon_sym_AMP] = ACTIONS(1), + [anon_sym_PIPE] = ACTIONS(1), + [anon_sym_CARET] = ACTIONS(1), + [anon_sym_GT] = ACTIONS(1), + [anon_sym_GT_EQ] = ACTIONS(1), + [anon_sym_EQ_EQ] = ACTIONS(1), + [anon_sym_LT_EQ] = ACTIONS(1), + [anon_sym_LT] = ACTIONS(1), + [anon_sym_BANG_EQ] = ACTIONS(1), + [anon_sym_AMP_AMP] = ACTIONS(1), + [anon_sym_PIPE_PIPE] = ACTIONS(1), + [anon_sym_CARET_CARET] = ACTIONS(1), + [anon_sym_if] = ACTIONS(1), + [anon_sym_then] = ACTIONS(1), + [anon_sym_else] = ACTIONS(1), + [anon_sym_let] = ACTIONS(1), + [anon_sym_in] = ACTIONS(1), + [anon_sym_EQ] = ACTIONS(1), + [anon_sym_SEMI] = ACTIONS(1), + [anon_sym_COLON] = ACTIONS(1), + [anon_sym_LPAREN] = ACTIONS(1), + [anon_sym_RPAREN] = ACTIONS(1), + [anon_sym_LBRACK] = ACTIONS(1), + [anon_sym_RBRACK] = ACTIONS(1), + [sym_varadic_dots] = ACTIONS(1), + [anon_sym_DASH_GT] = ACTIONS(1), + [anon_sym_GT_GT_GT] = ACTIONS(1), + }, + [STATE(1)] = { + [sym_source_file] = STATE(319), + [sym_base_ten] = STATE(284), + [sym_octal] = STATE(284), + [sym_hex] = STATE(284), + [sym_binary] = STATE(284), + [sym_integer] = STATE(298), + [sym_signed_integer] = STATE(109), + [sym_unsigned_integer] = STATE(109), + [sym_number] = STATE(80), + [sym__float] = STATE(78), + [sym_scalar] = STATE(109), + [sym_vector2] = STATE(109), + [sym_vector3] = STATE(109), + [sym_vector4] = STATE(109), + [sym_boolean] = STATE(109), + [sym_function_call] = STATE(109), + [sym_method_call] = STATE(109), + [sym_expression] = STATE(206), + [sym_unary_expression] = STATE(109), + [sym_binary_expression] = STATE(109), + [sym_if] = STATE(109), + [sym_let_in] = STATE(109), + [sym_member_access] = STATE(109), + [sym_parenthesis] = STATE(109), + [sym_list] = STATE(109), + [sym_struct_definition] = STATE(112), + [sym_dictionary_construction] = STATE(109), + [sym_closure_definition] = STATE(109), + [sym_constraint_set] = STATE(109), + [sym_identifier] = ACTIONS(5), + [sym_comment] = ACTIONS(3), + [sym__whitespace] = ACTIONS(3), + [sym_string] = ACTIONS(7), + [sym_self] = ACTIONS(5), + [aux_sym_base_ten_token1] = ACTIONS(9), + [aux_sym_octal_token1] = ACTIONS(11), + [aux_sym_hex_token1] = ACTIONS(13), + [aux_sym_binary_token1] = ACTIONS(15), + [anon_sym_LT_LPAREN] = ACTIONS(17), + [sym_true] = ACTIONS(19), + [sym_false] = ACTIONS(19), + [anon_sym_DASH] = ACTIONS(21), + [anon_sym_PLUS] = ACTIONS(21), + [anon_sym_BANG] = ACTIONS(21), + [anon_sym_if] = ACTIONS(23), + [anon_sym_let] = ACTIONS(25), + [anon_sym_LPAREN] = ACTIONS(27), + [anon_sym_LBRACK] = ACTIONS(29), + [anon_sym_LT_LT_LT] = ACTIONS(31), + }, + [STATE(2)] = { + [sym_base_ten] = STATE(284), + [sym_octal] = STATE(284), + [sym_hex] = STATE(284), + [sym_binary] = STATE(284), + [sym_integer] = STATE(298), + [sym_signed_integer] = STATE(109), + [sym_unsigned_integer] = STATE(109), + [sym_number] = STATE(80), + [sym__float] = STATE(78), + [sym_scalar] = STATE(109), + [sym_vector2] = STATE(109), + [sym_vector3] = STATE(109), + [sym_vector4] = STATE(109), + [sym_boolean] = STATE(109), + [sym_function_call] = STATE(109), + [sym_method_call] = STATE(109), + [sym_expression] = STATE(175), + [sym_unary_expression] = STATE(109), + [sym_binary_expression] = STATE(109), + [sym_if] = STATE(109), + [sym_let_in] = STATE(109), + [sym_member_access] = STATE(109), + [sym_parenthesis] = STATE(109), + [sym_list] = STATE(109), + [sym_struct_member] = STATE(283), + [sym__struct_final_element] = STATE(310), + [sym_struct_definition] = STATE(112), + [sym_dictionary_member_assignment] = STATE(273), + [sym_dictionary_construction] = STATE(109), + [sym_closure_definition] = STATE(109), + [sym_constraint_set] = STATE(109), + [aux_sym_struct_definition_repeat1] = STATE(252), + [sym_identifier] = ACTIONS(33), + [sym_comment] = ACTIONS(3), + [sym__whitespace] = ACTIONS(3), + [sym_string] = ACTIONS(7), + [sym_self] = ACTIONS(5), + [aux_sym_base_ten_token1] = ACTIONS(9), + [aux_sym_octal_token1] = ACTIONS(11), + [aux_sym_hex_token1] = ACTIONS(13), + [aux_sym_binary_token1] = ACTIONS(15), + [anon_sym_LT_LPAREN] = ACTIONS(17), + [sym_true] = ACTIONS(19), + [sym_false] = ACTIONS(19), + [anon_sym_DASH] = ACTIONS(21), + [anon_sym_PLUS] = ACTIONS(21), + [anon_sym_BANG] = ACTIONS(21), + [anon_sym_if] = ACTIONS(23), + [anon_sym_let] = ACTIONS(25), + [anon_sym_LPAREN] = ACTIONS(27), + [anon_sym_RPAREN] = ACTIONS(35), + [anon_sym_LBRACK] = ACTIONS(29), + [sym_varadic_dots] = ACTIONS(37), + [anon_sym_LT_LT_LT] = ACTIONS(31), + }, + [STATE(3)] = { + [sym_base_ten] = STATE(284), + [sym_octal] = STATE(284), + [sym_hex] = STATE(284), + [sym_binary] = STATE(284), + [sym_integer] = STATE(298), + [sym_signed_integer] = STATE(109), + [sym_unsigned_integer] = STATE(109), + [sym_number] = STATE(80), + [sym__float] = STATE(78), + [sym_scalar] = STATE(109), + [sym_vector2] = STATE(109), + [sym_vector3] = STATE(109), + [sym_vector4] = STATE(109), + [sym_boolean] = STATE(109), + [sym_function_call] = STATE(109), + [sym_method_call] = STATE(109), + [sym_expression] = STATE(208), + [sym_unary_expression] = STATE(109), + [sym_binary_expression] = STATE(109), + [sym_if] = STATE(109), + [sym_let_in] = STATE(109), + [sym_member_access] = STATE(109), + [sym_parenthesis] = STATE(109), + [sym_list] = STATE(109), + [sym_struct_member] = STATE(283), + [sym__struct_final_element] = STATE(314), + [sym_struct_definition] = STATE(112), + [sym_dictionary_member_assignment] = STATE(275), + [sym_dictionary_construction] = STATE(109), + [sym_closure_definition] = STATE(109), + [sym_constraint_set] = STATE(109), + [aux_sym_struct_definition_repeat1] = STATE(253), + [sym_identifier] = ACTIONS(33), + [sym_comment] = ACTIONS(3), + [sym__whitespace] = ACTIONS(3), + [sym_string] = ACTIONS(7), + [sym_self] = ACTIONS(5), + [aux_sym_base_ten_token1] = ACTIONS(9), + [aux_sym_octal_token1] = ACTIONS(11), + [aux_sym_hex_token1] = ACTIONS(13), + [aux_sym_binary_token1] = ACTIONS(15), + [anon_sym_LT_LPAREN] = ACTIONS(17), + [sym_true] = ACTIONS(19), + [sym_false] = ACTIONS(19), + [anon_sym_DASH] = ACTIONS(21), + [anon_sym_PLUS] = ACTIONS(21), + [anon_sym_BANG] = ACTIONS(21), + [anon_sym_if] = ACTIONS(23), + [anon_sym_let] = ACTIONS(25), + [anon_sym_LPAREN] = ACTIONS(27), + [anon_sym_RPAREN] = ACTIONS(39), + [anon_sym_LBRACK] = ACTIONS(29), + [sym_varadic_dots] = ACTIONS(37), + [anon_sym_LT_LT_LT] = ACTIONS(31), + }, +}; + +static const uint16_t ts_small_parse_table[] = { + [0] = 24, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(41), 1, + anon_sym_RBRACK, + STATE(8), 1, + aux_sym_list_repeat1, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(140), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [99] = 24, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(43), 1, + anon_sym_RBRACK, + STATE(4), 1, + aux_sym_list_repeat1, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(166), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [198] = 24, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(45), 1, + anon_sym_RBRACK, + STATE(7), 1, + aux_sym_list_repeat1, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(150), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [297] = 24, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(47), 1, + anon_sym_RBRACK, + STATE(8), 1, + aux_sym_list_repeat1, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(152), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [396] = 24, + ACTIONS(52), 1, + sym_string, + ACTIONS(55), 1, + aux_sym_base_ten_token1, + ACTIONS(58), 1, + aux_sym_octal_token1, + ACTIONS(61), 1, + aux_sym_hex_token1, + ACTIONS(64), 1, + aux_sym_binary_token1, + ACTIONS(67), 1, + anon_sym_LT_LPAREN, + ACTIONS(76), 1, + anon_sym_if, + ACTIONS(79), 1, + anon_sym_let, + ACTIONS(82), 1, + anon_sym_LPAREN, + ACTIONS(85), 1, + anon_sym_LBRACK, + ACTIONS(88), 1, + anon_sym_RBRACK, + ACTIONS(90), 1, + anon_sym_LT_LT_LT, + STATE(8), 1, + aux_sym_list_repeat1, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(205), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(49), 2, + sym_identifier, + sym_self, + ACTIONS(70), 2, + sym_true, + sym_false, + ACTIONS(73), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [495] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(185), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [588] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(86), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [681] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(94), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [774] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(95), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [867] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(96), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [960] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(97), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1053] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(98), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1146] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(99), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1239] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(100), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1332] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(101), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1425] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(102), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1518] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(178), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1611] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(144), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1704] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(193), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1797] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(196), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1890] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(104), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [1983] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(151), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2076] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(127), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2169] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(171), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2262] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(147), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2355] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(114), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2448] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(161), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2541] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(115), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2634] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(213), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2727] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(207), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2820] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(173), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [2913] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(180), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3006] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(86), 1, + sym_expression, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3099] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(181), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3192] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(182), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3285] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(183), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3378] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(184), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3471] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(186), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3564] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(187), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3657] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(188), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3750] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(189), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3843] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(191), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [3936] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(220), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4029] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(200), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4122] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(146), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4215] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(160), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4308] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(162), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4401] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(158), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4494] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(163), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4587] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(164), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4680] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(165), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4773] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(149), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4866] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(134), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [4959] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(135), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5052] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(136), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5145] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(137), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5238] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(138), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5331] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(139), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5424] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(142), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5517] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(143), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5610] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(116), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5703] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(122), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5796] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(92), 1, + sym_expression, + STATE(112), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5889] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(209), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [5982] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(133), 1, + sym_expression, + STATE(145), 1, + sym_struct_definition, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6075] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(210), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6168] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(154), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6261] = 22, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(103), 1, + sym_string, + ACTIONS(105), 1, + aux_sym_base_ten_token1, + ACTIONS(107), 1, + anon_sym_LT_LPAREN, + ACTIONS(113), 1, + anon_sym_if, + ACTIONS(115), 1, + anon_sym_let, + ACTIONS(117), 1, + anon_sym_LPAREN, + ACTIONS(119), 1, + anon_sym_LBRACK, + ACTIONS(121), 1, + anon_sym_LT_LT_LT, + STATE(125), 1, + sym__float, + STATE(129), 1, + sym_number, + STATE(145), 1, + sym_struct_definition, + STATE(211), 1, + sym_expression, + STATE(295), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(101), 2, + sym_identifier, + sym_self, + ACTIONS(109), 2, + sym_true, + sym_false, + ACTIONS(111), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(214), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6354] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(212), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6447] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(167), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6540] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(9), 1, + aux_sym_base_ten_token1, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(23), 1, + anon_sym_if, + ACTIONS(25), 1, + anon_sym_let, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(112), 1, + sym_struct_definition, + STATE(217), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(21), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6633] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(216), 1, + sym_struct_definition, + STATE(218), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6726] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(216), 1, + sym_struct_definition, + STATE(219), 1, + sym_expression, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6819] = 22, + ACTIONS(7), 1, + sym_string, + ACTIONS(11), 1, + aux_sym_octal_token1, + ACTIONS(13), 1, + aux_sym_hex_token1, + ACTIONS(15), 1, + aux_sym_binary_token1, + ACTIONS(17), 1, + anon_sym_LT_LPAREN, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_LBRACK, + ACTIONS(31), 1, + anon_sym_LT_LT_LT, + ACTIONS(93), 1, + aux_sym_base_ten_token1, + ACTIONS(97), 1, + anon_sym_if, + ACTIONS(99), 1, + anon_sym_let, + STATE(130), 1, + sym__float, + STATE(141), 1, + sym_number, + STATE(199), 1, + sym_expression, + STATE(216), 1, + sym_struct_definition, + STATE(298), 1, + sym_integer, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(5), 2, + sym_identifier, + sym_self, + ACTIONS(19), 2, + sym_true, + sym_false, + ACTIONS(95), 3, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + STATE(284), 4, + sym_base_ten, + sym_octal, + sym_hex, + sym_binary, + STATE(109), 19, + sym_signed_integer, + sym_unsigned_integer, + sym_scalar, + sym_vector2, + sym_vector3, + sym_vector4, + sym_boolean, + sym_function_call, + sym_method_call, + sym_unary_expression, + sym_binary_expression, + sym_if, + sym_let_in, + sym_member_access, + sym_parenthesis, + sym_list, + sym_dictionary_construction, + sym_closure_definition, + sym_constraint_set, + [6912] = 6, + ACTIONS(125), 1, + sym_identifier, + ACTIONS(127), 1, + sym_unit_quote, + STATE(88), 1, + sym__unit, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(129), 10, + anon_sym_STAR, + anon_sym_GT_GT, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_else, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(123), 21, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + anon_sym_GT_GT_GT, + [6961] = 4, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(135), 2, + aux_sym_signed_integer_token1, + aux_sym_unsigned_integer_token1, + ACTIONS(133), 10, + sym_identifier, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_else, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(131), 22, + ts_builtin_sym_end, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7006] = 4, + ACTIONS(141), 1, + anon_sym_DOT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(139), 11, + sym_identifier, + anon_sym_STAR, + anon_sym_GT_GT, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_else, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(137), 21, + ts_builtin_sym_end, + sym_unit_quote, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + anon_sym_GT_GT_GT, + [7050] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(145), 11, + sym_identifier, + anon_sym_STAR, + anon_sym_GT_GT, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_else, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(143), 22, + ts_builtin_sym_end, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + anon_sym_GT_GT_GT, + [7092] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(133), 11, + sym_identifier, + anon_sym_STAR, + anon_sym_GT_GT, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_else, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(131), 22, + ts_builtin_sym_end, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + anon_sym_GT_GT_GT, + [7134] = 4, + ACTIONS(151), 1, + anon_sym_DASH_GT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(149), 9, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(147), 22, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7177] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(155), 9, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(153), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + anon_sym_DASH_GT, + [7218] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(159), 9, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(157), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + anon_sym_DASH_GT, + [7259] = 7, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(161), 20, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7308] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(173), 9, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(171), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + anon_sym_DASH_GT, + [7349] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(177), 9, + anon_sym_STAR, + anon_sym_GT_GT, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(175), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + anon_sym_GT_GT_GT, + [7390] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(181), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(179), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7430] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(185), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(183), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7470] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(189), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(187), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7510] = 10, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 7, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(161), 17, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7564] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(199), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(197), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7604] = 8, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(161), 18, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7654] = 11, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(167), 7, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(161), 15, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7710] = 12, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 7, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(161), 13, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7768] = 14, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 5, + anon_sym_PIPE, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(161), 13, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7830] = 13, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 6, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(161), 13, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7890] = 15, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 4, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(161), 13, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [7954] = 17, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 2, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + ACTIONS(161), 9, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8022] = 18, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 2, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + ACTIONS(161), 8, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8092] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 2, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + ACTIONS(161), 7, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8164] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(149), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(147), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8204] = 20, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(223), 2, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + ACTIONS(219), 6, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8278] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(227), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(225), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8318] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(231), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(229), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8358] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(235), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(233), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8398] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(239), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(237), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8438] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(243), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(241), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8478] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(247), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(245), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8518] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(251), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(249), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8558] = 4, + ACTIONS(253), 1, + anon_sym_DASH_GT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(243), 9, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(241), 21, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8600] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(257), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(255), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8640] = 20, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(261), 2, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + ACTIONS(259), 6, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8714] = 20, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(265), 2, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + ACTIONS(263), 6, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8788] = 20, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(269), 2, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + ACTIONS(267), 6, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8862] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(273), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(271), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8902] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(277), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(275), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8942] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(281), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(279), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [8982] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(285), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(283), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [9022] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(289), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(287), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [9062] = 20, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(293), 2, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + ACTIONS(291), 6, + ts_builtin_sym_end, + anon_sym_COMMA, + anon_sym_else, + anon_sym_SEMI, + anon_sym_RPAREN, + anon_sym_RBRACK, + [9136] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(297), 8, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + anon_sym_EQ, + anon_sym_COLON, + ACTIONS(295), 23, + ts_builtin_sym_end, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_else, + anon_sym_SEMI, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_RBRACK, + [9176] = 4, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(135), 2, + aux_sym_signed_integer_token1, + aux_sym_unsigned_integer_token1, + ACTIONS(133), 7, + sym_identifier, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(131), 19, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [9215] = 5, + STATE(204), 1, + sym__unit, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(299), 2, + sym_identifier, + sym_unit_quote, + ACTIONS(129), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(123), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [9255] = 4, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(135), 2, + aux_sym_signed_integer_token1, + aux_sym_unsigned_integer_token1, + ACTIONS(133), 7, + sym_identifier, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(131), 18, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_LPAREN, + [9293] = 20, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(303), 1, + anon_sym_EQ, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(301), 2, + anon_sym_COMMA, + anon_sym_RPAREN, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [9362] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(145), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(143), 20, + sym_identifier, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [9397] = 4, + ACTIONS(305), 1, + anon_sym_DOT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(139), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(137), 19, + sym_identifier, + sym_unit_quote, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [9434] = 6, + ACTIONS(125), 1, + sym_identifier, + ACTIONS(127), 1, + sym_unit_quote, + STATE(88), 1, + sym__unit, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(129), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(123), 17, + anon_sym_DOT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_LPAREN, + [9475] = 6, + ACTIONS(307), 1, + anon_sym_EQ, + ACTIONS(309), 1, + anon_sym_COLON, + STATE(277), 1, + sym_declaration_type, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(243), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(241), 17, + anon_sym_DOT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + anon_sym_RPAREN, + [9516] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(133), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(131), 20, + sym_identifier, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [9551] = 20, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(313), 1, + anon_sym_COMMA, + ACTIONS(315), 1, + anon_sym_RPAREN_GT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [9619] = 13, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 4, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 9, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + [9673] = 15, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(161), 9, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + [9731] = 16, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + ACTIONS(161), 5, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + [9791] = 17, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 4, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [9853] = 18, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 3, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_CARET_CARET, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [9917] = 19, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(219), 2, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [9983] = 20, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(347), 1, + anon_sym_COMMA, + ACTIONS(349), 1, + anon_sym_RBRACK, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10051] = 4, + ACTIONS(351), 1, + anon_sym_DOT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(139), 7, + sym_identifier, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(137), 17, + sym_unit_quote, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_LPAREN, + [10087] = 19, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(259), 2, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10153] = 19, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(263), 2, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10219] = 20, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + ACTIONS(353), 1, + anon_sym_COMMA, + ACTIONS(355), 1, + anon_sym_RPAREN_GT, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10287] = 4, + ACTIONS(357), 1, + anon_sym_DASH_GT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(243), 7, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(241), 17, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [10323] = 19, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(291), 2, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10389] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(359), 2, + anon_sym_COMMA, + anon_sym_RPAREN, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10455] = 4, + ACTIONS(151), 1, + anon_sym_DASH_GT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(149), 7, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(147), 17, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [10491] = 14, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 3, + anon_sym_PIPE, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 9, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + [10547] = 20, + ACTIONS(47), 1, + anon_sym_RBRACK, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(347), 1, + anon_sym_COMMA, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10615] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(361), 2, + anon_sym_COMMA, + anon_sym_RPAREN, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10681] = 20, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(347), 1, + anon_sym_COMMA, + ACTIONS(363), 1, + anon_sym_RBRACK, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10749] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(155), 7, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(153), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + anon_sym_DASH_GT, + [10783] = 20, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + ACTIONS(365), 1, + anon_sym_COMMA, + ACTIONS(367), 1, + anon_sym_RPAREN_GT, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [10851] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(159), 7, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(157), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + anon_sym_DASH_GT, + [10885] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(133), 7, + sym_identifier, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(131), 18, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_LPAREN, + [10919] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(145), 7, + sym_identifier, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(143), 18, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_LPAREN, + [10953] = 7, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 15, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + [10995] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(173), 7, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(171), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + anon_sym_DASH_GT, + [11029] = 19, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(267), 2, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [11095] = 20, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + ACTIONS(369), 1, + anon_sym_COMMA, + ACTIONS(371), 1, + anon_sym_RPAREN_GT, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [11163] = 10, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 5, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 13, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + [11211] = 8, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 14, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + [11255] = 11, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(167), 5, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 11, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + [11305] = 12, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 5, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 9, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + [11357] = 20, + ACTIONS(41), 1, + anon_sym_RBRACK, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(347), 1, + anon_sym_COMMA, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [11425] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(373), 1, + anon_sym_else, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [11490] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(285), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(283), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [11523] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(289), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(287), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [11556] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(247), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(245), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [11589] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + ACTIONS(397), 1, + anon_sym_PIPE_PIPE, + ACTIONS(399), 1, + anon_sym_CARET_CARET, + ACTIONS(401), 1, + anon_sym_then, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [11654] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(185), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(183), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [11687] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(267), 1, + anon_sym_then, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + ACTIONS(397), 1, + anon_sym_PIPE_PIPE, + ACTIONS(399), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [11752] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(181), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(179), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [11785] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(403), 1, + anon_sym_RPAREN, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [11850] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(199), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(197), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [11883] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(297), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(295), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [11916] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(405), 1, + anon_sym_COLON, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [11981] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(189), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(187), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [12014] = 10, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 5, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 12, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + [12061] = 8, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 13, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + [12104] = 11, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(167), 5, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 10, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + [12153] = 12, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 5, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 8, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + [12204] = 14, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(389), 1, + anon_sym_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 3, + anon_sym_PIPE, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 8, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + [12259] = 13, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(167), 4, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 8, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + [12312] = 15, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(167), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(161), 8, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + [12369] = 16, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 4, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [12428] = 17, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(161), 3, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [12489] = 18, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + ACTIONS(397), 1, + anon_sym_PIPE_PIPE, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(161), 2, + anon_sym_CARET_CARET, + anon_sym_then, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [12552] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(149), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(147), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [12585] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(219), 1, + anon_sym_then, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + ACTIONS(397), 1, + anon_sym_PIPE_PIPE, + ACTIONS(399), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [12650] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(227), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(225), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [12683] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(407), 1, + anon_sym_else, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [12748] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(235), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(233), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [12781] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(239), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(237), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [12814] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(409), 1, + anon_sym_SEMI, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [12879] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(251), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(249), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [12912] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(257), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(255), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [12945] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(259), 1, + anon_sym_then, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + ACTIONS(397), 1, + anon_sym_PIPE_PIPE, + ACTIONS(399), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13010] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(263), 1, + anon_sym_then, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + ACTIONS(397), 1, + anon_sym_PIPE_PIPE, + ACTIONS(399), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13075] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(273), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(271), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [13108] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(277), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(275), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [13141] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(281), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(279), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [13174] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(177), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(175), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [13207] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(347), 1, + anon_sym_COMMA, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13272] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(411), 1, + ts_builtin_sym_end, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13337] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(291), 1, + anon_sym_then, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + ACTIONS(397), 1, + anon_sym_PIPE_PIPE, + ACTIONS(399), 1, + anon_sym_CARET_CARET, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13402] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(413), 1, + anon_sym_RPAREN, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13467] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(415), 1, + anon_sym_COLON, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13532] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(417), 1, + anon_sym_else, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13597] = 19, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + ACTIONS(419), 1, + anon_sym_RPAREN_GT, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13662] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(421), 1, + anon_sym_COLON, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13727] = 19, + ACTIONS(311), 1, + anon_sym_DOT, + ACTIONS(317), 1, + anon_sym_COLON_COLON, + ACTIONS(321), 1, + anon_sym_STAR_STAR, + ACTIONS(323), 1, + anon_sym_STAR, + ACTIONS(325), 1, + anon_sym_SLASH, + ACTIONS(329), 1, + anon_sym_AMP, + ACTIONS(331), 1, + anon_sym_PIPE, + ACTIONS(333), 1, + anon_sym_CARET, + ACTIONS(339), 1, + anon_sym_AMP_AMP, + ACTIONS(341), 1, + anon_sym_PIPE_PIPE, + ACTIONS(343), 1, + anon_sym_CARET_CARET, + ACTIONS(345), 1, + anon_sym_LPAREN, + ACTIONS(423), 1, + anon_sym_RPAREN_GT, + STATE(172), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(319), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(327), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(335), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(337), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13792] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(243), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(241), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [13825] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(231), 6, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(229), 18, + anon_sym_DOT, + anon_sym_COMMA, + anon_sym_RPAREN_GT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_LPAREN, + [13858] = 4, + ACTIONS(425), 1, + anon_sym_DASH_GT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(243), 7, + anon_sym_DASH, + anon_sym_STAR, + anon_sym_AMP, + anon_sym_PIPE, + anon_sym_CARET, + anon_sym_GT, + anon_sym_LT, + ACTIONS(241), 16, + anon_sym_DOT, + anon_sym_COLON_COLON, + anon_sym_PLUS, + anon_sym_STAR_STAR, + anon_sym_SLASH, + anon_sym_LT_LT, + anon_sym_GT_GT, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_CARET_CARET, + anon_sym_then, + anon_sym_LPAREN, + [13893] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(427), 1, + anon_sym_COMMA, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [13958] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + ACTIONS(397), 1, + anon_sym_PIPE_PIPE, + ACTIONS(399), 1, + anon_sym_CARET_CARET, + ACTIONS(429), 1, + anon_sym_then, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14023] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(377), 1, + anon_sym_STAR_STAR, + ACTIONS(379), 1, + anon_sym_STAR, + ACTIONS(381), 1, + anon_sym_SLASH, + ACTIONS(385), 1, + anon_sym_AMP, + ACTIONS(387), 1, + anon_sym_PIPE, + ACTIONS(389), 1, + anon_sym_CARET, + ACTIONS(395), 1, + anon_sym_AMP_AMP, + ACTIONS(397), 1, + anon_sym_PIPE_PIPE, + ACTIONS(399), 1, + anon_sym_CARET_CARET, + ACTIONS(431), 1, + anon_sym_then, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(375), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(383), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(391), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(393), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14088] = 19, + ACTIONS(163), 1, + anon_sym_DOT, + ACTIONS(165), 1, + anon_sym_COLON_COLON, + ACTIONS(169), 1, + anon_sym_LPAREN, + ACTIONS(191), 1, + anon_sym_STAR_STAR, + ACTIONS(193), 1, + anon_sym_STAR, + ACTIONS(195), 1, + anon_sym_SLASH, + ACTIONS(205), 1, + anon_sym_AMP, + ACTIONS(207), 1, + anon_sym_CARET, + ACTIONS(209), 1, + anon_sym_PIPE, + ACTIONS(215), 1, + anon_sym_AMP_AMP, + ACTIONS(217), 1, + anon_sym_PIPE_PIPE, + ACTIONS(221), 1, + anon_sym_CARET_CARET, + ACTIONS(433), 1, + anon_sym_COMMA, + STATE(90), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(201), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(203), 2, + anon_sym_LT_LT, + anon_sym_GT_GT, + ACTIONS(211), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(213), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14153] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(435), 7, + sym_identifier, + sym_self, + aux_sym_base_ten_token1, + sym_true, + sym_false, + anon_sym_if, + anon_sym_let, + ACTIONS(88), 12, + sym_string, + aux_sym_octal_token1, + aux_sym_hex_token1, + aux_sym_binary_token1, + anon_sym_LT_LPAREN, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_BANG, + anon_sym_LPAREN, + anon_sym_LBRACK, + anon_sym_RBRACK, + anon_sym_LT_LT_LT, + [14181] = 5, + STATE(245), 1, + sym__unit, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(129), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(437), 2, + sym_identifier, + sym_unit_quote, + ACTIONS(123), 9, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR, + anon_sym_SLASH, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14208] = 4, + ACTIONS(439), 1, + anon_sym_DOT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(139), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(137), 11, + sym_identifier, + sym_unit_quote, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR, + anon_sym_SLASH, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14233] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(133), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(131), 12, + sym_identifier, + sym_unit_quote, + anon_sym_DOT, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR, + anon_sym_SLASH, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14256] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(443), 1, + aux_sym_base_ten_token1, + ACTIONS(447), 1, + anon_sym_LPAREN, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(249), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(445), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14290] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(443), 1, + aux_sym_base_ten_token1, + ACTIONS(447), 1, + anon_sym_LPAREN, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(254), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(445), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14324] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(451), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(449), 11, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR, + anon_sym_SLASH, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_RPAREN, + anon_sym_GT_GT_GT, + [14346] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(447), 1, + anon_sym_LPAREN, + ACTIONS(453), 1, + aux_sym_base_ten_token1, + STATE(222), 1, + sym__float, + STATE(223), 1, + sym_number, + STATE(244), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(455), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14380] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(443), 1, + aux_sym_base_ten_token1, + ACTIONS(447), 1, + anon_sym_LPAREN, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(231), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(445), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14414] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(447), 1, + anon_sym_LPAREN, + ACTIONS(453), 1, + aux_sym_base_ten_token1, + STATE(222), 1, + sym__float, + STATE(223), 1, + sym_number, + STATE(231), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(455), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14448] = 4, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(461), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(459), 10, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR, + anon_sym_SLASH, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_RPAREN, + anon_sym_GT_GT_GT, + [14472] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(443), 1, + aux_sym_base_ten_token1, + ACTIONS(447), 1, + anon_sym_LPAREN, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(247), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(445), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14506] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(465), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(463), 11, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR, + anon_sym_SLASH, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_RPAREN, + anon_sym_GT_GT_GT, + [14528] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(447), 1, + anon_sym_LPAREN, + ACTIONS(453), 1, + aux_sym_base_ten_token1, + STATE(222), 1, + sym__float, + STATE(223), 1, + sym_number, + STATE(243), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(455), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14562] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(443), 1, + aux_sym_base_ten_token1, + ACTIONS(447), 1, + anon_sym_LPAREN, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(251), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(445), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14596] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(447), 1, + anon_sym_LPAREN, + ACTIONS(453), 1, + aux_sym_base_ten_token1, + STATE(222), 1, + sym__float, + STATE(223), 1, + sym_number, + STATE(246), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(455), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14630] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(469), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(467), 11, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR, + anon_sym_SLASH, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + anon_sym_RPAREN, + anon_sym_GT_GT_GT, + [14652] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(145), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(143), 11, + sym_identifier, + sym_unit_quote, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR, + anon_sym_SLASH, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14674] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(443), 1, + aux_sym_base_ten_token1, + ACTIONS(447), 1, + anon_sym_LPAREN, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(250), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(445), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14708] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(443), 1, + aux_sym_base_ten_token1, + ACTIONS(447), 1, + anon_sym_LPAREN, + STATE(78), 1, + sym__float, + STATE(80), 1, + sym_number, + STATE(248), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(445), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14742] = 9, + ACTIONS(441), 1, + sym_identifier, + ACTIONS(447), 1, + anon_sym_LPAREN, + ACTIONS(453), 1, + aux_sym_base_ten_token1, + STATE(222), 1, + sym__float, + STATE(223), 1, + sym_number, + STATE(242), 1, + sym_constraint_set_expression, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(455), 2, + anon_sym_DASH, + anon_sym_PLUS, + STATE(233), 5, + sym_scalar, + sym_constraint_set_parenthesis, + sym_constraint_set_unary_expression, + sym_constraint_set_binary_expression, + sym_constraint_set_method_call, + [14776] = 7, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + STATE(226), 1, + sym__constraint_set_relation, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(471), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(473), 2, + anon_sym_STAR, + anon_sym_SLASH, + ACTIONS(475), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(477), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14805] = 7, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + STATE(235), 1, + sym__constraint_set_relation, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(471), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(473), 2, + anon_sym_STAR, + anon_sym_SLASH, + ACTIONS(479), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(481), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14834] = 5, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(461), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(473), 2, + anon_sym_STAR, + anon_sym_SLASH, + ACTIONS(459), 6, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14858] = 3, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(177), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(175), 9, + anon_sym_COLON_COLON, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_STAR, + anon_sym_SLASH, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14878] = 6, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(471), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(473), 2, + anon_sym_STAR, + anon_sym_SLASH, + ACTIONS(483), 2, + anon_sym_GT, + anon_sym_LT, + ACTIONS(485), 4, + anon_sym_GT_EQ, + anon_sym_EQ_EQ, + anon_sym_LT_EQ, + anon_sym_BANG_EQ, + [14904] = 5, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(485), 2, + anon_sym_RPAREN, + anon_sym_GT_GT_GT, + ACTIONS(487), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(489), 2, + anon_sym_STAR, + anon_sym_SLASH, + [14924] = 4, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(489), 2, + anon_sym_STAR, + anon_sym_SLASH, + ACTIONS(459), 4, + anon_sym_DASH, + anon_sym_PLUS, + anon_sym_RPAREN, + anon_sym_GT_GT_GT, + [14942] = 5, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + ACTIONS(491), 1, + anon_sym_RPAREN, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(487), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(489), 2, + anon_sym_STAR, + anon_sym_SLASH, + [14961] = 5, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + ACTIONS(493), 1, + anon_sym_RPAREN, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(487), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(489), 2, + anon_sym_STAR, + anon_sym_SLASH, + [14980] = 5, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + ACTIONS(495), 1, + anon_sym_GT_GT_GT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(487), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(489), 2, + anon_sym_STAR, + anon_sym_SLASH, + [14999] = 7, + ACTIONS(37), 1, + sym_varadic_dots, + ACTIONS(497), 1, + sym_identifier, + ACTIONS(499), 1, + anon_sym_RPAREN, + STATE(255), 1, + aux_sym_struct_definition_repeat1, + STATE(283), 1, + sym_struct_member, + STATE(300), 1, + sym__struct_final_element, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15022] = 7, + ACTIONS(37), 1, + sym_varadic_dots, + ACTIONS(497), 1, + sym_identifier, + ACTIONS(501), 1, + anon_sym_RPAREN, + STATE(255), 1, + aux_sym_struct_definition_repeat1, + STATE(283), 1, + sym_struct_member, + STATE(315), 1, + sym__struct_final_element, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15045] = 5, + ACTIONS(457), 1, + anon_sym_COLON_COLON, + ACTIONS(503), 1, + anon_sym_GT_GT_GT, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(487), 2, + anon_sym_DASH, + anon_sym_PLUS, + ACTIONS(489), 2, + anon_sym_STAR, + anon_sym_SLASH, + [15064] = 5, + ACTIONS(505), 1, + sym_identifier, + STATE(255), 1, + aux_sym_struct_definition_repeat1, + STATE(311), 1, + sym_struct_member, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(508), 2, + anon_sym_RPAREN, + sym_varadic_dots, + [15082] = 4, + ACTIONS(510), 1, + sym_identifier, + ACTIONS(512), 1, + anon_sym_in, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + STATE(259), 2, + sym_let_in_assignment, + aux_sym_let_in_repeat1, + [15097] = 4, + ACTIONS(510), 1, + sym_identifier, + ACTIONS(514), 1, + anon_sym_in, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + STATE(262), 2, + sym_let_in_assignment, + aux_sym_let_in_repeat1, + [15112] = 4, + ACTIONS(510), 1, + sym_identifier, + ACTIONS(516), 1, + anon_sym_in, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + STATE(257), 2, + sym_let_in_assignment, + aux_sym_let_in_repeat1, + [15127] = 4, + ACTIONS(510), 1, + sym_identifier, + ACTIONS(518), 1, + anon_sym_in, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + STATE(262), 2, + sym_let_in_assignment, + aux_sym_let_in_repeat1, + [15142] = 4, + ACTIONS(510), 1, + sym_identifier, + ACTIONS(520), 1, + anon_sym_in, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + STATE(262), 2, + sym_let_in_assignment, + aux_sym_let_in_repeat1, + [15157] = 4, + ACTIONS(510), 1, + sym_identifier, + ACTIONS(522), 1, + anon_sym_in, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + STATE(260), 2, + sym_let_in_assignment, + aux_sym_let_in_repeat1, + [15172] = 4, + ACTIONS(524), 1, + sym_identifier, + ACTIONS(527), 1, + anon_sym_in, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + STATE(262), 2, + sym_let_in_assignment, + aux_sym_let_in_repeat1, + [15187] = 2, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(508), 3, + sym_identifier, + anon_sym_RPAREN, + sym_varadic_dots, + [15197] = 4, + ACTIONS(529), 1, + anon_sym_COMMA, + ACTIONS(532), 1, + anon_sym_RPAREN, + STATE(264), 1, + aux_sym_dictionary_construction_repeat1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15211] = 4, + ACTIONS(534), 1, + sym_identifier, + ACTIONS(536), 1, + anon_sym_RPAREN, + STATE(286), 1, + sym_dictionary_member_assignment, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15225] = 4, + ACTIONS(538), 1, + anon_sym_COMMA, + ACTIONS(540), 1, + anon_sym_RPAREN, + STATE(264), 1, + aux_sym_dictionary_construction_repeat1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15239] = 4, + ACTIONS(534), 1, + sym_identifier, + ACTIONS(540), 1, + anon_sym_RPAREN, + STATE(286), 1, + sym_dictionary_member_assignment, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15253] = 4, + ACTIONS(534), 1, + sym_identifier, + ACTIONS(542), 1, + anon_sym_RPAREN, + STATE(286), 1, + sym_dictionary_member_assignment, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15267] = 4, + ACTIONS(534), 1, + sym_identifier, + ACTIONS(544), 1, + anon_sym_RPAREN, + STATE(275), 1, + sym_dictionary_member_assignment, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15281] = 4, + ACTIONS(546), 1, + sym_identifier, + STATE(280), 1, + aux_sym_constraint_set_fields_repeat1, + STATE(320), 1, + sym_constraint_set_fields, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15295] = 4, + ACTIONS(534), 1, + sym_identifier, + ACTIONS(548), 1, + anon_sym_RPAREN, + STATE(273), 1, + sym_dictionary_member_assignment, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15309] = 4, + ACTIONS(542), 1, + anon_sym_RPAREN, + ACTIONS(550), 1, + anon_sym_COMMA, + STATE(264), 1, + aux_sym_dictionary_construction_repeat1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15323] = 4, + ACTIONS(552), 1, + anon_sym_COMMA, + ACTIONS(554), 1, + anon_sym_RPAREN, + STATE(272), 1, + aux_sym_dictionary_construction_repeat1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15337] = 4, + ACTIONS(534), 1, + sym_identifier, + ACTIONS(556), 1, + anon_sym_RPAREN, + STATE(286), 1, + sym_dictionary_member_assignment, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15351] = 4, + ACTIONS(558), 1, + anon_sym_COMMA, + ACTIONS(560), 1, + anon_sym_RPAREN, + STATE(266), 1, + aux_sym_dictionary_construction_repeat1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15365] = 4, + ACTIONS(546), 1, + sym_identifier, + STATE(280), 1, + aux_sym_constraint_set_fields_repeat1, + STATE(301), 1, + sym_constraint_set_fields, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15379] = 3, + ACTIONS(564), 1, + anon_sym_EQ, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(562), 2, + anon_sym_COMMA, + anon_sym_RPAREN, + [15391] = 2, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(566), 2, + aux_sym_signed_integer_token1, + aux_sym_unsigned_integer_token1, + [15400] = 3, + ACTIONS(568), 1, + aux_sym_base_ten_token1, + STATE(157), 1, + sym_number, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15411] = 3, + ACTIONS(570), 1, + sym_identifier, + STATE(299), 1, + aux_sym_constraint_set_fields_repeat1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15422] = 3, + ACTIONS(345), 1, + anon_sym_LPAREN, + STATE(195), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15433] = 2, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(572), 2, + sym_identifier, + anon_sym_in, + [15442] = 3, + ACTIONS(574), 1, + anon_sym_COMMA, + ACTIONS(576), 1, + anon_sym_RPAREN, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15453] = 2, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(578), 2, + aux_sym_signed_integer_token1, + aux_sym_unsigned_integer_token1, + [15462] = 3, + ACTIONS(534), 1, + sym_identifier, + STATE(286), 1, + sym_dictionary_member_assignment, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15473] = 2, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(532), 2, + anon_sym_COMMA, + anon_sym_RPAREN, + [15482] = 3, + ACTIONS(576), 1, + anon_sym_RPAREN, + ACTIONS(580), 1, + anon_sym_COMMA, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15493] = 3, + ACTIONS(582), 1, + aux_sym_base_ten_token1, + STATE(128), 1, + sym_number, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15504] = 3, + ACTIONS(584), 1, + anon_sym_COMMA, + ACTIONS(586), 1, + anon_sym_COLON, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15515] = 3, + ACTIONS(588), 1, + anon_sym_COLON, + STATE(277), 1, + sym_declaration_type, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15526] = 3, + ACTIONS(453), 1, + aux_sym_base_ten_token1, + STATE(238), 1, + sym_number, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15537] = 3, + ACTIONS(443), 1, + aux_sym_base_ten_token1, + STATE(81), 1, + sym_number, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15548] = 2, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(590), 2, + aux_sym_signed_integer_token1, + aux_sym_unsigned_integer_token1, + [15557] = 3, + ACTIONS(584), 1, + anon_sym_COMMA, + ACTIONS(592), 1, + anon_sym_COLON, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15568] = 3, + ACTIONS(594), 1, + aux_sym_signed_integer_token1, + ACTIONS(596), 1, + aux_sym_unsigned_integer_token1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15579] = 2, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + ACTIONS(598), 2, + aux_sym_signed_integer_token1, + aux_sym_unsigned_integer_token1, + [15588] = 3, + ACTIONS(169), 1, + anon_sym_LPAREN, + STATE(108), 1, + sym_dictionary_construction, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15599] = 3, + ACTIONS(600), 1, + aux_sym_signed_integer_token1, + ACTIONS(602), 1, + aux_sym_unsigned_integer_token1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15610] = 3, + ACTIONS(604), 1, + sym_identifier, + STATE(299), 1, + aux_sym_constraint_set_fields_repeat1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15621] = 2, + ACTIONS(607), 1, + anon_sym_RPAREN, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15629] = 2, + ACTIONS(609), 1, + anon_sym_COLON, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15637] = 2, + ACTIONS(611), 1, + sym_identifier, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15645] = 2, + ACTIONS(613), 1, + aux_sym_hex_token2, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15653] = 2, + ACTIONS(615), 1, + aux_sym_binary_token2, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15661] = 2, + ACTIONS(617), 1, + aux_sym_base_ten_token1, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15669] = 2, + ACTIONS(619), 1, + sym_identifier, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15677] = 2, + ACTIONS(621), 1, + anon_sym_LPAREN, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15685] = 2, + ACTIONS(623), 1, + anon_sym_EQ, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15693] = 2, + ACTIONS(625), 1, + sym_identifier, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15701] = 2, + ACTIONS(627), 1, + anon_sym_RPAREN, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15709] = 2, + ACTIONS(574), 1, + anon_sym_COMMA, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15717] = 2, + ACTIONS(629), 1, + sym_identifier, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15725] = 2, + ACTIONS(584), 1, + anon_sym_COMMA, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15733] = 2, + ACTIONS(631), 1, + anon_sym_RPAREN, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15741] = 2, + ACTIONS(633), 1, + anon_sym_RPAREN, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15749] = 2, + ACTIONS(635), 1, + sym_identifier, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15757] = 2, + ACTIONS(637), 1, + sym_identifier, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15765] = 2, + ACTIONS(639), 1, + anon_sym_EQ, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15773] = 2, + ACTIONS(641), 1, + ts_builtin_sym_end, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15781] = 2, + ACTIONS(643), 1, + anon_sym_COLON, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, + [15789] = 2, + ACTIONS(645), 1, + anon_sym_RPAREN, + ACTIONS(3), 2, + sym_comment, + sym__whitespace, +}; + +static const uint32_t ts_small_parse_table_map[] = { + [SMALL_STATE(4)] = 0, + [SMALL_STATE(5)] = 99, + [SMALL_STATE(6)] = 198, + [SMALL_STATE(7)] = 297, + [SMALL_STATE(8)] = 396, + [SMALL_STATE(9)] = 495, + [SMALL_STATE(10)] = 588, + [SMALL_STATE(11)] = 681, + [SMALL_STATE(12)] = 774, + [SMALL_STATE(13)] = 867, + [SMALL_STATE(14)] = 960, + [SMALL_STATE(15)] = 1053, + [SMALL_STATE(16)] = 1146, + [SMALL_STATE(17)] = 1239, + [SMALL_STATE(18)] = 1332, + [SMALL_STATE(19)] = 1425, + [SMALL_STATE(20)] = 1518, + [SMALL_STATE(21)] = 1611, + [SMALL_STATE(22)] = 1704, + [SMALL_STATE(23)] = 1797, + [SMALL_STATE(24)] = 1890, + [SMALL_STATE(25)] = 1983, + [SMALL_STATE(26)] = 2076, + [SMALL_STATE(27)] = 2169, + [SMALL_STATE(28)] = 2262, + [SMALL_STATE(29)] = 2355, + [SMALL_STATE(30)] = 2448, + [SMALL_STATE(31)] = 2541, + [SMALL_STATE(32)] = 2634, + [SMALL_STATE(33)] = 2727, + [SMALL_STATE(34)] = 2820, + [SMALL_STATE(35)] = 2913, + [SMALL_STATE(36)] = 3006, + [SMALL_STATE(37)] = 3099, + [SMALL_STATE(38)] = 3192, + [SMALL_STATE(39)] = 3285, + [SMALL_STATE(40)] = 3378, + [SMALL_STATE(41)] = 3471, + [SMALL_STATE(42)] = 3564, + [SMALL_STATE(43)] = 3657, + [SMALL_STATE(44)] = 3750, + [SMALL_STATE(45)] = 3843, + [SMALL_STATE(46)] = 3936, + [SMALL_STATE(47)] = 4029, + [SMALL_STATE(48)] = 4122, + [SMALL_STATE(49)] = 4215, + [SMALL_STATE(50)] = 4308, + [SMALL_STATE(51)] = 4401, + [SMALL_STATE(52)] = 4494, + [SMALL_STATE(53)] = 4587, + [SMALL_STATE(54)] = 4680, + [SMALL_STATE(55)] = 4773, + [SMALL_STATE(56)] = 4866, + [SMALL_STATE(57)] = 4959, + [SMALL_STATE(58)] = 5052, + [SMALL_STATE(59)] = 5145, + [SMALL_STATE(60)] = 5238, + [SMALL_STATE(61)] = 5331, + [SMALL_STATE(62)] = 5424, + [SMALL_STATE(63)] = 5517, + [SMALL_STATE(64)] = 5610, + [SMALL_STATE(65)] = 5703, + [SMALL_STATE(66)] = 5796, + [SMALL_STATE(67)] = 5889, + [SMALL_STATE(68)] = 5982, + [SMALL_STATE(69)] = 6075, + [SMALL_STATE(70)] = 6168, + [SMALL_STATE(71)] = 6261, + [SMALL_STATE(72)] = 6354, + [SMALL_STATE(73)] = 6447, + [SMALL_STATE(74)] = 6540, + [SMALL_STATE(75)] = 6633, + [SMALL_STATE(76)] = 6726, + [SMALL_STATE(77)] = 6819, + [SMALL_STATE(78)] = 6912, + [SMALL_STATE(79)] = 6961, + [SMALL_STATE(80)] = 7006, + [SMALL_STATE(81)] = 7050, + [SMALL_STATE(82)] = 7092, + [SMALL_STATE(83)] = 7134, + [SMALL_STATE(84)] = 7177, + [SMALL_STATE(85)] = 7218, + [SMALL_STATE(86)] = 7259, + [SMALL_STATE(87)] = 7308, + [SMALL_STATE(88)] = 7349, + [SMALL_STATE(89)] = 7390, + [SMALL_STATE(90)] = 7430, + [SMALL_STATE(91)] = 7470, + [SMALL_STATE(92)] = 7510, + [SMALL_STATE(93)] = 7564, + [SMALL_STATE(94)] = 7604, + [SMALL_STATE(95)] = 7654, + [SMALL_STATE(96)] = 7710, + [SMALL_STATE(97)] = 7768, + [SMALL_STATE(98)] = 7830, + [SMALL_STATE(99)] = 7890, + [SMALL_STATE(100)] = 7954, + [SMALL_STATE(101)] = 8022, + [SMALL_STATE(102)] = 8092, + [SMALL_STATE(103)] = 8164, + [SMALL_STATE(104)] = 8204, + [SMALL_STATE(105)] = 8278, + [SMALL_STATE(106)] = 8318, + [SMALL_STATE(107)] = 8358, + [SMALL_STATE(108)] = 8398, + [SMALL_STATE(109)] = 8438, + [SMALL_STATE(110)] = 8478, + [SMALL_STATE(111)] = 8518, + [SMALL_STATE(112)] = 8558, + [SMALL_STATE(113)] = 8600, + [SMALL_STATE(114)] = 8640, + [SMALL_STATE(115)] = 8714, + [SMALL_STATE(116)] = 8788, + [SMALL_STATE(117)] = 8862, + [SMALL_STATE(118)] = 8902, + [SMALL_STATE(119)] = 8942, + [SMALL_STATE(120)] = 8982, + [SMALL_STATE(121)] = 9022, + [SMALL_STATE(122)] = 9062, + [SMALL_STATE(123)] = 9136, + [SMALL_STATE(124)] = 9176, + [SMALL_STATE(125)] = 9215, + [SMALL_STATE(126)] = 9255, + [SMALL_STATE(127)] = 9293, + [SMALL_STATE(128)] = 9362, + [SMALL_STATE(129)] = 9397, + [SMALL_STATE(130)] = 9434, + [SMALL_STATE(131)] = 9475, + [SMALL_STATE(132)] = 9516, + [SMALL_STATE(133)] = 9551, + [SMALL_STATE(134)] = 9619, + [SMALL_STATE(135)] = 9673, + [SMALL_STATE(136)] = 9731, + [SMALL_STATE(137)] = 9791, + [SMALL_STATE(138)] = 9853, + [SMALL_STATE(139)] = 9917, + [SMALL_STATE(140)] = 9983, + [SMALL_STATE(141)] = 10051, + [SMALL_STATE(142)] = 10087, + [SMALL_STATE(143)] = 10153, + [SMALL_STATE(144)] = 10219, + [SMALL_STATE(145)] = 10287, + [SMALL_STATE(146)] = 10323, + [SMALL_STATE(147)] = 10389, + [SMALL_STATE(148)] = 10455, + [SMALL_STATE(149)] = 10491, + [SMALL_STATE(150)] = 10547, + [SMALL_STATE(151)] = 10615, + [SMALL_STATE(152)] = 10681, + [SMALL_STATE(153)] = 10749, + [SMALL_STATE(154)] = 10783, + [SMALL_STATE(155)] = 10851, + [SMALL_STATE(156)] = 10885, + [SMALL_STATE(157)] = 10919, + [SMALL_STATE(158)] = 10953, + [SMALL_STATE(159)] = 10995, + [SMALL_STATE(160)] = 11029, + [SMALL_STATE(161)] = 11095, + [SMALL_STATE(162)] = 11163, + [SMALL_STATE(163)] = 11211, + [SMALL_STATE(164)] = 11255, + [SMALL_STATE(165)] = 11305, + [SMALL_STATE(166)] = 11357, + [SMALL_STATE(167)] = 11425, + [SMALL_STATE(168)] = 11490, + [SMALL_STATE(169)] = 11523, + [SMALL_STATE(170)] = 11556, + [SMALL_STATE(171)] = 11589, + [SMALL_STATE(172)] = 11654, + [SMALL_STATE(173)] = 11687, + [SMALL_STATE(174)] = 11752, + [SMALL_STATE(175)] = 11785, + [SMALL_STATE(176)] = 11850, + [SMALL_STATE(177)] = 11883, + [SMALL_STATE(178)] = 11916, + [SMALL_STATE(179)] = 11981, + [SMALL_STATE(180)] = 12014, + [SMALL_STATE(181)] = 12061, + [SMALL_STATE(182)] = 12104, + [SMALL_STATE(183)] = 12153, + [SMALL_STATE(184)] = 12204, + [SMALL_STATE(185)] = 12259, + [SMALL_STATE(186)] = 12312, + [SMALL_STATE(187)] = 12369, + [SMALL_STATE(188)] = 12428, + [SMALL_STATE(189)] = 12489, + [SMALL_STATE(190)] = 12552, + [SMALL_STATE(191)] = 12585, + [SMALL_STATE(192)] = 12650, + [SMALL_STATE(193)] = 12683, + [SMALL_STATE(194)] = 12748, + [SMALL_STATE(195)] = 12781, + [SMALL_STATE(196)] = 12814, + [SMALL_STATE(197)] = 12879, + [SMALL_STATE(198)] = 12912, + [SMALL_STATE(199)] = 12945, + [SMALL_STATE(200)] = 13010, + [SMALL_STATE(201)] = 13075, + [SMALL_STATE(202)] = 13108, + [SMALL_STATE(203)] = 13141, + [SMALL_STATE(204)] = 13174, + [SMALL_STATE(205)] = 13207, + [SMALL_STATE(206)] = 13272, + [SMALL_STATE(207)] = 13337, + [SMALL_STATE(208)] = 13402, + [SMALL_STATE(209)] = 13467, + [SMALL_STATE(210)] = 13532, + [SMALL_STATE(211)] = 13597, + [SMALL_STATE(212)] = 13662, + [SMALL_STATE(213)] = 13727, + [SMALL_STATE(214)] = 13792, + [SMALL_STATE(215)] = 13825, + [SMALL_STATE(216)] = 13858, + [SMALL_STATE(217)] = 13893, + [SMALL_STATE(218)] = 13958, + [SMALL_STATE(219)] = 14023, + [SMALL_STATE(220)] = 14088, + [SMALL_STATE(221)] = 14153, + [SMALL_STATE(222)] = 14181, + [SMALL_STATE(223)] = 14208, + [SMALL_STATE(224)] = 14233, + [SMALL_STATE(225)] = 14256, + [SMALL_STATE(226)] = 14290, + [SMALL_STATE(227)] = 14324, + [SMALL_STATE(228)] = 14346, + [SMALL_STATE(229)] = 14380, + [SMALL_STATE(230)] = 14414, + [SMALL_STATE(231)] = 14448, + [SMALL_STATE(232)] = 14472, + [SMALL_STATE(233)] = 14506, + [SMALL_STATE(234)] = 14528, + [SMALL_STATE(235)] = 14562, + [SMALL_STATE(236)] = 14596, + [SMALL_STATE(237)] = 14630, + [SMALL_STATE(238)] = 14652, + [SMALL_STATE(239)] = 14674, + [SMALL_STATE(240)] = 14708, + [SMALL_STATE(241)] = 14742, + [SMALL_STATE(242)] = 14776, + [SMALL_STATE(243)] = 14805, + [SMALL_STATE(244)] = 14834, + [SMALL_STATE(245)] = 14858, + [SMALL_STATE(246)] = 14878, + [SMALL_STATE(247)] = 14904, + [SMALL_STATE(248)] = 14924, + [SMALL_STATE(249)] = 14942, + [SMALL_STATE(250)] = 14961, + [SMALL_STATE(251)] = 14980, + [SMALL_STATE(252)] = 14999, + [SMALL_STATE(253)] = 15022, + [SMALL_STATE(254)] = 15045, + [SMALL_STATE(255)] = 15064, + [SMALL_STATE(256)] = 15082, + [SMALL_STATE(257)] = 15097, + [SMALL_STATE(258)] = 15112, + [SMALL_STATE(259)] = 15127, + [SMALL_STATE(260)] = 15142, + [SMALL_STATE(261)] = 15157, + [SMALL_STATE(262)] = 15172, + [SMALL_STATE(263)] = 15187, + [SMALL_STATE(264)] = 15197, + [SMALL_STATE(265)] = 15211, + [SMALL_STATE(266)] = 15225, + [SMALL_STATE(267)] = 15239, + [SMALL_STATE(268)] = 15253, + [SMALL_STATE(269)] = 15267, + [SMALL_STATE(270)] = 15281, + [SMALL_STATE(271)] = 15295, + [SMALL_STATE(272)] = 15309, + [SMALL_STATE(273)] = 15323, + [SMALL_STATE(274)] = 15337, + [SMALL_STATE(275)] = 15351, + [SMALL_STATE(276)] = 15365, + [SMALL_STATE(277)] = 15379, + [SMALL_STATE(278)] = 15391, + [SMALL_STATE(279)] = 15400, + [SMALL_STATE(280)] = 15411, + [SMALL_STATE(281)] = 15422, + [SMALL_STATE(282)] = 15433, + [SMALL_STATE(283)] = 15442, + [SMALL_STATE(284)] = 15453, + [SMALL_STATE(285)] = 15462, + [SMALL_STATE(286)] = 15473, + [SMALL_STATE(287)] = 15482, + [SMALL_STATE(288)] = 15493, + [SMALL_STATE(289)] = 15504, + [SMALL_STATE(290)] = 15515, + [SMALL_STATE(291)] = 15526, + [SMALL_STATE(292)] = 15537, + [SMALL_STATE(293)] = 15548, + [SMALL_STATE(294)] = 15557, + [SMALL_STATE(295)] = 15568, + [SMALL_STATE(296)] = 15579, + [SMALL_STATE(297)] = 15588, + [SMALL_STATE(298)] = 15599, + [SMALL_STATE(299)] = 15610, + [SMALL_STATE(300)] = 15621, + [SMALL_STATE(301)] = 15629, + [SMALL_STATE(302)] = 15637, + [SMALL_STATE(303)] = 15645, + [SMALL_STATE(304)] = 15653, + [SMALL_STATE(305)] = 15661, + [SMALL_STATE(306)] = 15669, + [SMALL_STATE(307)] = 15677, + [SMALL_STATE(308)] = 15685, + [SMALL_STATE(309)] = 15693, + [SMALL_STATE(310)] = 15701, + [SMALL_STATE(311)] = 15709, + [SMALL_STATE(312)] = 15717, + [SMALL_STATE(313)] = 15725, + [SMALL_STATE(314)] = 15733, + [SMALL_STATE(315)] = 15741, + [SMALL_STATE(316)] = 15749, + [SMALL_STATE(317)] = 15757, + [SMALL_STATE(318)] = 15765, + [SMALL_STATE(319)] = 15773, + [SMALL_STATE(320)] = 15781, + [SMALL_STATE(321)] = 15789, +}; + +static const TSParseActionEntry ts_parse_actions[] = { + [0] = {.entry = {.count = 0, .reusable = false}}, + [1] = {.entry = {.count = 1, .reusable = false}}, RECOVER(), + [3] = {.entry = {.count = 1, .reusable = true}}, SHIFT_EXTRA(), + [5] = {.entry = {.count = 1, .reusable = false}}, SHIFT(109), + [7] = {.entry = {.count = 1, .reusable = true}}, SHIFT(109), + [9] = {.entry = {.count = 1, .reusable = false}}, SHIFT(79), + [11] = {.entry = {.count = 1, .reusable = true}}, SHIFT(305), + [13] = {.entry = {.count = 1, .reusable = true}}, SHIFT(303), + [15] = {.entry = {.count = 1, .reusable = true}}, SHIFT(304), + [17] = {.entry = {.count = 1, .reusable = true}}, SHIFT(46), + [19] = {.entry = {.count = 1, .reusable = false}}, SHIFT(106), + [21] = {.entry = {.count = 1, .reusable = true}}, SHIFT(65), + [23] = {.entry = {.count = 1, .reusable = false}}, SHIFT(27), + [25] = {.entry = {.count = 1, .reusable = false}}, SHIFT(256), + [27] = {.entry = {.count = 1, .reusable = true}}, SHIFT(2), + [29] = {.entry = {.count = 1, .reusable = true}}, SHIFT(5), + [31] = {.entry = {.count = 1, .reusable = true}}, SHIFT(276), + [33] = {.entry = {.count = 1, .reusable = false}}, SHIFT(131), + [35] = {.entry = {.count = 1, .reusable = true}}, SHIFT(83), + [37] = {.entry = {.count = 1, .reusable = true}}, SHIFT(287), + [39] = {.entry = {.count = 1, .reusable = true}}, SHIFT(148), + [41] = {.entry = {.count = 1, .reusable = true}}, SHIFT(123), + [43] = {.entry = {.count = 1, .reusable = true}}, SHIFT(110), + [45] = {.entry = {.count = 1, .reusable = true}}, SHIFT(170), + [47] = {.entry = {.count = 1, .reusable = true}}, SHIFT(177), + [49] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(109), + [52] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(109), + [55] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(79), + [58] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(305), + [61] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(303), + [64] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(304), + [67] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(46), + [70] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(106), + [73] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(65), + [76] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(27), + [79] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(256), + [82] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(2), + [85] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(5), + [88] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), + [90] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), SHIFT_REPEAT(276), + [93] = {.entry = {.count = 1, .reusable = false}}, SHIFT(126), + [95] = {.entry = {.count = 1, .reusable = true}}, SHIFT(33), + [97] = {.entry = {.count = 1, .reusable = false}}, SHIFT(75), + [99] = {.entry = {.count = 1, .reusable = false}}, SHIFT(261), + [101] = {.entry = {.count = 1, .reusable = false}}, SHIFT(214), + [103] = {.entry = {.count = 1, .reusable = true}}, SHIFT(214), + [105] = {.entry = {.count = 1, .reusable = false}}, SHIFT(124), + [107] = {.entry = {.count = 1, .reusable = true}}, SHIFT(74), + [109] = {.entry = {.count = 1, .reusable = false}}, SHIFT(215), + [111] = {.entry = {.count = 1, .reusable = true}}, SHIFT(48), + [113] = {.entry = {.count = 1, .reusable = false}}, SHIFT(76), + [115] = {.entry = {.count = 1, .reusable = false}}, SHIFT(258), + [117] = {.entry = {.count = 1, .reusable = true}}, SHIFT(3), + [119] = {.entry = {.count = 1, .reusable = true}}, SHIFT(6), + [121] = {.entry = {.count = 1, .reusable = true}}, SHIFT(270), + [123] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_scalar, 1, 0, 2), + [125] = {.entry = {.count = 1, .reusable = false}}, SHIFT(88), + [127] = {.entry = {.count = 1, .reusable = true}}, SHIFT(88), + [129] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_scalar, 1, 0, 2), + [131] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_number, 1, 0, 0), + [133] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_number, 1, 0, 0), + [135] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_base_ten, 1, 0, 0), + [137] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__float, 1, 0, 1), + [139] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__float, 1, 0, 1), + [141] = {.entry = {.count = 1, .reusable = true}}, SHIFT(292), + [143] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__float, 3, 0, 12), + [145] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__float, 3, 0, 12), + [147] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_dictionary_construction, 2, 0, 0), + [149] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_dictionary_construction, 2, 0, 0), + [151] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_struct_definition, 2, 0, 0), + [153] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_struct_definition, 3, 0, 9), + [155] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_struct_definition, 3, 0, 9), + [157] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_struct_definition, 3, 0, 11), + [159] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_struct_definition, 3, 0, 11), + [161] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_binary_expression, 3, 0, 14), + [163] = {.entry = {.count = 1, .reusable = true}}, SHIFT(316), + [165] = {.entry = {.count = 1, .reusable = true}}, SHIFT(309), + [167] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_binary_expression, 3, 0, 14), + [169] = {.entry = {.count = 1, .reusable = true}}, SHIFT(271), + [171] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_struct_definition, 4, 0, 18), + [173] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_struct_definition, 4, 0, 18), + [175] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_scalar, 2, 0, 5), + [177] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_scalar, 2, 0, 5), + [179] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_parenthesis, 3, 0, 0), + [181] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_parenthesis, 3, 0, 0), + [183] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_function_call, 2, 0, 6), + [185] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_function_call, 2, 0, 6), + [187] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_member_access, 3, 0, 13), + [189] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_member_access, 3, 0, 13), + [191] = {.entry = {.count = 1, .reusable = true}}, SHIFT(10), + [193] = {.entry = {.count = 1, .reusable = false}}, SHIFT(11), + [195] = {.entry = {.count = 1, .reusable = true}}, SHIFT(11), + [197] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_dictionary_construction, 3, 0, 10), + [199] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_dictionary_construction, 3, 0, 10), + [201] = {.entry = {.count = 1, .reusable = true}}, SHIFT(66), + [203] = {.entry = {.count = 1, .reusable = true}}, SHIFT(12), + [205] = {.entry = {.count = 1, .reusable = false}}, SHIFT(13), + [207] = {.entry = {.count = 1, .reusable = false}}, SHIFT(15), + [209] = {.entry = {.count = 1, .reusable = false}}, SHIFT(14), + [211] = {.entry = {.count = 1, .reusable = false}}, SHIFT(16), + [213] = {.entry = {.count = 1, .reusable = true}}, SHIFT(16), + [215] = {.entry = {.count = 1, .reusable = true}}, SHIFT(17), + [217] = {.entry = {.count = 1, .reusable = true}}, SHIFT(18), + [219] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_let_in, 4, 0, 15), + [221] = {.entry = {.count = 1, .reusable = true}}, SHIFT(19), + [223] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_let_in, 4, 0, 15), + [225] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_dictionary_construction, 4, 0, 17), + [227] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_dictionary_construction, 4, 0, 17), + [229] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_boolean, 1, 0, 0), + [231] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_boolean, 1, 0, 0), + [233] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_list, 4, 0, 0), + [235] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_list, 4, 0, 0), + [237] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_method_call, 4, 0, 19), + [239] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_method_call, 4, 0, 19), + [241] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_expression, 1, 0, 0), + [243] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_expression, 1, 0, 0), + [245] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_list, 2, 0, 0), + [247] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_list, 2, 0, 0), + [249] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_vector2, 5, 0, 20), + [251] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_vector2, 5, 0, 20), + [253] = {.entry = {.count = 1, .reusable = true}}, SHIFT(20), + [255] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_dictionary_construction, 5, 0, 23), + [257] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_dictionary_construction, 5, 0, 23), + [259] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_closure_definition, 5, 0, 24), + [261] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_closure_definition, 5, 0, 24), + [263] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if, 6, 0, 25), + [265] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if, 6, 0, 25), + [267] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_let_in, 3, 0, 7), + [269] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_let_in, 3, 0, 7), + [271] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_vector3, 7, 0, 26), + [273] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_vector3, 7, 0, 26), + [275] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_constraint_set, 7, 0, 27), + [277] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_constraint_set, 7, 0, 27), + [279] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_vector4, 9, 0, 28), + [281] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_vector4, 9, 0, 28), + [283] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_signed_integer, 2, 0, 4), + [285] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_signed_integer, 2, 0, 4), + [287] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_unsigned_integer, 2, 0, 4), + [289] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_unsigned_integer, 2, 0, 4), + [291] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_unary_expression, 2, 0, 3), + [293] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_unary_expression, 2, 0, 3), + [295] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_list, 3, 0, 0), + [297] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_list, 3, 0, 0), + [299] = {.entry = {.count = 1, .reusable = true}}, SHIFT(204), + [301] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_declaration_type, 2, 0, 0), + [303] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_declaration_type, 2, 0, 0), + [305] = {.entry = {.count = 1, .reusable = true}}, SHIFT(288), + [307] = {.entry = {.count = 1, .reusable = false}}, SHIFT(25), + [309] = {.entry = {.count = 1, .reusable = false}}, SHIFT(26), + [311] = {.entry = {.count = 1, .reusable = true}}, SHIFT(312), + [313] = {.entry = {.count = 1, .reusable = true}}, SHIFT(70), + [315] = {.entry = {.count = 1, .reusable = true}}, SHIFT(197), + [317] = {.entry = {.count = 1, .reusable = true}}, SHIFT(302), + [319] = {.entry = {.count = 1, .reusable = true}}, SHIFT(50), + [321] = {.entry = {.count = 1, .reusable = true}}, SHIFT(51), + [323] = {.entry = {.count = 1, .reusable = false}}, SHIFT(52), + [325] = {.entry = {.count = 1, .reusable = true}}, SHIFT(52), + [327] = {.entry = {.count = 1, .reusable = true}}, SHIFT(53), + [329] = {.entry = {.count = 1, .reusable = false}}, SHIFT(54), + [331] = {.entry = {.count = 1, .reusable = false}}, SHIFT(55), + [333] = {.entry = {.count = 1, .reusable = false}}, SHIFT(56), + [335] = {.entry = {.count = 1, .reusable = false}}, SHIFT(57), + [337] = {.entry = {.count = 1, .reusable = true}}, SHIFT(57), + [339] = {.entry = {.count = 1, .reusable = true}}, SHIFT(58), + [341] = {.entry = {.count = 1, .reusable = true}}, SHIFT(59), + [343] = {.entry = {.count = 1, .reusable = true}}, SHIFT(60), + [345] = {.entry = {.count = 1, .reusable = true}}, SHIFT(269), + [347] = {.entry = {.count = 1, .reusable = true}}, SHIFT(221), + [349] = {.entry = {.count = 1, .reusable = true}}, SHIFT(107), + [351] = {.entry = {.count = 1, .reusable = true}}, SHIFT(279), + [353] = {.entry = {.count = 1, .reusable = true}}, SHIFT(30), + [355] = {.entry = {.count = 1, .reusable = true}}, SHIFT(111), + [357] = {.entry = {.count = 1, .reusable = true}}, SHIFT(72), + [359] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_struct_member, 4, 0, 22), + [361] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_dictionary_member_assignment, 3, 0, 16), + [363] = {.entry = {.count = 1, .reusable = true}}, SHIFT(194), + [365] = {.entry = {.count = 1, .reusable = true}}, SHIFT(71), + [367] = {.entry = {.count = 1, .reusable = true}}, SHIFT(201), + [369] = {.entry = {.count = 1, .reusable = true}}, SHIFT(32), + [371] = {.entry = {.count = 1, .reusable = true}}, SHIFT(117), + [373] = {.entry = {.count = 1, .reusable = true}}, SHIFT(63), + [375] = {.entry = {.count = 1, .reusable = true}}, SHIFT(35), + [377] = {.entry = {.count = 1, .reusable = true}}, SHIFT(36), + [379] = {.entry = {.count = 1, .reusable = false}}, SHIFT(37), + [381] = {.entry = {.count = 1, .reusable = true}}, SHIFT(37), + [383] = {.entry = {.count = 1, .reusable = true}}, SHIFT(38), + [385] = {.entry = {.count = 1, .reusable = false}}, SHIFT(39), + [387] = {.entry = {.count = 1, .reusable = false}}, SHIFT(40), + [389] = {.entry = {.count = 1, .reusable = false}}, SHIFT(9), + [391] = {.entry = {.count = 1, .reusable = false}}, SHIFT(41), + [393] = {.entry = {.count = 1, .reusable = true}}, SHIFT(41), + [395] = {.entry = {.count = 1, .reusable = true}}, SHIFT(42), + [397] = {.entry = {.count = 1, .reusable = true}}, SHIFT(43), + [399] = {.entry = {.count = 1, .reusable = true}}, SHIFT(44), + [401] = {.entry = {.count = 1, .reusable = true}}, SHIFT(22), + [403] = {.entry = {.count = 1, .reusable = true}}, SHIFT(89), + [405] = {.entry = {.count = 1, .reusable = false}}, SHIFT(29), + [407] = {.entry = {.count = 1, .reusable = true}}, SHIFT(31), + [409] = {.entry = {.count = 1, .reusable = true}}, SHIFT(282), + [411] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_source_file, 1, 0, 0), + [413] = {.entry = {.count = 1, .reusable = true}}, SHIFT(174), + [415] = {.entry = {.count = 1, .reusable = false}}, SHIFT(77), + [417] = {.entry = {.count = 1, .reusable = true}}, SHIFT(47), + [419] = {.entry = {.count = 1, .reusable = true}}, SHIFT(203), + [421] = {.entry = {.count = 1, .reusable = false}}, SHIFT(62), + [423] = {.entry = {.count = 1, .reusable = true}}, SHIFT(119), + [425] = {.entry = {.count = 1, .reusable = true}}, SHIFT(67), + [427] = {.entry = {.count = 1, .reusable = true}}, SHIFT(68), + [429] = {.entry = {.count = 1, .reusable = true}}, SHIFT(69), + [431] = {.entry = {.count = 1, .reusable = true}}, SHIFT(73), + [433] = {.entry = {.count = 1, .reusable = true}}, SHIFT(21), + [435] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_list_repeat1, 2, 0, 0), + [437] = {.entry = {.count = 1, .reusable = true}}, SHIFT(245), + [439] = {.entry = {.count = 1, .reusable = true}}, SHIFT(291), + [441] = {.entry = {.count = 1, .reusable = true}}, SHIFT(233), + [443] = {.entry = {.count = 1, .reusable = true}}, SHIFT(82), + [445] = {.entry = {.count = 1, .reusable = true}}, SHIFT(232), + [447] = {.entry = {.count = 1, .reusable = true}}, SHIFT(239), + [449] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_constraint_set_parenthesis, 3, 0, 0), + [451] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_constraint_set_parenthesis, 3, 0, 0), + [453] = {.entry = {.count = 1, .reusable = true}}, SHIFT(224), + [455] = {.entry = {.count = 1, .reusable = true}}, SHIFT(236), + [457] = {.entry = {.count = 1, .reusable = true}}, SHIFT(306), + [459] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_constraint_set_binary_expression, 3, 0, 14), + [461] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_constraint_set_binary_expression, 3, 0, 14), + [463] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_constraint_set_expression, 1, 0, 0), + [465] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_constraint_set_expression, 1, 0, 0), + [467] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_constraint_set_method_call, 6, 0, 29), + [469] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_constraint_set_method_call, 6, 0, 29), + [471] = {.entry = {.count = 1, .reusable = true}}, SHIFT(228), + [473] = {.entry = {.count = 1, .reusable = true}}, SHIFT(230), + [475] = {.entry = {.count = 1, .reusable = false}}, SHIFT(226), + [477] = {.entry = {.count = 1, .reusable = true}}, SHIFT(226), + [479] = {.entry = {.count = 1, .reusable = false}}, SHIFT(235), + [481] = {.entry = {.count = 1, .reusable = true}}, SHIFT(235), + [483] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_constraint_set_unary_expression, 2, 0, 3), + [485] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_constraint_set_unary_expression, 2, 0, 3), + [487] = {.entry = {.count = 1, .reusable = true}}, SHIFT(240), + [489] = {.entry = {.count = 1, .reusable = true}}, SHIFT(229), + [491] = {.entry = {.count = 1, .reusable = true}}, SHIFT(237), + [493] = {.entry = {.count = 1, .reusable = true}}, SHIFT(227), + [495] = {.entry = {.count = 1, .reusable = true}}, SHIFT(202), + [497] = {.entry = {.count = 1, .reusable = true}}, SHIFT(290), + [499] = {.entry = {.count = 1, .reusable = true}}, SHIFT(85), + [501] = {.entry = {.count = 1, .reusable = true}}, SHIFT(155), + [503] = {.entry = {.count = 1, .reusable = true}}, SHIFT(118), + [505] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_struct_definition_repeat1, 2, 0, 0), SHIFT_REPEAT(290), + [508] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_struct_definition_repeat1, 2, 0, 0), + [510] = {.entry = {.count = 1, .reusable = false}}, SHIFT(308), + [512] = {.entry = {.count = 1, .reusable = false}}, SHIFT(64), + [514] = {.entry = {.count = 1, .reusable = false}}, SHIFT(61), + [516] = {.entry = {.count = 1, .reusable = false}}, SHIFT(49), + [518] = {.entry = {.count = 1, .reusable = false}}, SHIFT(24), + [520] = {.entry = {.count = 1, .reusable = false}}, SHIFT(45), + [522] = {.entry = {.count = 1, .reusable = false}}, SHIFT(34), + [524] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_let_in_repeat1, 2, 0, 0), SHIFT_REPEAT(308), + [527] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_let_in_repeat1, 2, 0, 0), + [529] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_dictionary_construction_repeat1, 2, 0, 0), SHIFT_REPEAT(285), + [532] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_dictionary_construction_repeat1, 2, 0, 0), + [534] = {.entry = {.count = 1, .reusable = true}}, SHIFT(318), + [536] = {.entry = {.count = 1, .reusable = true}}, SHIFT(198), + [538] = {.entry = {.count = 1, .reusable = true}}, SHIFT(265), + [540] = {.entry = {.count = 1, .reusable = true}}, SHIFT(192), + [542] = {.entry = {.count = 1, .reusable = true}}, SHIFT(105), + [544] = {.entry = {.count = 1, .reusable = true}}, SHIFT(190), + [546] = {.entry = {.count = 1, .reusable = true}}, SHIFT(289), + [548] = {.entry = {.count = 1, .reusable = true}}, SHIFT(103), + [550] = {.entry = {.count = 1, .reusable = true}}, SHIFT(274), + [552] = {.entry = {.count = 1, .reusable = true}}, SHIFT(268), + [554] = {.entry = {.count = 1, .reusable = true}}, SHIFT(93), + [556] = {.entry = {.count = 1, .reusable = true}}, SHIFT(113), + [558] = {.entry = {.count = 1, .reusable = true}}, SHIFT(267), + [560] = {.entry = {.count = 1, .reusable = true}}, SHIFT(176), + [562] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_struct_member, 2, 0, 8), + [564] = {.entry = {.count = 1, .reusable = true}}, SHIFT(28), + [566] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_octal, 2, 0, 0), + [568] = {.entry = {.count = 1, .reusable = true}}, SHIFT(156), + [570] = {.entry = {.count = 1, .reusable = true}}, SHIFT(294), + [572] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_let_in_assignment, 4, 0, 21), + [574] = {.entry = {.count = 1, .reusable = true}}, SHIFT(263), + [576] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__struct_final_element, 1, 0, 0), + [578] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_integer, 1, 0, 0), + [580] = {.entry = {.count = 1, .reusable = true}}, SHIFT(321), + [582] = {.entry = {.count = 1, .reusable = true}}, SHIFT(132), + [584] = {.entry = {.count = 1, .reusable = true}}, SHIFT(317), + [586] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_constraint_set_fields, 1, 0, 0), + [588] = {.entry = {.count = 1, .reusable = true}}, SHIFT(26), + [590] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_hex, 2, 0, 0), + [592] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_constraint_set_fields, 2, 0, 0), + [594] = {.entry = {.count = 1, .reusable = true}}, SHIFT(168), + [596] = {.entry = {.count = 1, .reusable = true}}, SHIFT(169), + [598] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_binary, 2, 0, 0), + [600] = {.entry = {.count = 1, .reusable = true}}, SHIFT(120), + [602] = {.entry = {.count = 1, .reusable = true}}, SHIFT(121), + [604] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_constraint_set_fields_repeat1, 2, 0, 0), SHIFT_REPEAT(313), + [607] = {.entry = {.count = 1, .reusable = true}}, SHIFT(87), + [609] = {.entry = {.count = 1, .reusable = true}}, SHIFT(241), + [611] = {.entry = {.count = 1, .reusable = true}}, SHIFT(281), + [613] = {.entry = {.count = 1, .reusable = true}}, SHIFT(293), + [615] = {.entry = {.count = 1, .reusable = true}}, SHIFT(296), + [617] = {.entry = {.count = 1, .reusable = true}}, SHIFT(278), + [619] = {.entry = {.count = 1, .reusable = true}}, SHIFT(307), + [621] = {.entry = {.count = 1, .reusable = true}}, SHIFT(225), + [623] = {.entry = {.count = 1, .reusable = true}}, SHIFT(23), + [625] = {.entry = {.count = 1, .reusable = true}}, SHIFT(297), + [627] = {.entry = {.count = 1, .reusable = true}}, SHIFT(84), + [629] = {.entry = {.count = 1, .reusable = true}}, SHIFT(179), + [631] = {.entry = {.count = 1, .reusable = true}}, SHIFT(153), + [633] = {.entry = {.count = 1, .reusable = true}}, SHIFT(159), + [635] = {.entry = {.count = 1, .reusable = true}}, SHIFT(91), + [637] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_constraint_set_fields_repeat1, 2, 0, 0), + [639] = {.entry = {.count = 1, .reusable = true}}, SHIFT(25), + [641] = {.entry = {.count = 1, .reusable = true}}, ACCEPT_INPUT(), + [643] = {.entry = {.count = 1, .reusable = true}}, SHIFT(234), + [645] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__struct_final_element, 2, 0, 0), +}; + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef TREE_SITTER_HIDE_SYMBOLS +#define TS_PUBLIC +#elif defined(_WIN32) +#define TS_PUBLIC __declspec(dllexport) +#else +#define TS_PUBLIC __attribute__((visibility("default"))) +#endif + +TS_PUBLIC const TSLanguage *tree_sitter_command_cad_model(void) { + static const TSLanguage language = { + .abi_version = LANGUAGE_VERSION, + .symbol_count = SYMBOL_COUNT, + .alias_count = ALIAS_COUNT, + .token_count = TOKEN_COUNT, + .external_token_count = EXTERNAL_TOKEN_COUNT, + .state_count = STATE_COUNT, + .large_state_count = LARGE_STATE_COUNT, + .production_id_count = PRODUCTION_ID_COUNT, + .supertype_count = SUPERTYPE_COUNT, + .field_count = FIELD_COUNT, + .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH, + .parse_table = &ts_parse_table[0][0], + .small_parse_table = ts_small_parse_table, + .small_parse_table_map = ts_small_parse_table_map, + .parse_actions = ts_parse_actions, + .symbol_names = ts_symbol_names, + .field_names = ts_field_names, + .field_map_slices = ts_field_map_slices, + .field_map_entries = ts_field_map_entries, + .symbol_metadata = ts_symbol_metadata, + .public_symbol_map = ts_symbol_map, + .alias_map = ts_non_terminal_alias_map, + .alias_sequences = &ts_alias_sequences[0][0], + .lex_modes = (const void*)ts_lex_modes, + .lex_fn = ts_lex, + .keyword_lex_fn = ts_lex_keywords, + .keyword_capture_token = sym_identifier, + .primary_state_ids = ts_primary_state_ids, + .name = "command_cad_model", + .max_reserved_word_set_size = 0, + .metadata = { + .major_version = 0, + .minor_version = 1, + .patch_version = 0, + }, + }; + return &language; +} +#ifdef __cplusplus +} +#endif diff --git a/tree-sitter-command-cad-model/src/tree_sitter/alloc.h b/tree-sitter-command-cad-model/src/tree_sitter/alloc.h new file mode 100644 index 0000000..1abdd12 --- /dev/null +++ b/tree-sitter-command-cad-model/src/tree_sitter/alloc.h @@ -0,0 +1,54 @@ +#ifndef TREE_SITTER_ALLOC_H_ +#define TREE_SITTER_ALLOC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +// Allow clients to override allocation functions +#ifdef TREE_SITTER_REUSE_ALLOCATOR + +extern void *(*ts_current_malloc)(size_t size); +extern void *(*ts_current_calloc)(size_t count, size_t size); +extern void *(*ts_current_realloc)(void *ptr, size_t size); +extern void (*ts_current_free)(void *ptr); + +#ifndef ts_malloc +#define ts_malloc ts_current_malloc +#endif +#ifndef ts_calloc +#define ts_calloc ts_current_calloc +#endif +#ifndef ts_realloc +#define ts_realloc ts_current_realloc +#endif +#ifndef ts_free +#define ts_free ts_current_free +#endif + +#else + +#ifndef ts_malloc +#define ts_malloc malloc +#endif +#ifndef ts_calloc +#define ts_calloc calloc +#endif +#ifndef ts_realloc +#define ts_realloc realloc +#endif +#ifndef ts_free +#define ts_free free +#endif + +#endif + +#ifdef __cplusplus +} +#endif + +#endif // TREE_SITTER_ALLOC_H_ diff --git a/tree-sitter-command-cad-model/src/tree_sitter/array.h b/tree-sitter-command-cad-model/src/tree_sitter/array.h new file mode 100644 index 0000000..a17a574 --- /dev/null +++ b/tree-sitter-command-cad-model/src/tree_sitter/array.h @@ -0,0 +1,291 @@ +#ifndef TREE_SITTER_ARRAY_H_ +#define TREE_SITTER_ARRAY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "./alloc.h" + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4101) +#elif defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#endif + +#define Array(T) \ + struct { \ + T *contents; \ + uint32_t size; \ + uint32_t capacity; \ + } + +/// Initialize an array. +#define array_init(self) \ + ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL) + +/// Create an empty array. +#define array_new() \ + { NULL, 0, 0 } + +/// Get a pointer to the element at a given `index` in the array. +#define array_get(self, _index) \ + (assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index]) + +/// Get a pointer to the first element in the array. +#define array_front(self) array_get(self, 0) + +/// Get a pointer to the last element in the array. +#define array_back(self) array_get(self, (self)->size - 1) + +/// Clear the array, setting its size to zero. Note that this does not free any +/// memory allocated for the array's contents. +#define array_clear(self) ((self)->size = 0) + +/// Reserve `new_capacity` elements of space in the array. If `new_capacity` is +/// less than the array's current capacity, this function has no effect. +#define array_reserve(self, new_capacity) \ + _array__reserve((Array *)(self), array_elem_size(self), new_capacity) + +/// Free any memory allocated for this array. Note that this does not free any +/// memory allocated for the array's contents. +#define array_delete(self) _array__delete((Array *)(self)) + +/// Push a new `element` onto the end of the array. +#define array_push(self, element) \ + (_array__grow((Array *)(self), 1, array_elem_size(self)), \ + (self)->contents[(self)->size++] = (element)) + +/// Increase the array's size by `count` elements. +/// New elements are zero-initialized. +#define array_grow_by(self, count) \ + do { \ + if ((count) == 0) break; \ + _array__grow((Array *)(self), count, array_elem_size(self)); \ + memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \ + (self)->size += (count); \ + } while (0) + +/// Append all elements from one array to the end of another. +#define array_push_all(self, other) \ + array_extend((self), (other)->size, (other)->contents) + +/// Append `count` elements to the end of the array, reading their values from the +/// `contents` pointer. +#define array_extend(self, count, contents) \ + _array__splice( \ + (Array *)(self), array_elem_size(self), (self)->size, \ + 0, count, contents \ + ) + +/// Remove `old_count` elements from the array starting at the given `index`. At +/// the same index, insert `new_count` new elements, reading their values from the +/// `new_contents` pointer. +#define array_splice(self, _index, old_count, new_count, new_contents) \ + _array__splice( \ + (Array *)(self), array_elem_size(self), _index, \ + old_count, new_count, new_contents \ + ) + +/// Insert one `element` into the array at the given `index`. +#define array_insert(self, _index, element) \ + _array__splice((Array *)(self), array_elem_size(self), _index, 0, 1, &(element)) + +/// Remove one element from the array at the given `index`. +#define array_erase(self, _index) \ + _array__erase((Array *)(self), array_elem_size(self), _index) + +/// Pop the last element off the array, returning the element by value. +#define array_pop(self) ((self)->contents[--(self)->size]) + +/// Assign the contents of one array to another, reallocating if necessary. +#define array_assign(self, other) \ + _array__assign((Array *)(self), (const Array *)(other), array_elem_size(self)) + +/// Swap one array with another +#define array_swap(self, other) \ + _array__swap((Array *)(self), (Array *)(other)) + +/// Get the size of the array contents +#define array_elem_size(self) (sizeof *(self)->contents) + +/// Search a sorted array for a given `needle` value, using the given `compare` +/// callback to determine the order. +/// +/// If an existing element is found to be equal to `needle`, then the `index` +/// out-parameter is set to the existing value's index, and the `exists` +/// out-parameter is set to true. Otherwise, `index` is set to an index where +/// `needle` should be inserted in order to preserve the sorting, and `exists` +/// is set to false. +#define array_search_sorted_with(self, compare, needle, _index, _exists) \ + _array__search_sorted(self, 0, compare, , needle, _index, _exists) + +/// Search a sorted array for a given `needle` value, using integer comparisons +/// of a given struct field (specified with a leading dot) to determine the order. +/// +/// See also `array_search_sorted_with`. +#define array_search_sorted_by(self, field, needle, _index, _exists) \ + _array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists) + +/// Insert a given `value` into a sorted array, using the given `compare` +/// callback to determine the order. +#define array_insert_sorted_with(self, compare, value) \ + do { \ + unsigned _index, _exists; \ + array_search_sorted_with(self, compare, &(value), &_index, &_exists); \ + if (!_exists) array_insert(self, _index, value); \ + } while (0) + +/// Insert a given `value` into a sorted array, using integer comparisons of +/// a given struct field (specified with a leading dot) to determine the order. +/// +/// See also `array_search_sorted_by`. +#define array_insert_sorted_by(self, field, value) \ + do { \ + unsigned _index, _exists; \ + array_search_sorted_by(self, field, (value) field, &_index, &_exists); \ + if (!_exists) array_insert(self, _index, value); \ + } while (0) + +// Private + +typedef Array(void) Array; + +/// This is not what you're looking for, see `array_delete`. +static inline void _array__delete(Array *self) { + if (self->contents) { + ts_free(self->contents); + self->contents = NULL; + self->size = 0; + self->capacity = 0; + } +} + +/// This is not what you're looking for, see `array_erase`. +static inline void _array__erase(Array *self, size_t element_size, + uint32_t index) { + assert(index < self->size); + char *contents = (char *)self->contents; + memmove(contents + index * element_size, contents + (index + 1) * element_size, + (self->size - index - 1) * element_size); + self->size--; +} + +/// This is not what you're looking for, see `array_reserve`. +static inline void _array__reserve(Array *self, size_t element_size, uint32_t new_capacity) { + if (new_capacity > self->capacity) { + if (self->contents) { + self->contents = ts_realloc(self->contents, new_capacity * element_size); + } else { + self->contents = ts_malloc(new_capacity * element_size); + } + self->capacity = new_capacity; + } +} + +/// This is not what you're looking for, see `array_assign`. +static inline void _array__assign(Array *self, const Array *other, size_t element_size) { + _array__reserve(self, element_size, other->size); + self->size = other->size; + memcpy(self->contents, other->contents, self->size * element_size); +} + +/// This is not what you're looking for, see `array_swap`. +static inline void _array__swap(Array *self, Array *other) { + Array swap = *other; + *other = *self; + *self = swap; +} + +/// This is not what you're looking for, see `array_push` or `array_grow_by`. +static inline void _array__grow(Array *self, uint32_t count, size_t element_size) { + uint32_t new_size = self->size + count; + if (new_size > self->capacity) { + uint32_t new_capacity = self->capacity * 2; + if (new_capacity < 8) new_capacity = 8; + if (new_capacity < new_size) new_capacity = new_size; + _array__reserve(self, element_size, new_capacity); + } +} + +/// This is not what you're looking for, see `array_splice`. +static inline void _array__splice(Array *self, size_t element_size, + uint32_t index, uint32_t old_count, + uint32_t new_count, const void *elements) { + uint32_t new_size = self->size + new_count - old_count; + uint32_t old_end = index + old_count; + uint32_t new_end = index + new_count; + assert(old_end <= self->size); + + _array__reserve(self, element_size, new_size); + + char *contents = (char *)self->contents; + if (self->size > old_end) { + memmove( + contents + new_end * element_size, + contents + old_end * element_size, + (self->size - old_end) * element_size + ); + } + if (new_count > 0) { + if (elements) { + memcpy( + (contents + index * element_size), + elements, + new_count * element_size + ); + } else { + memset( + (contents + index * element_size), + 0, + new_count * element_size + ); + } + } + self->size += new_count - old_count; +} + +/// A binary search routine, based on Rust's `std::slice::binary_search_by`. +/// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`. +#define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \ + do { \ + *(_index) = start; \ + *(_exists) = false; \ + uint32_t size = (self)->size - *(_index); \ + if (size == 0) break; \ + int comparison; \ + while (size > 1) { \ + uint32_t half_size = size / 2; \ + uint32_t mid_index = *(_index) + half_size; \ + comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \ + if (comparison <= 0) *(_index) = mid_index; \ + size -= half_size; \ + } \ + comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \ + if (comparison == 0) *(_exists) = true; \ + else if (comparison < 0) *(_index) += 1; \ + } while (0) + +/// Helper macro for the `_sorted_by` routines below. This takes the left (existing) +/// parameter by reference in order to work with the generic sorting function above. +#define _compare_int(a, b) ((int)*(a) - (int)(b)) + +#ifdef _MSC_VER +#pragma warning(pop) +#elif defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#ifdef __cplusplus +} +#endif + +#endif // TREE_SITTER_ARRAY_H_ diff --git a/tree-sitter-command-cad-model/src/tree_sitter/parser.h b/tree-sitter-command-cad-model/src/tree_sitter/parser.h new file mode 100644 index 0000000..858107d --- /dev/null +++ b/tree-sitter-command-cad-model/src/tree_sitter/parser.h @@ -0,0 +1,286 @@ +#ifndef TREE_SITTER_PARSER_H_ +#define TREE_SITTER_PARSER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define ts_builtin_sym_error ((TSSymbol)-1) +#define ts_builtin_sym_end 0 +#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 + +#ifndef TREE_SITTER_API_H_ +typedef uint16_t TSStateId; +typedef uint16_t TSSymbol; +typedef uint16_t TSFieldId; +typedef struct TSLanguage TSLanguage; +typedef struct TSLanguageMetadata { + uint8_t major_version; + uint8_t minor_version; + uint8_t patch_version; +} TSLanguageMetadata; +#endif + +typedef struct { + TSFieldId field_id; + uint8_t child_index; + bool inherited; +} TSFieldMapEntry; + +// Used to index the field and supertype maps. +typedef struct { + uint16_t index; + uint16_t length; +} TSMapSlice; + +typedef struct { + bool visible; + bool named; + bool supertype; +} TSSymbolMetadata; + +typedef struct TSLexer TSLexer; + +struct TSLexer { + int32_t lookahead; + TSSymbol result_symbol; + void (*advance)(TSLexer *, bool); + void (*mark_end)(TSLexer *); + uint32_t (*get_column)(TSLexer *); + bool (*is_at_included_range_start)(const TSLexer *); + bool (*eof)(const TSLexer *); + void (*log)(const TSLexer *, const char *, ...); +}; + +typedef enum { + TSParseActionTypeShift, + TSParseActionTypeReduce, + TSParseActionTypeAccept, + TSParseActionTypeRecover, +} TSParseActionType; + +typedef union { + struct { + uint8_t type; + TSStateId state; + bool extra; + bool repetition; + } shift; + struct { + uint8_t type; + uint8_t child_count; + TSSymbol symbol; + int16_t dynamic_precedence; + uint16_t production_id; + } reduce; + uint8_t type; +} TSParseAction; + +typedef struct { + uint16_t lex_state; + uint16_t external_lex_state; +} TSLexMode; + +typedef struct { + uint16_t lex_state; + uint16_t external_lex_state; + uint16_t reserved_word_set_id; +} TSLexerMode; + +typedef union { + TSParseAction action; + struct { + uint8_t count; + bool reusable; + } entry; +} TSParseActionEntry; + +typedef struct { + int32_t start; + int32_t end; +} TSCharacterRange; + +struct TSLanguage { + uint32_t abi_version; + uint32_t symbol_count; + uint32_t alias_count; + uint32_t token_count; + uint32_t external_token_count; + uint32_t state_count; + uint32_t large_state_count; + uint32_t production_id_count; + uint32_t field_count; + uint16_t max_alias_sequence_length; + const uint16_t *parse_table; + const uint16_t *small_parse_table; + const uint32_t *small_parse_table_map; + const TSParseActionEntry *parse_actions; + const char * const *symbol_names; + const char * const *field_names; + const TSMapSlice *field_map_slices; + const TSFieldMapEntry *field_map_entries; + const TSSymbolMetadata *symbol_metadata; + const TSSymbol *public_symbol_map; + const uint16_t *alias_map; + const TSSymbol *alias_sequences; + const TSLexerMode *lex_modes; + bool (*lex_fn)(TSLexer *, TSStateId); + bool (*keyword_lex_fn)(TSLexer *, TSStateId); + TSSymbol keyword_capture_token; + struct { + const bool *states; + const TSSymbol *symbol_map; + void *(*create)(void); + void (*destroy)(void *); + bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); + unsigned (*serialize)(void *, char *); + void (*deserialize)(void *, const char *, unsigned); + } external_scanner; + const TSStateId *primary_state_ids; + const char *name; + const TSSymbol *reserved_words; + uint16_t max_reserved_word_set_size; + uint32_t supertype_count; + const TSSymbol *supertype_symbols; + const TSMapSlice *supertype_map_slices; + const TSSymbol *supertype_map_entries; + TSLanguageMetadata metadata; +}; + +static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) { + uint32_t index = 0; + uint32_t size = len - index; + while (size > 1) { + uint32_t half_size = size / 2; + uint32_t mid_index = index + half_size; + const TSCharacterRange *range = &ranges[mid_index]; + if (lookahead >= range->start && lookahead <= range->end) { + return true; + } else if (lookahead > range->end) { + index = mid_index; + } + size -= half_size; + } + const TSCharacterRange *range = &ranges[index]; + return (lookahead >= range->start && lookahead <= range->end); +} + +/* + * Lexer Macros + */ + +#ifdef _MSC_VER +#define UNUSED __pragma(warning(suppress : 4101)) +#else +#define UNUSED __attribute__((unused)) +#endif + +#define START_LEXER() \ + bool result = false; \ + bool skip = false; \ + UNUSED \ + bool eof = false; \ + int32_t lookahead; \ + goto start; \ + next_state: \ + lexer->advance(lexer, skip); \ + start: \ + skip = false; \ + lookahead = lexer->lookahead; + +#define ADVANCE(state_value) \ + { \ + state = state_value; \ + goto next_state; \ + } + +#define ADVANCE_MAP(...) \ + { \ + static const uint16_t map[] = { __VA_ARGS__ }; \ + for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \ + if (map[i] == lookahead) { \ + state = map[i + 1]; \ + goto next_state; \ + } \ + } \ + } + +#define SKIP(state_value) \ + { \ + skip = true; \ + state = state_value; \ + goto next_state; \ + } + +#define ACCEPT_TOKEN(symbol_value) \ + result = true; \ + lexer->result_symbol = symbol_value; \ + lexer->mark_end(lexer); + +#define END_STATE() return result; + +/* + * Parse Table Macros + */ + +#define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT) + +#define STATE(id) id + +#define ACTIONS(id) id + +#define SHIFT(state_value) \ + {{ \ + .shift = { \ + .type = TSParseActionTypeShift, \ + .state = (state_value) \ + } \ + }} + +#define SHIFT_REPEAT(state_value) \ + {{ \ + .shift = { \ + .type = TSParseActionTypeShift, \ + .state = (state_value), \ + .repetition = true \ + } \ + }} + +#define SHIFT_EXTRA() \ + {{ \ + .shift = { \ + .type = TSParseActionTypeShift, \ + .extra = true \ + } \ + }} + +#define REDUCE(symbol_name, children, precedence, prod_id) \ + {{ \ + .reduce = { \ + .type = TSParseActionTypeReduce, \ + .symbol = symbol_name, \ + .child_count = children, \ + .dynamic_precedence = precedence, \ + .production_id = prod_id \ + }, \ + }} + +#define RECOVER() \ + {{ \ + .type = TSParseActionTypeRecover \ + }} + +#define ACCEPT_INPUT() \ + {{ \ + .type = TSParseActionTypeAccept \ + }} + +#ifdef __cplusplus +} +#endif + +#endif // TREE_SITTER_PARSER_H_ diff --git a/tree-sitter-command-cad-model/test/corpus/closure.txt b/tree-sitter-command-cad-model/test/corpus/closure.txt new file mode 100644 index 0000000..f3da4bf --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/closure.txt @@ -0,0 +1,219 @@ +================== +Empty +================== + +() -> std.None: "" + +--- + +(source_file + (expression + (closure_definition + (struct_definition) + (expression + (member_access + (expression + (identifier)) + (identifier))) + (expression + (string))))) + +================== +With argument +================== + +(argument: std.types.UInt) -> std.None: "" + +--- + +(source_file + (expression + (closure_definition + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (member_access + (expression + (identifier)) + (identifier))) + (identifier)))))) + (expression + (member_access + (expression + (identifier)) + (identifier))) + (expression + (string))))) + +================== +With Binary Expression +================== + +(argument: std.types.UInt) -> std.None: a + b + +--- + +(source_file + (expression + (closure_definition + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (member_access + (expression + (identifier)) + (identifier))) + (identifier)))))) + (expression + (member_access + (expression + (identifier)) + (identifier))) + (expression + (binary_expression + (expression + (identifier)) + (expression + (identifier))))))) + +================== +Call function +================== + +value(value=value) + +--- + +(source_file + (expression + (function_call + (expression + (identifier)) + (dictionary_construction + (dictionary_member_assignment + (identifier) + (expression + (identifier))))))) + +================== +Call no arguments +================== + +value() + +--- + +(source_file + (expression + (function_call + (expression + (identifier)) + (dictionary_construction)))) + +================== +Call function deep in tree +================== + +value.value(value=value) + +--- + +(source_file + (expression + (function_call + (expression + (member_access + (expression + (identifier)) + (identifier))) + (dictionary_construction + (dictionary_member_assignment + (identifier) + (expression + (identifier))))))) + +================== +Call method +================== + +value::value(value=value) + +--- + +(source_file + (expression + (method_call + (expression + (identifier)) + (identifier) + (dictionary_construction + (dictionary_member_assignment + (identifier) + (expression + (identifier))))))) + +================== +Call method no arguments +================== + +value::value() + +--- + +(source_file + (expression + (method_call + (expression + (identifier)) + (identifier) + (dictionary_construction)))) + +================== +Call method deep in tree +================== + +value.value::value(value=value) + +--- + +(source_file + (expression + (method_call + (expression + (member_access + (expression + (identifier)) + (identifier))) + (identifier) + (dictionary_construction + (dictionary_member_assignment + (identifier) + (expression + (identifier))))))) + +================== +Call method inside parenthasis +================== + +(value::value()) + +--- + +(source_file + (expression + (parenthesis + (expression + (method_call + (expression + (identifier)) + (identifier) + (dictionary_construction)))))) + diff --git a/tree-sitter-command-cad-model/test/corpus/comments.txt b/tree-sitter-command-cad-model/test/corpus/comments.txt new file mode 100644 index 0000000..8406d56 --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/comments.txt @@ -0,0 +1,35 @@ +================== +Comment +================== + +# There's a comment before the code. +"" + +--- + +(source_file + (comment) + (expression + (string))) + +================== +Comment before closure +================== + +# There's a comment before the code. +()->std.None: "" + +--- + +(source_file + (comment) + (expression + (closure_definition + (struct_definition) + (expression + (member_access + (expression + (identifier)) + (identifier))) + (expression + (string))))) diff --git a/tree-sitter-command-cad-model/test/corpus/constraint_sets.txt b/tree-sitter-command-cad-model/test/corpus/constraint_sets.txt new file mode 100644 index 0000000..b27539c --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/constraint_sets.txt @@ -0,0 +1,54 @@ +================== +Equal +================== + +<<>> + +--- + +(source_file + (expression + (constraint_set + (constraint_set_fields + (identifier)) + (constraint_set_expression + (identifier)) + (constraint_set_expression + (scalar + (number)))))) + +================== +Precedence +================== + +<<>> + +--- + +(source_file + (expression + (constraint_set + (constraint_set_fields + (identifier) + (identifier) + (identifier) + (identifier)) + (constraint_set_expression + (constraint_set_binary_expression + (constraint_set_expression + (constraint_set_parenthesis + (constraint_set_expression + (constraint_set_binary_expression + (constraint_set_expression + (identifier)) + (constraint_set_expression + (constraint_set_binary_expression + (constraint_set_expression + (identifier)) + (constraint_set_expression + (identifier)))))))) + (constraint_set_expression + (identifier)))) + (constraint_set_expression + (scalar + (number)))))) diff --git a/tree-sitter-command-cad-model/test/corpus/dictionary_construction.txt b/tree-sitter-command-cad-model/test/corpus/dictionary_construction.txt new file mode 100644 index 0000000..39085d4 --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/dictionary_construction.txt @@ -0,0 +1,83 @@ +================== +Empty +================== + +() + +--- + +(source_file + (expression + (dictionary_construction))) + +================== +One +================== + +(one = a) + +--- + +(source_file + (expression + (dictionary_construction + (dictionary_member_assignment + (identifier) + (expression + (identifier)))))) + +================== +One, +================== + +(one = a,) + +--- + +(source_file + (expression + (dictionary_construction + (dictionary_member_assignment + (identifier) + (expression + (identifier)))))) + +================== +One, two +================== + +(one = a, two = b) + +--- + +(source_file + (expression + (dictionary_construction + (dictionary_member_assignment + (identifier) + (expression + (identifier))) + (dictionary_member_assignment + (identifier) + (expression + (identifier)))))) + +================== +One, two, +================== + +(one = a, two = b,) + +--- + +(source_file + (expression + (dictionary_construction + (dictionary_member_assignment + (identifier) + (expression + (identifier))) + (dictionary_member_assignment + (identifier) + (expression + (identifier)))))) diff --git a/tree-sitter-command-cad-model/test/corpus/errors.txt b/tree-sitter-command-cad-model/test/corpus/errors.txt new file mode 100644 index 0000000..2492f5c --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/errors.txt @@ -0,0 +1,15 @@ +================== +Missing second operand +================== + +a + + +--- + +(source_file + (expression + (binary_expression + (expression + (identifier)) + (expression + (MISSING identifier))))) diff --git a/tree-sitter-command-cad-model/test/corpus/if.txt b/tree-sitter-command-cad-model/test/corpus/if.txt new file mode 100644 index 0000000..6c2ff2c --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/if.txt @@ -0,0 +1,40 @@ +================== +If else +================== + +if true then "" else "" + +--- + +(source_file + (expression + (if + (expression + (boolean (true))) + (expression + (string)) + (expression + (string))))) + +================== +With other operations +================== + +1 + if true then "" else "" + +--- + +(source_file + (expression + (binary_expression + (expression + (scalar + (number))) + (expression + (if + (expression + (boolean (true))) + (expression + (string)) + (expression + (string))))))) diff --git a/tree-sitter-command-cad-model/test/corpus/let_in.txt b/tree-sitter-command-cad-model/test/corpus/let_in.txt new file mode 100644 index 0000000..2561590 --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/let_in.txt @@ -0,0 +1,65 @@ +================== +Empty +================== + +let +in +"" + +--- + +(source_file + (expression + (let_in + (expression + (string))))) + +================== +1 Assignment +================== + +let + first = 1; +in +"" + +--- + +(source_file + (expression + (let_in + (let_in_assignment + (identifier) + (expression + (scalar + (number)))) + (expression + (string))))) + +================== +2 Assignments +================== + +let + first = 1; + second = 2; +in +"" + +--- + +(source_file + (expression + (let_in + (let_in_assignment + (identifier) + (expression + (scalar + (number)))) + (let_in_assignment + (identifier) + (expression + (scalar + (number)))) + (expression + (string))))) diff --git a/tree-sitter-command-cad-model/test/corpus/list.txt b/tree-sitter-command-cad-model/test/corpus/list.txt new file mode 100644 index 0000000..7ff28fb --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/list.txt @@ -0,0 +1,65 @@ +================== +Empty +================== + +[] + +--- + +(source_file + (expression + (list))) + +================== +Single +================== + +[this,] + +--- + +(source_file + (expression + (list + (expression + (identifier))))) + +================== +Multi +================== + +[this, is, a, test] + +--- + +(source_file + (expression + (list + (expression + (identifier)) + (expression + (identifier)) + (expression + (identifier)) + (expression + (identifier))))) + +================== +Multi with comma at the end +================== + +[this, is, a, test,] + +--- + +(source_file + (expression + (list + (expression + (identifier)) + (expression + (identifier)) + (expression + (identifier)) + (expression + (identifier))))) diff --git a/tree-sitter-command-cad-model/test/corpus/precedence.txt b/tree-sitter-command-cad-model/test/corpus/precedence.txt new file mode 100644 index 0000000..399ce79 --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/precedence.txt @@ -0,0 +1,65 @@ +================== +Precedence +================== + +(a + b * c ** d >= e) + c + +--- + +(source_file + (expression + (binary_expression + (expression + (parenthesis + (expression + (binary_expression + (expression + (binary_expression + (expression + (identifier)) + (expression + (binary_expression + (expression + (identifier)) + (expression + (binary_expression + (expression + (identifier)) + (expression + (identifier)))))))) + (expression + (identifier)))))) + (expression + (identifier))))) + +================== +Vectors +================== + +<(a, b)> + <(a, b)> == <(a, b)> + +--- + +(source_file + (expression + (binary_expression + (expression + (binary_expression + (expression + (vector2 + (expression + (identifier)) + (expression + (identifier)))) + (expression + (vector2 + (expression + (identifier)) + (expression + (identifier)))))) + (expression + (vector2 + (expression + (identifier)) + (expression + (identifier))))))) diff --git a/tree-sitter-command-cad-model/test/corpus/primitives.txt b/tree-sitter-command-cad-model/test/corpus/primitives.txt new file mode 100644 index 0000000..cdc74f3 --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/primitives.txt @@ -0,0 +1,289 @@ +================== +String +================== + +"Some text" + +--- + +(source_file + (expression + (string))) + +================== +Escaped string +================== + +"Some \"text\"" + +--- + +(source_file + (expression + (string))) + +================== +Unsigned Integer +================== + +123u + +--- + +(source_file + (expression + (unsigned_integer (integer (base_ten))))) + +================== +Signed Integer Positive +================== + +123i + +--- + +(source_file + (expression + (signed_integer (integer (base_ten))))) + + +================== +Unsigned Integer Octal +================== + +0o123u + +--- + +(source_file + (expression + (unsigned_integer (integer (octal))))) + +================== +Signed Integer Positive Octal +================== + +0o123i + +--- + +(source_file + (expression + (signed_integer (integer (octal))))) + +================== +Unsigned Integer hex +================== + +0xABCu + +--- + +(source_file + (expression + (unsigned_integer (integer (hex))))) + +================== +Signed Integer Positive Hex +================== + +0xABCi + +--- + +(source_file + (expression + (signed_integer (integer (hex))))) + +================== +Unsigned Integer Binary +================== + +0b1010u + +--- + +(source_file + (expression + (unsigned_integer (integer (binary))))) + +================== +Signed Integer Positive Binary +================== + +0b1010i + +--- + +(source_file + (expression + (signed_integer (integer (binary))))) + +================== +Signed Integer Positive with plus +================== + ++123 i + +--- + +(source_file + (expression + (unary_expression + (expression + (signed_integer (integer (base_ten))))))) + +================== +Signed Integer Positive with minus +================== + +-123i + +--- + +(source_file + (expression + (unary_expression + (expression + (signed_integer (integer (base_ten))))))) + +================== +Scalar no decimal no unit +================== + +123 + +--- + +(source_file + (expression + (scalar + (number)))) + +================== +Scalar no unit +================== + +123.321 + +--- + +(source_file + (expression + (scalar + (number) + (number)))) + +================== +Scalar with unquoted unit +================== + +123 mm + +--- + +(source_file + (expression + (scalar + (number) + (identifier)))) + +================== +Scalar with quoted unit +================== + +123 'mm' + +--- + +(source_file + (expression + (scalar + (number) + (unit_quote)))) + +================== +Scalar with quoted unit that would not work as an identifier +================== + +123 'm^2' + +--- + +(source_file + (expression + (scalar + (number) + (unit_quote)))) + +================== +Vector2 +================== + +<(1m, 2m)> + +--- + +(source_file + (expression + (vector2 + (expression + (scalar + (number) + (identifier))) + (expression + (scalar + (number) + (identifier)))))) + +================== +Vector3 +================== + +<(1m, 2m, 3m)> + +--- + +(source_file + (expression + (vector3 + (expression + (scalar + (number) + (identifier))) + (expression + (scalar + (number) + (identifier))) + (expression + (scalar + (number) + (identifier)))))) + +================== +Vector4 +================== + +<(1m, 2m, 3m, 4m)> + +--- + +(source_file + (expression + (vector4 + (expression + (scalar + (number) + (identifier))) + (expression + (scalar + (number) + (identifier))) + (expression + (scalar + (number) + (identifier))) + (expression + (scalar + (number) + (identifier)))))) diff --git a/tree-sitter-command-cad-model/test/corpus/struct_definition.txt b/tree-sitter-command-cad-model/test/corpus/struct_definition.txt new file mode 100644 index 0000000..8765bcc --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/struct_definition.txt @@ -0,0 +1,248 @@ +================== +Empty +================== + +() + +--- + +(source_file + (expression + (dictionary_construction))) + +================== +One +================== + +( one: std.Constraint ) + +--- + +(source_file + (expression + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier)))))))) + +================== +One, +================== + +( one: std.Constraint, ) + +--- + +(source_file + (expression + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier)))))))) + +================== +... +================== + +( ... ) + +--- + +(source_file + (expression + (struct_definition + (varadic_dots)))) + +================== +..., +================== + +( ..., ) + +--- + +(source_file + (expression + (struct_definition + (varadic_dots)))) + +================== +One, ... +================== + +( one: std.Constraint, ... ) + +--- + +(source_file + (expression + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier))))) + (varadic_dots)))) + +================== +One, ..., +================== + +( one: std.Constraint, ..., ) + +--- + +(source_file + (expression + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier))))) + (varadic_dots)))) + +================== +One, two +================== + +( one: std.Constraint, two: std.Constraint ) + +--- + +(source_file + (expression + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier))))) + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier)))))))) + +================== +One, two, +================== + +( one: std.Constraint, two: std.Constraint, ) + +--- + +(source_file + (expression + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier))))) + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier)))))))) + +================== +One, two, ..., +================== + +( one: std.Constraint, two: std.Constraint, ..., ) + +--- + +(source_file + (expression + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier))))) + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier))))) + (varadic_dots)))) + +================== +Default +================== + +( one: std.Constraint = a ) + +--- + +(source_file + (expression + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier)))) + (expression + (identifier)))))) + +================== +Default, +================== + +( one: std.Constraint = a, ) + +--- + +(source_file + (expression + (struct_definition + (struct_member + (identifier) + (declaration_type + (expression + (member_access + (expression + (identifier)) + (identifier)))) + (expression + (identifier)))))) diff --git a/tree-sitter-command-cad-model/test/corpus/variable_paths.txt b/tree-sitter-command-cad-model/test/corpus/variable_paths.txt new file mode 100644 index 0000000..83ec5f2 --- /dev/null +++ b/tree-sitter-command-cad-model/test/corpus/variable_paths.txt @@ -0,0 +1,44 @@ +================== +Variable Path +================== + +this.is.a.test + +--- + +(source_file + (expression + (member_access + (expression + (member_access + (expression + (member_access + (expression + (identifier)) + (identifier))) + (identifier))) + (identifier)))) + +================== +Self Path +================== + +self.this.is.a.test + +--- + +(source_file + (expression + (member_access + (expression + (member_access + (expression + (member_access + (expression + (member_access + (expression + (self)) + (identifier))) + (identifier))) + (identifier))) + (identifier)))) diff --git a/tree-sitter-command-cad-model/tree-sitter.json b/tree-sitter-command-cad-model/tree-sitter.json new file mode 100644 index 0000000..839ac35 --- /dev/null +++ b/tree-sitter-command-cad-model/tree-sitter.json @@ -0,0 +1,34 @@ +{ + "grammars": [ + { + "name": "command_cad_model", + "camelcase": "CommandCadModel", + "scope": "source.command_cad_model", + "file-types": [ + ".ccm" + ], + "injection-regex": "^command_cad_model$" + } + ], + "metadata": { + "version": "0.1.0", + "license": "AGPL", + "description": "Scripts for creating models in Command CAD", + "authors": [ + { + "name": "James Carl" + } + ], + "links": { + "repository": "https://github.com/IamTheCarl/CommandCAD" + } + }, + "bindings": { + "c": true, + "go": true, + "node": true, + "python": true, + "rust": true, + "swift": true + } +} \ No newline at end of file diff --git a/units/Cargo.toml b/units/Cargo.toml new file mode 100644 index 0000000..72ab85b --- /dev/null +++ b/units/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "units" +version = "0.1.0" +edition = "2021" + +[dependencies] +common_data_types = { path = "../common_data_types" } + +[build-dependencies] +csv = "1.3.0" +serde = { version = "1.0", features = [ "derive" ] } +uneval = "0.2.4" +common_data_types = { path = "../common_data_types" } \ No newline at end of file diff --git a/build.rs b/units/build.rs similarity index 97% rename from build.rs rename to units/build.rs index c024905..2721240 100644 --- a/build.rs +++ b/units/build.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fs::File, io::Write}; +use std::{collections::HashMap, fs::File}; use serde::Deserialize; @@ -43,10 +43,12 @@ macro_rules! get_base_unit { } fn main() { + println!("cargo:rerun-if-changed=src/units.csv"); + let mut source_data = csv::ReaderBuilder::new() .flexible(false) .has_headers(true) - .from_path("src/script/execution/types/math/units.csv") + .from_path("src/units.csv") .unwrap(); let mut conversion_factors = ConversionFactorDatabase::new(); @@ -97,7 +99,7 @@ fn main() { // Record conversion factor. let already_exists = conversion_factors .insert( - row.keyboard_friendly_abbreviation.clone().into(), + row.keyboard_friendly_abbreviation.clone(), ConversionFactor { constant: row.conversion_constant, coefficient: row.conversion_coefficient, @@ -114,7 +116,7 @@ fn main() { } // Self-deduplicating list of names for the dimensions. - dimension_names.insert(dimension, row.dimension_name.clone().into()); + dimension_names.insert(dimension, row.dimension_name.clone()); unit_list .entry(row.dimension_name) @@ -595,19 +597,10 @@ fn main() { ] .iter() .collect(); - let mut conversion_factor_file = File::create(conversion_factor_file_path).unwrap(); - writeln!( - &mut conversion_factor_file, - "#[allow(clippy::approx_constant)]" - ) - .unwrap(); + let conversion_factor_file = File::create(conversion_factor_file_path).unwrap(); uneval::write(conversion_factors, conversion_factor_file).unwrap(); uneval::to_out_dir(dimension_names, "dimension_names.rs").unwrap(); uneval::to_out_dir(unit_list, "unit_list.rs").unwrap(); uneval::to_out_dir(base_units, "base_units.rs").unwrap(); - - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=src/script/execution/types/math/units.csv"); - println!("cargo:rerun-if-changed=crates/common_data_types/src/lib.rs"); } diff --git a/units/src/lib.rs b/units/src/lib.rs new file mode 100644 index 0000000..141e839 --- /dev/null +++ b/units/src/lib.rs @@ -0,0 +1,73 @@ +use common_data_types::{ + BaseUnits, ConversionFactor, ConversionFactorDatabase, Dimension, DimensionNameDatabase, + RatioTypeHint, UnitDescription, UnitList, +}; +use std::{ + borrow::{Borrow, Cow}, + sync::OnceLock, +}; + +// One conversion factors approximates f64::consts::TAU. +#[allow(clippy::approx_constant)] +pub fn get_conversion_factor(name: &str) -> Option<&'static ConversionFactor> { + static CONVERSION_FACTORS: OnceLock = OnceLock::new(); + let database = CONVERSION_FACTORS + .get_or_init(|| include!(concat!(env!("OUT_DIR"), "/conversion_factors.rs"))); + + database.get(name) +} + +fn get_named_dimensions() -> &'static DimensionNameDatabase { + static DIMENSIONS: OnceLock = OnceLock::new(); + DIMENSIONS.get_or_init(|| include!(concat!(env!("OUT_DIR"), "/dimension_names.rs"))) +} + +pub fn get_dimension_name(dimension: &Dimension) -> Cow<'static, str> { + let database = get_named_dimensions(); + + if let Some(name) = database.get(dimension) { + Cow::Borrowed(name) + } else { + // This is a custom unit. We will need to generate a name to represent it. + format!( + "", + dimension.length, + dimension.mass, + dimension.time, + dimension.electric_current, + dimension.thermodynamic_temprature, + dimension.amount_of_substance, + dimension.luminous_intensity, + dimension.ratio_type_hint + ) + .into() + } +} + +pub fn list_named_dimensions() -> impl Iterator { + let database = get_named_dimensions(); + + database + .iter() + .map(|(dimension, name)| (name.as_str(), *dimension)) +} + +pub fn get_unit_list() -> &'static Vec<(String, Vec)> { + static UNIT_LIST: OnceLock = OnceLock::new(); + let list = UNIT_LIST.get_or_init(|| include!(concat!(env!("OUT_DIR"), "/unit_list.rs"))); + + list +} + +pub fn get_base_unit_name(dimension: &Dimension) -> Option<&'static str> { + static BASE_UNITS: OnceLock = OnceLock::new(); + let database = BASE_UNITS.get_or_init(|| include!(concat!(env!("OUT_DIR"), "/base_units.rs"))); + + let name = database.get(dimension).map(|cow| cow.borrow()); + + match name { + Some(name) => Some(name), + Option::None if *dimension != Dimension::zero() => Some("?"), + _ => Option::None, + } +} diff --git a/src/script/execution/types/math/units.csv b/units/src/units.csv similarity index 100% rename from src/script/execution/types/math/units.csv rename to units/src/units.csv