diff --git a/Cargo.lock b/Cargo.lock index 4665cd15a927..a4bc2497a67f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ "gimli", ] @@ -33,16 +33,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "once_cell", "version_check 0.9.4", ] [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -70,20 +70,11 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "approx" @@ -105,9 +96,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "ascii" @@ -146,7 +137,6 @@ dependencies = [ "lazy_static", "regex", "serde", - "serde_json", "uuid 0.8.2", ] @@ -163,9 +153,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" dependencies = [ "concurrent-queue", "event-listener", @@ -174,9 +164,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" dependencies = [ "flate2", "futures-core", @@ -187,23 +177,23 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" dependencies = [ + "async-lock", "async-task", "concurrent-queue", "fastrand", "futures-lite", - "once_cell", "slab", ] [[package]] name = "async-global-executor" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da5b41ee986eed3f524c380e6d64965aea573882a8907682ad100f7859305ca" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ "async-channel", "async-executor", @@ -216,31 +206,32 @@ dependencies = [ [[package]] name = "async-io" -version = "1.9.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" +checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" dependencies = [ + "async-lock", "autocfg 1.1.0", "concurrent-queue", "futures-lite", "libc", "log 0.4.17", - "once_cell", "parking", "polling", "slab", "socket2", "waker-fn", - "winapi 0.3.9", + "windows-sys", ] [[package]] name = "async-lock" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" dependencies = [ "event-listener", + "futures-lite", ] [[package]] @@ -253,7 +244,7 @@ dependencies = [ "async-global-executor", "async-io", "async-lock", - "crossbeam-utils 0.8.11", + "crossbeam-utils 0.8.14", "futures-channel", "futures-core", "futures-io", @@ -298,9 +289,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -315,9 +306,9 @@ checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" [[package]] name = "atomic-waker" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" +checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" [[package]] name = "atty" @@ -325,7 +316,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi 0.3.9", ] @@ -364,9 +355,9 @@ dependencies = [ "bytes 1.1.0", "hex", "http", - "hyper 0.14.20", + "hyper 0.14.23", "ring", - "time 0.3.14", + "time 0.3.17", "tokio", "tower", "tracing", @@ -529,7 +520,7 @@ dependencies = [ "percent-encoding 2.1.0", "regex", "ring", - "time 0.3.14", + "time 0.3.17", "tracing", ] @@ -580,7 +571,7 @@ dependencies = [ "fastrand", "http", "http-body", - "hyper 0.14.20", + "hyper 0.14.23", "hyper-rustls 0.22.1", "lazy_static", "pin-project-lite", @@ -613,7 +604,7 @@ dependencies = [ "futures-core", "http", "http-body", - "hyper 0.14.20", + "hyper 0.14.23", "once_cell", "percent-encoding 2.1.0", "pin-project-lite", @@ -662,10 +653,10 @@ version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e93b0c93a3b963da946a0b8ef3853a7252298eb75cdbfb21dad60f5ed0ded861" dependencies = [ - "itoa 1.0.3", + "itoa 1.0.5", "num-integer", "ryu", - "time 0.3.14", + "time 0.3.17", ] [[package]] @@ -695,9 +686,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.5.16" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" +checksum = "e5694b64066a2459918d8074c2ce0d5a88f409431994c2356617c8ae0c4721fc" dependencies = [ "async-trait", "axum-core", @@ -706,16 +697,16 @@ dependencies = [ "futures-util", "http", "http-body", - "hyper 0.14.20", - "itoa 1.0.3", + "hyper 0.14.23", + "itoa 1.0.5", "matchit", "memchr", "mime 0.3.16", "percent-encoding 2.1.0", "pin-project-lite", + "rustversion", "serde", "sync_wrapper", - "tokio", "tower", "tower-http", "tower-layer", @@ -724,9 +715,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.8" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" +checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34" dependencies = [ "async-trait", "bytes 1.1.0", @@ -734,15 +725,16 @@ dependencies = [ "http", "http-body", "mime 0.3.16", + "rustversion", "tower-layer", "tower-service", ] [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", @@ -774,9 +766,15 @@ dependencies = [ [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bimap" @@ -798,18 +796,19 @@ dependencies = [ [[package]] name = "bincode" -version = "2.0.0-rc.1" +version = "2.0.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f609ceb2c41b0d0277314a789ef0e7eb14593d5485f7c67320bed3924ebb1b33" +checksum = "7bb50c5a2ef4b9b1e7ae73e3a73b52ea24b20312d629f9c4df28260b7ad2c3c4" dependencies = [ "bincode_derive", + "serde", ] [[package]] name = "bincode_derive" -version = "2.0.0-rc.1" +version = "2.0.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "913287a8f3e00db4c7ae1b87e9b9b8cebd6b89217eaadfc281fa5c897da35dc3" +checksum = "0a45a23389446d2dd25dc8e73a7a3b3c43522b630cac068927f0649d43d719d2" dependencies = [ "virtue", ] @@ -858,16 +857,16 @@ dependencies = [ [[package]] name = "blocking" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" dependencies = [ "async-channel", + "async-lock", "async-task", "atomic-waker", "fastrand", "futures-lite", - "once_cell", ] [[package]] @@ -908,9 +907,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-tools" @@ -920,9 +919,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byte-unit" -version = "4.0.14" +version = "4.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ebf10dda65f19ff0f42ea15572a359ed60d7fc74fdc984d90310937be0014b" +checksum = "3348673602e04848647fffaa8e9a861e7b5d5cae6570727b41bde0f722514484" dependencies = [ "serde", "utf8-width", @@ -936,9 +935,9 @@ checksum = "be0fdd54b507df8f22012890aadd099979befdba27713c767993f8380112ca7c" [[package]] name = "bytemuck" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" [[package]] name = "byteorder" @@ -972,12 +971,6 @@ dependencies = [ "either", ] -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - [[package]] name = "cached" version = "0.39.0" @@ -988,7 +981,7 @@ dependencies = [ "async_once", "cached_proc_macro", "cached_proc_macro_types", - "futures 0.3.24", + "futures 0.3.26", "hashbrown", "instant", "lazy_static", @@ -1023,9 +1016,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cesu8" @@ -1047,16 +1040,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", "num-integer", "num-traits", "serde", - "time 0.1.44", + "time 0.1.45", "wasm-bindgen", "winapi 0.3.9", ] @@ -1086,7 +1079,7 @@ dependencies = [ "once_cell", "strsim", "termcolor", - "terminal_size 0.2.3", + "terminal_size", "textwrap 0.16.0", ] @@ -1128,6 +1121,16 @@ dependencies = [ "enso-prelude", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "combine" version = "3.8.1" @@ -1153,11 +1156,11 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" dependencies = [ - "cache-padded", + "crossbeam-utils 0.8.14", ] [[package]] @@ -1171,16 +1174,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "terminal_size 0.1.17", "unicode-width", - "winapi 0.3.9", + "windows-sys", ] [[package]] @@ -1203,8 +1205,8 @@ checksum = "22a3a81dfaf6b66bce5d159eddae701e3a002f194d378cbf7be5f053c281d9be" dependencies = [ "console-api", "crossbeam-channel", - "crossbeam-utils 0.8.11", - "futures 0.3.24", + "crossbeam-utils 0.8.14", + "futures 0.3.26", "hdrhistogram", "humantime 2.1.0", "prost-types", @@ -1230,18 +1232,18 @@ dependencies = [ [[package]] name = "const_format" -version = "0.2.26" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "939dc9e2eb9077e0679d2ce32de1ded8531779360b003b4a972a7a39ec263495" +checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.22" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" +checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650" dependencies = [ "proc-macro2", "quote", @@ -1360,7 +1362,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.11", + "crossbeam-utils 0.8.14", ] [[package]] @@ -1371,20 +1373,19 @@ checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.8.11", + "crossbeam-utils 0.8.14", ] [[package]] name = "crossbeam-epoch" -version = "0.9.10" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg 1.1.0", "cfg-if 1.0.0", - "crossbeam-utils 0.8.11", - "memoffset 0.6.5", - "once_cell", + "crossbeam-utils 0.8.14", + "memoffset", "scopeguard", ] @@ -1401,12 +1402,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if 1.0.0", - "once_cell", ] [[package]] @@ -1452,10 +1452,54 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.23" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "cxx" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322296e2f2e5af4270b54df9e85a02ff037e271af20ba3e7fe1575515dc840b8" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +checksum = "017a1385b05d631e7875b1f151c9f012d37b53491e2a87f65bff5c262b2111d8" dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c26bbb078acf09bc1ecda02d4223f03bdd28bd4874edcb0379138efc499ce971" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357f40d1f06a24b60ae1fe122542c1fb05d28d32acb2aed064e84bc2ad1e252e" +dependencies = [ + "proc-macro2", "quote", "syn", ] @@ -1497,9 +1541,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "dataview" @@ -1589,7 +1633,7 @@ dependencies = [ "ensogl-hardcoded-theme", "ensogl-text-msdf", "ide-view", - "parser-scala", + "parser", "span-tree", "uuid 0.8.2", "wasm-bindgen", @@ -1696,9 +1740,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", "crypto-common", @@ -1743,7 +1787,7 @@ dependencies = [ "enso-text", "failure", "itertools", - "parser-scala", + "parser", "regex", "serde", "uuid 0.8.2", @@ -1758,9 +1802,9 @@ checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d" [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encode_unicode" @@ -1792,7 +1836,7 @@ dependencies = [ "failure", "flatbuffers", "flatc-rust", - "futures 0.3.24", + "futures 0.3.26", "hex", "json-rpc", "mockall", @@ -1825,7 +1869,7 @@ dependencies = [ "aws-config", "aws-sdk-ecr", "aws-sdk-s3", - "base64 0.13.0", + "base64 0.13.1", "byte-unit", "bytes 1.1.0", "cached", @@ -1842,7 +1886,7 @@ dependencies = [ "flate2", "flume", "fs_extra", - "futures 0.3.24", + "futures 0.3.26", "futures-util", "glob", "handlebars", @@ -1870,7 +1914,7 @@ dependencies = [ "regex", "reqwest", "scopeguard", - "semver 1.0.14", + "semver 1.0.16", "serde", "serde_json", "serde_yaml", @@ -1884,7 +1928,7 @@ dependencies = [ "tracing-subscriber", "unicase 2.6.0", "url 2.3.0", - "uuid 1.1.2", + "uuid 1.2.2", "walkdir", "which", "whoami", @@ -1897,7 +1941,7 @@ version = "0.1.0" dependencies = [ "anyhow", "fn-error-context", - "futures 0.3.24", + "futures 0.3.26", "futures-util", "serde", "serde_json", @@ -1928,7 +1972,7 @@ dependencies = [ "enso-build", "enso-build-base", "enso-formatter", - "futures 0.3.24", + "futures 0.3.26", "futures-util", "glob", "humantime 2.1.0", @@ -1996,7 +2040,7 @@ dependencies = [ "config-reader", "enso-prelude", "ensogl", - "semver 1.0.14", + "semver 1.0.16", ] [[package]] @@ -2017,7 +2061,7 @@ name = "enso-debug-api" version = "0.1.0" dependencies = [ "derivative", - "futures 0.3.24", + "futures 0.3.26", "js-sys", "wasm-bindgen", "web-sys", @@ -2042,7 +2086,7 @@ dependencies = [ "enso-prelude", "enso-profiler", "ensogl-core", - "futures 0.3.24", + "futures 0.3.26", ] [[package]] @@ -2114,7 +2158,7 @@ dependencies = [ "ensogl-text-msdf", "failure", "flo_stream", - "futures 0.3.24", + "futures 0.3.26", "fuzzly", "ide-view", "itertools", @@ -2122,9 +2166,10 @@ dependencies = [ "json-rpc", "mockall", "nalgebra", + "parser", "parser-scala", "regex", - "semver 1.0.14", + "semver 1.0.16", "serde", "serde_json", "sha3", @@ -2211,7 +2256,7 @@ dependencies = [ "enso-executor", "enso-prelude", "flo_stream", - "futures 0.3.24", + "futures 0.3.26", ] [[package]] @@ -2233,7 +2278,7 @@ dependencies = [ "rand_distr", "serde", "serde_json", - "uuid 1.1.2", + "uuid 1.2.2", ] [[package]] @@ -2263,7 +2308,7 @@ dependencies = [ name = "enso-parser-jni" version = "0.1.0" dependencies = [ - "bincode 2.0.0-rc.1", + "bincode 2.0.0-rc.2", "enso-parser", "enso-prelude", "jni", @@ -2294,14 +2339,14 @@ dependencies = [ "enso-shapely", "enso-web", "failure", - "futures 0.3.24", + "futures 0.3.26", "gen-iter", "itertools", "lazy_static", "paste", "serde", "serde_json", - "smallvec 1.9.0", + "smallvec 1.10.0", "wasm-bindgen", "wasm-bindgen-test", "weak-table", @@ -2314,7 +2359,7 @@ version = "0.1.0" dependencies = [ "enso-profiler-macros", "enso-web", - "futures 0.3.24", + "futures 0.3.26", "serde", "serde_json", "wasm-bindgen", @@ -2327,7 +2372,7 @@ dependencies = [ "derivative", "enso-prelude", "enso-profiler", - "futures 0.3.24", + "futures 0.3.26", "serde", "serde_json", ] @@ -2337,7 +2382,7 @@ name = "enso-profiler-demo-data" version = "0.1.0" dependencies = [ "enso-profiler", - "futures 0.3.24", + "futures 0.3.26", ] [[package]] @@ -2359,7 +2404,7 @@ version = "0.1.0" dependencies = [ "enso-profiler", "enso-profiler-data", - "futures 0.3.24", + "futures 0.3.26", ] [[package]] @@ -2450,7 +2495,8 @@ dependencies = [ "enso-text", "failure", "flo_stream", - "futures 0.3.24", + "futures 0.3.26", + "parser", "parser-scala", "span-tree", "wasm-bindgen-test", @@ -2564,9 +2610,9 @@ dependencies = [ "num-traits", "num_enum", "rustc-hash", - "semver 1.0.14", + "semver 1.0.16", "serde", - "smallvec 1.9.0", + "smallvec 1.10.0", "typenum", "wasm-bindgen", "wasm-bindgen-test", @@ -2773,7 +2819,7 @@ dependencies = [ "ensogl-text", "ensogl-text-msdf", "ensogl-tooltip", - "futures 0.3.24", + "futures 0.3.26", "qstring", "serde", "url 2.3.0", @@ -2797,7 +2843,7 @@ dependencies = [ "ensogl-text", "ensogl-text-msdf", "ensogl-tooltip", - "futures 0.3.24", + "futures 0.3.26", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -3048,7 +3094,7 @@ dependencies = [ name = "ensogl-text" version = "0.1.0" dependencies = [ - "bincode 2.0.0-rc.1", + "bincode 2.0.0-rc.2", "const_format", "enso-frp", "enso-prelude", @@ -3102,7 +3148,7 @@ dependencies = [ "ensogl-text-embedded-fonts", "ensogl-text-font-family", "failure", - "futures 0.3.24", + "futures 0.3.26", "ide-ci", "js-sys", "nalgebra", @@ -3133,9 +3179,9 @@ dependencies = [ [[package]] name = "enum_dispatch" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb" +checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2" dependencies = [ "once_cell", "proc-macro2", @@ -3222,14 +3268,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -3252,9 +3298,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", "miniz_oxide", @@ -3266,8 +3312,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b02e0d3667b27514149c1ac9b372d700f3e6df4bbaf6b7c5df12915de2996049" dependencies = [ - "futures 0.3.24", - "smallvec 1.9.0", + "futures 0.3.26", + "smallvec 1.10.0", ] [[package]] @@ -3342,9 +3388,18 @@ dependencies = [ [[package]] name = "fragile" -version = "1.2.1" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7464c5c4a3f014d9b2ec4073650e5c06596f385060af740fc45ad5a19f959e8" +dependencies = [ + "fragile 2.0.0", +] + +[[package]] +name = "fragile" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85dcb89d2b10c5f6133de2efd8c11959ce9dbb46a2f7a4cab208c4eeda6ce1ab" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "fs-err" @@ -3388,9 +3443,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -3403,9 +3458,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -3413,15 +3468,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -3430,9 +3485,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-lite" @@ -3451,9 +3506,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -3462,15 +3517,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-timer" @@ -3480,9 +3535,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -3550,9 +3605,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -3563,21 +3618,21 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gloo-timers" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ "futures-channel", "futures-core", @@ -3645,9 +3700,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ "bytes 1.1.0", "fnv", @@ -3670,9 +3725,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "handlebars" -version = "4.3.5" +version = "4.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e4ab33f1213cdc25b5fa45c76881240cfe79284cf2b395e8b9e312a30a2fd" +checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" dependencies = [ "log 0.4.17", "pest", @@ -3697,7 +3752,7 @@ version = "7.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "byteorder", "flate2", "nom", @@ -3710,7 +3765,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "bitflags", "bytes 1.1.0", "headers-core", @@ -3744,6 +3799,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" @@ -3778,7 +3842,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes 1.1.0", "fnv", - "itoa 1.0.3", + "itoa 1.0.5", ] [[package]] @@ -3816,7 +3880,7 @@ checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" dependencies = [ "anyhow", "async-channel", - "base64 0.13.0", + "base64 0.13.1", "futures-lite", "http", "infer", @@ -3868,7 +3932,7 @@ dependencies = [ "log 0.3.9", "mime 0.2.6", "num_cpus", - "time 0.1.44", + "time 0.1.45", "traitobject", "typeable", "unicase 1.4.2", @@ -3877,9 +3941,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes 1.1.0", "futures-channel", @@ -3890,7 +3954,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.3", + "itoa 1.0.5", "pin-project-lite", "socket2", "tokio", @@ -3907,7 +3971,7 @@ checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" dependencies = [ "ct-logs", "futures-util", - "hyper 0.14.20", + "hyper 0.14.23", "log 0.4.17", "rustls 0.19.1", "rustls-native-certs", @@ -3918,13 +3982,13 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.0" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", - "hyper 0.14.20", - "rustls 0.20.6", + "hyper 0.14.23", + "rustls 0.20.8", "tokio", "tokio-rustls 0.23.4", ] @@ -3935,7 +3999,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.20", + "hyper 0.14.23", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -3948,7 +4012,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes 1.1.0", - "hyper 0.14.20", + "hyper 0.14.23", "native-tls", "tokio", "tokio-native-tls", @@ -3960,7 +4024,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5617e92fc2f2501c3e2bc6ce547cad841adba2bae5b921c7e52510beca6d084c" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "bytes 1.1.0", "http", "httpdate", @@ -3972,17 +4036,28 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.50" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", + "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "winapi 0.3.9", ] +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "ide-ci" version = "0.1.0" @@ -4009,7 +4084,7 @@ dependencies = [ "flate2", "flume", "fs_extra", - "futures 0.3.24", + "futures 0.3.26", "futures-util", "glob", "graphql_client", @@ -4040,7 +4115,7 @@ dependencies = [ "regex", "reqwest", "scopeguard", - "semver 1.0.14", + "semver 1.0.16", "serde", "serde_json", "serde_yaml", @@ -4057,7 +4132,7 @@ dependencies = [ "tracing-subscriber", "unicase 2.6.0", "url 2.3.0", - "uuid 1.1.2", + "uuid 1.2.2", "walkdir", "warp", "which", @@ -4089,7 +4164,7 @@ dependencies = [ "multi-map", "nalgebra", "ordered-float", - "parser-scala", + "parser", "serde", "serde_json", "span-tree", @@ -4195,7 +4270,7 @@ version = "0.1.0" dependencies = [ "analytics", "ast", - "base64 0.13.0", + "base64 0.13.1", "bimap", "engine-protocol", "enso-config", @@ -4258,9 +4333,9 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg 1.1.0", "hashbrown", @@ -4268,12 +4343,13 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.1" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfddc9561e8baf264e0e45e197fd7696320026eb10a8180340debc27b18f535b" +checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729" dependencies = [ "console", "number_prefix", + "portable-atomic", "unicode-width", ] @@ -4301,12 +4377,12 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" dependencies = [ "libc", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -4320,9 +4396,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "itertools" @@ -4341,9 +4417,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jni" @@ -4384,18 +4460,18 @@ dependencies = [ "enso-shapely", "enso-web", "failure", - "futures 0.3.24", + "futures 0.3.26", "serde", "serde_json", ] [[package]] name = "jsonwebtoken" -version = "8.1.1" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa4b4af834c6cfd35d8763d359661b90f2e45d8f750a0849156c7f4671af09c" +checksum = "09f4f04699947111ec1733e71778d763555737579e44b85844cae8e1940a1828" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "pem", "ring", "serde", @@ -4405,9 +4481,12 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] [[package]] name = "kernel32-sys" @@ -4486,15 +4565,24 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.137" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libm" -version = "0.2.5" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] [[package]] name = "linux-raw-sys" @@ -4548,7 +4636,7 @@ dependencies = [ "enso-prelude", "lazy_static", "regex", - "time 0.3.14", + "time 0.3.17", "tokio", "tokio-stream", ] @@ -4576,15 +4664,15 @@ dependencies = [ [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matchit" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" [[package]] name = "matrixmultiply" @@ -4607,7 +4695,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -4616,15 +4704,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "memoffset" version = "0.7.1" @@ -4667,9 +4746,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] @@ -4695,14 +4774,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log 0.4.17", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -4725,7 +4804,7 @@ checksum = "01458f8a19b10cb28195290942e3149161c75acf67ebc8fbf714ab67a2b943bc" dependencies = [ "cfg-if 0.1.10", "downcast", - "fragile", + "fragile 1.2.2", "lazy_static", "mockall_derive", "predicates", @@ -4808,14 +4887,14 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", ] [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -4831,9 +4910,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.37" +version = "0.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" dependencies = [ "cfg-if 0.1.10", "libc", @@ -4852,14 +4931,14 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags", "cfg-if 1.0.0", "libc", - "memoffset 0.7.1", + "memoffset", "pin-utils", "static_assertions", ] @@ -4872,14 +4951,23 @@ checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -4895,6 +4983,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -4949,28 +5047,28 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4978,15 +5076,6 @@ dependencies = [ "syn", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "number_prefix" version = "0.4.0" @@ -4995,9 +5084,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] @@ -5009,7 +5098,7 @@ source = "git+https://github.com/enso-org/octocrab#88f81c840085ce0aa591f9f53d6c6 dependencies = [ "arc-swap", "async-trait", - "base64 0.13.0", + "base64 0.13.1", "bytes 1.1.0", "cfg-if 1.0.0", "chrono", @@ -5027,9 +5116,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "oorandom" @@ -5045,9 +5134,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -5077,9 +5166,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ "autocfg 1.1.0", "cc", @@ -5090,18 +5179,18 @@ dependencies = [ [[package]] name = "ordered-float" -version = "3.1.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ffdb14730ed2ef599c65810c15b000896e21e8776b512de0db0c3d7335cc2a" +checksum = "d84eb1409416d254e4a9c8fa56cc24701755025b458f0fcd8e59e1f5f40c23bf" dependencies = [ "num-traits", ] [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "ouroboros" @@ -5126,6 +5215,12 @@ dependencies = [ "syn", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owned_ttf_parser" version = "0.15.2" @@ -5148,7 +5243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ "lock_api 0.3.4", - "parking_lot_core 0.6.2", + "parking_lot_core 0.6.3", "rustc_version 0.2.3", ] @@ -5159,14 +5254,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api 0.4.9", - "parking_lot_core 0.9.3", + "parking_lot_core 0.9.6", ] [[package]] name = "parking_lot_core" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" dependencies = [ "cfg-if 0.1.10", "cloudabi", @@ -5179,15 +5274,30 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", - "smallvec 1.9.0", - "windows-sys 0.36.1", + "smallvec 1.10.0", + "windows-sys", +] + +[[package]] +name = "parser" +version = "0.1.0" +dependencies = [ + "ast", + "enso-parser", + "enso-prelude", + "enso-profiler", + "enso-text", + "failure", + "serde", + "serde_json", + "uuid 0.8.2", ] [[package]] @@ -5197,12 +5307,10 @@ dependencies = [ "ast", "bytes 1.1.0", "console_error_panic_hook", - "enso-data-structures", "enso-prelude", "enso-profiler", - "enso-text", "failure", - "futures 0.3.24", + "futures 0.3.26", "ide-ci", "js-sys", "matches", @@ -5210,7 +5318,6 @@ dependencies = [ "serde", "serde_json", "tokio", - "uuid 0.8.2", "wasm-bindgen", "wasm-bindgen-test", "websocket", @@ -5218,15 +5325,15 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "path-absolutize" -version = "3.0.13" +version = "3.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3de4b40bd9736640f14c438304c09538159802388febb02c8abaae0846c1f13" +checksum = "0f1d4993b16f7325d90c18c3c6a3327db7808752db8d208cea0acee0abd52c52" dependencies = [ "path-dedot", ] @@ -5239,9 +5346,9 @@ checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" [[package]] name = "path-dedot" -version = "3.0.17" +version = "3.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d611d5291372b3738a34ebf0d1f849e58b1dcc1101032f76a346eaa1f8ddbb5b" +checksum = "9a81540d94551664b72b72829b12bd167c73c9d25fbac0e04fafa8023f7e4901" dependencies = [ "once_cell", ] @@ -5279,11 +5386,11 @@ checksum = "7a7cf3f8ecebb0f4895f4892a8be0a0dc81b498f9d56735cb769dc31bf00815b" [[package]] name = "pem" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", ] [[package]] @@ -5300,9 +5407,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pest" -version = "2.5.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" +checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" dependencies = [ "thiserror", "ucd-trie", @@ -5310,9 +5417,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344" +checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" dependencies = [ "pest", "pest_generator", @@ -5320,9 +5427,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c" +checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" dependencies = [ "pest", "pest_meta", @@ -5333,13 +5440,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20" +checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" dependencies = [ "once_cell", "pest", - "sha1 0.10.5", + "sha2", ] [[package]] @@ -5376,15 +5483,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "platforms" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8ec293fd25f7fcfeb7c70129241419a62c6200a26a725f680aff07c91d0ed05" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" dependencies = [ "serde", ] @@ -5419,16 +5526,16 @@ dependencies = [ [[package]] name = "polling" -version = "2.3.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" dependencies = [ "autocfg 1.1.0", "cfg-if 1.0.0", "libc", "log 0.4.17", "wepoll-ffi", - "winapi 0.3.9", + "windows-sys", ] [[package]] @@ -5437,11 +5544,17 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6519412c9e0d4be579b9f0618364d19cb434b324fc6ddb1b27b1e682c7105ed" +[[package]] +name = "portable-atomic" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b" + [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" @@ -5458,15 +5571,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" +checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" [[package]] name = "predicates-tree" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" +checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" dependencies = [ "predicates-core", "termtree", @@ -5484,13 +5597,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -5519,24 +5631,24 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.19" +version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.44" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.11.0" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c3c31cdec40583bb68f0b18403400d01ec4289c383aa047560439952c4dd7" +checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" dependencies = [ "bytes 1.1.0", "prost-derive", @@ -5544,9 +5656,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.0" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" +checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" dependencies = [ "anyhow", "itertools", @@ -5557,9 +5669,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.11.1" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dfaa718ad76a44b3415e6c4d53b17c8f99160dcb3a99b10470fce8ad43f6e3e" +checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" dependencies = [ "bytes 1.1.0", "prost", @@ -5594,9 +5706,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -5704,7 +5816,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", ] [[package]] @@ -5796,25 +5908,23 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" dependencies = [ - "autocfg 1.1.0", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", - "crossbeam-utils 0.8.11", + "crossbeam-utils 0.8.14", "num_cpus", ] @@ -5848,7 +5958,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "redox_syscall 0.2.16", "thiserror", ] @@ -5875,9 +5985,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -5890,11 +6000,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ - "base64 0.13.0", + "base64 0.21.0", "bytes 1.1.0", "encoding_rs", "futures-core", @@ -5902,8 +6012,8 @@ dependencies = [ "h2", "http", "http-body", - "hyper 0.14.20", - "hyper-rustls 0.23.0", + "hyper 0.14.23", + "hyper-rustls 0.23.2", "hyper-tls", "ipnet", "js-sys", @@ -5913,8 +6023,8 @@ dependencies = [ "once_cell", "percent-encoding 2.1.0", "pin-project-lite", - "rustls 0.20.6", - "rustls-pemfile 1.0.1", + "rustls 0.20.8", + "rustls-pemfile 1.0.2", "serde", "serde_json", "serde_urlencoded", @@ -5926,6 +6036,7 @@ dependencies = [ "url 2.3.0", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots", "winreg", @@ -5988,21 +6099,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.14", + "semver 1.0.16", ] [[package]] name = "rustix" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -6011,7 +6122,7 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "log 0.4.17", "ring", "sct 0.6.1", @@ -6020,9 +6131,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log 0.4.17", "ring", @@ -6048,23 +6159,23 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", ] [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.13.0", + "base64 0.21.0", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "rustybuzz" @@ -6074,7 +6185,7 @@ checksum = "a617c811f5c9a7060fe511d35d13bf5b9f0463ce36d63ce666d05779df2b4eba" dependencies = [ "bitflags", "bytemuck", - "smallvec 1.9.0", + "smallvec 1.10.0", "ttf-parser", "unicode-bidi-mirroring", "unicode-ccc", @@ -6084,9 +6195,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "safemem" @@ -6105,19 +6216,18 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] name = "scoped-tls" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" @@ -6125,6 +6235,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + [[package]] name = "sct" version = "0.6.1" @@ -6156,9 +6272,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -6169,9 +6285,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -6194,9 +6310,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" dependencies = [ "serde", ] @@ -6209,9 +6325,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] @@ -6239,9 +6355,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -6250,20 +6366,20 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ - "itoa 1.0.3", + "itoa 1.0.5", "ryu", "serde", ] [[package]] name = "serde_path_to_error" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "184c643044780f7ceb59104cef98a5a6f12cb2288a7bc701ab93a362b49fd47d" +checksum = "26b04f22b563c91331a10074bda3dd5492e3cc39d56bd557e91c0af42b6c7341" dependencies = [ "serde", ] @@ -6286,19 +6402,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.3", + "itoa 1.0.5", "ryu", "serde", ] [[package]] name = "serde_yaml" -version = "0.9.16" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b5b431e8907b50339b51223b97d102db8d987ced36f6e4d03621db9316c834" +checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" dependencies = [ "indexmap", - "itoa 1.0.3", + "itoa 1.0.5", "ryu", "serde", "unsafe-libyaml", @@ -6318,13 +6434,13 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -6344,7 +6460,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -6361,7 +6477,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -6416,7 +6532,7 @@ dependencies = [ "num-bigint", "num-traits", "thiserror", - "time 0.3.14", + "time 0.3.17", ] [[package]] @@ -6439,15 +6555,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "snafu" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2" +checksum = "cb0656e7e3ffb70f6c39b3c2a86332bb74aa3c679da781642590f3c1118c5045" dependencies = [ "backtrace", "doc-comment", @@ -6456,9 +6572,9 @@ dependencies = [ [[package]] name = "snafu-derive" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5" +checksum = "475b3bbe5245c26f2d8a6f62d67c1f30eb9fffeccee721c45d162c3ebbdf81b2" dependencies = [ "heck", "proc-macro2", @@ -6478,17 +6594,16 @@ dependencies = [ [[package]] name = "sourcemap" -version = "6.1.0" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad6f449ac2dc2eaa01e766408b76b55fc0a20c842b63aa11a8448caa72f50b" +checksum = "aebe057d110ddba043708da3fb010bf562ff6e9d4d60c9ee92860527bcbeccd6" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "if_chain", - "lazy_static", - "regex", "rustc_version 0.2.3", "serde", "serde_json", + "unicode-id", "url 2.3.0", ] @@ -6502,7 +6617,7 @@ dependencies = [ "enso-profiler", "enso-text", "failure", - "parser-scala", + "parser", "wasm-bindgen-test", ] @@ -6563,9 +6678,9 @@ checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -6592,9 +6707,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.26.4" +version = "0.26.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7890fff842b8db56f2033ebee8f6efe1921475c3830c115995552914fb967580" +checksum = "5c18a6156d1f27a9592ee18c1a846ca8dd5c258b7179fc193ae87c74ebb666f5" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", @@ -6632,23 +6747,13 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi 0.3.9", -] - [[package]] name = "terminal_size" version = "0.2.3" @@ -6656,14 +6761,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907" dependencies = [ "rustix", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "termtree" -version = "0.2.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" +checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" [[package]] name = "textwrap" @@ -6680,23 +6785,23 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" dependencies = [ - "terminal_size 0.2.3", + "terminal_size", ] [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -6714,9 +6819,9 @@ dependencies = [ [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -6725,21 +6830,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.14" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ - "itoa 1.0.3", - "libc", - "num_threads", + "itoa 1.0.5", + "serde", + "time-core", "time-macros", ] +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + [[package]] name = "time-macros" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] [[package]] name = "tinytemplate" @@ -6768,15 +6882,15 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.23.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg 1.1.0", "bytes 1.1.0", "libc", "memchr", - "mio 0.8.4", + "mio 0.8.5", "num_cpus", "parking_lot 0.12.1", "pin-project-lite", @@ -6784,7 +6898,7 @@ dependencies = [ "socket2", "tokio-macros", "tracing", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -6831,9 +6945,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -6886,16 +7000,16 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.6", + "rustls 0.20.8", "tokio", "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite", @@ -6969,30 +7083,47 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + [[package]] name = "tonic" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cd56bdb54ef93935a6a79dbd1d91f1ebd4c64150fd61654031fd6b8b775c91" +checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.13.0", + "base64 0.13.1", "bytes 1.1.0", "futures-core", "futures-util", "h2", "http", "http-body", - "hyper 0.14.20", + "hyper 0.14.23", "hyper-timeout", "percent-encoding 2.1.0", "pin-project", @@ -7030,9 +7161,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ "bitflags", "bytes 1.1.0", @@ -7049,9 +7180,9 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" @@ -7116,16 +7247,16 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "ansi_term", "matchers", + "nu-ansi-term", "once_cell", "regex", "sharded-slab", - "smallvec 1.9.0", + "smallvec 1.10.0", "thread_local", "tracing", "tracing-core", @@ -7140,9 +7271,9 @@ checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "ttf-parser" @@ -7156,14 +7287,14 @@ version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "byteorder", "bytes 1.1.0", "http", "httparse", "log 0.4.17", "rand 0.8.5", - "sha-1 0.10.0", + "sha-1 0.10.1", "thiserror", "url 2.3.0", "utf-8", @@ -7186,9 +7317,9 @@ checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" @@ -7216,9 +7347,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-bidi-mirroring" @@ -7238,11 +7369,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07547e3ee45e28326cc23faac56d44f58f16ab23e413db526debce3b0bfd2742" +[[package]] +name = "unicode-id" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d70b6494226b36008c8366c288d77190b3fad2eb4c10533139c1c1f461127f1a" + [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" @@ -7261,9 +7398,9 @@ checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" @@ -7288,9 +7425,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" +checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" [[package]] name = "untrusted" @@ -7345,18 +7482,17 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "serde", - "sha1 0.6.1", ] [[package]] name = "uuid" -version = "1.1.2" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "serde", ] @@ -7396,9 +7532,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "virtue" -version = "0.0.7" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757cfbfe0d17ee6f22fe97e536d463047d451b47cf9d11e2b7d1398b0ef274dd" +checksum = "7b60dcd6a64dd45abf9bd426970c9843726da7fc08f44cd6fcebf68c21220a63" [[package]] name = "void" @@ -7444,7 +7580,7 @@ dependencies = [ "futures-util", "headers", "http", - "hyper 0.14.20", + "hyper 0.14.23", "log 0.4.17", "mime 0.3.16", "mime_guess", @@ -7574,6 +7710,19 @@ dependencies = [ "quote", ] +[[package]] +name = "wasm-streams" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "weak-table" version = "0.3.2" @@ -7582,9 +7731,9 @@ checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" [[package]] name = "web-sys" -version = "0.3.55" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -7612,9 +7761,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki 0.22.0", ] @@ -7666,7 +7815,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2108c9c18a6e746addc085c18cedb66b672e8ffea6a993712decc295b0d8ae55" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "byteorder", "bytes 1.1.0", "httparse", @@ -7681,9 +7830,9 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6cae39139c6e837afebd915935e7adc8af5c28425935de606d0e8c9d3268f6" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "bytes 1.1.0", - "futures 0.3.24", + "futures 0.3.26", "native-tls", "rand 0.8.5", "tokio", @@ -7714,9 +7863,9 @@ dependencies = [ [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -7725,11 +7874,10 @@ dependencies = [ [[package]] name = "whoami" -version = "1.2.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" +checksum = "45dbc71f0cdca27dc261a9bd37ddec174e4a0af2b900b890f378460f745426e3" dependencies = [ - "bumpalo", "wasm-bindgen", "web-sys", ] @@ -7777,19 +7925,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -7797,85 +7932,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -7888,18 +7993,18 @@ dependencies = [ [[package]] name = "wiremock" -version = "0.5.14" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc3c7b7557dbfdad6431b5a51196c9110cef9d83f6a9b26699f35cdc0ae113ec" +checksum = "12316b50eb725e22b2f6b9c4cbede5b7b89984274d113a7440c86e5c3fc6f99b" dependencies = [ "assert-json-diff", "async-trait", - "base64 0.13.0", + "base64 0.13.1", "deadpool", - "futures 0.3.24", + "futures 0.3.26", "futures-timer", "http-types", - "hyper 0.14.20", + "hyper 0.14.23", "log 0.4.17", "once_cell", "regex", @@ -7922,13 +8027,13 @@ dependencies = [ name = "wstest" version = "0.1.0" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "clap 3.2.23", "either", "enso-prelude", - "futures 0.3.24", + "futures 0.3.26", "regex", - "time 0.3.14", + "time 0.3.17", "tokio", "tokio-stream", "url 2.3.0", @@ -7970,12 +8075,12 @@ checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" [[package]] name = "zip" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d" +checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080" dependencies = [ "byteorder", "crc32fast", - "crossbeam-utils 0.8.11", + "crossbeam-utils 0.8.14", "flate2", ] diff --git a/Cargo.toml b/Cargo.toml index d6d29212eb75..edd028090a0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ resolver = "2" # where plausible. members = [ "app/gui", + "app/gui/language/parser", "app/gui/enso-profiler-enso-data", "build/cli", "build/macros", diff --git a/app/gui/Cargo.toml b/app/gui/Cargo.toml index 2f3f38f689c3..1361dab8cd06 100644 --- a/app/gui/Cargo.toml +++ b/app/gui/Cargo.toml @@ -32,10 +32,11 @@ ensogl-hardcoded-theme = { path = "../../lib/rust/ensogl/app/theme/hardcoded" } ensogl-drop-manager = { path = "../../lib/rust/ensogl/component/drop-manager" } fuzzly = { path = "../../lib/rust/fuzzly" } ast = { path = "language/ast/impl" } +parser = { path = "language/parser" } +parser-scala = { path = "language/parser-scala" } ide-view = { path = "view" } engine-protocol = { path = "controller/engine-protocol" } json-rpc = { path = "../../lib/rust/json-rpc" } -parser-scala = { path = "language/parser" } span-tree = { path = "language/span-tree" } bimap = { version = "0.4.0" } console_error_panic_hook = { workspace = true } diff --git a/app/gui/controller/double-representation/Cargo.toml b/app/gui/controller/double-representation/Cargo.toml index f7a65b94d6af..e54379a77843 100644 --- a/app/gui/controller/double-representation/Cargo.toml +++ b/app/gui/controller/double-representation/Cargo.toml @@ -9,8 +9,8 @@ crate-type = ["cdylib", "rlib"] [dependencies] ast = { path = "../../language/ast/impl" } +parser = { path = "../../language/parser" } engine-protocol = { path = "../engine-protocol" } -parser-scala = { path = "../../language/parser" } enso-data-structures = { path = "../../../../lib/rust/data-structures" } enso-prelude = { path = "../../../../lib/rust/prelude" } enso-profiler = { path = "../../../../lib/rust/profiler" } diff --git a/app/gui/controller/double-representation/src/alias_analysis.rs b/app/gui/controller/double-representation/src/alias_analysis.rs index 644f3adace14..ed6599389262 100644 --- a/app/gui/controller/double-representation/src/alias_analysis.rs +++ b/app/gui/controller/double-representation/src/alias_analysis.rs @@ -240,13 +240,6 @@ impl AliasAnalyzer { self.process_assignment(&assignment); } else if let Some(lambda) = ast::macros::as_lambda(ast) { self.process_lambda(&lambda); - } else if let Ok(macro_match) = ast::known::Match::try_from(ast) { - // Macros (except for lambdas which were covered in the previous check) never introduce - // new scopes or different context. We skip the keywords ("if" in "if-then-else" is not - // an identifier) and process the matched subtrees as usual. - self.process_given_subtrees(macro_match.shape(), macro_match.iter_pat_match_subcrumbs()) - } else if let Ok(ambiguous) = ast::known::Ambiguous::try_from(ast) { - self.process_given_subtrees(ambiguous.shape(), ambiguous.iter_pat_match_subcrumbs()) } else if self.is_in_pattern() { // We are in the pattern (be it a lambda's or assignment's left side). Three options: // 1) This is a destructuring pattern match using infix syntax, like `head,tail`. @@ -371,8 +364,6 @@ mod tests { use super::test_utils::*; use super::*; - wasm_bindgen_test_configure!(run_in_browser); - /// Checks if actual observed sequence of located identifiers matches the expected one. /// Expected identifiers are described as code spans in the node's text representation. fn validate_identifiers( @@ -386,7 +377,7 @@ mod tests { } /// Runs the test for the given test case description. - fn run_case(parser: &parser_scala::Parser, case: Case) { + fn run_case(parser: &parser::Parser, case: Case) { debug!("\n===========================================================================\n"); debug!("Case: {}", case.code); let ast = parser.parse_line_ast(&case.code).unwrap(); @@ -397,15 +388,15 @@ mod tests { } /// Runs the test for the test case expressed using markdown notation. See `Case` for details. - fn run_markdown_case(parser: &parser_scala::Parser, marked_code: impl AsRef) { + fn run_markdown_case(parser: &parser::Parser, marked_code: impl AsRef) { debug!("Running test case for {}", marked_code.as_ref()); let case = Case::from_markdown(marked_code.as_ref()); run_case(parser, case) } - #[wasm_bindgen_test] + #[test] fn test_alias_analysis() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let test_cases = [ "»foo«", "«five» = 5", @@ -433,21 +424,11 @@ mod tests { "»A« -> »b«", "a -> »A« -> a", "a -> a -> »A«", - "x»,«y -> »B«", - "x»,«y -> y", - "x »,« »Y« -> _", "(»foo«)", "(«foo») = (»bar«)", "if »A« then »B«", "if »a« then »b« else »c«", - "case »foo« of\n »Number« a -> a\n »Wildcard« -> »bar«\n a»,«b -> a", - // === Macros Ambiguous === "(»foo«", - "if »a«", - "case »a«", - // "->»a«", // TODO [mwu] restore (and implement) when parser is able to parse this - // "a ->", // TODO [mwu] restore (and implement) when parser is able to parse this - // === Definition === "«foo» a b c = »foo« a »d«", "«foo» a b c = d -> a d", diff --git a/app/gui/controller/double-representation/src/connection.rs b/app/gui/controller/double-representation/src/connection.rs index ff4de4ccca8e..276787ab1e43 100644 --- a/app/gui/controller/double-representation/src/connection.rs +++ b/app/gui/controller/double-representation/src/connection.rs @@ -158,7 +158,7 @@ mod tests { use ast::crumbs; use ast::crumbs::InfixCrumb; - use parser_scala::Parser; + use parser::Parser; struct TestRun { graph: GraphInfo, @@ -182,7 +182,7 @@ mod tests { } fn from_main_def(code: impl Str) -> TestRun { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let module = parser.parse_module(code, default()).unwrap(); let definition = DefinitionInfo::from_root_line(&module.lines[0]).unwrap(); Self::from_definition(definition) @@ -199,15 +199,15 @@ mod tests { } } - #[wasm_bindgen_test] + #[test] pub fn connection_listing_test_plain() { use InfixCrumb::LeftOperand; use InfixCrumb::RightOperand; let code_block = r" -d,e = p +d = p a = d -b = e +b = d c = a + b fun a = a b f = fun 2"; @@ -221,21 +221,21 @@ f = fun 2"; assert_eq!(&c.destination.crumbs, &crumbs![RightOperand, LeftOperand]); let c = &run.connections[1]; - assert_eq!(run.endpoint_node_repr(&c.source), "b = e"); + assert_eq!(run.endpoint_node_repr(&c.source), "b = d"); assert_eq!(&c.source.crumbs, &crumbs![LeftOperand]); assert_eq!(run.endpoint_node_repr(&c.destination), "c = a + b"); assert_eq!(&c.destination.crumbs, &crumbs![RightOperand, RightOperand]); let c = &run.connections[2]; - assert_eq!(run.endpoint_node_repr(&c.source), "d,e = p"); - assert_eq!(&c.source.crumbs, &crumbs![LeftOperand, LeftOperand]); + assert_eq!(run.endpoint_node_repr(&c.source), "d = p"); + assert_eq!(&c.source.crumbs, &crumbs![LeftOperand]); assert_eq!(run.endpoint_node_repr(&c.destination), "a = d"); assert_eq!(&c.destination.crumbs, &crumbs![RightOperand]); let c = &run.connections[3]; - assert_eq!(run.endpoint_node_repr(&c.source), "d,e = p"); - assert_eq!(&c.source.crumbs, &crumbs![LeftOperand, RightOperand]); - assert_eq!(run.endpoint_node_repr(&c.destination), "b = e"); + assert_eq!(run.endpoint_node_repr(&c.source), "d = p"); + assert_eq!(&c.source.crumbs, &crumbs![LeftOperand]); + assert_eq!(run.endpoint_node_repr(&c.destination), "b = d"); assert_eq!(&c.destination.crumbs, &crumbs![RightOperand]); // Note that line `fun a = a b` des not introduce any connections, as it is a definition. @@ -243,13 +243,13 @@ f = fun 2"; assert_eq!(run.connections.len(), 4); } - #[wasm_bindgen_test] + #[test] pub fn inline_definition() { let run = TestRun::from_main_def("main = a"); assert!(run.connections.is_empty()); } - #[wasm_bindgen_test] + #[test] pub fn listing_dependent_nodes() { let code_block = "\ f,g = p @@ -259,7 +259,6 @@ f = fun 2"; d = a + b e = b"; let mut expected_dependent_nodes = HashMap::<&'static str, Vec<&'static str>>::new(); - expected_dependent_nodes.insert("f,g = p", vec!["a = f", "b = g", "d = a + b", "e = b"]); expected_dependent_nodes.insert("a = f", vec!["d = a + b"]); expected_dependent_nodes.insert("b = g", vec!["d = a + b", "e = b"]); expected_dependent_nodes.insert("c = 2", vec![]); diff --git a/app/gui/controller/double-representation/src/definition.rs b/app/gui/controller/double-representation/src/definition.rs index f5218e56ef3c..927e0e146416 100644 --- a/app/gui/controller/double-representation/src/definition.rs +++ b/app/gui/controller/double-representation/src/definition.rs @@ -11,7 +11,7 @@ use ast::crumbs::InfixCrumb; use ast::crumbs::Located; use ast::known; use ast::opr; -use parser_scala::Parser; +use parser::Parser; use std::iter::FusedIterator; @@ -284,9 +284,7 @@ impl DefinitionInfo { let elem = line.elem.ok_or(MissingLineWithAst)?; let off = line.off; let first_line = ast::BlockLine { elem, off }; - let is_orphan = false; - let ty = ast::BlockType::Discontinuous {}; - let block = ast::Block { ty, indent, empty_lines, first_line, lines, is_orphan }; + let block = ast::Block { indent, empty_lines, first_line, lines }; let body_ast = Ast::new(block, None); self.set_body_ast(body_ast); Ok(()) @@ -603,10 +601,6 @@ mod tests { use crate::module; use crate::INDENT; - use wasm_bindgen_test::wasm_bindgen_test; - - wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - fn assert_eq_strings(lhs: Vec, rhs: Vec) { let lhs = lhs.iter().map(|s| s.as_ref()).collect_vec(); let rhs = rhs.iter().map(|s| s.as_ref()).collect_vec(); @@ -621,9 +615,9 @@ mod tests { format!(" {line}") } - #[wasm_bindgen_test] + #[test] fn generating_definition_to_add() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let mut to_add = ToAdd { name: DefinitionName::new_method("Main", "add"), explicit_parameter_names: vec!["arg1".into(), "arg2".into()], @@ -649,9 +643,9 @@ mod tests { assert_eq!(ast.repr(), "Main.add arg1 arg2 =\n arg1 + arg2\n arg1 - arg2"); } - #[wasm_bindgen_test] + #[test] fn definition_name_tests() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let ast = parser.parse_line_ast("Foo.Bar.baz").unwrap(); let name = DefinitionName::from_ast(&ast).unwrap(); @@ -664,16 +658,16 @@ mod tests { assert_eq!(ast.get_traversing(&name.extended_target[1].crumbs).unwrap().repr(), "Bar"); } - #[wasm_bindgen_test] + #[test] fn definition_name_rejecting_incomplete_names() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let ast = parser.parse_line_ast("Foo. .baz").unwrap(); assert!(DefinitionName::from_ast(&ast).is_none()); } - #[wasm_bindgen_test] + #[test] fn definition_info_name() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let ast = parser.parse_line_ast("Foo.bar a b c = baz").unwrap(); let definition = DefinitionInfo::from_root_line_ast(&ast).unwrap(); @@ -681,9 +675,9 @@ mod tests { assert_eq!(ast.get_traversing(&definition.name.crumbs).unwrap().repr(), "Foo.bar"); } - #[wasm_bindgen_test] + #[test] fn located_definition_args() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let ast = parser.parse_line_ast("foo bar baz = a + b + c").unwrap(); let definition = DefinitionInfo::from_root_line_ast(&ast).unwrap(); let (arg0, arg1) = definition.args.expect_tuple(); @@ -700,7 +694,7 @@ mod tests { assert_eq!(ast.get_traversing(&arg1.crumbs).unwrap(), &arg1.item); } - #[wasm_bindgen_test] + #[test] fn match_is_not_definition() { let cons = Ast::cons("Foo"); let arg = Ast::number(5); @@ -723,28 +717,24 @@ mod tests { assert!(def_opt.is_some()); } - #[wasm_bindgen_test] + #[test] fn list_definition_test() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); - // TODO [mwu] - // Due to a parser bug, extension methods defining operators cannot be currently - // correctly recognized. When it is fixed, the following should be also supported - // and covered in test: `Int.+ a = _` and `Int.+ = _`. - // Issue link: https://github.com/enso-org/enso/issues/565 let definition_lines = vec![ "main = _", "Foo.Bar.foo = _", "Foo.Bar.baz a b = _", - "+ = _", + "+ a = _", + "Int.+ a = _", "bar = _", "add a b = 50", "* a b = _", ]; let expected_def_names_in_module = - vec!["main", "Foo.Bar.foo", "Foo.Bar.baz", "+", "bar", "add", "*"]; + vec!["main", "Foo.Bar.foo", "Foo.Bar.baz", "+", "Int.+", "bar", "add", "*"]; // In definition there are no extension methods nor arg-less definitions. - let expected_def_names_in_def = vec!["add", "*"]; + let expected_def_names_in_def = vec!["+", "add", "*"]; // === Program with definitions in root === let program = definition_lines.join("\n"); @@ -770,7 +760,7 @@ mod tests { assert_eq_strings(to_names(&nested_defs), expected_def_names_in_def); } - #[wasm_bindgen_test] + #[test] fn finding_root_definition() { let program_to_expected_main_pos = vec![ ("main = bar", 0), @@ -780,7 +770,7 @@ mod tests { ("foo = bar\n\nmain = bar", 2), ]; - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let main_id = Id::new_plain_name("main"); for (program, expected_line_index) in program_to_expected_main_pos { let module = parser.parse_module(program, default()).unwrap(); @@ -793,7 +783,7 @@ mod tests { } } - #[wasm_bindgen_test] + #[test] fn getting_nested_definition() { let program = r" main = @@ -806,7 +796,7 @@ main = add foo bar"; - let module = parser_scala::Parser::new_or_panic().parse_module(program, default()).unwrap(); + let module = parser::Parser::new().parse_module(program, default()).unwrap(); let check_def = |id, expected_body| { let definition = module::get_definition(&module, &id).unwrap(); assert_eq!(definition.body().repr(), expected_body); diff --git a/app/gui/controller/double-representation/src/graph.rs b/app/gui/controller/double-representation/src/graph.rs index 12e6aac1fc52..ffafc50ef9f3 100644 --- a/app/gui/controller/double-representation/src/graph.rs +++ b/app/gui/controller/double-representation/src/graph.rs @@ -225,29 +225,26 @@ mod tests { use ast::macros::DocumentationCommentInfo; use ast::test_utils::expect_single_line; use ast::HasRepr; - use wasm_bindgen_test::wasm_bindgen_test; - - wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); /// Takes a program with main definition in root and returns main's graph. - fn main_graph(parser: &parser_scala::Parser, program: impl Str) -> GraphInfo { - let module = parser.parse_module(program.into(), default()).unwrap(); + fn main_graph(parser: &parser::Parser, program: impl Str) -> GraphInfo { + let module = parser.parse_module(program.as_ref(), default()).unwrap(); let name = DefinitionName::new_plain("main"); let main = module.def_iter().find_by_name(&name).unwrap(); GraphInfo::from_definition(main.item) } - fn find_graph(parser: &parser_scala::Parser, program: impl Str, name: impl Str) -> GraphInfo { - let module = parser.parse_module(program.into(), default()).unwrap(); + fn find_graph(parser: &parser::Parser, program: impl Str, name: impl Str) -> GraphInfo { + let module = parser.parse_module(program.as_ref(), default()).unwrap(); let crumbs = name.into().split('.').map(DefinitionName::new_plain).collect(); let id = Id { crumbs }; let definition = get_definition(&module, &id).unwrap(); GraphInfo::from_definition(definition) } - #[wasm_bindgen_test] + #[test] fn detect_a_node() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); // Each of these programs should have a `main` definition with a single `2+2` node. let programs = vec![ "main = 2+2", @@ -265,8 +262,8 @@ mod tests { } } - fn new_expression_node(parser: &parser_scala::Parser, expression: &str) -> NodeInfo { - let node_ast = parser.parse(expression.to_string(), default()).unwrap(); + fn new_expression_node(parser: &parser::Parser, expression: &str) -> NodeInfo { + let node_ast = parser.parse(expression, default()); let line_ast = expect_single_line(&node_ast).clone(); NodeInfo::from_main_line_ast(&line_ast).unwrap() } @@ -281,16 +278,16 @@ mod tests { fn assert_same(left: &NodeInfo, right: &NodeInfo) { assert_eq!(left.id(), right.id()); assert_eq!( - left.documentation.as_ref().map(DocumentationCommentInfo::to_string), - right.documentation.as_ref().map(DocumentationCommentInfo::to_string) + left.documentation.as_ref().map(DocumentationCommentInfo::pretty_text), + right.documentation.as_ref().map(DocumentationCommentInfo::pretty_text) ); assert_eq!(left.main_line.repr(), right.main_line.repr()); } - #[wasm_bindgen_test] + #[test] fn add_node_to_graph_with_single_line() { let program = "main = print \"hello\""; - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let mut graph = main_graph(&parser, program); let nodes = graph.nodes(); assert_eq!(nodes.len(), 1); @@ -310,14 +307,14 @@ mod tests { assert_all(nodes.as_slice(), &[node_to_add1, node_to_add0, initial_node]); } - #[wasm_bindgen_test] + #[test] fn add_node_to_graph_with_multiple_lines() { // TODO [dg] Also add test for binding node when it's possible to update its id. let program = r#"main = foo = node foo a = not_node print "hello""#; - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let mut graph = main_graph(&parser, program); let node_to_add0 = new_expression_node(&parser, "4 + 4"); @@ -365,7 +362,7 @@ mod tests { assert_eq!(graph.nodes()[1].expression().repr(), "not_node"); } - #[wasm_bindgen_test] + #[test] fn add_node_to_graph_with_blank_line() { // The trailing `foo` definition is necessary for the blank line after "node2" to be // included in the `main` block. Otherwise, the block would end on "node2" and the blank @@ -376,7 +373,7 @@ mod tests { node2 foo = 5"; - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let mut graph = main_graph(&parser, program); let id2 = graph.nodes()[0].id(); @@ -396,15 +393,14 @@ foo = 5"; node1 node2 node3 - node4 -"; + node4"; // `foo` is not part of expected code, as it belongs to module, not `main` graph. graph.expect_code(expected_code); } - #[wasm_bindgen_test] + #[test] fn multiple_node_graph() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let program = r" main = ## Faux docstring @@ -433,9 +429,9 @@ main = assert_eq!(nodes.len(), 4); } - #[wasm_bindgen_test] + #[test] fn removing_node_from_graph() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let program = r" main = foo = 2 + 2 @@ -459,9 +455,9 @@ main = graph.expect_code(expected_code); } - #[wasm_bindgen_test] + #[test] fn removing_last_node_from_graph() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let program = r" main = foo = 2 + 2"; @@ -477,9 +473,9 @@ main = graph.expect_code("main = Nothing"); } - #[wasm_bindgen_test] + #[test] fn add_first_node_to_empty_graph() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let program = r"main = Nothing"; let mut graph = main_graph(&parser, program); assert!(graph.nodes().is_empty()); @@ -489,15 +485,14 @@ main = assert_eq!(graph.nodes()[0].expression().repr(), "node0"); } - - #[wasm_bindgen_test] + #[test] fn editing_nodes_expression_in_graph() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let program = r" main = foo = 2 + 2 bar = 3 + 17"; - let new_expression = parser.parse("print \"HELLO\"".to_string(), default()).unwrap(); + let new_expression = parser.parse("print \"HELLO\"", default()); let new_expression = expect_single_line(&new_expression).clone(); let mut graph = main_graph(&parser, program); diff --git a/app/gui/controller/double-representation/src/import.rs b/app/gui/controller/double-representation/src/import.rs index 85b15c338cb2..9bbb84958216 100644 --- a/app/gui/controller/double-representation/src/import.rs +++ b/app/gui/controller/double-representation/src/import.rs @@ -5,11 +5,7 @@ use crate::prelude::*; use crate::name::NamePath; use crate::name::QualifiedName; -use ast::known; use ast::Ast; -use ast::HasRepr; -use serde::Deserialize; -use serde::Serialize; use std::collections::BTreeSet; @@ -18,7 +14,6 @@ use std::collections::BTreeSet; // === Constants === // ================= -const LIST_SEPARATOR: char = ','; const ALIAS_KEYWORD: &str = "as"; const ALL_KEYWORD: &str = "all"; const HIDING_KEYWORD: &str = "hiding"; @@ -40,7 +35,7 @@ pub type Id = u64; /// A structure describing what names are imported from the module in a specific import declaration. #[allow(missing_docs)] -#[derive(Clone, Debug, Eq, Deserialize, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum ImportedNames { /// The import is `import [as ]` and only module name is imported. Module { alias: Option }, @@ -53,35 +48,8 @@ pub enum ImportedNames { List { names: BTreeSet }, } -impl ImportedNames { - /// Create [`ImportedNames`] structure from the second `Match` segment body. - /// - /// The unqualified imports are always parsed as [`Match`](crate::Shape::Match) AST node, where - /// the second segment starts from `import` and ends with end of the import declaration. Thus, - /// the second segment body may be `all`, `all hiding `, or just - /// comma separated name list. - fn from_unqualified_import_match_second_segment(segment: impl AsRef) -> Self { - let is_token_sep = |c: char| c.is_ascii_whitespace() || c == LIST_SEPARATOR; - let scope_split = segment.as_ref().split(is_token_sep); - let mut scope_tokens = scope_split.filter(|tok| !tok.is_empty()); - let first_token = scope_tokens.next(); - let second_token = scope_tokens.next(); - let third_and_further_tokens = scope_tokens; - match (first_token, second_token) { - (Some("all"), Some("hiding")) => - Self::AllExcept { not_imported: third_and_further_tokens.map(Into::into).collect() }, - (Some("all"), _) => Self::All, - (first_name, second_name) => { - let all_names = - first_name.into_iter().chain(second_name).chain(third_and_further_tokens); - Self::List { names: all_names.map(Into::into).collect() } - } - } - } -} - /// Representation of a single import declaration. -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize, Hash)] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] pub struct Info { /// The path of the qualified name of the imported module. pub module: NamePath, @@ -110,55 +78,27 @@ impl Info { QualifiedName::from_all_segments(&self.module) } - /// Construct from an AST. Fails if the Ast is not an import declaration. + /// Construct from an AST, if the Ast is an import declaration. pub fn from_ast(ast: &Ast) -> Option { - let macro_match = known::Match::try_from(ast).ok()?; - Self::from_match(macro_match) - } - - /// Construct from a macro match AST. Fails if the Ast is not an import declaration. - pub fn from_match(ast: known::Match) -> Option { - if ast::macros::is_match_qualified_import(&ast) { - Some(Self { - module: Self::module_name_from_str(ast.segs.head.body.repr()), - // TODO[ao] the current parser does not recognize aliases for imports. Should be - // fixed with the new parser. Once new parser will be integrated, the alias - // support will be implemented as task - // https://www.pivotaltracker.com/story/show/183590537 - imported: ImportedNames::Module { alias: None }, - }) - } else if ast::macros::is_match_unqualified_import(&ast) { - let module = ast.segs.head.body.repr(); - let imported = ast.segs.tail.first().map_or_default(|s| s.body.repr()); - Some(Self::from_module_and_scope_str(module, imported)) + if let ast::Shape::Tree(ast::Tree { + type_info: ast::TreeType::Import { module, imported }, + .. + }) = ast.shape() + { + let module = module.clone(); + let imported = match imported.clone() { + ast::ImportedNames::All { except } if except.is_empty() => ImportedNames::All, + ast::ImportedNames::All { except } => + ImportedNames::AllExcept { not_imported: except }, + ast::ImportedNames::List { names } => ImportedNames::List { names }, + ast::ImportedNames::Module { alias } => ImportedNames::Module { alias }, + }; + Some(Info { module, imported }) } else { None } } - /// Create [`Info`] from unqualified import segment's body representations. - /// - /// The unqualified imports are always parsed as [`Match`](crate::Shape::Match) AST node, where - /// the first segment contains keyword `from` and module name, and second segment the rest of - /// the import. - fn from_module_and_scope_str(module: impl AsRef, imported: impl AsRef) -> Self { - Self { - module: Self::module_name_from_str(module), - imported: ImportedNames::from_unqualified_import_match_second_segment(imported), - } - } - - fn module_name_from_str(module: impl AsRef) -> NamePath { - let name = module.as_ref().trim(); - if name.is_empty() { - default() - } else { - let segments = name.split(ast::opr::predefined::ACCESS); - let trimmed = segments.map(str::trim); - trimmed.map(Into::into).collect() - } - } - /// Return the ID of the import. /// /// The ID is based on a hash of the qualified name of the imported target. This ID is GUI @@ -211,7 +151,7 @@ impl Display for Info { #[cfg(test)] mod tests { use super::*; - use parser_scala::Parser; + use parser::Parser; struct Fixture { parser: Parser, @@ -219,7 +159,7 @@ mod tests { impl Fixture { fn new() -> Self { - Self { parser: Parser::new_or_panic() } + Self { parser: Parser::new() } } fn run_case(&self, code: &str, expected: Info) { @@ -229,7 +169,7 @@ mod tests { } } - #[wasm_bindgen_test] + #[test] fn qualified_import_info_from_ast() { let test = Fixture::new(); let make_info = |module: &[&str]| Info { @@ -241,16 +181,12 @@ mod tests { let normal_case_expected = make_info(&["Standard", "Base", "Data"]); test.run_case(normal_case, normal_case_expected); - let weird_spaces = "import Standard .Base . Data "; - let weird_spaces_expected = make_info(&["Standard", "Base", "Data"]); - test.run_case(weird_spaces, weird_spaces_expected); - let single_segment = "import local"; let single_segment_expected = make_info(&["local"]); test.run_case(single_segment, single_segment_expected); } - #[wasm_bindgen_test] + #[test] fn unrestricted_import_info_from_ast() { let test = Fixture::new(); let make_info = |module: &[&str]| Info { @@ -261,13 +197,9 @@ mod tests { let normal_case = "from Standard.Base import all"; let normal_case_expected = make_info(&["Standard", "Base"]); test.run_case(normal_case, normal_case_expected); - - let weird_spaces = "from Standard . Base import all "; - let weird_spaces_expected = make_info(&["Standard", "Base"]); - test.run_case(weird_spaces, weird_spaces_expected); } - #[wasm_bindgen_test] + #[test] fn restricted_import_info_from_ast() { let test = Fixture::new(); let make_info = |module: &[&str], names: &[&str]| Info { @@ -288,7 +220,7 @@ mod tests { test.run_case(single_name, single_name_expected); } - #[wasm_bindgen_test] + #[test] fn hiding_import_info_from_ast() { let test = Fixture::new(); let make_info = |module: &[&str], hidden_names: &[&str]| Info { diff --git a/app/gui/controller/double-representation/src/lib.rs b/app/gui/controller/double-representation/src/lib.rs index ad2e8dc2d783..3be0d97a97e7 100644 --- a/app/gui/controller/double-representation/src/lib.rs +++ b/app/gui/controller/double-representation/src/lib.rs @@ -65,11 +65,6 @@ pub mod prelude { pub use enso_prelude::*; pub use enso_profiler as profiler; pub use enso_profiler::prelude::*; - - #[cfg(test)] - pub use wasm_bindgen_test::wasm_bindgen_test; - #[cfg(test)] - pub use wasm_bindgen_test::wasm_bindgen_test_configure; } @@ -206,7 +201,7 @@ mod tests { use crate::definition::DefinitionProvider; use ast::macros::DocumentationCommentInfo; - use parser_scala::Parser; + use parser::Parser; /// Expect `main` method, where first line is a documentation comment. @@ -229,9 +224,9 @@ mod tests { assert_eq!(doc.line().repr(), doc2.line().repr()) } - #[wasm_bindgen_test] + #[test] fn parse_single_line_comment() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); // Typical single line case. let code = r#" @@ -266,15 +261,15 @@ main = run_case(&parser, code, expected); } - #[wasm_bindgen_test] + #[test] fn parse_multi_line_comment() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let code = r#" main = ## First line Second line node"#; - let expected = " First line\n Second line"; + let expected = " First line\nSecond line"; run_case(&parser, code, expected); } } diff --git a/app/gui/controller/double-representation/src/module.rs b/app/gui/controller/double-representation/src/module.rs index 41a276942d73..d13e7f0a5fce 100644 --- a/app/gui/controller/double-representation/src/module.rs +++ b/app/gui/controller/double-representation/src/module.rs @@ -208,7 +208,7 @@ impl Info { // TODO [mwu] // Ideally we should not require parser but should use some sane way of generating AST from // the `ImportInfo` value. - pub fn add_import(&mut self, parser: &parser_scala::Parser, to_add: import::Info) -> usize { + pub fn add_import(&mut self, parser: &parser::Parser, to_add: import::Info) -> usize { // Find last import that is not "after" the added one lexicographically. let previous_import = self.enumerate_imports().take_while(|(_, import)| &to_add > import).last(); @@ -224,7 +224,7 @@ impl Info { /// For more details the mechanics see [`add_import`] documentation. pub fn add_import_if_missing( &mut self, - parser: &parser_scala::Parser, + parser: &parser::Parser, to_add: import::Info, ) -> Option { (!self.contains_import(to_add.id())).then(|| self.add_import(parser, to_add)) @@ -279,7 +279,7 @@ impl Info { &mut self, method: definition::ToAdd, location: Placement, - parser: &parser_scala::Parser, + parser: &parser::Parser, ) -> FallibleResult { let no_indent = 0; let definition_ast = method.ast(no_indent, parser)?; @@ -509,13 +509,10 @@ mod tests { use crate::definition::DefinitionName; use engine_protocol::language_server::MethodPointer; - use wasm_bindgen_test::wasm_bindgen_test; - wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - - #[wasm_bindgen_test] + #[test] fn import_listing() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let expect_imports = |code: &str, expected: &[&[&str]]| { let ast = parser.parse_module(code, default()).unwrap(); let info = Info { ast }; @@ -536,9 +533,9 @@ mod tests { ]]); } - #[wasm_bindgen_test] + #[test] fn import_adding_and_removing() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let code = "import Foo.Bar.Baz"; let ast = parser.parse_module(code, default()).unwrap(); let mut info = Info { ast }; @@ -565,9 +562,9 @@ mod tests { info.expect_code("import Bar.Gar"); } - #[wasm_bindgen_test] + #[test] fn implicit_method_resolution() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let module_name = QualifiedName::from_all_segments(["local", "ProjectName", "Main"]).unwrap(); let expect_find = |method: &MethodPointer, code, expected: &definition::Id| { @@ -623,7 +620,7 @@ mod tests { expect_not_found(&ptr, "bar a b = a + b"); } - #[wasm_bindgen_test] + #[test] fn test_definition_location() { let code = r" some def = @@ -639,13 +636,13 @@ other def = last def = inline expression"; - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let module = parser.parse_module(code, default()).unwrap(); let module = Info { ast: module }; let id = definition::Id::new_plain_name("other"); let span = definition_span(&module.ast, &id).unwrap(); - assert!(code[span].ends_with("last line of other def\n")); + assert!(code[span].ends_with("last line of other def")); let id = definition::Id::new_plain_name("last"); let span = definition_span(&module.ast, &id).unwrap(); @@ -656,9 +653,9 @@ last def = inline expression"; assert!(code[span].ends_with("nested body")); } - #[wasm_bindgen_test] + #[test] fn add_method() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let module = r#"Main.method1 arg = body main = Main.method1 10"#; diff --git a/app/gui/controller/double-representation/src/node.rs b/app/gui/controller/double-representation/src/node.rs index 307434a9342b..f2f3f73cb960 100644 --- a/app/gui/controller/double-representation/src/node.rs +++ b/app/gui/controller/double-representation/src/node.rs @@ -237,7 +237,7 @@ impl NodeInfo { } /// Obtain documentation text. - pub fn documentation_text(&self) -> Option { + pub fn documentation_text(&self) -> Option { self.documentation.as_ref().map(|doc| doc.pretty_text()) } } diff --git a/app/gui/controller/double-representation/src/refactorings/collapse.rs b/app/gui/controller/double-representation/src/refactorings/collapse.rs index b50c9ee9de27..926b00d55d87 100644 --- a/app/gui/controller/double-representation/src/refactorings/collapse.rs +++ b/app/gui/controller/double-representation/src/refactorings/collapse.rs @@ -16,7 +16,7 @@ use crate::node::NodeInfo; use ast::crumbs::Located; use ast::BlockLine; -use parser_scala::Parser; +use parser::Parser; use std::collections::BTreeSet; @@ -441,9 +441,9 @@ mod tests { } #[allow(unused_parens)] // False warning. - #[wasm_bindgen_test] + #[test] fn test_collapse() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let module_name = "Main".to_owned(); let introduced_name = Identifier::try_from("custom_new").unwrap(); let refactored_name = DefinitionName::new_plain("custom_old"); diff --git a/app/gui/controller/double-representation/src/text.rs b/app/gui/controller/double-representation/src/text.rs index 41b36ef27d28..bdbb79ceed29 100644 --- a/app/gui/controller/double-representation/src/text.rs +++ b/app/gui/controller/double-representation/src/text.rs @@ -185,7 +185,7 @@ mod test { use ast::HasIdMap; use enso_prelude::default; - use parser_scala::Parser; + use parser::Parser; use uuid::Uuid; /// A sample text edit used to test "text api" properties. @@ -244,10 +244,8 @@ mod test { fn assert_edit_keeps_main_node_ids(&self, parser: &Parser) { let ast1 = parser.parse_module(&self.code, default()).unwrap(); let mut id_map = ast1.id_map(); - apply_code_change_to_id_map(&mut id_map, &self.change, &self.code); let code2 = self.resulting_code(); - let ast2 = parser.parse_module(code2, id_map.clone()).unwrap(); self.assert_same_node_ids(&ast1, &ast2); } @@ -257,8 +255,6 @@ mod test { fn assert_same_node_ids(&self, ast1: &ast::known::Module, ast2: &ast::known::Module) { let ids1 = main_nodes(ast1); let ids2 = main_nodes(ast2); - debug!("IDs1: {ids1:?}"); - debug!("IDs2: {ids2:?}"); assert_eq!(ids1, ids2, "Node ids mismatch in {self:?}"); } } @@ -300,9 +296,9 @@ mod test { assert_eq!(case.resulting_code(), "fooc"); } - #[wasm_bindgen_test] + #[test] fn applying_code_changes_to_id_map() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); // All the cases describe edit to a middle line in three line main definition. let cases = [ diff --git a/app/gui/language/ast/impl/Cargo.toml b/app/gui/language/ast/impl/Cargo.toml index a591132ac779..c32a7705ae5e 100644 --- a/app/gui/language/ast/impl/Cargo.toml +++ b/app/gui/language/ast/impl/Cargo.toml @@ -13,7 +13,6 @@ failure = { workspace = true } lazy_static = { workspace = true } regex = { workspace = true } serde = { version = "1.0", features = ["derive", "rc"] } -serde_json = { workspace = true } uuid = { version = "0.8.1", features = ["serde", "v4", "wasm-bindgen"] } ast-macros = { path = "../macros" } enso-data-structures = { path = "../../../../../lib/rust/data-structures" } diff --git a/app/gui/language/ast/impl/src/crumbs.rs b/app/gui/language/ast/impl/src/crumbs.rs index 78d40ae07382..e1897caf2610 100644 --- a/app/gui/language/ast/impl/src/crumbs.rs +++ b/app/gui/language/ast/impl/src/crumbs.rs @@ -7,10 +7,9 @@ use enso_text::index::*; use crate::enumerate_non_empty_lines; use crate::known; use crate::HasTokens; -use crate::MacroPatternMatch; use crate::Shape; -use crate::Shifted; use crate::ShiftedVec1; +use crate::SpanSeed; use crate::TokenConsumer; use enso_text as text; @@ -162,41 +161,6 @@ macro_rules! crumbs { /// /// Crumbs are potentially invalidated by any AST change. -// === InvalidSuffix === - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct InvalidSuffixCrumb; - - -// === TextLineFmt === - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TextLineFmtCrumb { - pub segment_index: usize, -} - - -// === TextBlockFmt === - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TextBlockFmtCrumb { - pub text_line_index: usize, - pub segment_index: usize, -} - - -// === TextUnclosed === - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TextUnclosedCrumb { - pub text_line_crumb: TextLineFmtCrumb, -} - - // === Prefix === #[allow(missing_docs)] @@ -266,119 +230,6 @@ pub enum BlockCrumb { } -// === Import === - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum ImportCrumb { - Path { index: usize }, - Rename, - OnlyNames { index: usize }, - HidingNames { index: usize }, -} - -// === Export === - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum ExportCrumb { - Path { index: usize }, - Rename, - OnlyNames { index: usize }, - HidingNames { index: usize }, -} - - -// === Mixfix === - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum MixfixCrumb { - Name { index: usize }, - Args { index: usize }, -} - - -// === Group === - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct GroupCrumb; - - -// === Def === - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum DefCrumb { - Name, - Args { index: usize }, - Body, -} - - -// === Match === - -#[allow(missing_docs)] -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum MatchCrumb { - Pfx { val: Vec }, - Segs { val: SegmentMatchCrumb, index: usize }, -} - -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum PatternMatchCrumb { - Begin, - End, - Nothing, - Build, - Err, - Tok, - Blank, - Var, - Cons, - Opr, - Annotation, - Mod, - Num, - Text, - Block, - Macro, - Invalid, - Except, - Tag, - Cls, - Or, - Seq { right: bool }, - Many { index: usize }, -} - -#[allow(missing_docs)] -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum SegmentMatchCrumb { - Head, - Body { val: Vec }, -} - - -// === Ambiguous === - -#[allow(missing_docs)] -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct AmbiguousCrumb { - pub index: usize, - pub field: AmbiguousSegmentCrumb, -} - -#[allow(missing_docs)] -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum AmbiguousSegmentCrumb { - Head, - Body, -} - - // === Conversion Traits === macro_rules! from_crumb { @@ -464,10 +315,7 @@ impl IntoIterator for Crumb { impl_crumbs! { - ( InvalidSuffix , InvalidSuffixCrumb , is_invalid_suffix ), - ( TextLineFmt , TextLineFmtCrumb , is_text_line_fmt ), - ( TextBlockFmt , TextBlockFmtCrumb , is_text_block_fmt ), - ( TextUnclosed , TextUnclosedCrumb , is_text_unclosed ), + // Translated types ( Prefix , PrefixCrumb , is_prefix ), ( Infix , InfixCrumb , is_infix ), ( SectionLeft , SectionLeftCrumb , is_section_left ), @@ -475,13 +323,8 @@ impl_crumbs! { ( SectionSides , SectionSidesCrumb , is_section_sides ), ( Module , ModuleCrumb , is_module ), ( Block , BlockCrumb , is_block ), - ( Match , MatchCrumb , is_match ), - ( Ambiguous , AmbiguousCrumb , is_ambiguous ), - ( Import , ImportCrumb , is_import ), - ( Export , ExportCrumb , is_export ), - ( Mixfix , MixfixCrumb , is_mixfix ), - ( Group , GroupCrumb , is_group ), - ( Def , DefCrumb , is_def ) + // Tree + ( Tree , TreeCrumb , is_tree ) } @@ -529,129 +372,6 @@ pub trait Crumbable { } } -impl Crumbable for crate::InvalidSuffix { - type Crumb = InvalidSuffixCrumb; - - fn get(&self, _crumb: &Self::Crumb) -> FallibleResult<&Ast> { - Ok(&self.elem) - } - - fn set(&self, _crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut ret = self.clone(); - ret.elem = new_ast; - Ok(ret) - } - - fn iter_subcrumbs(&self) -> Box> { - Box::new(std::iter::once(InvalidSuffixCrumb)) - } -} - -impl Crumbable for crate::TextLineFmt { - type Crumb = TextLineFmtCrumb; - - fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { - let segment = self.text.get_or_err(crumb.segment_index, "text segment")?; - if let crate::SegmentFmt::SegmentExpr(expr) = segment { - expr.value.as_ref().ok_or_else(|| NotPresent("expression".into()).into()) - } else { - Err(NotPresent("expression segment".into()).into()) - } - } - - fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut text = self.clone(); - let segment = text.text.get_mut_or_err(crumb.segment_index, "text segment")?; - if let crate::SegmentFmt::SegmentExpr(expr) = segment { - expr.value = Some(new_ast); - Ok(text) - } else { - Err(NotPresent("expression segment".into()).into()) - } - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - Box::new(self.text.iter().enumerate().filter_map(|(segment_index, segment)| { - if let crate::SegmentFmt::SegmentExpr(_) = segment { - Some(TextLineFmtCrumb { segment_index }) - } else { - None - } - })) - } -} - -impl Crumbable for crate::TextBlockFmt { - type Crumb = TextBlockFmtCrumb; - - fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { - let line = self.text.get_or_err(crumb.text_line_index, "line")?; - let segment = line.text.get_or_err(crumb.segment_index, "segment")?; - if let crate::SegmentFmt::SegmentExpr(expr) = segment { - expr.value.as_ref().ok_or_else(|| NotPresent("expression value".into()).into()) - } else { - Err(NotPresent("expression segment".into()).into()) - } - } - - fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut text = self.clone(); - let line = text.text.get_mut_or_err(crumb.text_line_index, "line")?; - let segment = line.text.get_mut_or_err(crumb.segment_index, "segment")?; - if let crate::SegmentFmt::SegmentExpr(expr) = segment { - expr.value = Some(new_ast); - Ok(text) - } else { - Err(NotPresent("expression segment".into()).into()) - } - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - Box::new(self.text.iter().enumerate().flat_map(|(text_line_index, line)| { - line.text - .iter() - .enumerate() - .filter(|(_, segment)| matches!(segment, crate::SegmentFmt::SegmentExpr(_))) - .map(move |(segment_index, _)| TextBlockFmtCrumb { text_line_index, segment_index }) - })) - } -} - -impl Crumbable for crate::TextUnclosed { - type Crumb = TextUnclosedCrumb; - - fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { - if let crate::TextLine::TextLineFmt(text_line) = &self.line { - text_line.get(&crumb.text_line_crumb) - } else { - Err(NotPresent("formatted text line".into()).into()) - } - } - - fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut text = self.clone(); - if let crate::TextLine::TextLineFmt(text_line) = text.line { - let text_line_fmt = text_line.set(&crumb.text_line_crumb, new_ast)?; - text.line = crate::TextLine::TextLineFmt(text_line_fmt); - Ok(text) - } else { - Err(NotPresent("formatted text line".into()).into()) - } - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - if let crate::TextLine::TextLineFmt(text_line) = &self.line { - Box::new( - text_line - .iter_subcrumbs() - .map(|text_line_crumb| TextUnclosedCrumb { text_line_crumb }), - ) - } else { - Box::new(std::iter::empty()) - } - } -} - impl Crumbable for crate::Prefix { type Crumb = PrefixCrumb; @@ -836,569 +556,6 @@ impl Crumbable for crate::Block { } } -/// Helper function for getting an Ast from MacroPatternMatch based on crumb position. -fn pattern_get<'a>( - pat: &'a MacroPatternMatch>, - path: &[PatternMatchCrumb], -) -> FallibleResult<&'a Ast> { - use crate::MacroPatternMatchRaw::*; - - let missing = |str: &str| Result::Err(NotPresent(str.into()).into()); - - let mut pattern = pat; - for crumb in path { - match (pattern.deref(), crumb) { - (Begin(_), PatternMatchCrumb::Begin) => return missing("elem"), - (End(_), PatternMatchCrumb::End) => return missing("elem"), - (Nothing(_), PatternMatchCrumb::Nothing) => return missing("elem"), - (Build(pat), PatternMatchCrumb::Build) => return Ok(&pat.elem.wrapped), - (Err(pat), PatternMatchCrumb::Err) => return Ok(&pat.elem.wrapped), - (Tok(pat), PatternMatchCrumb::Tok) => return Ok(&pat.elem.wrapped), - (Blank(pat), PatternMatchCrumb::Blank) => return Ok(&pat.elem.wrapped), - (Var(pat), PatternMatchCrumb::Var) => return Ok(&pat.elem.wrapped), - (Cons(pat), PatternMatchCrumb::Cons) => return Ok(&pat.elem.wrapped), - (Opr(pat), PatternMatchCrumb::Opr) => return Ok(&pat.elem.wrapped), - (Annotation(pat), PatternMatchCrumb::Annotation) => return Ok(&pat.elem.wrapped), - (Mod(pat), PatternMatchCrumb::Mod) => return Ok(&pat.elem.wrapped), - (Num(pat), PatternMatchCrumb::Num) => return Ok(&pat.elem.wrapped), - (Text(pat), PatternMatchCrumb::Text) => return Ok(&pat.elem.wrapped), - (Block(pat), PatternMatchCrumb::Block) => return Ok(&pat.elem.wrapped), - (Macro(pat), PatternMatchCrumb::Macro) => return Ok(&pat.elem.wrapped), - (Invalid(pat), PatternMatchCrumb::Invalid) => return Ok(&pat.elem.wrapped), - (Except(pat), PatternMatchCrumb::Except) => pattern = &pat.elem, - (Tag(pat), PatternMatchCrumb::Tag) => pattern = &pat.elem, - (Cls(pat), PatternMatchCrumb::Cls) => pattern = &pat.elem, - (Or(pat), PatternMatchCrumb::Or) => pattern = &pat.elem, - (Seq(pat), PatternMatchCrumb::Seq { right }) => - pattern = if *right { &pat.elem.1 } else { &pat.elem.0 }, - (Many(pat), PatternMatchCrumb::Many { index }) => - pattern = pat.elem.get_or_err(*index, "elem")?, - _ => return missing("crumb"), - } - } - missing("crumb") -} - -/// Helper function that updates Ast in MacroPatternMatch based on crumb position. -fn pattern_set( - pat: &MacroPatternMatch>, - path: &[PatternMatchCrumb], - ast: Ast, -) -> FallibleResult>> { - use crate::MacroPatternMatchRaw::*; - - let missing = |str: &str| Result::Err(NotPresent(str.into()).into()); - - let mut result = pat.clone(); - let mut pattern = Rc::make_mut(&mut result); - for crumb in path { - match (pattern, crumb) { - (Begin(_), PatternMatchCrumb::Begin) => return missing("elem"), - (End(_), PatternMatchCrumb::End) => return missing("elem"), - (Nothing(_), PatternMatchCrumb::Nothing) => return missing("elem"), - (Build(pat), PatternMatchCrumb::Build) => { - pat.elem.wrapped = ast; - break; - } - (Err(pat), PatternMatchCrumb::Err) => { - pat.elem.wrapped = ast; - break; - } - (Tok(pat), PatternMatchCrumb::Tok) => { - pat.elem.wrapped = ast; - break; - } - (Blank(pat), PatternMatchCrumb::Blank) => { - pat.elem.wrapped = ast; - break; - } - (Var(pat), PatternMatchCrumb::Var) => { - pat.elem.wrapped = ast; - break; - } - (Cons(pat), PatternMatchCrumb::Cons) => { - pat.elem.wrapped = ast; - break; - } - (Opr(pat), PatternMatchCrumb::Opr) => { - pat.elem.wrapped = ast; - break; - } - (Annotation(pat), PatternMatchCrumb::Annotation) => { - pat.elem.wrapped = ast; - break; - } - (Mod(pat), PatternMatchCrumb::Mod) => { - pat.elem.wrapped = ast; - break; - } - (Num(pat), PatternMatchCrumb::Num) => { - pat.elem.wrapped = ast; - break; - } - (Text(pat), PatternMatchCrumb::Text) => { - pat.elem.wrapped = ast; - break; - } - (Block(pat), PatternMatchCrumb::Block) => { - pat.elem.wrapped = ast; - break; - } - (Macro(pat), PatternMatchCrumb::Macro) => { - pat.elem.wrapped = ast; - break; - } - (Invalid(pat), PatternMatchCrumb::Invalid) => { - pat.elem.wrapped = ast; - break; - } - (Except(pat), PatternMatchCrumb::Except) => { - pat.elem = pat.elem.clone(); - pattern = Rc::make_mut(&mut pat.elem); - } - (Tag(pat), PatternMatchCrumb::Tag) => { - pat.elem = pat.elem.clone(); - pattern = Rc::make_mut(&mut pat.elem); - } - (Cls(pat), PatternMatchCrumb::Cls) => { - pat.elem = pat.elem.clone(); - pattern = Rc::make_mut(&mut pat.elem); - } - (Or(pat), PatternMatchCrumb::Or) => { - pat.elem = pat.elem.clone(); - pattern = Rc::make_mut(&mut pat.elem); - } - (Seq(pat), PatternMatchCrumb::Seq { right }) => - if *right { - pat.elem.1 = pat.elem.1.clone(); - pattern = Rc::make_mut(&mut pat.elem.1); - } else { - pat.elem.0 = pat.elem.0.clone(); - pattern = Rc::make_mut(&mut pat.elem.0); - }, - (Many(pat), PatternMatchCrumb::Many { index }) => { - let elem = pat.elem.get_mut_or_err(*index, "elem")?; - *elem = elem.clone(); - pattern = Rc::make_mut(elem); - } - _ => return missing("crumb"), - } - } - Ok(result) -} - -/// Helper function that returns subcrumbs for MacroPatternMatch. -fn pattern_subcrumbs(pat: &MacroPatternMatch>) -> Vec> { - use crate::MacroPatternMatchRaw::*; - - let mut crumbs = vec![]; - let mut patterns = vec![(vec![], pat)]; - while let Some((mut crumb, pattern)) = patterns.pop() { - match pattern.deref() { - Begin(_) => (), - End(_) => (), - Nothing(_) => (), - Build(_) => { - crumb.push(PatternMatchCrumb::Build); - crumbs.push(crumb) - } - Err(_) => { - crumb.push(PatternMatchCrumb::Err); - crumbs.push(crumb) - } - Tok(_) => { - crumb.push(PatternMatchCrumb::Tok); - crumbs.push(crumb) - } - Blank(_) => { - crumb.push(PatternMatchCrumb::Blank); - crumbs.push(crumb) - } - Var(_) => { - crumb.push(PatternMatchCrumb::Var); - crumbs.push(crumb) - } - Cons(_) => { - crumb.push(PatternMatchCrumb::Cons); - crumbs.push(crumb) - } - Opr(_) => { - crumb.push(PatternMatchCrumb::Opr); - crumbs.push(crumb) - } - Annotation(_) => { - crumb.push(PatternMatchCrumb::Annotation); - crumbs.push(crumb) - } - Mod(_) => { - crumb.push(PatternMatchCrumb::Mod); - crumbs.push(crumb) - } - Num(_) => { - crumb.push(PatternMatchCrumb::Num); - crumbs.push(crumb) - } - Text(_) => { - crumb.push(PatternMatchCrumb::Text); - crumbs.push(crumb) - } - Block(_) => { - crumb.push(PatternMatchCrumb::Block); - crumbs.push(crumb) - } - Macro(_) => { - crumb.push(PatternMatchCrumb::Macro); - crumbs.push(crumb) - } - Invalid(_) => { - crumb.push(PatternMatchCrumb::Invalid); - crumbs.push(crumb) - } - FailedMatch(_) => (), - Except(pat) => { - crumb.push(PatternMatchCrumb::Except); - patterns.push((crumb, &pat.elem)) - } - Tag(pat) => { - crumb.push(PatternMatchCrumb::Tag); - patterns.push((crumb, &pat.elem)) - } - Cls(pat) => { - crumb.push(PatternMatchCrumb::Cls); - patterns.push((crumb, &pat.elem)) - } - Or(pat) => { - crumb.push(PatternMatchCrumb::Or); - patterns.push((crumb, &pat.elem)); - } - Seq(pat) => { - let mut crumb1 = crumb.clone(); - let mut crumb2 = crumb.clone(); - crumb1.push(PatternMatchCrumb::Seq { right: false }); - crumb2.push(PatternMatchCrumb::Seq { right: true }); - patterns.push((crumb2, &pat.elem.1)); - patterns.push((crumb1, &pat.elem.0)); - } - Many(pat) => - for (index, pat) in pat.elem.iter().enumerate().rev() { - let mut new_crumb = crumb.clone(); - new_crumb.push(PatternMatchCrumb::Many { index }); - patterns.push((new_crumb, pat)); - }, - } - } - crumbs -} - -impl Crumbable for crate::Match { - type Crumb = MatchCrumb; - - fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { - match crumb { - MatchCrumb::Pfx { val } => match &self.pfx { - None => Err(NotPresent("prefix".into()).into()), - Some(pat) => pattern_get(pat, val), - }, - MatchCrumb::Segs { val, index } => match &val { - SegmentMatchCrumb::Head => Ok(&self.segs.get_or_err(*index, "elem")?.head), - SegmentMatchCrumb::Body { val } => - pattern_get(&self.segs.get_or_err(*index, "elem")?.body, val), - }, - } - } - - fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult - where Self: Sized { - let mut new_self = self.clone(); - - match crumb { - MatchCrumb::Pfx { val } => match new_self.pfx { - None => return Err(NotPresent("prefix".into()).into()), - Some(pat) => new_self.pfx = Some(pattern_set(&pat, val, new_ast)?), - }, - - MatchCrumb::Segs { index, val: SegmentMatchCrumb::Body { val } } => { - let mut seg = new_self.segs.get_mut_or_err(*index, "segment")?; - seg.body = pattern_set(&seg.body, val, new_ast)? - } - MatchCrumb::Segs { index, val: SegmentMatchCrumb::Head } => { - let mut seg = new_self.segs.get_mut_or_err(*index, "segment")?; - seg.head = new_ast - } - } - Ok(new_self) - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - let mut crumbs = vec![]; - if let Some(pat) = &self.pfx { - for crumb in pattern_subcrumbs(pat) { - crumbs.push(MatchCrumb::Pfx { val: crumb }); - } - } - for (index, seg) in self.segs.iter().enumerate() { - crumbs.push(MatchCrumb::Segs { index, val: SegmentMatchCrumb::Head }); - for crumb in pattern_subcrumbs(&seg.body) { - crumbs.push(MatchCrumb::Segs { index, val: SegmentMatchCrumb::Body { val: crumb } }) - } - } - Box::new(crumbs.into_iter()) - } -} - -impl Crumbable for crate::Ambiguous { - type Crumb = AmbiguousCrumb; - - fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { - let seg = self.segs.get_or_err(crumb.index, "seg")?; - let ast = match crumb.field { - AmbiguousSegmentCrumb::Head => &seg.head, - AmbiguousSegmentCrumb::Body => - &seg.body.as_ref().ok_or_else(|| NotPresent("body".into()))?.wrapped, - }; - Ok(ast) - } - - fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut new_self = self.clone(); - let mut seg = new_self.segs.get_mut_or_err(crumb.index, "seg")?; - match crumb.field { - AmbiguousSegmentCrumb::Head => seg.head = new_ast, - AmbiguousSegmentCrumb::Body => - seg.body.as_mut().ok_or_else(|| NotPresent("body".into()))?.wrapped = new_ast, - }; - Ok(new_self) - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - let head = |index| AmbiguousCrumb { index, field: AmbiguousSegmentCrumb::Head }; - let body = |index| AmbiguousCrumb { index, field: AmbiguousSegmentCrumb::Body }; - let crumbs = self.segs.iter().enumerate().flat_map(move |(index, seg)| { - iter::once(head(index)).chain(seg.body.iter().map(move |_| body(index))) - }); - Box::new(crumbs) - } -} - -impl Crumbable for crate::Import { - type Crumb = ImportCrumb; - - fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { - match crumb { - ImportCrumb::Path { index } => - self.path.get_or_err(*index, "path").map_err(|err| err.into()), - ImportCrumb::Rename => - self.rename.as_ref().ok_or_else(|| NotPresent("rename".into()).into()), - ImportCrumb::OnlyNames { index } => self - .onlyNames - .as_ref() - .ok_or_else(|| failure::Error::from(NotPresent("onlyNames".into())))? - .get_or_err(*index, "onlyNames") - .map_err(|err| err.into()), - ImportCrumb::HidingNames { index } => self - .hidingNames - .as_ref() - .ok_or_else(|| failure::Error::from(NotPresent("hidingNames".into())))? - .get_or_err(*index, "hidingNames") - .map_err(|err| err.into()), - } - } - - fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut import = self.clone(); - match crumb { - ImportCrumb::Path { index } => { - let path = import.path.get_mut_or_err(*index, "path")?; - *path = new_ast; - } - ImportCrumb::Rename => { - import.rename = Some(new_ast); - } - ImportCrumb::OnlyNames { index } => { - let only_names = import.onlyNames.clone(); - let mut only_names = only_names - .ok_or_else(|| failure::Error::from(NotPresent("onlyNames".into())))?; - let elem = only_names.get_mut_or_err(*index, "onlyNames")?; - *elem = new_ast; - import.onlyNames = Some(only_names); - } - ImportCrumb::HidingNames { index } => { - let hiding_names = import.onlyNames.clone(); - let mut hiding_names = hiding_names - .ok_or_else(|| failure::Error::from(NotPresent("hidingNames".into())))?; - let elem = hiding_names.get_mut_or_err(*index, "hidingNames")?; - *elem = new_ast; - import.hidingNames = Some(hiding_names); - } - } - Ok(import) - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - let path_iter = self.path.iter().enumerate().map(|(index, _)| ImportCrumb::Path { index }); - let rename_iter = self.rename.iter().map(|_| ImportCrumb::Rename); - let only_iter = self - .onlyNames - .iter() - .flat_map(|v| v.iter().enumerate().map(|(index, _)| ImportCrumb::OnlyNames { index })); - let hiding_iter = self.hidingNames.iter().flat_map(|v| { - v.iter().enumerate().map(|(index, _)| ImportCrumb::HidingNames { index }) - }); - Box::new(path_iter.chain(rename_iter).chain(only_iter).chain(hiding_iter)) - } -} - -impl Crumbable for crate::Export { - type Crumb = ExportCrumb; - - fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { - match crumb { - ExportCrumb::Path { index } => - self.path.get_or_err(*index, "path").map_err(|err| err.into()), - ExportCrumb::Rename => - self.rename.as_ref().ok_or_else(|| NotPresent("rename".into()).into()), - ExportCrumb::OnlyNames { index } => self - .onlyNames - .as_ref() - .ok_or_else(|| failure::Error::from(NotPresent("onlyNames".into())))? - .get_or_err(*index, "onlyNames") - .map_err(|err| err.into()), - ExportCrumb::HidingNames { index } => self - .hidingNames - .as_ref() - .ok_or_else(|| failure::Error::from(NotPresent("hidingNames".into())))? - .get_or_err(*index, "hidingNames") - .map_err(|err| err.into()), - } - } - - fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut export = self.clone(); - match crumb { - ExportCrumb::Path { index } => { - let path = export.path.get_mut_or_err(*index, "path")?; - *path = new_ast; - } - ExportCrumb::Rename => { - export.rename = Some(new_ast); - } - ExportCrumb::OnlyNames { index } => { - let only_names = export.onlyNames.clone(); - let mut only_names = only_names - .ok_or_else(|| failure::Error::from(NotPresent("onlyNames".into())))?; - let elem = only_names.get_mut_or_err(*index, "onlyNames")?; - *elem = new_ast; - export.onlyNames = Some(only_names); - } - ExportCrumb::HidingNames { index } => { - let hiding_names = export.onlyNames.clone(); - let mut hiding_names = hiding_names - .ok_or_else(|| failure::Error::from(NotPresent("hidingNames".into())))?; - let elem = hiding_names.get_mut_or_err(*index, "hidingNames")?; - *elem = new_ast; - export.hidingNames = Some(hiding_names); - } - } - Ok(export) - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - let path_iter = self.path.iter().enumerate().map(|(index, _)| ExportCrumb::Path { index }); - let rename_iter = self.rename.iter().map(|_| ExportCrumb::Rename); - let only_iter = self - .onlyNames - .iter() - .flat_map(|v| v.iter().enumerate().map(|(index, _)| ExportCrumb::OnlyNames { index })); - let hiding_iter = self.hidingNames.iter().flat_map(|v| { - v.iter().enumerate().map(|(index, _)| ExportCrumb::HidingNames { index }) - }); - Box::new(path_iter.chain(rename_iter).chain(only_iter).chain(hiding_iter)) - } -} - -impl Crumbable for crate::Mixfix { - type Crumb = MixfixCrumb; - - fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { - match crumb { - MixfixCrumb::Name { index } => - self.name.get_or_err(*index, "name").map_err(|err| err.into()), - MixfixCrumb::Args { index } => - self.args.get_or_err(*index, "arg").map_err(|err| err.into()), - } - } - - fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut mixfix = self.clone(); - match crumb { - MixfixCrumb::Name { index } => { - *mixfix.name.get_mut_or_err(*index, "name")? = new_ast; - } - MixfixCrumb::Args { index } => { - *mixfix.args.get_mut_or_err(*index, "arg")? = new_ast; - } - } - Ok(mixfix) - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - let name_iter = self.name.iter().enumerate().map(|(index, _)| MixfixCrumb::Name { index }); - let args_iter = self.args.iter().enumerate().map(|(index, _)| MixfixCrumb::Args { index }); - Box::new(name_iter.chain(args_iter)) - } -} - -impl Crumbable for crate::Group { - type Crumb = GroupCrumb; - - fn get(&self, _crumb: &Self::Crumb) -> FallibleResult<&Ast> { - Ok(self.body.as_ref().ok_or_else(|| NotPresent("body".into()))?) - } - - fn set(&self, _crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut group = self.clone(); - group.body = Some(new_ast); - Ok(group) - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - Box::new(self.body.iter().map(|_| GroupCrumb)) - } -} - -impl Crumbable for crate::Def { - type Crumb = DefCrumb; - - fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { - match crumb { - DefCrumb::Name => Ok(&self.name), - DefCrumb::Args { index } => - self.args.get_or_err(*index, "arg").map_err(|err| err.into()), - DefCrumb::Body => self.body.as_ref().ok_or_else(|| NotPresent("body".into()).into()), - } - } - - fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { - let mut def = self.clone(); - match crumb { - DefCrumb::Name => def.name = new_ast, - DefCrumb::Args { index } => { - let arg = def.args.get_mut_or_err(*index, "arg")?; - *arg = new_ast; - } - DefCrumb::Body => def.body = Some(new_ast), - } - Ok(def) - } - - fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { - let name_iter = std::iter::once(DefCrumb::Name); - let args_iter = self.args.iter().enumerate().map(|(index, _)| DefCrumb::Args { index }); - let body_iter = self.body.iter().map(|_| DefCrumb::Body); - Box::new(name_iter.chain(args_iter).chain(body_iter)) - } -} - /// Just delegates the implementation to shape. impl Crumbable for Ast { type Crumb = Crumb; @@ -1442,6 +599,49 @@ where +// =================== +// === Tree crumbs === +// =================== + +impl Crumbable for crate::Tree { + type Crumb = TreeCrumb; + + fn get(&self, crumb: &Self::Crumb) -> FallibleResult<&Ast> { + match self + .span_info + .get(crumb.index) + .ok_or_else(|| IndexOutOfBounds("Tree child".into()))? + { + SpanSeed::Child(crate::SpanSeedChild { node }) => Ok(node), + _ => Err(MismatchedCrumbType.into()), + } + } + + fn set(&self, crumb: &Self::Crumb, new_ast: Ast) -> FallibleResult { + let mut result = self.clone(); + let child = result + .span_info + .get_mut(crumb.index) + .ok_or_else(|| IndexOutOfBounds("Tree child".into()))?; + *child = SpanSeed::Child(crate::SpanSeedChild { node: new_ast }); + Ok(result) + } + + fn iter_subcrumbs<'a>(&'a self) -> Box + 'a> { + Box::new(self.span_info.iter().enumerate().filter_map(|(index, thing)| { + matches!(thing, SpanSeed::Child(_)).as_some(TreeCrumb { index }) + })) + } +} + +#[allow(missing_docs)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct TreeCrumb { + pub index: usize, +} + + + // =========================== // === Recursive Traversal === // =========================== @@ -1674,35 +874,6 @@ mod tests { } - // === InvalidSuffix === - - #[test] - fn invalid_suffix_crumb() -> FallibleResult { - let elem = Ast::var("foo"); - let ast = Ast::invalid_suffix(elem, "@"); - let to_crumb_enum = Crumb::InvalidSuffix; - let baz = Ast::var("baz"); - - assert_eq!(ast.repr(), "foo@"); - assert_eq!(get(to_crumb_enum, &ast, InvalidSuffixCrumb)?.repr(), "foo"); - assert_eq!(set(to_crumb_enum, &ast, InvalidSuffixCrumb, baz)?.repr(), "baz@"); - - Ok(()) - } - - #[test] - fn iterate_invalid_suffix() -> FallibleResult { - let elem = Ast::var("foo"); - let ast = Ast::invalid_suffix(elem, "@"); - - let mut iter = ast.iter_subcrumbs(); - - assert_eq!(iter.next(), Some(Crumb::InvalidSuffix(InvalidSuffixCrumb))); - assert_eq!(iter.next(), None); - - Ok(()) - } - // === Infix === #[test] @@ -1766,147 +937,6 @@ mod tests { } - - // =========== - // == Text === - // =========== - - - // === TextLineFmt === - - #[test] - fn text_line_fmt_crumb() { - let expr = SegmentExpr { value: Some(Ast::var("foo")) }; - let text = vec![SegmentFmt::SegmentExpr(expr)]; - let ast = Ast::text_line_fmt(text); - let to_crumb_enum = Crumb::TextLineFmt; - let bar = Ast::var("bar"); - let crumb = TextLineFmtCrumb { segment_index: 0 }; - - assert_eq!(ast.repr(), "'`foo`'"); - assert_eq!(get(to_crumb_enum, &ast, crumb).unwrap().repr(), "foo"); - assert_eq!(set(to_crumb_enum, &ast, crumb, bar).unwrap().repr(), "'`bar`'"); - } - - #[test] - fn iterate_text_line_fmt() { - let expr1 = SegmentExpr { value: Some(Ast::var("foo")) }; - let expr2 = SegmentPlain { value: "qux".into() }; - let expr3 = SegmentExpr { value: Some(Ast::var("bar")) }; - let text = vec![ - SegmentFmt::SegmentExpr(expr1), - SegmentFmt::SegmentPlain(expr2), - SegmentFmt::SegmentExpr(expr3), - ]; - let ast = Ast::text_line_fmt(text); - - let (segment1, segment2) = ast.iter_subcrumbs().expect_tuple(); - - assert_eq!(segment1, Crumb::TextLineFmt(TextLineFmtCrumb { segment_index: 0 })); - assert_eq!(segment2, Crumb::TextLineFmt(TextLineFmtCrumb { segment_index: 2 })); - } - - // === TextBlockFmt === - - #[test] - fn text_block_fmt_crumb() { - let empty_lines = default(); - let expr = SegmentExpr { value: Some(Ast::var("foo")) }; - let text = vec![SegmentFmt::SegmentExpr(expr)]; - let line1 = TextBlockLine { empty_lines, text }; - - let empty_lines = default(); - let expr = SegmentExpr { value: Some(Ast::var("bar")) }; - let text = vec![SegmentFmt::SegmentExpr(expr)]; - let line2 = TextBlockLine { empty_lines, text }; - - let lines = vec![line1, line2]; - let ast = Ast::text_block_fmt(lines, 0); - let qux = Ast::var("qux"); - let baz = Ast::var("baz"); - - let to_crumb_enum = Crumb::TextBlockFmt; - assert_eq!(ast.repr(), "'''\n`foo`\n`bar`"); - - let crumb1 = TextBlockFmtCrumb { text_line_index: 0, segment_index: 0 }; - let crumb2 = TextBlockFmtCrumb { text_line_index: 1, segment_index: 0 }; - - assert_eq!(get(to_crumb_enum, &ast, crumb1).unwrap().repr(), "foo"); - assert_eq!(get(to_crumb_enum, &ast, crumb2).unwrap().repr(), "bar"); - - assert_eq!(set(to_crumb_enum, &ast, crumb1, qux).unwrap().repr(), "'''\n`qux`\n`bar`"); - assert_eq!(set(to_crumb_enum, &ast, crumb2, baz).unwrap().repr(), "'''\n`foo`\n`baz`"); - } - - #[test] - fn iterate_text_block_fmt() { - let empty_lines = default(); - let expr = SegmentExpr { value: Some(Ast::var("foo")) }; - let text = vec![SegmentFmt::SegmentExpr(expr)]; - let line1 = TextBlockLine { empty_lines, text }; - - let empty_lines = default(); - let expr = SegmentPlain { value: "qux".into() }; - let text = vec![SegmentFmt::SegmentPlain(expr)]; - let line2 = TextBlockLine { empty_lines, text }; - - let empty_lines = default(); - let expr1 = SegmentPlain { value: "qux".into() }; - let expr2 = SegmentExpr { value: Some(Ast::var("bar")) }; - let text = vec![SegmentFmt::SegmentPlain(expr1), SegmentFmt::SegmentExpr(expr2)]; - let line3 = TextBlockLine { empty_lines, text }; - - let lines = vec![line1, line2, line3]; - let ast = Ast::text_block_fmt(lines, 0); - - let crumb1 = TextBlockFmtCrumb { text_line_index: 0, segment_index: 0 }; - let crumb2 = TextBlockFmtCrumb { text_line_index: 2, segment_index: 1 }; - - let (line1, line2) = ast.iter_subcrumbs().expect_tuple(); - - assert_eq!(line1, Crumb::TextBlockFmt(crumb1)); - assert_eq!(line2, Crumb::TextBlockFmt(crumb2)); - } - - - // == TextUnclosed === - - #[test] - fn text_unclosed_crumb() { - let expr = SegmentExpr { value: Some(Ast::var("foo")) }; - let text = vec![SegmentFmt::SegmentExpr(expr)]; - let text_line = TextLineFmt { text }; - let line = TextLine::TextLineFmt(text_line); - let ast = Ast::text_unclosed(line); - let to_crumb_enum = Crumb::TextUnclosed; - let bar = Ast::var("bar"); - let text_line_crumb = TextLineFmtCrumb { segment_index: 0 }; - let crumb = TextUnclosedCrumb { text_line_crumb }; - - assert_eq!(ast.repr(), "'`foo`"); - assert_eq!(get(to_crumb_enum, &ast, crumb).unwrap().repr(), "foo"); - assert_eq!(set(to_crumb_enum, &ast, crumb, bar).unwrap().repr(), "'`bar`"); - } - - #[test] - fn iterate_text_unclosed() { - let expr1 = SegmentExpr { value: Some(Ast::var("foo")) }; - let expr2 = SegmentExpr { value: Some(Ast::var("bar")) }; - let text = vec![SegmentFmt::SegmentExpr(expr1), SegmentFmt::SegmentExpr(expr2)]; - let text_line = TextLineFmt { text }; - let line = TextLine::TextLineFmt(text_line); - let ast = Ast::text_unclosed(line); - let text_line_crumb = TextLineFmtCrumb { segment_index: 0 }; - let crumb1 = TextUnclosedCrumb { text_line_crumb }; - let text_line_crumb = TextLineFmtCrumb { segment_index: 1 }; - let crumb2 = TextUnclosedCrumb { text_line_crumb }; - - let (segment1, segment2) = ast.iter_subcrumbs().expect_tuple(); - assert_eq!(segment1, Crumb::TextUnclosed(crumb1)); - assert_eq!(segment2, Crumb::TextUnclosed(crumb2)); - } - - // === Prefix === #[test] @@ -2115,114 +1145,6 @@ mod tests { } - // === Match === - - fn match_() -> Match { - let var = Ast::var(""); - let pat = Rc::new(MacroPatternRaw::Nothing(MacroPatternRawNothing {})); - let tok = Rc::new(MacroPatternMatchRaw::Tok(MacroPatternMatchRawTok { - pat: MacroPatternRawTok { spaced: None, ast: var.clone() }, - elem: Shifted { off: 0, wrapped: var.clone() }, - })); - let body = Rc::new(MacroPatternMatchRaw::Seq(MacroPatternMatchRawSeq { - pat: MacroPatternRawSeq { pat1: pat.clone(), pat2: pat }, - elem: (tok.clone(), tok), - })); - let segs = ShiftedVec1 { - head: MacroMatchSegment { head: var.clone(), body: body.clone() }, - tail: vec![], - }; - Match { pfx: Some(body), segs, resolved: Some(var) } - } - - #[test] - fn iterate_match() { - let crumb1 = vec![PatternMatchCrumb::Seq { right: false }, PatternMatchCrumb::Tok]; - let crumb2 = vec![PatternMatchCrumb::Seq { right: true }, PatternMatchCrumb::Tok]; - let (c1, c2, c3, c4, c5) = match_().iter_subcrumbs().expect_tuple(); - assert_eq!(c1, MatchCrumb::Pfx { val: crumb1.clone() }); - assert_eq!(c2, MatchCrumb::Pfx { val: crumb2.clone() }); - assert_eq!(c3, MatchCrumb::Segs { val: SegmentMatchCrumb::Head, index: 0 }); - assert_eq!(c4, MatchCrumb::Segs { - val: SegmentMatchCrumb::Body { val: crumb1 }, - index: 0, - }); - assert_eq!(c5, MatchCrumb::Segs { - val: SegmentMatchCrumb::Body { val: crumb2 }, - index: 0, - }); - } - - #[test] - fn mismatch_match() { - let match_ = match_(); - let incorrect1 = match_.get(&MatchCrumb::Pfx { val: vec![] }); - let incorrect2 = - match_.get(&MatchCrumb::Pfx { val: vec![PatternMatchCrumb::Seq { right: true }] }); - let incorrect3 = match_.get(&MatchCrumb::Pfx { - val: vec![PatternMatchCrumb::Seq { right: false }, PatternMatchCrumb::Seq { - right: true, - }], - }); - incorrect1.expect_err("Using empty crumb on match should fail"); - incorrect2.expect_err("Using 1'seq' crumb on match should fail"); - incorrect3.expect_err("Using 2'seq' crumb on match should fail"); - } - - #[test] - fn modify_match() { - let crumb1 = vec![PatternMatchCrumb::Seq { right: false }, PatternMatchCrumb::Tok]; - let crumb2 = vec![PatternMatchCrumb::Seq { right: true }, PatternMatchCrumb::Tok]; - let crumb3 = MatchCrumb::Pfx { val: crumb1.clone() }; - let crumb4 = MatchCrumb::Pfx { val: crumb2.clone() }; - let crumb5 = MatchCrumb::Segs { val: SegmentMatchCrumb::Head, index: 0 }; - let crumb6 = MatchCrumb::Segs { val: SegmentMatchCrumb::Body { val: crumb1 }, index: 0 }; - let crumb7 = MatchCrumb::Segs { val: SegmentMatchCrumb::Body { val: crumb2 }, index: 0 }; - let match1 = match_(); - let ast = [match1.resolved.clone().unwrap(), Ast::var("X"), Ast::var("Y"), Ast::var("Z")]; - let match2 = match1.set(&crumb3, ast[1].clone()).unwrap(); - let match3 = match2.set(&crumb5, ast[2].clone()).unwrap(); - let match4 = match3.set(&crumb7, ast[3].clone()).unwrap(); - - assert_eq!(match1.get(&crumb3).unwrap(), &ast[0]); - assert_eq!(match1.get(&crumb4).unwrap(), &ast[0]); - assert_eq!(match1.get(&crumb5).unwrap(), &ast[0]); - assert_eq!(match1.get(&crumb6).unwrap(), &ast[0]); - assert_eq!(match1.get(&crumb7).unwrap(), &ast[0]); - - assert_eq!(match4.get(&crumb3).unwrap(), &ast[1]); - assert_eq!(match4.get(&crumb4).unwrap(), &ast[0]); - assert_eq!(match4.get(&crumb5).unwrap(), &ast[2]); - assert_eq!(match4.get(&crumb6).unwrap(), &ast[0]); - assert_eq!(match4.get(&crumb7).unwrap(), &ast[3]); - } - - #[test] - fn ambiguous() { - let ast = [Ast::var("A"), Ast::var("B"), Ast::var("C")]; - let body = Some(Shifted::new(0, ast[1].clone())); - let seg1 = MacroAmbiguousSegment { head: ast[0].clone(), body }; - let seg2 = MacroAmbiguousSegment { head: ast[2].clone(), body: None }; - let segs = ShiftedVec1 { head: seg1, tail: vec![Shifted::new(0, seg2)] }; - let paths = Tree { value: None, branches: vec![] }; - let shape = Ambiguous { segs, paths }; - - let (c1, c2, c3) = shape.iter_subcrumbs().expect_tuple(); - - assert_eq!(c1, AmbiguousCrumb { index: 0, field: AmbiguousSegmentCrumb::Head }); - assert_eq!(c2, AmbiguousCrumb { index: 0, field: AmbiguousSegmentCrumb::Body }); - assert_eq!(c3, AmbiguousCrumb { index: 1, field: AmbiguousSegmentCrumb::Head }); - - assert_eq!(shape.set(&c1, ast[1].clone()).unwrap().get(&c1).unwrap(), &ast[1]); - assert_eq!(shape.set(&c2, ast[2].clone()).unwrap().get(&c2).unwrap(), &ast[2]); - assert_eq!(shape.set(&c3, ast[1].clone()).unwrap().get(&c3).unwrap(), &ast[1]); - - assert_eq!(shape.get(&c1).unwrap(), &ast[0]); - assert_eq!(shape.get(&c2).unwrap(), &ast[1]); - assert_eq!(shape.get(&c3).unwrap(), &ast[2]); - } - - // === TraversableAst === #[test] diff --git a/app/gui/language/ast/impl/src/id_map.rs b/app/gui/language/ast/impl/src/id_map.rs index 479ed74b0790..c5afa4f95efa 100644 --- a/app/gui/language/ast/impl/src/id_map.rs +++ b/app/gui/language/ast/impl/src/id_map.rs @@ -45,9 +45,9 @@ impl IdMap { -// ====================== -// === IdMapForParser === -// ====================== +// ================= +// === JsonIdMap === +// ================= /// Strongly typed index of char. /// diff --git a/app/gui/language/ast/impl/src/known.rs b/app/gui/language/ast/impl/src/known.rs index 2fae1652fa9e..a18e2ba6b838 100644 --- a/app/gui/language/ast/impl/src/known.rs +++ b/app/gui/language/ast/impl/src/known.rs @@ -9,11 +9,6 @@ use crate::HasTokens; use crate::Shape; use crate::TokenConsumer; -use serde::Deserialize; -use serde::Deserializer; -use serde::Serialize; -use serde::Serializer; - // ================= @@ -165,25 +160,6 @@ impl<'a, T> From<&'a KnownAst> for &'a Ast { } } -impl Serialize for KnownAst { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - self.ast.serialize(serializer) - } -} - -impl<'de, T, E> Deserialize<'de> for KnownAst -where - for<'t> &'t Shape: TryInto<&'t T, Error = E>, - E: fmt::Display, -{ - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - let ast = Ast::deserialize(deserializer)?; - Self::try_new(ast).map_err(serde::de::Error::custom) - } -} - impl HasTokens for KnownAst { fn feed_to(&self, consumer: &mut impl TokenConsumer) { self.ast.feed_to(consumer) diff --git a/app/gui/language/ast/impl/src/lib.rs b/app/gui/language/ast/impl/src/lib.rs index b3109f6f6084..9066b9f66a02 100644 --- a/app/gui/language/ast/impl/src/lib.rs +++ b/app/gui/language/ast/impl/src/lib.rs @@ -3,6 +3,7 @@ #![feature(generators, generator_trait)] #![feature(trivial_bounds)] #![feature(type_alias_impl_trait)] +#![feature(let_chains)] // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] @@ -16,12 +17,6 @@ use enso_text::index::*; use enso_text::traits::*; use enso_text::unit::*; -use serde::de::Deserializer; -use serde::de::Visitor; -use serde::ser::SerializeStruct; -use serde::ser::Serializer; -use serde::Deserialize; -use serde::Serialize; use uuid::Uuid; @@ -114,28 +109,12 @@ pub struct NoSuchChild; -// ============ -// === Tree === -// ============ - -/// A tree structure where each node may store value of `V` and has arbitrary -/// number of children nodes, each marked with a single `K`. -/// -/// It is used to describe ambiguous macro match. -#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] -pub struct Tree { - pub value: Option, - pub branches: Vec<(K, Tree)>, -} - - - // =============== // === Shifted === // =============== /// A value of type `T` annotated with offset value `off`. -#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Deref, DerefMut, Iterator)] +#[derive(Clone, Eq, PartialEq, Debug, Deref, DerefMut, Iterator)] pub struct Shifted { #[deref] #[deref_mut] @@ -144,7 +123,7 @@ pub struct Shifted { } /// A non-empty sequence of `T`s interspersed by offsets. -#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Iterator)] +#[derive(Clone, Eq, PartialEq, Debug, Iterator)] pub struct ShiftedVec1 { pub head: T, pub tail: Vec>, @@ -191,21 +170,6 @@ impl Layer for Layered { -// ============ -// === Unit === -// ============ - -/// A unit type defined as an empty struct. -/// -/// Because it is defined using {} syntax, serde_json will serialize it to -/// an empty object rather than null node. This is to workaround issue with -/// using units in `Option`, reported here: -/// https://github.com/serde-rs/serde/issues/1690 -#[ast_node] -pub struct Unit {} - - - // =========== // === AST === // =========== @@ -216,9 +180,9 @@ pub struct Unit {} /// to either of the implementation need to be applied to the other one as well. /// /// Each AST node is annotated with span and an optional ID. -#[derive(CloneRef, Eq, PartialEq, Debug, Deref, DerefMut)] +#[derive(CloneRef, Eq, PartialEq, Debug, Deref)] pub struct Ast { - pub wrapped: Rc>>>, + wrapped: Rc>>>, } impl Clone for Ast { @@ -282,7 +246,7 @@ impl Ast { } /// Just wraps shape, id and len into Ast node. - fn from_ast_id_len(shape: Shape, id: Option, char_count: usize) -> Ast { + pub fn from_ast_id_len(shape: Shape, id: Option, char_count: usize) -> Ast { let with_length = WithLength { wrapped: shape, length: char_count }; let with_id = WithID { wrapped: with_length, id }; Ast { wrapped: Rc::new(with_id) } @@ -373,77 +337,6 @@ impl>> From for Ast { } -// === Serialization & Deserialization === // - -/// Literals used in `Ast` serialization and deserialization. -pub mod ast_schema { - pub const STRUCT_NAME: &str = "Ast"; - pub const SHAPE: &str = "shape"; - pub const ID: &str = "id"; - pub const LENGTH: &str = "span"; // scala parser is still using `span` - pub const FIELDS: [&str; 3] = [SHAPE, ID, LENGTH]; - pub const COUNT: usize = FIELDS.len(); -} - -impl Serialize for Ast { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - use ast_schema::*; - let mut state = serializer.serialize_struct(STRUCT_NAME, COUNT)?; - state.serialize_field(SHAPE, &self.shape())?; - if self.id.is_some() { - state.serialize_field(ID, &self.id)?; - } - state.serialize_field(LENGTH, &self.length)?; - state.end() - } -} - -/// Type to provide serde::de::Visitor to deserialize data into `Ast`. -struct AstDeserializationVisitor; - -impl<'de> Visitor<'de> for AstDeserializationVisitor { - type Value = Ast; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - use ast_schema::*; - write!(formatter, "an object with `{SHAPE}` and `{LENGTH}` fields") - } - - fn visit_map(self, mut map: A) -> Result - where A: serde::de::MapAccess<'de> { - use ast_schema::*; - - let mut shape: Option> = None; - let mut id: Option> = None; - let mut len: Option = None; - - while let Some(key) = map.next_key()? { - match key { - SHAPE => shape = Some(map.next_value()?), - ID => id = Some(map.next_value()?), - LENGTH => len = Some(map.next_value()?), - _ => {} - } - } - - let shape = shape.ok_or_else(|| serde::de::Error::missing_field(SHAPE))?; - let id = id.unwrap_or(None); // allow missing `id` field - let len = len.ok_or_else(|| serde::de::Error::missing_field(LENGTH))?; - Ok(Ast::from_ast_id_len(shape, id, len)) - } -} - -impl<'de> Deserialize<'de> for Ast { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - use ast_schema::FIELDS; - let visitor = AstDeserializationVisitor; - deserializer.deserialize_struct("AstOf", &FIELDS, visitor) - } -} - - // ============= // === Shape === @@ -455,20 +348,6 @@ impl<'de> Deserialize<'de> for Ast { #[ast(flat)] #[derive(HasTokens)] pub enum Shape { - Unrecognized { - str: String, - }, - Unexpected { - msg: String, - stream: Vec>, - }, - InvalidQuote { - quote: Builder, - }, - InlineBlock { - quote: Builder, - }, - // === Identifiers === Blank {}, Var { @@ -478,7 +357,8 @@ pub enum Shape { name: String, }, Opr { - name: String, + name: String, + right_assoc: bool, }, Annotation { name: String, @@ -486,40 +366,12 @@ pub enum Shape { Mod { name: String, }, - InvalidSuffix { - elem: T, - suffix: String, - }, // === Number === Number { base: Option, int: String, }, - DanglingBase { - base: String, - }, - - // === Text === - TextLineRaw { - text: Vec, - }, - TextLineFmt { - text: Vec>, - }, - TextBlockRaw { - text: Vec>, - spaces: usize, - offset: usize, - }, - TextBlockFmt { - text: Vec>>, - spaces: usize, - offset: usize, - }, - TextUnclosed { - line: TextLine, - }, // === Applications === Prefix { @@ -557,9 +409,6 @@ pub enum Shape { /// Block is the sequence of equally indented lines. Lines may contain some child `T` or be /// empty. Block is used for all code blocks except for the root one, which uses `Module`. Block { - /// Type of Block, depending on whether it is introduced by an operator. - /// Note [mwu] Doesn't really do anything right now, likely to be removed. - ty: BlockType, /// Absolute's block indent, counting from the module's root. indent: usize, /// Leading empty lines. Each line is represented by absolute count of spaces @@ -569,146 +418,159 @@ pub enum Shape { first_line: BlockLine, /// Rest of lines, each of them optionally having contents. lines: Vec>>, - /// If false, the Block will start with a leading newline. - is_orphan: bool, - }, - - // === Macros === - Match { - pfx: Option>>, - segs: ShiftedVec1>, - resolved: Option, }, - Ambiguous { - segs: ShiftedVec1>, - paths: Tree, + Tree { + /// The information needed to iterate child tokens and nodes. + span_info: Vec>, + /// Semantic information about the node. + type_info: TreeType, + /// Information needed to produce a representation, for nodes whose contents are not child + /// nodes. + leaf_info: Option, + /// Suitable for naming variables referring to this node. + descriptive_name: Option<&'static str>, + /// Extra characters after the node (e.g. a comment). + trailing_token: Option, }, +} - // === Spaceless AST === - Comment(Comment), - Documented(Documented), - Import(Import), - Export(Export), - JavaImport(JavaImport), - Mixfix(Mixfix), - Group(Group), - SequenceLiteral(SequenceLiteral), - TypesetLiteral(TypesetLiteral), - Def(Def), - Foreign(Foreign), - Modified(Modified), -} - -/// Macrot that calls its argument (possibly other macro +/// Macro that calls its argument (possibly other macro #[macro_export] macro_rules! with_shape_variants { ($f:ident) => { - $f! { [Unrecognized] [Unexpected Ast] [InvalidQuote] [InlineBlock] - [Blank] [Var] [Cons] [Opr] [Annotation] [Mod] [InvalidSuffix Ast] - [Number] [DanglingBase] - [TextLineRaw] [TextLineFmt Ast] [TextBlockRaw] [TextBlockFmt Ast] [TextUnclosed Ast] + $f! { + [Blank] [Var] [Cons] [Opr] [Annotation] [Mod] + [Number] [Prefix Ast] [Infix Ast] [SectionLeft Ast] [SectionRight Ast] [SectionSides Ast] [Module Ast] [Block Ast] - [Match Ast] [Ambiguous Ast] - // Note: Spaceless AST is intentionally omitted here. + [Tree Ast] } }; } +// === [`Tree`] data === -// =============== -// === Builder === -// =============== - -#[ast(flat)] -#[derive(HasTokens)] -pub enum Builder { - Empty, - Letter { char: char }, - Space { span: usize }, - Text { str: String }, - Seq { first: Rc, second: Rc }, -} - +/// Low-level builders. +impl Tree { + pub fn expression(span_info: Vec>) -> Self { + Self { + span_info, + type_info: default(), + leaf_info: default(), + descriptive_name: default(), + trailing_token: default(), + } + } + pub fn leaf(leaf_info: String) -> Self { + Self { + span_info: default(), + type_info: default(), + leaf_info: Some(leaf_info), + descriptive_name: default(), + trailing_token: None, + } + } -// ============ -// === Text === -// ============ + pub fn with_type_info(self, type_info: TreeType) -> Self { + let Self { span_info, type_info: _, leaf_info, descriptive_name, trailing_token } = self; + Self { span_info, type_info, leaf_info, descriptive_name, trailing_token } + } -// === Text Block Lines === + pub fn with_descriptive_name(self, descriptive_name: &'static str) -> Self { + let Self { span_info, type_info, leaf_info, descriptive_name: _, trailing_token } = self; + Self { + span_info, + type_info, + leaf_info, + descriptive_name: Some(descriptive_name), + trailing_token, + } + } -#[ast] -pub struct TextBlockLine { - pub empty_lines: Vec, - pub text: Vec, + pub fn with_trailing_token(self, trailing_token: String) -> Self { + let Self { span_info, type_info, leaf_info, descriptive_name, trailing_token: _ } = self; + Self { + span_info, + type_info, + leaf_info, + descriptive_name, + trailing_token: Some(trailing_token), + } + } } -#[ast(flat)] -#[derive(HasTokens)] -pub enum TextLine { - TextLineRaw(TextLineRaw), - TextLineFmt(TextLineFmt), -} +/// High-level helper builders. +impl Tree { + pub fn lambda(span_info: Vec>) -> Self { + Tree::expression(span_info).with_type_info(TreeType::Lambda) + } + pub fn group(span_info: Vec>) -> Self { + Tree::expression(span_info).with_type_info(TreeType::Group) + } -// === Text Segments === -#[ast(flat)] -#[derive(HasTokens)] -pub enum SegmentRaw { - SegmentPlain(SegmentPlain), - SegmentRawEscape(SegmentRawEscape), -} + pub fn text(leaf_info: String) -> Self { + Tree::leaf(leaf_info).with_descriptive_name("text") + } -#[ast(flat)] -#[derive(HasTokens)] -pub enum SegmentFmt { - SegmentPlain(SegmentPlain), - SegmentRawEscape(SegmentRawEscape), - SegmentExpr(SegmentExpr), - SegmentEscape(SegmentEscape), + pub fn expression_with_comment(expression: Option, space: usize, comment: String) -> Self { + let mut span_info = vec![]; + span_info.extend(expression.map(SpanSeed::child)); + span_info.extend(SpanSeed::space(space)); + Tree::expression(span_info) + .with_type_info(TreeType::ExpressionWithComment) + .with_trailing_token(comment) + } } -#[ast_node] -pub struct SegmentPlain { - pub value: String, -} -#[ast_node] -pub struct SegmentRawEscape { - pub code: RawEscape, +/// The semantic information about a node. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub enum TreeType { + /// A normal expression. + #[default] + Expression, + /// A documentation-comment. + Documentation { rendered: ImString }, + /// An import declaration. + Import { module: Vec, imported: ImportedNames }, + /// A lambda. + Lambda, + /// A parenthesized expression. + Group, + /// A comment at the end of a line, possibly following an expression. + ExpressionWithComment, } -#[ast_node] -pub struct SegmentExpr { - pub value: Option, -} -#[ast_node] -pub struct SegmentEscape { - pub code: Escape, -} - - -// === Text Segment Escapes === -#[ast(flat)] -#[derive(HasTokens)] -pub enum RawEscape { - Unfinished {}, - Invalid { str: char }, - Slash {}, - Quote {}, - RawQuote {}, +/// Describes the names imported by an import declaration. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ImportedNames { + Module { alias: Option }, + All { except: std::collections::BTreeSet }, + List { names: std::collections::BTreeSet }, } +/// Represents the syntax tree, and its correspondence to the source text; with context information +/// provided by an evaluator, this can be used to produce a complete [`SpanTree`]. #[ast] -#[derive(HasTokens)] -pub enum Escape { - Character { c: char }, - Control { name: String, code: u8 }, - Number { digits: String }, - Unicode16 { digits: String }, - Unicode21 { digits: String }, - Unicode32 { digits: String }, +pub enum SpanSeed { + Space { space: usize }, + Token { token: String }, + Child { node: T }, +} + +impl SpanSeed { + pub fn space(space: usize) -> Option { + match space { + 0 => None, + space => Some(SpanSeed::Space(SpanSeedSpace { space })), + } + } + + pub fn child(node: T) -> Self { + Self::Child(SpanSeedChild { node }) + } } @@ -717,12 +579,6 @@ pub enum Escape { // === Block === // ============= -#[ast_node] -pub enum BlockType { - Continuous {}, - Discontinuous {}, -} - /// Holder for line in `Block` or `Module`. Lines store value of `T` and trailing whitespace info. #[ast] pub struct BlockLine { @@ -733,222 +589,6 @@ pub struct BlockLine { } -// ============= -// === Macro === -// ============= - -#[ast] -pub struct MacroMatchSegment { - pub head: T, - pub body: MacroPatternMatch>, -} - -#[ast] -pub struct MacroAmbiguousSegment { - pub head: T, - pub body: Option>, -} - -pub type MacroPattern = Rc; -#[ast] -pub enum MacroPatternRaw { - // === Boundary Patterns === - Begin {}, - End {}, - - // === Structural Patterns === - Nothing {}, - Seq { pat1: MacroPattern, pat2: MacroPattern }, - Or { pat1: MacroPattern, pat2: MacroPattern }, - Many { pat: MacroPattern }, - Except { not: MacroPattern, pat: MacroPattern }, - - // === Meta Patterns === - Build { pat: MacroPattern }, - Err { msg: String, pat: MacroPattern }, - Tag { tag: String, pat: MacroPattern }, - Cls { cls: PatternClass, pat: MacroPattern }, - - // === Token Patterns === - Tok { spaced: Spaced, ast: Ast }, - Blank { spaced: Spaced }, - Var { spaced: Spaced }, - Cons { spaced: Spaced }, - Opr { spaced: Spaced, max_prec: Option }, - Annotation { spaced: Spaced }, - Mod { spaced: Spaced }, - Num { spaced: Spaced }, - Text { spaced: Spaced }, - Block { spaced: Spaced }, - Macro { spaced: Spaced }, - Invalid { spaced: Spaced }, - FailedMatch { spaced: Spaced }, -} - -#[ast] -pub enum PatternClass { - Normal, - Pattern, -} -pub type Spaced = Option; - -// Note: Switch Implementation -#[ast(flat)] -pub enum Switch { - Left { value: T }, - Right { value: T }, -} - -// Note: Switch Implementation -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Switch is not defined as Either because an iterator generated for such -// type would only iterate over right element, while we require both. -// -// Switch however does not need to be #[ast], when derive(Iterator) supports -// enum with struct variants, this attribute should be possible to remove. - -impl Deref for Switch { - type Target = T; - - fn deref(&self) -> &T { - match self { - Switch::Left(elem) => &elem.value, - Switch::Right(elem) => &elem.value, - } - } -} - -impl DerefMut for Switch { - fn deref_mut(&mut self) -> &mut T { - match self { - Switch::Left(elem) => &mut elem.value, - Switch::Right(elem) => &mut elem.value, - } - } -} - -pub type MacroPatternMatch = Rc>; - -#[ast] -#[derive(HasTokens)] -pub enum MacroPatternMatchRaw { - // === Boundary Matches === - Begin { pat: MacroPatternRawBegin }, - End { pat: MacroPatternRawEnd }, - - // === Structural Matches === - Nothing { pat: MacroPatternRawNothing }, - Seq { pat: MacroPatternRawSeq, elem: (MacroPatternMatch, MacroPatternMatch) }, - Or { pat: MacroPatternRawOr, elem: Switch> }, - Many { pat: MacroPatternRawMany, elem: Vec> }, - Except { pat: MacroPatternRawExcept, elem: MacroPatternMatch }, - - // === Meta Matches === - Build { pat: MacroPatternRawBuild, elem: T }, - Err { pat: MacroPatternRawErr, elem: T }, - Tag { pat: MacroPatternRawTag, elem: MacroPatternMatch }, - Cls { pat: MacroPatternRawCls, elem: MacroPatternMatch }, - - // === Token Matches === - Tok { pat: MacroPatternRawTok, elem: T }, - Blank { pat: MacroPatternRawBlank, elem: T }, - Var { pat: MacroPatternRawVar, elem: T }, - Cons { pat: MacroPatternRawCons, elem: T }, - Opr { pat: MacroPatternRawOpr, elem: T }, - Annotation { pat: MacroPatternRawAnnotation, elem: T }, - Mod { pat: MacroPatternRawMod, elem: T }, - Num { pat: MacroPatternRawNum, elem: T }, - Text { pat: MacroPatternRawText, elem: T }, - Block { pat: MacroPatternRawBlock, elem: T }, - Macro { pat: MacroPatternRawMacro, elem: T }, - Invalid { pat: MacroPatternRawInvalid, elem: T }, - FailedMatch { pat: MacroPatternRawFailedMatch }, -} - -// ============================================================================= -// === Spaceless AST =========================================================== -// ============================================================================= - -#[ast] -pub struct Comment { - pub lines: Vec, -} - -#[ast] -pub struct Documented { - pub doc: T, - pub emp: i32, - pub ast: T, -} - -#[allow(non_snake_case)] -#[ast] -pub struct Import { - pub path: Vec, - pub rename: Option, - pub isAll: bool, - pub onlyNames: Option>, - pub hidingNames: Option>, -} - -#[allow(non_snake_case)] -#[ast] -pub struct Export { - pub path: Vec, - pub rename: Option, - pub isAll: bool, - pub onlyNames: Option>, - pub hidingNames: Option>, -} - -#[ast] -pub struct JavaImport { - pub path: Vec, - pub rename: Option, -} - -#[ast] -pub struct Mixfix { - pub name: Vec, - pub args: Vec, -} - -#[ast] -pub struct Group { - pub body: Option, -} - -#[ast] -pub struct SequenceLiteral { - pub items: Vec, -} - -#[ast] -pub struct TypesetLiteral { - pub expression: Option, -} - -#[ast] -pub struct Def { - pub name: T, // being with Cons - pub args: Vec, - pub body: Option, -} - -#[ast] -pub struct Foreign { - pub indent: usize, - pub lang: String, - pub code: Vec, -} - -#[ast] -pub struct Modified { - pub modifier: String, - pub definition: T, -} - - // =========== // === AST === @@ -1211,11 +851,10 @@ pub trait HasID { fn id(&self) -> Option; } -#[derive(Eq, PartialEq, Debug, Deref, DerefMut, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Debug, Deref, DerefMut)] pub struct WithID { #[deref] #[deref_mut] - #[serde(flatten)] pub wrapped: T, pub id: Option, } @@ -1297,11 +936,10 @@ pub fn traverse_with_span(ast: &impl HasTokens, mut f: impl FnMut(enso_text::Ran /// /// Even if `T` is `Spanned`, keeping `length` variable is desired for performance /// purposes. -#[derive(Eq, PartialEq, Debug, Deref, DerefMut, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Debug, Deref, DerefMut)] pub struct WithLength { #[deref] #[deref_mut] - #[serde(flatten)] pub wrapped: T, pub length: usize, } @@ -1357,18 +995,11 @@ impl Module { let is_empty = |line: &&BlockLine>| line.elem.is_none(); let empty_lines = self.lines.iter().take_while(is_empty); let empty_lines = empty_lines.map(|line| line.off).collect_vec(); - let ty = BlockType::Discontinuous {}; let first_line = self.lines.iter().find_map(|line| { Some(BlockLine { off: line.off, elem: line.elem.as_ref()?.clone() }) })?; let lines = self.lines.iter().skip_while(is_empty).skip(1).cloned().collect(); - // We virtually never want a block to be an orphan (i.e. start with a first line attached - // to whatever AST was there before). While it may seem tempting to have this for single - // line blocks, it does not make sense. If we want expression inline, it shouldn't be a - // block at all. Also, having inline expression can make invalid AST when the only line is - // an assignment and we want to use block as a definition's body. - let is_orphan = false; - Some(Block { ty, indent, empty_lines, first_line, lines, is_orphan }) + Some(Block { indent, empty_lines, first_line, lines }) } } @@ -1459,18 +1090,11 @@ impl Block { /// If there are no tail lines, the first line will be "inline" and the whole block. /// If there are tail lines, block will be leaded with a newline. pub fn from_lines(first_line: &Ast, tail_lines: &[Option]) -> Block { - let ty = BlockType::Discontinuous {}; let indent = 0; let empty_lines = Vec::new(); let first_line = BlockLine::new(first_line.clone_ref()); let lines = tail_lines.iter().cloned().map(BlockLine::new).collect(); - // We virtually never want a block to be an orphan (i.e. start with a first line attached - // to whatever AST was there before). While it may seem tempting to have this for single - // line blocks, it does not make sense. If we want expression inline, it shouldn't be a - // block at all. Also, having inline expression can make invalid AST when the only line is - // an assignment and we want to use block as a definition's body. - let is_orphan = false; - Block { ty, indent, empty_lines, first_line, lines, is_orphan } + Block { indent, empty_lines, first_line, lines } } } @@ -1501,9 +1125,6 @@ impl Module { // === AST === impl Ast { - // TODO smart constructors for other cases - // as part of https://github.com/enso-org/enso/issues/338 - /// Creates Blank ast node (underscore). pub fn blank() -> Ast { Ast::from(Blank {}) @@ -1536,7 +1157,9 @@ impl Ast { /// Creates an AST node with `Opr` shape. pub fn opr(name: impl Str) -> Ast { - let opr = Opr { name: name.into() }; + let name = name.into(); + let right_assoc = crate::assoc::Assoc::of(&name) == crate::assoc::Assoc::Right; + let opr = Opr { name, right_assoc }; Ast::from(opr) } @@ -1570,14 +1193,6 @@ impl Ast { Ast::from(opr) } - /// Creates an AST node with `InvalidSuffix` shape. - pub fn invalid_suffix(elem: impl Into, suffix: impl Str) -> Ast { - let elem = elem.into(); - let suffix = suffix.into(); - let invalid_suffix = InvalidSuffix { elem, suffix }; - Ast::from(invalid_suffix) - } - /// Creates an AST node with `Infix` shape. pub fn infix(larg: impl Into, opr: impl Str, rarg: impl Into) -> Ast { let larg = larg.into(); @@ -1594,135 +1209,25 @@ impl Ast { Module::from_line(line_ast).into() } - /// Creates an AST node with `TextLineFmt` shape. - pub fn text_line_fmt(text: Vec>) -> Ast { - let text_line_fmt = TextLineFmt { text }; - Ast::from(text_line_fmt) - } - - /// Creates an AST node with `TextUnclosed` shape. - pub fn text_unclosed(line: TextLine) -> Ast { - let text_unclosed = TextUnclosed { line }; - Ast::from(text_unclosed) - } - - /// Creates an AST node with `TextBlockFmt` shape. - pub fn text_block_fmt(text: Vec>>, offset: usize) -> Ast { - let spaces = 0; - let text_block_fmt = TextBlockFmt { text, spaces, offset }; - Ast::from(text_block_fmt) - } - /// Creates an AST node with `Infix` shape, where both its operands are Vars. pub fn infix_var(larg: impl Str, opr: impl Str, rarg: impl Str) -> Ast { let infix = Infix::from_vars(larg, opr, rarg); Ast::from(infix) } -} - -// === Text Conversion Boilerplate === - -// support for transitive conversions, like: -// RawEscapeSth -> RawEscape -> SegmentRawEscape -> SegmentRaw - -impl From for SegmentRaw { - fn from(value: Unfinished) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} -impl From for SegmentRaw { - fn from(value: Invalid) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} -impl From for SegmentRaw { - fn from(value: Slash) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} -impl From for SegmentRaw { - fn from(value: Quote) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} -impl From for SegmentRaw { - fn from(value: RawQuote) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} - - -// === RawEscapeSth -> RawEscape -> SegmentRawEscape -> SegmentFmt === - -impl From for SegmentFmt { - fn from(value: Unfinished) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} -impl From for SegmentFmt { - fn from(value: Invalid) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} -impl From for SegmentFmt { - fn from(value: Slash) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} -impl From for SegmentFmt { - fn from(value: Quote) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} -impl From for SegmentFmt { - fn from(value: RawQuote) -> Self { - SegmentRawEscape { code: value.into() }.into() - } -} - -impl From for SegmentFmt { - fn from(value: Escape) -> Self { - SegmentEscape { code: value }.into() - } -} - - -// === EscapeSth -> Escape -> SegmentEscape -> SegmentFmt === - -impl From for SegmentFmt { - fn from(value: EscapeCharacter) -> Self { - SegmentEscape { code: value.into() }.into() - } -} - -impl From for SegmentFmt { - fn from(value: EscapeControl) -> Self { - SegmentEscape { code: value.into() }.into() - } -} - -impl From for SegmentFmt { - fn from(value: EscapeNumber) -> Self { - SegmentEscape { code: value.into() }.into() - } -} - -impl From for SegmentFmt { - fn from(value: EscapeUnicode16) -> Self { - SegmentEscape { code: value.into() }.into() - } -} - -impl From for SegmentFmt { - fn from(value: EscapeUnicode21) -> Self { - SegmentEscape { code: value.into() }.into() - } -} - -impl From for SegmentFmt { - fn from(value: EscapeUnicode32) -> Self { - SegmentEscape { code: value.into() }.into() + /// Creates a raw text literal that evaluates to the given string. + pub fn raw_text_literal(value: impl Str) -> Ast { + let value: &str = value.as_ref(); + let mut escaped = String::with_capacity(value.len() + 2); + escaped.push('\''); + for char in value.chars() { + match char { + '\'' => escaped.push_str("\\'"), + char => escaped.push(char), + } + } + escaped.push('\''); + Self::from(Tree::text(escaped)) } } @@ -1736,16 +1241,6 @@ impl From for SegmentFmt { mod tests { use super::*; - use serde::de::DeserializeOwned; - - /// Assert that given value round trips JSON serialization. - fn round_trips(input_val: &T) - where T: Serialize + DeserializeOwned + PartialEq + Debug { - let json_str = serde_json::to_string(&input_val).unwrap(); - let deserialized_val: T = serde_json::from_str(&json_str).unwrap(); - assert_eq!(*input_val, deserialized_val); - } - #[test] fn ast_updating_id() { let var = Var { name: "foo".into() }; @@ -1803,43 +1298,6 @@ mod tests { assert_eq!(ast.wrapped.wrapped.length, ident.chars().count()); } - #[test] - fn serialization_round_trip() { - let make_var = || Var { name: "foo".into() }; - round_trips(&make_var()); - - let ast_without_id = Ast::new(make_var(), None); - round_trips(&ast_without_id); - - let id = Id::parse_str("15").ok(); - let ast_with_id = Ast::new(make_var(), id); - round_trips(&ast_with_id); - } - - #[test] - fn deserialize_var() { - let var_name = "foo"; - let uuid_str = "51e74fb9-75a4-499d-9ea3-a90a2663b4a1"; - - let sample_json = serde_json::json!({ - "shape": { "Var":{"name": var_name}}, - "id": uuid_str, - "span": var_name.len() - }); - let sample_json_text = sample_json.to_string(); - let ast: Ast = serde_json::from_str(&sample_json_text).unwrap(); - - let expected_uuid = Id::parse_str(uuid_str).ok(); - assert_eq!(ast.id, expected_uuid); - - let expected_length = 3; - assert_eq!(ast.length, expected_length); - - let expected_var = Var { name: var_name.into() }; - let expected_shape = Shape::from(expected_var); - assert_eq!(*ast.shape(), expected_shape); - } - #[test] /// Check if Ast can be iterated. fn iterating() { @@ -1876,7 +1334,6 @@ mod tests { #[test] fn all_lines_of_block() { - let ty = BlockType::Discontinuous {}; let indent = 4; let empty_lines = vec![5]; let first_line = BlockLine { elem: Ast::var("head"), off: 3 }; @@ -1885,8 +1342,7 @@ mod tests { BlockLine { elem: None, off: 1 }, BlockLine { elem: Some(Ast::var("tail2")), off: 3 }, ]; - let is_orphan = false; - let block = Block { ty, indent, empty_lines, first_line, lines, is_orphan }; + let block = Block { indent, empty_lines, first_line, lines }; let expected_repr = "\n \n head \n tail0 \n \n tail2 "; assert_eq!(block.repr(), expected_repr); diff --git a/app/gui/language/ast/impl/src/macros.rs b/app/gui/language/ast/impl/src/macros.rs index c334a2299ee5..5e3c0d57cee9 100644 --- a/app/gui/language/ast/impl/src/macros.rs +++ b/app/gui/language/ast/impl/src/macros.rs @@ -3,12 +3,9 @@ use crate::prelude::*; -use crate::crumbs::AmbiguousCrumb; use crate::crumbs::Located; -use crate::crumbs::MatchCrumb; use crate::known; use crate::BlockLine; -use crate::Shifted; // ============== @@ -23,12 +20,6 @@ pub mod skip_and_freeze; // === Recognized Macros Keywords === // ================================== -/// The keyword introducing a disabled code line. -pub const DISABLING_COMMENT_INTRODUCER: &str = "#"; - -/// The keyword introducing a documentation block. -pub const DOCUMENTATION_COMMENT_INTRODUCER: &str = "##"; - /// The keyword introducing an qualified import declaration. See: /// https://dev.enso.org/docs/enso/syntax/imports.html#import-syntax pub const QUALIFIED_IMPORT_KEYWORD: &str = "import"; @@ -36,29 +27,21 @@ pub const QUALIFIED_IMPORT_KEYWORD: &str = "import"; /// The keyword introducing an unqualified import declaration. pub const UNQUALIFIED_IMPORT_KEYWORD: &str = "from"; -/// The keyword introducing an unqualified export declaration. -pub const QUALIFIED_EXPORT_KEYWORD: &str = "export"; - // ======================== // === Disable Comments === // ======================== -/// Try Interpreting the line as disabling comment. Return the text after `#`. -pub fn as_disable_comment(ast: &Ast) -> Option { - let r#match = crate::known::Match::try_from(ast).ok()?; - let first_segment = &r#match.segs.head; - if crate::identifier::name(&first_segment.head) == Some(DISABLING_COMMENT_INTRODUCER) { - Some(first_segment.body.repr()) - } else { - None - } -} - /// Check if this AST is a disabling comment. pub fn is_disable_comment(ast: &Ast) -> bool { - as_disable_comment(ast).is_some() + if let crate::Shape::Tree(tree) = ast.shape() + && tree.type_info == crate::TreeType::ExpressionWithComment + && !tree.span_info.iter().any(|e| matches!(e, crate::SpanSeed::Child(_))) { + true + } else { + false + } } @@ -72,40 +55,32 @@ pub fn is_disable_comment(ast: &Ast) -> bool { /// Describes the AST of a documentation comment. #[derive(Clone, Debug)] pub struct DocumentationCommentAst { - ast: known::Match, - body: crate::MacroPatternMatch>, + ast: known::Tree, + rendered: ImString, } impl DocumentationCommentAst { /// Interpret given Ast as a documentation comment. Return `None` if it is not recognized. pub fn new(ast: &Ast) -> Option { - let ast = crate::known::Match::try_from(ast).ok()?; - let first_segment = &ast.segs.head; - let introducer = crate::identifier::name(&first_segment.head)?; - if introducer == DOCUMENTATION_COMMENT_INTRODUCER { - let body = first_segment.body.clone_ref(); - Some(DocumentationCommentAst { ast, body }) + let ast = crate::known::Tree::try_from(ast).ok()?; + if let crate::TreeType::Documentation { rendered } = &ast.type_info { + let rendered = rendered.clone(); + Some(DocumentationCommentAst { ast, rendered }) } else { None } } - - /// Get the documentation comment's AST. - pub fn ast(&self) -> known::Match { - self.ast.clone_ref() - } } // === Line Description === /// Describes the line with a documentation comment. -#[derive(Clone, Debug, Deref)] +#[derive(Clone, Debug)] pub struct DocumentationCommentLine { /// Stores the documentation AST and the trailing whitespace length. - #[deref] - line: BlockLine, - body: crate::MacroPatternMatch>, + line: BlockLine, + rendered: ImString, } impl DocumentationCommentLine { @@ -117,22 +92,17 @@ impl DocumentationCommentLine { /// Treat given documentation AST as the line with a given trailing whitespace. pub fn from_doc_ast(ast_doc: DocumentationCommentAst, off: usize) -> Self { - Self { line: BlockLine { elem: ast_doc.ast, off }, body: ast_doc.body } - } - - /// Get the documentation comment's AST. - pub fn ast(&self) -> known::Match { - self.line.elem.clone_ref() + Self { line: BlockLine { elem: ast_doc.ast, off }, rendered: ast_doc.rendered } } /// Get the line with this comment. - pub fn line(&self) -> &BlockLine { + fn line(&self) -> &BlockLine { &self.line } /// Convenience function that throws away some information to return the line description that /// is used in AST blocks. - pub fn block_line(&self) -> BlockLine> { + fn block_line(&self) -> BlockLine> { self.line.as_ref().map(|known_ast| Some(known_ast.ast().clone_ref())) } } @@ -142,10 +112,9 @@ impl DocumentationCommentLine { /// Structure holding the documentation comment AST and related information necessary to deal with /// them. -#[derive(Clone, Debug, Deref)] +#[derive(Clone, Debug)] pub struct DocumentationCommentInfo { /// Description of the line with the documentation comment. - #[deref] pub line: DocumentationCommentLine, /// The absolute indent of the block that contains the line with documentation comment. pub block_indent: usize, @@ -157,18 +126,28 @@ impl DocumentationCommentInfo { Some(Self { line: DocumentationCommentLine::new(line)?, block_indent }) } + /// Get the line with this comment. + pub fn line(&self) -> &BlockLine { + self.line.line() + } + + /// Get the documentation comment's AST. + pub fn ast(&self) -> known::Tree { + self.line.line.elem.clone_ref() + } + + /// Convenience function that throws away some information to return the line description that + /// is used in AST blocks. + pub fn block_line(&self) -> BlockLine> { + self.line.block_line() + } + /// Get the documentation text. /// - /// The text is pretty printed as per UI perspective -- all lines leading whitespace is stripped - /// up to the column following comment introducer (`##`). - pub fn pretty_text(&self) -> String { - let mut repr = self.body.repr(); - // Trailing whitespace must be maintained. - repr.extend(std::iter::repeat(' ').take(self.line.off)); - let indent = self.block_indent + DOCUMENTATION_COMMENT_INTRODUCER.len(); - let old = format!("\n{}", " ".repeat(indent)); - let new = "\n"; - repr.replace(&old, new) + /// The text is pretty printed as per UI perspective--leading whitespace is stripped from all + /// lines up to the column following comment introducer (`##`). + pub fn pretty_text(&self) -> ImString { + self.line.rendered.clone() } /// Generates the source code text of the comment line from a pretty text. @@ -177,25 +156,12 @@ impl DocumentationCommentInfo { let mut lines = text.lines(); // First line must always exist, even for an empty comment. let first_line = format!("##{}", lines.next().unwrap_or_default()); - let other_lines = lines.map(|line| format!("{indent} {line}")); + let other_lines = lines.map(|line| format!("{indent} {line}")); let mut out_lines = std::iter::once(first_line).chain(other_lines); out_lines.join("\n") } } - -impl AsRef for DocumentationCommentInfo { - fn as_ref(&self) -> &Ast { - self.line.elem.ast() - } -} - -impl Display for DocumentationCommentInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.pretty_text()) - } -} - /// Check if given Ast stores a documentation comment. pub fn is_documentation_comment(ast: &Ast) -> bool { DocumentationCommentAst::new(ast).is_some() @@ -203,112 +169,27 @@ pub fn is_documentation_comment(ast: &Ast) -> bool { -// =============== -// === Imports === -// =============== - -/// If the given AST node is an import declaration, returns it as a Match (which is the only shape -/// capable of storing import declarations). Returns `None` otherwise. -pub fn ast_as_import_match(ast: &Ast) -> Option { - let macro_match = known::Match::try_from(ast).ok()?; - is_match_import(¯o_match).then_some(macro_match) -} - -/// If the given AST node is a qualified import declaration (`import `), returns it as -/// a Match (which is the only shape capable of storing import declarations). Returns `None` -/// otherwise. -pub fn is_match_qualified_import(ast: &known::Match) -> bool { - let segment = &ast.segs.head; - let keyword = crate::identifier::name(&segment.head); - keyword.contains_if(|str| *str == QUALIFIED_IMPORT_KEYWORD) -} - -/// If the given AST node is an unqualified import declaration (`from import <...>`), -/// returns it as a Match (which is the only shape capable of storing import declarations). Returns -/// `None` otherwise. -pub fn is_match_unqualified_import(ast: &known::Match) -> bool { - let first_segment = &ast.segs.head; - let first_keyword = crate::identifier::name(&first_segment.head); - let second_segment = &ast.segs.tail.first(); - let second_keyword = second_segment.and_then(|s| crate::identifier::name(&s.head)); - first_keyword == Some(UNQUALIFIED_IMPORT_KEYWORD) - && second_keyword == Some(QUALIFIED_IMPORT_KEYWORD) -} - -/// Check if the given macro match node is an import declaration. -pub fn is_match_import(ast: &known::Match) -> bool { - is_match_qualified_import(ast) || is_match_unqualified_import(ast) -} - -/// Check if the given ast node is an import declaration. -pub fn is_ast_import(ast: &Ast) -> bool { - ast_as_import_match(ast).is_some() -} - - - // =============== // === Lambdas === // =============== -/// Describes the lambda-expression's three pieces: the argument, the arrow operator and the body. +/// Describes the lambda-expression's pieces: the argument and the body. #[allow(missing_docs)] #[derive(Clone, Debug)] pub struct LambdaInfo<'a> { pub arg: Located<&'a Ast>, - pub opr: Located<&'a Ast>, pub body: Located<&'a Ast>, } -/// If this is the builtin macro for `->` (lambda expression), returns it as known `Match`. -pub fn as_lambda_match(ast: &Ast) -> Option { - let macro_match = known::Match::try_from(ast).ok()?; - let segment = ¯o_match.segs.head; - crate::opr::is_arrow_opr(&segment.head).then_some(macro_match) -} - /// Describes the given Ast as lambda, if this is a matched `->` builtin macro. pub fn as_lambda(ast: &Ast) -> Option { - let _ = as_lambda_match(ast)?; - let mut child_iter = ast.iter_subcrumbs(); - let arg = ast.get_located(child_iter.next()?).ok()?; - let opr = ast.get_located(child_iter.next()?).ok()?; - let body = ast.get_located(child_iter.next()?).ok()?; - let is_arrow = crate::opr::is_arrow_opr(opr.item); - is_arrow.then_some(LambdaInfo { arg, opr, body }) -} - - - -// =================== -// === Match Utils === -// =================== - -impl crate::Match { - /// Iterates matched ASTs. Skips segment heads ("keywords"). - /// For example, for `(a)` it iterates only over `a`, skkipping segment heads `(` and `)`. - pub fn iter_pat_match_subcrumbs(&self) -> impl Iterator + '_ { - self.iter_subcrumbs().filter(|crumb| { - use crate::crumbs::SegmentMatchCrumb; - match crumb { - MatchCrumb::Segs { val, .. } => val != &SegmentMatchCrumb::Head, - _ => true, - } - }) - } -} - - - -// ======================= -// === Ambiguous Utils === -// ======================= - -impl crate::Ambiguous { - /// Iterates matched ASTs. Skips segment heads ("keywords"). - /// For example, for `(a)` it iterates only over `a`, skkipping segment heads `(` and `)`. - pub fn iter_pat_match_subcrumbs(&self) -> impl Iterator + '_ { - self.iter_subcrumbs() - .filter(|crumb| crumb.field != crate::crumbs::AmbiguousSegmentCrumb::Head) + if let crate::Shape::Tree(crate::Tree { type_info: crate::TreeType::Lambda, .. }) = ast.shape() + { + let mut iter = ast.iter_subcrumbs().map(|crumb| ast.get_located(crumb).unwrap()); + let arg = iter.next().unwrap(); + let body = iter.next().unwrap(); + Some(LambdaInfo { arg, body }) + } else { + None } } diff --git a/app/gui/language/ast/impl/src/macros/skip_and_freeze.rs b/app/gui/language/ast/impl/src/macros/skip_and_freeze.rs index 640f042f509f..ccaa61422576 100644 --- a/app/gui/language/ast/impl/src/macros/skip_and_freeze.rs +++ b/app/gui/language/ast/impl/src/macros/skip_and_freeze.rs @@ -9,7 +9,6 @@ use enso_prelude::*; use crate::known; use crate::Ast; -use crate::Crumbable; use crate::HasRepr; @@ -132,26 +131,12 @@ fn preserving_macro( } /// Check if AST contains a prefix-like macro call with a given name. -/// -/// We check for both [`known::Prefix`] and [`known::Match`], because first one is used when we -/// modify AST using this module, and the second one can be provided by the Engine. -/// -/// Using [`known::Match`] everywhere would be perfect, but it is extremely annoying to construct -/// without using the parser. To construct [`known::Match`] we need to know exactly how the specific -/// macro is represented in the parser and create a sequence of `MacroPatternMatch` segments. The -/// parser generates a huge AST for a simple `skip foo` expression, and it is not wise to exactly -/// repeat this AST generation here. If our generated AST is different from the one generated by the -/// parser anyway, we would rather generate a much more simple [`known::Prefix`]. It is easier to -/// both construct and deconstruct later. pub fn is_macro_call(ast: &Ast, identifier: &str) -> bool { if let Ok(prefix) = known::Prefix::try_from(ast) { let name = crate::identifier::name(&prefix.func); name == Some(identifier) - } else if let Ok(macro_match) = known::Match::try_from(ast) { - let first_segment = ¯o_match.segs.head; - let name = crate::identifier::name(&first_segment.head); - name == Some(identifier) } else { + // TODO: Check for a [`Tree`] macro (https://github.com/enso-org/enso/issues/5572). false } } @@ -160,11 +145,8 @@ pub fn is_macro_call(ast: &Ast, identifier: &str) -> bool { pub fn maybe_prefix_macro_body(ast: &Ast) -> Option { if let Ok(prefix) = known::Prefix::try_from(ast) { Some(prefix.arg.clone()) - } else if let Ok(macro_match) = known::Match::try_from(ast) { - let body_crumb = macro_match.iter_subcrumbs().nth(1)?; - let body_ast = macro_match.get(&body_crumb).ok()?; - Some(body_ast.clone()) } else { + // TODO: Check for a [`Tree`] macro (https://github.com/enso-org/enso/issues/5572). None } } diff --git a/app/gui/language/ast/impl/src/opr.rs b/app/gui/language/ast/impl/src/opr.rs index 483388e895db..5fe49163a7ad 100644 --- a/app/gui/language/ast/impl/src/opr.rs +++ b/app/gui/language/ast/impl/src/opr.rs @@ -107,7 +107,7 @@ pub fn is_assignment(ast: &Ast) -> bool { pub fn assignment() -> known::Opr { // TODO? We could cache and reuse, if we care. let name = predefined::ASSIGNMENT.into(); - let opr = Opr { name }; + let opr = Opr { name, right_assoc: false }; known::Opr::new(opr, None) } @@ -150,7 +150,10 @@ pub fn make_operator(opr: &Ast) -> Option { /// Describes associativity of the given operator AST. pub fn assoc(ast: &known::Opr) -> Assoc { - Assoc::of(&ast.name) + match ast.right_assoc { + true => Assoc::Right, + false => Assoc::Left, + } } @@ -327,7 +330,7 @@ pub struct Chain { /// Subsequent operands applied to the `target`. pub args: Vec, /// Operator AST. Generally all operators in the chain should be the same (except for id). - /// It is not specified which exactly operator's in the chain this AST belongs to. + /// It is not specified exactly which operators in the chain this AST belongs to. pub operator: known::Opr, } diff --git a/app/gui/language/ast/impl/src/repr.rs b/app/gui/language/ast/impl/src/repr.rs index b7bf169ebc1a..179a1fbb7ce9 100644 --- a/app/gui/language/ast/impl/src/repr.rs +++ b/app/gui/language/ast/impl/src/repr.rs @@ -53,66 +53,6 @@ pub const FMT_BLOCK_QUOTES: &str = "'''"; -// =============== -// === Builder === -// =============== - -has_tokens!(Empty); -has_tokens!(Letter, self.char); -has_tokens!(Space, self); -has_tokens!(Text, self.str); -has_tokens!(Seq, self.first, self.second); - - -// ===================== -// === TextBlockLine === -// ===================== - -/// Not an instance of `Tokenizer`, as it needs to know parent block's offset. -impl TextBlockLine { - fn feed_to(&self, consumer: &mut impl TokenConsumer, offset: usize) { - for empty_line_spaces in &self.empty_lines { - (NEWLINE, empty_line_spaces).feed_to(consumer); - } - (NEWLINE, offset, &self.text).feed_to(consumer); - } -} - - - -// ===================== -// === Text Segments === -// ===================== - -has_tokens!(SegmentPlain, self.value); -has_tokens!(SegmentRawEscape, BACKSLASH, self.code); -has_tokens!(SegmentExpr, EXPR_QUOTE, self.value, EXPR_QUOTE); -has_tokens!(SegmentEscape, BACKSLASH, self.code); - - -// ================= -// === RawEscape === -// ================= - -has_tokens!(Unfinished); -has_tokens!(Invalid, self.str); -has_tokens!(Slash, BACKSLASH); -has_tokens!(Quote, FMT_QUOTE); -has_tokens!(RawQuote, RAW_QUOTE); - - -// ============== -// === Escape === -// ============== - -has_tokens!(EscapeCharacter, self.c); -has_tokens!(EscapeControl, self.name); -has_tokens!(EscapeNumber, self.digits); -has_tokens!(EscapeUnicode16, UNICODE16_INTRODUCER, self.digits); -has_tokens!(EscapeUnicode21, UNICODE21_OPENER.deref(), self.digits, UNICODE21_CLOSER.deref()); -has_tokens!(EscapeUnicode32, UNICODE32_INTRODUCER, self.digits); - - // ============= // === Block === // ============= @@ -120,68 +60,20 @@ has_tokens!(EscapeUnicode32, UNICODE32_INTRODUCER, self.digits); has_tokens!(BlockLine, self.elem, self.off); -// ============= -// === Macro === -// ============= - -// === Macro Segments == - -has_tokens!(MacroMatchSegment, self.head, self.body); -has_tokens!(MacroAmbiguousSegment, self.head, self.body); - - -// === MacroPatternMatch subtypes === - -has_tokens!(MacroPatternMatchRawBegin); -has_tokens!(MacroPatternMatchRawEnd); -has_tokens!(MacroPatternMatchRawNothing); -has_tokens!(MacroPatternMatchRawSeq, self.elem); -has_tokens!(MacroPatternMatchRawOr, self.elem); -has_tokens!(MacroPatternMatchRawMany, self.elem); -has_tokens!(MacroPatternMatchRawExcept, self.elem); -has_tokens!(MacroPatternMatchRawBuild, self.elem); -has_tokens!(MacroPatternMatchRawErr, self.elem); -has_tokens!(MacroPatternMatchRawTag, self.elem); -has_tokens!(MacroPatternMatchRawCls, self.elem); -has_tokens!(MacroPatternMatchRawTok, self.elem); -has_tokens!(MacroPatternMatchRawBlank, self.elem); -has_tokens!(MacroPatternMatchRawVar, self.elem); -has_tokens!(MacroPatternMatchRawCons, self.elem); -has_tokens!(MacroPatternMatchRawOpr, self.elem); -has_tokens!(MacroPatternMatchRawAnnotation, self.elem); -has_tokens!(MacroPatternMatchRawMod, self.elem); -has_tokens!(MacroPatternMatchRawNum, self.elem); -has_tokens!(MacroPatternMatchRawText, self.elem); -has_tokens!(MacroPatternMatchRawBlock, self.elem); -has_tokens!(MacroPatternMatchRawMacro, self.elem); -has_tokens!(MacroPatternMatchRawInvalid, self.elem); -has_tokens!(MacroPatternMatchRawFailedMatch); - - -// === Switch === - -has_tokens!(Switch, self.deref()); - +// =============== // === Shifted === +// =============== has_tokens!(Shifted, self.off, self.wrapped); has_tokens!(ShiftedVec1, self.head, self.tail); + // ============================================================================= // === Shape =================================================================== // ============================================================================= -// =============== -// === Invalid === -// =============== - -has_tokens!(Unrecognized, self.str); -has_tokens!(Unexpected, self.stream); -has_tokens!(InvalidQuote, self.quote); -has_tokens!(InlineBlock, self.quote); - // =================== // === Identifiers === @@ -193,7 +85,6 @@ has_tokens!(Cons, self.name); has_tokens!(Opr, self.name); has_tokens!(Annotation, self.name); has_tokens!(Mod, self.name, MOD_SUFFIX); -has_tokens!(InvalidSuffix, self.elem, self.suffix); // ============== @@ -205,55 +96,6 @@ struct NumberBase(T); has_tokens!(NumberBase, self.0, NUMBER_BASE_SEPARATOR); has_tokens!(Number, self.base.as_ref().map(NumberBase), self.int); -has_tokens!(DanglingBase, self.base, NUMBER_BASE_SEPARATOR); - - - -// ============ -// === Text === -// ============ - - -// === Lines === - -has_tokens!(TextLineRaw, RAW_QUOTE, self.text, RAW_QUOTE); -has_tokens!(TextLineFmt, FMT_QUOTE, self.text, FMT_QUOTE); - - -// === TextBlockRaw == - -impl HasTokens for TextBlockRaw { - fn feed_to(&self, consumer: &mut impl TokenConsumer) { - (RAW_BLOCK_QUOTES, self.spaces).feed_to(consumer); - for line in self.text.iter() { - line.feed_to(consumer, self.offset); - } - } -} - - -// === TextBlockFmt == - -impl HasTokens for TextBlockFmt { - fn feed_to(&self, consumer: &mut impl TokenConsumer) { - (FMT_BLOCK_QUOTES, self.spaces).feed_to(consumer); - for line in self.text.iter() { - line.feed_to(consumer, self.offset); - } - } -} - - -// === TextUnclosed == - -impl HasTokens for TextUnclosed { - fn feed_to(&self, consumer: &mut impl TokenConsumer) { - match &self.line { - TextLine::TextLineRaw(line) => (RAW_QUOTE, &line.text).feed_to(consumer), - TextLine::TextLineFmt(line) => (FMT_QUOTE, &line.text).feed_to(consumer), - } - } -} @@ -291,7 +133,7 @@ impl HasTokens for Module { impl HasTokens for Block { fn feed_to(&self, consumer: &mut impl TokenConsumer) { - (!self.is_orphan).as_some(NEWLINE).feed_to(consumer); + NEWLINE.feed_to(consumer); for empty_line_space in &self.empty_lines { (empty_line_space, NEWLINE).feed_to(consumer); } @@ -304,171 +146,26 @@ impl HasTokens for Block { -// ============== -// === Macros === -// ============== - -// === Match == +// ============ +// === Tree === +// ============ -impl HasTokens for Match { +impl HasTokens for Tree { fn feed_to(&self, consumer: &mut impl TokenConsumer) { - for pat_match in &self.pfx { - for sast in pat_match.iter() { - // reverse the order for prefix: ast before spacing - (&sast.wrapped, &sast.off).feed_to(consumer); + if let Some(str) = &self.leaf_info { + Token::Str(str).feed_to(consumer) + } else { + for element in &self.span_info { + match element { + SpanSeed::Space(SpanSeedSpace { space }) => + Token::Off(*space).feed_to(consumer), + SpanSeed::Token(SpanSeedToken { token }) => Token::Str(token).feed_to(consumer), + SpanSeed::Child(SpanSeedChild { node }) => node.feed_to(consumer), + } } } - self.segs.feed_to(consumer); - } -} - - -// === Ambiguous === - -has_tokens!(Ambiguous, self.segs); - - - -// ===================== -// === Spaceless AST === -// ===================== - -spaceless_ast!(Comment); -spaceless_ast!(Documented); -spaceless_ast!(Import); -spaceless_ast!(Export); -spaceless_ast!(JavaImport); -spaceless_ast!(Mixfix); -spaceless_ast!(Group); -spaceless_ast!(SequenceLiteral); -spaceless_ast!(TypesetLiteral); -spaceless_ast!(Def); -spaceless_ast!(Foreign); -spaceless_ast!(Modified); - - - -// ============= -// === Tests === -// ============= - -/// Tests for spacelesss AST. Other AST is covered by parsing tests that verify -/// that correct lengths and text representation are generated. Only spaceless AST -/// is not returned by the parser and can't be covered in this way. -#[cfg(test)] -mod tests { - use super::*; - - // === Comment === - - fn make_comment() -> Shape { - Comment { lines: vec![] }.into() - } - - #[test] - #[should_panic] - fn comment_panics_on_repr() { - make_comment().repr(); - } - - #[test] - #[should_panic] - fn comment_panics_on_length() { - make_comment().len(); - } - - - // === Import === - - fn make_import() -> Shape { - let path = vec![Ast::var("Target")]; - Import { path, rename: None, isAll: false, onlyNames: None, hidingNames: None }.into() - } - - #[test] - #[should_panic] - fn import_panics_on_repr() { - make_import().repr(); - } - - #[test] - #[should_panic] - fn import_panics_on_length() { - make_import().len(); - } - - - // === Mixfix === - - fn make_mixfix() -> Shape { - Mixfix { name: vec![], args: vec![] }.into() - } - - #[test] - #[should_panic] - fn mixfix_panics_on_repr() { - make_mixfix().repr(); - } - - #[test] - #[should_panic] - fn mixfix_panics_on_length() { - make_mixfix().len(); - } - - - // === Group === - - fn make_group() -> Shape { - Group { body: None }.into() - } - - #[test] - #[should_panic] - fn group_panics_on_repr() { - make_group().repr(); - } - - #[test] - #[should_panic] - fn group_panics_on_length() { - make_group().len(); - } - - - // === Def === - - fn make_def() -> Shape { - Def { name: Ast::cons("Foo"), args: vec![], body: None }.into() - } - - #[test] - #[should_panic] - fn def_panics_on_repr() { - make_def().repr(); - } - - #[test] - #[should_panic] - fn def_panics_on_length() { - make_def().len(); - } - - // === Foreign === - - fn make_foreign() -> Shape { - Foreign { indent: 0, lang: "Python".into(), code: vec![] }.into() - } - - #[test] - #[should_panic] - fn foreign_panics_on_repr() { - make_foreign().repr(); - } - - #[test] - #[should_panic] - fn foreign_panics_on_length() { - make_foreign().len(); + if let Some(str) = &self.trailing_token { + Token::Str(str).feed_to(consumer) + } } } diff --git a/app/gui/language/ast/macros/src/lib.rs b/app/gui/language/ast/macros/src/lib.rs index 4b93b1e98624..d0955017b61d 100644 --- a/app/gui/language/ast/macros/src/lib.rs +++ b/app/gui/language/ast/macros/src/lib.rs @@ -45,7 +45,6 @@ pub fn ast_node( let output = quote! { #[derive(Clone,Eq,PartialEq,Debug)] #[derive(Iterator)] - #[derive(Serialize,Deserialize)] #input }; output.into() @@ -283,32 +282,3 @@ pub fn has_tokens(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let maker = syn::parse::(input).unwrap(); maker.has_tokens().into() } - -/// Generates `HasTokens` instances that are just sum of their parts. -/// -/// Takes 1+ parameters: -/// * first goes the typename for which implementations are generated (can take type parameters, as -/// long as they implement `HasTokens`) -/// * then arbitrary number (0 or more) of expressions, that shall yield values implementing -/// `HasTokens`. The `self` can be used in th expressions. -/// -/// For example, for invocation: -/// ```text -/// has_tokens!(SegmentExpr, EXPR_QUOTE, self.value, EXPR_QUOTE); -/// ``` -/// the following output is produced: -/// ```text -/// impl HasTokens for SegmentExpr { -/// fn feed_to(&self, consumer:&mut impl TokenConsumer) { -/// EXPR_QUOTE.feed(consumer); -/// self.value.feed(consumer); -/// EXPR_QUOTE.feed(consumer); -/// } -/// } -/// ``` - -/// Generates `HasTokens` implementations for spaceless AST that panics when used. -#[proc_macro] -pub fn spaceless_ast(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - crate::token::spaceless_ast(input) -} diff --git a/app/gui/language/ast/macros/src/token.rs b/app/gui/language/ast/macros/src/token.rs index 5f5eb7e865a7..152de682d619 100644 --- a/app/gui/language/ast/macros/src/token.rs +++ b/app/gui/language/ast/macros/src/token.rs @@ -9,20 +9,6 @@ use syn::Token; -/// Generates `HasTokens` implementations for spaceless AST that panics when used. -pub fn spaceless_ast(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let target = syn::parse::(input).unwrap(); - let ty_args = path_segment_generic_args(&target); - let ret = quote! { - impl<#(#ty_args),*> HasTokens for #target { - fn feed_to(&self, consumer:&mut impl TokenConsumer) { - panic!("HasTokens not supported for Spaceless AST!") - } - } - }; - ret.into() -} - /// Inner logic for `derive_has_tokens`. pub fn derive_for_enum(decl: &syn::DeriveInput, data: &syn::DataEnum) -> TokenStream { let ident = &decl.ident; diff --git a/app/gui/language/parser/.gitignore b/app/gui/language/parser-scala/.gitignore similarity index 100% rename from app/gui/language/parser/.gitignore rename to app/gui/language/parser-scala/.gitignore diff --git a/app/gui/language/parser-scala/Cargo.toml b/app/gui/language/parser-scala/Cargo.toml new file mode 100644 index 000000000000..e93083c63d55 --- /dev/null +++ b/app/gui/language/parser-scala/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "parser-scala" +version = "0.1.0" +authors = ["Enso Team "] +edition = "2021" +build = "build.rs" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +ast = { path = "../ast/impl" } +enso-prelude = { path = "../../../../lib/rust/prelude" } +enso-profiler = { path = "../../../../lib/rust/profiler" } +console_error_panic_hook = { workspace = true } +failure = { workspace = true } +js-sys = { workspace = true } +matches = { workspace = true } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", features = ["unbounded_depth"] } +wasm-bindgen = { workspace = true } + +[dev-dependencies] +wasm-bindgen-test = { workspace = true } + +[build-dependencies] +ide-ci = { path = "../../../../build/ci_utils" } +bytes = { workspace = true } +futures = { workspace = true } +reqwest = { workspace = true } +tokio = { workspace = true } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +websocket = "0.26.5" diff --git a/app/gui/language/parser/build.rs b/app/gui/language/parser-scala/build.rs similarity index 100% rename from app/gui/language/parser/build.rs rename to app/gui/language/parser-scala/build.rs diff --git a/app/gui/language/parser-scala/src/api.rs b/app/gui/language/parser-scala/src/api.rs new file mode 100644 index 000000000000..f057d7a1dfce --- /dev/null +++ b/app/gui/language/parser-scala/src/api.rs @@ -0,0 +1,29 @@ +//! A module containing structures and traits used in parser API. + +use crate::prelude::*; + + + +// =========== +// == Error == +// =========== + +/// A result of parsing code. +pub type Result = std::result::Result; + +/// An error which may be result of parsing code. +#[derive(Debug, Fail)] +pub enum Error { + /// Error due to inner workings of the parser. + #[fail(display = "Internal parser error: {:?}.", _0)] + ParsingError(String), + /// Error related to wrapping = communication with the parser service. + #[fail(display = "Interop error: {}.", _0)] + InteropError(#[cause] Box), +} + +/// Wraps an arbitrary `std::error::Error` as an `InteropError.` +pub fn interop_error(error: T) -> Error +where T: Fail { + Error::InteropError(Box::new(error)) +} diff --git a/app/gui/language/parser/src/jsclient.rs b/app/gui/language/parser-scala/src/jsclient.rs similarity index 62% rename from app/gui/language/parser/src/jsclient.rs rename to app/gui/language/parser-scala/src/jsclient.rs index efae77304bea..6422a24cdf91 100644 --- a/app/gui/language/parser/src/jsclient.rs +++ b/app/gui/language/parser-scala/src/jsclient.rs @@ -4,11 +4,6 @@ use crate::prelude::*; use wasm_bindgen::prelude::*; use crate::api; -use crate::api::Ast; -use crate::from_json_str_without_recursion_limit; - -use ast::id_map::JsonIdMap; -use ast::IdMap; @@ -43,10 +38,6 @@ impl From for Error { #[wasm_bindgen(module = "/pkg/scala-parser.js")] extern "C" { - #[wasm_bindgen(catch)] - fn parse(input: String, ids: String) -> std::result::Result; - #[wasm_bindgen(catch)] - fn parse_with_metadata(content: String) -> std::result::Result; #[wasm_bindgen(catch)] fn doc_parser_generate_html_source(content: String) -> std::result::Result; #[wasm_bindgen(catch)] @@ -65,31 +56,6 @@ impl Client { Ok(Client {}) } - /// Parses Enso code with JS-based parser. - pub fn parse(&self, program: String, ids: IdMap) -> api::Result { - let ast = || { - let ids = JsonIdMap::from_id_map(&ids, &program.clone().into()); - let json_ids = serde_json::to_string(&ids)?; - let json_ast = parse(program, json_ids)?; - let ast = from_json_str_without_recursion_limit(&json_ast)?; - Result::Ok(ast) - }; - Ok(ast()?) - } - - /// Parses Enso code with metadata. - pub fn parse_with_metadata( - &self, - program: String, - ) -> api::Result> { - let result = || { - let json = &parse_with_metadata(program)?; - let module = from_json_str_without_recursion_limit(json)?; - Result::Ok(module) - }; - Ok(result()?) - } - /// Calls JS doc parser to generate HTML from documented Enso code. pub fn generate_html_docs(&self, program: String) -> api::Result { let html_code = || { diff --git a/app/gui/language/parser-scala/src/lib.rs b/app/gui/language/parser-scala/src/lib.rs new file mode 100644 index 000000000000..e5a2d77d847b --- /dev/null +++ b/app/gui/language/parser-scala/src/lib.rs @@ -0,0 +1,108 @@ +//! Crate wrapping parser API in nice-to-use Rust code. +//! +//! The Parser is a library written in scala. There are two implementations of Rust wrappers to +//! this parser: one for local parser which binds scala parser compiled to WebAssembly to the Rust +//! crate. The second is calling a Parser running remotely using WebSockets. + +// === Features === +#![feature(trait_alias)] +// === Standard Linter Configuration === +#![deny(non_ascii_idents)] +#![warn(unsafe_code)] +#![allow(clippy::bool_to_int_with_if)] +#![allow(clippy::let_and_return)] +// === Non-Standard Linter Configuration === +#![warn(missing_docs)] +#![warn(trivial_casts)] +#![warn(trivial_numeric_casts)] +#![warn(unused_import_braces)] +#![warn(unused_qualifications)] +#![warn(missing_copy_implementations)] +#![warn(missing_debug_implementations)] + + +// ============== +// === Export === +// ============== + +pub mod api; + + + +mod jsclient; +mod wsclient; + +use crate::prelude::*; + +use std::panic; + + + +#[allow(missing_docs)] +pub mod prelude { + pub use ast::traits::*; + pub use enso_prelude::*; + pub use enso_profiler as profiler; + pub use enso_profiler::prelude::*; +} + + + +// ========================================== +// === Documentation Parser and Generator === +// ========================================== + +/// Handle to a doc parser implementation. +/// +/// Currently this component is implemented as a wrapper over documentation +/// parser written in Scala. Depending on compilation target (native or wasm) +/// it uses either implementation provided by `wsclient` or `jsclient`. +#[derive(Clone, CloneRef, Debug, Deref, DerefMut)] +pub struct DocParser(pub Rc>); + +impl DocParser { + /// Obtains a default doc parser implementation. + #[cfg(not(target_arch = "wasm32"))] + pub fn new() -> api::Result { + let client = wsclient::Client::new()?; + let doc_parser = Rc::new(RefCell::new(client)); + Ok(DocParser(doc_parser)) + } + + /// Obtains a default doc parser implementation. + #[cfg(target_arch = "wasm32")] + pub fn new() -> api::Result { + let client = jsclient::Client::new()?; + let doc_parser = Rc::new(RefCell::new(client)); + Ok(DocParser(doc_parser)) + } + + /// Obtains a default doc parser implementation, panicking in case of failure. + pub fn new_or_panic() -> DocParser { + DocParser::new().unwrap_or_else(|e| panic!("Failed to create doc parser: {e:?}")) + } + + /// Parses program with documentation and generates HTML code. + /// If the program does not have any documentation will return empty string. + pub fn generate_html_docs(&self, program: String) -> api::Result { + self.borrow_mut().generate_html_docs(program) + } + + /// Parses pure documentation code and generates HTML code. + /// Will return empty string for empty entry. + pub fn generate_html_doc_pure(&self, code: String) -> api::Result { + self.borrow_mut().generate_html_doc_pure(code) + } +} + + +// === Support === + +/// Websocket parser client. +/// Used as an interface for our (scala) parser. +#[cfg(not(target_arch = "wasm32"))] +type Client = wsclient::Client; +/// Javascript parser client. +/// Used as an interface for our (scala) parser. +#[cfg(target_arch = "wasm32")] +type Client = jsclient::Client; diff --git a/app/gui/language/parser/src/test_utils.rs b/app/gui/language/parser-scala/src/test_utils.rs similarity index 100% rename from app/gui/language/parser/src/test_utils.rs rename to app/gui/language/parser-scala/src/test_utils.rs diff --git a/app/gui/language/parser/src/wsclient.rs b/app/gui/language/parser-scala/src/wsclient.rs similarity index 74% rename from app/gui/language/parser/src/wsclient.rs rename to app/gui/language/parser-scala/src/wsclient.rs index bacd7a1d2483..36ab21fa20dc 100644 --- a/app/gui/language/parser/src/wsclient.rs +++ b/app/gui/language/parser-scala/src/wsclient.rs @@ -4,12 +4,7 @@ use crate::api::Error::*; use crate::prelude::*; use crate::api; -use crate::api::Ast; -use crate::api::Metadata; -use crate::api::ParsedSourceFile; -use ast::id_map::JsonIdMap; -use ast::IdMap; use std::fmt::Formatter; use websocket::stream::sync::TcpStream; use websocket::ClientBuilder; @@ -93,24 +88,10 @@ impl From for Error { #[allow(clippy::enum_variant_names)] #[derive(Debug, serde::Serialize, serde::Deserialize)] pub enum Request { - ParseRequest { program: String, ids: JsonIdMap }, - ParseRequestWithMetadata { content: String }, DocParserGenerateHtmlSource { program: String }, DocParserGenerateHtmlFromDoc { code: String }, } -/// All responses that Parser Service might reply with. -#[derive(Debug, serde::Deserialize)] -pub enum Response { - #[serde(bound(deserialize = "M: Metadata"))] - Success { - module: ParsedSourceFile, - }, - Error { - message: String, - }, -} - /// All responses that Doc Parser Service might reply with. #[derive(Debug, serde::Deserialize)] pub enum ResponseDoc { @@ -171,19 +152,6 @@ mod internal { Ok(()) } - /// Obtains a text message from peer and deserializes it using JSON - /// into a `Response`. - /// - /// Should be called exactly once after each `send_request` invocation. - pub fn recv_response(&mut self) -> Result> { - let response = self.connection.recv_message()?; - match response { - websocket::OwnedMessage::Text(text) => - crate::from_json_str_without_recursion_limit(&text).map_err(Into::into), - _ => Err(Error::NonTextResponse(response)), - } - } - /// Obtains a text message from peer and deserializes it using JSON /// into a `ResponseDoc`. /// @@ -196,15 +164,6 @@ mod internal { } } - /// Sends given `Request` to peer and receives a `Response`. - /// - /// Both request and response are exchanged in JSON using text messages - /// over WebSocket. - pub fn rpc_call(&mut self, request: Request) -> Result> { - self.send_request(request)?; - self.recv_response() - } - /// Sends given `Request` to peer and receives a `ResponseDoc`. /// /// Both request and response are exchanged in JSON using text messages @@ -237,30 +196,6 @@ impl Client { Ok(client) } - /// Sends a request to parser service to parse Enso code. - pub fn parse(&mut self, program: String, ids: IdMap) -> api::Result { - let ids = JsonIdMap::from_id_map(&ids, &program.as_str().into()); - let request = Request::ParseRequest { program, ids }; - let response = self.rpc_call::(request)?; - match response { - Response::Success { module } => Ok(module.ast.into()), - Response::Error { message } => Err(ParsingError(message)), - } - } - - /// Sends a request to parser service to parse code with metadata. - pub fn parse_with_metadata( - &mut self, - program: String, - ) -> api::Result> { - let request = Request::ParseRequestWithMetadata { content: program }; - let response = self.rpc_call(request)?; - match response { - Response::Success { module } => Ok(module), - Response::Error { message } => Err(ParsingError(message)), - } - } - /// Sends a request to parser service to generate HTML code from documented Enso code. pub fn generate_html_docs(&mut self, program: String) -> api::Result { let request = Request::DocParserGenerateHtmlSource { program }; diff --git a/app/gui/language/parser/Cargo.toml b/app/gui/language/parser/Cargo.toml index 3f8edf830898..166d1fffa88a 100644 --- a/app/gui/language/parser/Cargo.toml +++ b/app/gui/language/parser/Cargo.toml @@ -1,37 +1,19 @@ [package] -name = "parser-scala" +name = "parser" version = "0.1.0" authors = ["Enso Team "] edition = "2021" -build = "build.rs" [lib] crate-type = ["cdylib", "rlib"] [dependencies] ast = { path = "../ast/impl" } -enso-data-structures = { path = "../../../../lib/rust/data-structures" } +enso-parser = { path = "../../../../lib/rust/parser" } enso-prelude = { path = "../../../../lib/rust/prelude" } enso-profiler = { path = "../../../../lib/rust/profiler" } -enso-text = { path = "../../../../lib/rust/text" } -console_error_panic_hook = { workspace = true } -failure = { workspace = true } -js-sys = { workspace = true } -matches = { workspace = true } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["unbounded_depth"] } -uuid = { version = "0.8", features = ["serde", "v5", "wasm-bindgen"] } -wasm-bindgen = { workspace = true } - -[dev-dependencies] -wasm-bindgen-test = { workspace = true } - -[build-dependencies] -ide-ci = { path = "../../../../build/ci_utils" } -bytes = { workspace = true } -futures = { workspace = true } -reqwest = { workspace = true } -tokio = { workspace = true } - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -websocket = "0.26.5" +enso-text = { path = "../../../../lib/rust/text" } +failure = { version = "0.1" } +uuid = { version = "0.8" } diff --git a/app/gui/language/parser/src/api.rs b/app/gui/language/parser/src/api.rs index e6bd45116c40..3edec36f9d36 100644 --- a/app/gui/language/parser/src/api.rs +++ b/app/gui/language/parser/src/api.rs @@ -1,8 +1,7 @@ //! A module containing structures and traits used in parser API. -use crate::prelude::*; +use enso_prelude::*; use enso_text::index::*; -use enso_text::traits::*; use enso_text::unit::*; use ast::id_map::JsonIdMap; @@ -10,16 +9,66 @@ use ast::HasIdMap; use ast::HasRepr; use ast::IdMap; use enso_text::Range; -use serde::de::DeserializeOwned; -use serde::Deserialize; -use serde::Serialize; -// ============== -// === Export === -// ============== -pub use ast::Ast; +/// A parsed file containing source code and attached metadata. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ParsedSourceFile { + /// Ast representation. + pub ast: ast::known::Module, + /// Raw metadata in json. + pub metadata: M, +} + +const NEWLINES_BEFORE_TAG: usize = 3; +const METADATA_TAG: &str = "#### METADATA ####"; + +impl ParsedSourceFile { + /// Serialize to the SourceFile structure, + pub fn serialize(&self) -> std::result::Result { + fn to_json_single_line( + val: &impl serde::Serialize, + ) -> std::result::Result { + let json = serde_json::to_string(val)?; + let line = json.chars().filter(|c| *c != '\n' && *c != '\r').collect(); + Ok(line) + } + + let code = self.ast.repr().into(); + let before_tag = "\n".repeat(NEWLINES_BEFORE_TAG); + let before_idmap = "\n"; + let json_id_map = JsonIdMap::from_id_map(&self.ast.id_map(), &code); + let id_map = to_json_single_line(&json_id_map)?; + let before_metadata = "\n"; + let metadata = to_json_single_line(&self.metadata)?; + + let id_map_start = + code.len().value + before_tag.len() + METADATA_TAG.len() + before_idmap.len(); + let id_map_start_bytes = Byte::from(id_map_start); + let metadata_start = id_map_start + id_map.len() + before_metadata.len(); + let metadata_start_bytes = Byte::from(metadata_start); + let content = format!( + "{code}{before_tag}{METADATA_TAG}{before_idmap}{id_map}{before_metadata}{metadata}" + ); + Ok(SourceFile { + content, + code: (0.byte()..code.len().to_byte()).into(), + id_map: (id_map_start_bytes..id_map_start_bytes + ByteDiff::from(id_map.len())).into(), + metadata: (metadata_start_bytes..metadata_start_bytes + ByteDiff::from(metadata.len())) + .into(), + }) + } +} + +impl Display for ParsedSourceFile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.serialize() { + Ok(serialized) => write!(f, "{serialized}"), + Err(_) => write!(f, "[UNREPRESENTABLE SOURCE FILE]"), + } + } +} @@ -48,7 +97,9 @@ pub trait PruneUnusedIds { } /// Things that are metadata. -pub trait Metadata: Default + Serialize + DeserializeOwned + PruneUnusedIds {} +pub trait Metadata: + Default + serde::de::DeserializeOwned + serde::Serialize + PruneUnusedIds { +} /// Raw metadata. impl PruneUnusedIds for serde_json::Value {} @@ -164,98 +215,6 @@ impl SourceFile { } -// === Parsed Source File === - -/// Parsed file / module with metadata. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] -#[serde(bound = "M: Metadata")] -#[serde(from = "ParsedSourceFileWithUnusedIds")] -pub struct ParsedSourceFile { - /// Ast representation. - pub ast: ast::known::Module, - /// Raw metadata in json. - pub metadata: M, -} - -/// Helper for deserialization. `metadata` is filled with `default()` value if not present or is -/// invalid. -/// -/// [`PruneUnusedIds::prune_unused_ids`] is called on deserialization. -#[derive(Deserialize)] -struct ParsedSourceFileWithUnusedIds { - ast: ast::known::Module, - #[serde(bound(deserialize = "Metadata:Default+DeserializeOwned"))] - #[serde(deserialize_with = "enso_prelude::deserialize_or_default")] - metadata: Metadata, -} - -impl From> for ParsedSourceFile { - fn from(file: ParsedSourceFileWithUnusedIds) -> Self { - let ast = file.ast; - let mut metadata = file.metadata; - metadata.prune_unused_ids(&ast.id_map()); - Self { ast, metadata } - } -} - -impl TryFrom<&ParsedSourceFile> for String { - type Error = serde_json::Error; - fn try_from(val: &ParsedSourceFile) -> std::result::Result { - Ok(val.serialize()?.content) - } -} - -impl Display for ParsedSourceFile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.serialize() { - Ok(serialized) => write!(f, "{serialized}"), - Err(_) => write!(f, "[NOT REPRESENTABLE SOURCE FILE]"), - } - } -} - -// === Parsed Source File Serialization === - -const NEWLINES_BEFORE_TAG: usize = 3; - -const METADATA_TAG: &str = "#### METADATA ####"; - -fn to_json_single_line(val: &impl Serialize) -> std::result::Result { - let json = serde_json::to_string(val)?; - let line = json.chars().filter(|c| *c != '\n' && *c != '\r').collect(); - Ok(line) -} - -impl ParsedSourceFile { - /// Serialize to the SourceFile structure, - pub fn serialize(&self) -> std::result::Result { - let code = self.ast.repr().into(); - let before_tag = "\n".repeat(NEWLINES_BEFORE_TAG); - let before_idmap = "\n"; - let json_id_map = JsonIdMap::from_id_map(&self.ast.id_map(), &code); - let id_map = to_json_single_line(&json_id_map)?; - let before_metadata = "\n"; - let metadata = to_json_single_line(&self.metadata)?; - - let id_map_start = - code.len().value + before_tag.len() + METADATA_TAG.len() + before_idmap.len(); - let id_map_start_bytes = Byte::from(id_map_start); - let metadata_start = id_map_start + id_map.len() + before_metadata.len(); - let metadata_start_bytes = Byte::from(metadata_start); - Ok(SourceFile { - content: format!( - "{code}{before_tag}{METADATA_TAG}{before_idmap}{id_map}{before_metadata}{metadata}" - ), - code: (0.byte()..code.len().to_byte()).into(), - id_map: (id_map_start_bytes..id_map_start_bytes + ByteDiff::from(id_map.len())) - .into(), - metadata: (metadata_start_bytes..metadata_start_bytes + ByteDiff::from(metadata.len())) - .into(), - }) - } -} - - // =========== // == Error == @@ -293,55 +252,3 @@ pub fn interop_error(error: T) -> Error where T: Fail { Error::InteropError(Box::new(error)) } - - - -// ============= -// === Tests === -// ============= - -#[cfg(test)] -mod test { - use super::*; - - #[derive(Clone, Debug, Default, Deserialize, Serialize)] - struct Metadata { - foo: usize, - } - - impl PruneUnusedIds for Metadata {} - impl crate::api::Metadata for Metadata {} - - #[test] - fn serializing_parsed_source_file() { - let main = ast::Ast::var("main"); - let node = ast::Ast::infix_var("2", "+", "2"); - let infix = ast::Ast::infix(main, "=", node); - let ast: ast::known::Module = ast::Ast::one_line_module(infix).try_into().unwrap(); - let repr = ast.repr().into(); - let metadata = Metadata { foo: 321 }; - let source = ParsedSourceFile { ast, metadata }; - let serialized = source.serialize().unwrap(); - - let expected_json_id_map = JsonIdMap::from_id_map(&source.ast.id_map(), &repr); - let expected_id_map = to_json_single_line(&expected_json_id_map).unwrap(); - let expected_metadata = to_json_single_line(&source.metadata).unwrap(); - let expected_content = format!( - r#"main = 2 + 2 - - -#### METADATA #### -{expected_id_map} -{expected_metadata}"# - ); - - assert_eq!(serialized.content, expected_content); - assert_eq!(serialized.code_slice(), "main = 2 + 2"); - assert_eq!(serialized.id_map_slice(), expected_id_map.as_str()); - assert_eq!(serialized.metadata_slice(), expected_metadata.as_str()); - - // Check that SourceFile round-trips. - let source_file = SourceFile::new(serialized.content.clone()); - assert_eq!(source_file, serialized); - } -} diff --git a/app/gui/language/parser/src/bin/run-scala-parser.rs b/app/gui/language/parser/src/bin/run-scala-parser.rs deleted file mode 100644 index 0569457cd698..000000000000 --- a/app/gui/language/parser/src/bin/run-scala-parser.rs +++ /dev/null @@ -1,49 +0,0 @@ -// === Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] -#![allow(clippy::bool_to_int_with_if)] -#![allow(clippy::let_and_return)] - -use enso_prelude::*; - - - -/// Simple interactive tester - calls parser with its argument (or a -/// hardcoded default) and prints the result, then calls doc parser -/// and prints the HTML code or an error message. -fn main() { - let default_input = String::from("import Foo.Bar\nfoo = a + 2"); - let program = std::env::args().nth(1).unwrap_or(default_input); - debug!("Will parse: {}", program); - - let parser = parser_scala::Parser::new_or_panic(); - let output = parser.parse(program, default()); - match output { - Ok(result) => debug!("Parser responded with: {result:?}"), - Err(e) => debug!("Failed to obtain a response: {e:?}"), - } - - - let default_input = String::from("##\n DEPRECATED\n Foo bar baz\ntype Foo\n type Bar"); - let program = std::env::args().nth(1).unwrap_or(default_input); - debug!("Will parse: {}", program); - - let parser = parser_scala::DocParser::new_or_panic(); - let output = parser.generate_html_docs(program); - match output { - Ok(result) => debug!("Doc parser responded with: {result:?}"), - Err(e) => debug!("Failed to obtain a response: {e:?}"), - } - - - let default_input = String::from("Computes the _logical_ conjunction of *two* booleans"); - let program = std::env::args().nth(1).unwrap_or(default_input); - debug!("Will parse: {}", program); - - let parser = parser_scala::DocParser::new_or_panic(); - let output = parser.generate_html_doc_pure(program); - match output { - Ok(result) => debug!("Doc parser responded with: {result:?}"), - Err(e) => debug!("Failed to obtain a response: {e:?}"), - } -} diff --git a/app/gui/language/parser/src/lib.rs b/app/gui/language/parser/src/lib.rs index 09b12291a4c0..bc503db03ecb 100644 --- a/app/gui/language/parser/src/lib.rs +++ b/app/gui/language/parser/src/lib.rs @@ -1,11 +1,10 @@ -//! Crate wrapping parser API in nice-to-use Rust code. -//! -//! The Parser is a library written in scala. There are two implementations of Rust wrappers to -//! this parser: one for local parser which binds scala parser compiled to WebAssembly to the Rust -//! crate. The second is calling a Parser running remotely using WebSockets. +//! [`Parser`] adapts a [`enso_syntax::Parser`] to produce the [`ast::AST`]/[`span_tree`] +//! representation used by the Graph Editor. // === Features === -#![feature(trait_alias)] +#![feature(extend_one)] +#![feature(let_chains)] +#![feature(if_let_guard)] // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] @@ -13,42 +12,26 @@ #![allow(clippy::let_and_return)] // === Non-Standard Linter Configuration === #![warn(missing_docs)] -#![warn(trivial_casts)] -#![warn(trivial_numeric_casts)] -#![warn(unused_import_braces)] -#![warn(unused_qualifications)] -#![warn(missing_copy_implementations)] -#![warn(missing_debug_implementations)] +use enso_prelude::*; +use enso_profiler::prelude::*; -// ============== -// === Export === -// ============== - -pub mod api; - +use ast::prelude::FallibleResult; +use ast::HasIdMap; +use ast::IdMap; +use enso_profiler as profiler; -mod jsclient; -pub mod test_utils; -mod wsclient; -use crate::prelude::*; +mod translation; -use ast::Ast; -use ast::BlockLine; -use ast::IdMap; -use std::panic; +// ============== +// === Export === +// ============== -#[allow(missing_docs)] -pub mod prelude { - pub use ast::traits::*; - pub use enso_prelude::*; - pub use enso_profiler as profiler; - pub use enso_profiler::prelude::*; -} +pub mod api; @@ -56,99 +39,109 @@ pub mod prelude { // === Parser === // ============== -/// Websocket parser client. -/// Used as an interface for our (scala) parser. -#[cfg(not(target_arch = "wasm32"))] -type Client = wsclient::Client; -/// Javascript parser client. -/// Used as an interface for our (scala) parser. -#[cfg(target_arch = "wasm32")] -type Client = jsclient::Client; - -/// Handle to a parser implementation. -/// -/// Currently this component is implemented as a wrapper over parser written -/// in Scala. Depending on compilation target (native or wasm) it uses either -/// implementation provided by `wsclient` or `jsclient`. -#[derive(Clone, CloneRef, Debug, Deref, DerefMut)] -pub struct Parser(pub Rc>); +/// Parses Enso syntax. +#[derive(Debug, Default, Clone, CloneRef)] +pub struct Parser { + parser: Rc, +} -impl Parser { - /// Obtains a default parser implementation. - #[cfg(not(target_arch = "wasm32"))] - pub fn new() -> api::Result { - let client = wsclient::Client::new()?; - let parser = Rc::new(RefCell::new(client)); - Ok(Parser(parser)) - } - /// Obtains a default parser implementation. - #[cfg(target_arch = "wasm32")] - pub fn new() -> api::Result { - let client = jsclient::Client::new()?; - let parser = Rc::new(RefCell::new(client)); - Ok(Parser(parser)) - } +// === Core methods provided by the underlying parser === - /// Obtains a default parser implementation, panicking in case of failure. - pub fn new_or_panic() -> Parser { - Parser::new().unwrap_or_else(|e| panic!("Failed to create a parser: {e:?}")) +impl Parser { + /// Create a new parser. + pub fn new() -> Self { + let parser = Rc::new(enso_parser::Parser::new()); + Self { parser } } - /// Parse program. - pub fn parse(&self, program: String, ids: IdMap) -> api::Result { - self.borrow_mut().parse(program, ids) + /// Parse the given source code with the specified ID map. + #[profile(Task)] + pub fn parse(&self, program: impl Str, ids: IdMap) -> ast::Ast { + let tree = self.parser.run(program.as_ref()); + let ids = ids + .vec + .into_iter() + .map(|(range, id)| ((range.start.value, range.end.value), id)) + .collect(); + translation::tree_to_ast(&tree, ids) } - /// Parse contents of the program source file, where program code may be followed by idmap and - /// metadata. - /// - /// If metadata deserialization fails, error is ignored and default value for metadata is used. - /// Other errors are returned through `Result`. - #[profile(Detail)] + /// Parse the given source code, using metadata (including ID map) found in the input string. + #[profile(Task)] pub fn parse_with_metadata( &self, - program: String, - ) -> api::Result> { - self.borrow_mut().parse_with_metadata(program) + program: impl Str, + ) -> api::ParsedSourceFile { + let (code, meta) = enso_parser::metadata::extract(program.as_ref()); + if meta.is_none() { + info!("parse_with_metadata: No metadata found."); + } + let meta_lines = meta.and_then(|meta| meta.split_once('\n')); + if meta.is_some() && meta_lines.is_none() { + warn!("parse_with_metadata: Expected two lines of metadata."); + } + let ids = meta_lines.map(|lines| lines.0); + let application_metadata = meta_lines.map(|lines| lines.1); + let ids = enso_parser::metadata::parse_metadata(ids.unwrap_or_default()); + if ids.is_none() { + warn!("parse_with_metadata: Failed to parse ID map."); + } + let ids = ids + .unwrap_or_default() + .iter() + .map(|((start, len), id)| ((*start, start + len), uuid::Uuid::from_u128(id.as_u128()))) + .collect(); + let tree = self.parser.run(code); + let metadata = application_metadata.and_then(|meta| serde_json::from_str(meta).ok()); + if application_metadata.is_some() && metadata.is_none() { + warn!("parse_with_metadata: Failed to deserialize metadata."); + } + let ast = translation::tree_to_ast(&tree, ids); + let id_map = ast.id_map(); + let ast = ast::known::Module::try_from(ast).unwrap(); + let mut metadata: M = metadata.unwrap_or_default(); + metadata.prune_unused_ids(&id_map); + api::ParsedSourceFile { ast, metadata } } +} + - /// Parse program into module. +// === Convenience methods === + +impl Parser { + /// Parse the given source code as a module, and return a [`ast::known::Module`]. pub fn parse_module(&self, program: impl Str, ids: IdMap) -> api::Result { - let ast = self.parse(program.into(), ids)?; + let ast = self.parse(program.as_ref(), ids); ast::known::Module::try_from(ast).map_err(|_| api::Error::NonModuleRoot) } - /// Program is expected to be single non-empty line module. The line's AST is - /// returned. The program is parsed with empty IdMap. - #[profile(Debug)] - pub fn parse_line_ast(&self, program: impl Str) -> FallibleResult { - self.parse_line_with_id_map(program, default()).map(|line| line.elem) + /// Parse the given line of source code, and return just the [`ast::Ast`]. + pub fn parse_line_ast(&self, program: impl Str) -> FallibleResult { + self.parse_line(program).map(|line| line.elem) } - /// Program is expected to be single non-empty line module. The line's AST is - /// returned. The program is parsed with empty IdMap. - pub fn parse_line(&self, program: impl Str) -> FallibleResult> { + /// Parse the given line of source code. + pub fn parse_line(&self, program: impl Str) -> FallibleResult> { self.parse_line_with_id_map(program, default()) } - /// Program is expected to be single non-empty line module. The line's AST is returned. + /// Parse the given line of source code, attaching the given IDs. pub fn parse_line_ast_with_id_map( &self, program: impl Str, id_map: IdMap, - ) -> FallibleResult { + ) -> FallibleResult { self.parse_line_with_id_map(program, id_map).map(|line| line.elem) } /// Program is expected to be single non-empty line module. Return the parsed line. - pub fn parse_line_with_id_map( + fn parse_line_with_id_map( &self, program: impl Str, id_map: IdMap, - ) -> FallibleResult> { + ) -> FallibleResult> { let module = self.parse_module(program, id_map)?; - let mut lines = module.lines.clone().into_iter().filter_map(|line| line.map(|elem| elem).transpose()); if let Some(first_non_empty_line) = lines.next() { @@ -163,63 +156,68 @@ impl Parser { } } -/// Deserialize value from json text. -/// -/// Unlike `serde_json::from_str` it runs with recursion limit disabled, allowing deserialization of -/// deeply nested ASTs. -pub fn from_json_str_without_recursion_limit<'de, Value: serde::Deserialize<'de>>( - json_text: &'de str, -) -> Result { - let mut de = serde_json::Deserializer::from_str(json_text); - de.disable_recursion_limit(); - Value::deserialize(&mut de) -} - - - -// ========================================== -// === Documentation Parser and Generator === -// ========================================== - -/// Handle to a doc parser implementation. -/// -/// Currently this component is implemented as a wrapper over documentation -/// parser written in Scala. Depending on compilation target (native or wasm) -/// it uses either implementation provided by `wsclient` or `jsclient`. -#[derive(Clone, CloneRef, Debug, Deref, DerefMut)] -pub struct DocParser(pub Rc>); - -impl DocParser { - /// Obtains a default doc parser implementation. - #[cfg(not(target_arch = "wasm32"))] - pub fn new() -> api::Result { - let client = wsclient::Client::new()?; - let doc_parser = Rc::new(RefCell::new(client)); - Ok(DocParser(doc_parser)) - } +#[cfg(test)] +mod tests { + use super::*; + use ast::HasRepr; - /// Obtains a default doc parser implementation. - #[cfg(target_arch = "wasm32")] - pub fn new() -> api::Result { - let client = jsclient::Client::new()?; - let doc_parser = Rc::new(RefCell::new(client)); - Ok(DocParser(doc_parser)) + #[test] + fn test_group_repr() { + let code = "bar (Foo (a b))"; + let ast = Parser::new().parse_line_ast(code).unwrap(); + assert_eq!(ast.repr(), code); } - /// Obtains a default doc parser implementation, panicking in case of failure. - pub fn new_or_panic() -> DocParser { - DocParser::new().unwrap_or_else(|e| panic!("Failed to create doc parser: {e:?}")) + #[test] + fn test_text_repr() { + let code = "operator17 = operator16.order_by (Sort_Column.Name 'Orders Value' Sort_Direction.Descending)"; + let ast = Parser::new().parse_line_ast(code).unwrap(); + assert_eq!(ast.repr(), code); } - /// Parses program with documentation and generates HTML code. - /// If the program does not have any documentation will return empty string. - pub fn generate_html_docs(&self, program: String) -> api::Result { - self.borrow_mut().generate_html_docs(program) + #[test] + fn test_orders_repr() { + let code = r#" +from Standard.Base import all +from Standard.Table import all +import Standard.Visualization +import Standard.Examples + +main = + ## The file contains three different sheets relating to operations of an + online store. + operator2 = enso_project.data / 'store_data.xlsx' + ## Read the customers table. + operator3 = operator2.read (Excel (Worksheet 'Customers')) + ## Read the products table. + operator4 = operator2.read (Excel (Worksheet 'Items')) + ## Read the orders history. + operator5 = operator2.read (Excel (Worksheet 'Orders')) + ## Join the item data to the order history, to get information on item + prices in the orders table. + operator8 = operator5.join operator4 Join_Kind.Inner ['Item ID'] + operator1 = operator8.at 'Unit Price' + operator9 = operator8.at 'Quantity' + ## Multiply item prices and counts to get total order value. + product1 = operator1 * operator9 + operator10 = operator8.set 'Order Value' product1 + ## Group all orders by the Customer ID, to compute the total value of orders + placed by each client. + operator11 = operator10.aggregate [Aggregate_Column.Group_By 'Customer ID', Aggregate_Column.Sum 'Order Value' 'Orders Value'] + ## Join the customer data into orders table, to include names in the final + ranking. + operator16 = operator3.join operator11 Join_Kind.Inner ["Customer ID"] + ## Sort the customers by their lifetime value, with the most valuable + customers at the start of the table. + operator17 = operator16.order_by (Sort_Column.Name 'Orders Value' Sort_Direction.Descending) +"#; + let ast = Parser::new().parse_module(code, default()).unwrap(); + assert_eq!(ast.repr(), code); } - /// Parses pure documentation code and generates HTML code. - /// Will return empty string for empty entry. - pub fn generate_html_doc_pure(&self, code: String) -> api::Result { - self.borrow_mut().generate_html_doc_pure(code) + #[test] + fn test_as_lambda() { + let ast = Parser::new().parse_line_ast("a->4").unwrap(); + assert!(ast::macros::as_lambda(&ast).is_some(), "{ast:?}"); } } diff --git a/app/gui/language/parser/src/translation.rs b/app/gui/language/parser/src/translation.rs new file mode 100644 index 000000000000..5b11e50acf7c --- /dev/null +++ b/app/gui/language/parser/src/translation.rs @@ -0,0 +1,998 @@ +use enso_prelude::*; +use enso_profiler::prelude::*; + +use ast::Ast; +use enso_parser::syntax; +use enso_parser::syntax::tree; +use enso_parser::syntax::Tree; +use enso_profiler as profiler; +use std::collections::BTreeMap; + + + +/// Enable extra log messages and assertions. +const DEBUG: bool = false; + + + +// ======================= +// === Translation API === +// ======================= + +/// Translates an [`AST`] from the [`Tree`] representation produced by [`enso_parser`] to the +/// [`Ast`] representation used by the GUI (see [`crate`] documentation for high-level overview). +/// The returned tree will contain IDs from the given map. +#[profile(Detail)] +pub fn tree_to_ast(mut tree: &Tree, ids: BTreeMap<(usize, usize), uuid::Uuid>) -> Ast { + use ast::HasRepr; + let mut context = Translate { ids, ..Default::default() }; + let ast = loop { + match &*tree.variant { + tree::Variant::BodyBlock(block) => break context.translate_module(block), + tree::Variant::Invalid(tree::Invalid { ast, error }) => { + warn!("Parser reports invalid module: {}", error.message); + tree = ast + } + _ => unreachable!("enso_parser always returns a tree with a BodyBlock as root."), + } + }; + if DEBUG { + debug_assert_eq!(ast.repr(), tree.code(), "Ast should represent same code as Tree."); + if !context.ids.is_empty() || !context.ids_missed.is_empty() { + warn!( + "ids not matched: {:?}\nids missed: {:?}\nids assigned: {:?}", + &context.ids, &context.ids_missed, &context.ids_assigned + ); + } + } + ast +} + + +// === Implementation === + +#[derive(Debug, Default)] +struct Translate { + /// The offset, in bytes, from the beginning of the input source code. This must be tracked + /// during [`Tree`] traversal because [`Tree`] doesn't have absolute source references, only + /// token lengths. + offset: usize, + /// IDs to associate with byte ranges of the source code. + ids: BTreeMap<(usize, usize), uuid::Uuid>, + /// The [`AstBuilder`] interface supports associating IDs with byte ranges; however, it's + /// important to ensure that the byte ranges don't include any leading whitespace. This would + /// be a difficult invariant to maintain through careful usage of [`AstBuilder`]; instead, + /// we record where whitespace occurs in [`space_after`], and use that to adjust the byte + /// ranges. + space_after: BTreeMap, + + // === Diagnostic information used when [`DEBUG`] is enabled === + /// IDs that were in [`ids`], and now have been successfully attached to [`Ast`] nodes. + ids_assigned: Vec<((usize, usize), uuid::Uuid)>, + /// Byte ranges of [`Ast`] nodes that we didn't find any IDs for. + ids_missed: Vec<(usize, usize)>, +} + +impl Translate { + /// This must be called at the beginning of each [`Tree`], as they are processed in depth-first + /// order. It updates the internal counter to include the leading whitespace bytes, and returns + /// the visible width (indent) of the leading space. + fn visit_space(&mut self, span: &enso_parser::source::Span) -> usize { + let space = span.left_offset.code.repr.len(); + self.space_after.insert(self.offset, space); + self.offset += space; + span.left_offset.visible.width_in_spaces + } + + /// This must be called at the beginning of each [`Token`], as they are processed in depth-first + /// order. It updates the internal counter for the token's bytes, and returns its contents. + fn visit_token(&mut self, token: &syntax::Token) -> WithInitialSpace { + self.visit_token_ref(syntax::token::Ref::::from(token)) + } + + /// This must be called at the beginning of each [`Token`], as they are processed in depth-first + /// order. It updates the internal counter for the token's bytes, and returns its contents. + fn visit_token_ref(&mut self, token: syntax::token::Ref) -> WithInitialSpace { + let space = token.left_offset.visible.width_in_spaces; + let body = token.code.to_string(); + self.space_after.insert(self.offset, space); + self.offset += token.left_offset.code.repr.len(); + self.offset += token.code.repr.len(); + WithInitialSpace { space, body } + } +} + +impl Translate { + /// Translate a [`Tree`]. + /// The returned [`Ast`] can be [`None`] if an empty block is encountered. + fn translate(&mut self, tree: &Tree) -> WithInitialSpace> { + let space = self.visit_space(&tree.span); + let body = self.translate_expression_body(tree); + WithInitialSpace { space, body } + } + + /// Translate a [`Tree`], except for the leading space. + /// This can return [`None`] if an empty block is encountered. + fn translate_expression_body(&mut self, tree: &Tree) -> Option { + let builder = self.start_ast(); + Some(match &*tree.variant { + tree::Variant::BodyBlock(block) => { + let block = self.translate_block(&block.statements)?.expect_unspaced(); + self.finish_ast(block, builder) + } + tree::Variant::Ident(tree::Ident { token }) => { + let name = self.visit_token(token).expect_unspaced(); + match token.is_type { + true => self.finish_ast(ast::Cons { name }, builder), + false => self.finish_ast(ast::Var { name }, builder), + } + } + tree::Variant::Number(tree::Number { base, integer, fractional_digits }) => { + let base = base.as_ref().map(|base| self.visit_token(base).expect_unspaced()); + let mut int = integer + .as_ref() + .map(|integer| self.visit_token(integer).expect_unspaced()) + .unwrap_or_default(); + if let Some(tree::FractionalDigits { dot, digits }) = fractional_digits { + let dot = self.visit_token(dot).expect_unspaced(); + let digits = self.visit_token(digits).expect_unspaced(); + int = format!("{int}{dot}{digits}"); + } + self.finish_ast(ast::Number { base, int }, builder) + } + tree::Variant::App(tree::App { func, arg }) => { + let func = self.translate(func); + let arg = self.translate(arg); + let app = maybe_prefix(func, arg).expect_unspaced()?; + self.finish_ast(app, builder) + } + tree::Variant::OprApp(tree::OprApp { lhs: Some(_), opr: Ok(opr), rhs: Some(_) }) + if opr.properties.is_arrow() => + { + let ast = ast::Tree::lambda(self.translate_items(tree)); + self.finish_ast(ast, builder) + } + tree::Variant::OprApp(tree::OprApp { lhs, opr, rhs }) => { + let larg = lhs.as_ref().map(|a| self.translate(a)).unwrap_or_default(); + let opr = self.translate_operators(opr); + let rarg = rhs.as_ref().map(|a| self.translate(a)).unwrap_or_default(); + let opr_app = infix(larg, opr, rarg).expect_unspaced(); + self.finish_ast(opr_app, builder) + } + tree::Variant::OprSectionBoundary(tree::OprSectionBoundary { ast, .. }) => + self.translate(ast).expect_unspaced()?, + tree::Variant::Function(func) => { + let func = self.translate_function(func); + self.finish_ast(func, builder) + } + tree::Variant::ForeignFunction(func) => { + let func = self.translate_foreign_function(func); + self.finish_ast(func, builder) + } + tree::Variant::UnaryOprApp(tree::UnaryOprApp { opr, rhs }) => { + let opr = self.translate_operator(opr); + if let Some(arg) = rhs { + let non_block_operand = "Unary operator cannot be applied to an (empty) block."; + let arg = self.translate(arg).expect(non_block_operand); + let section = section_right(opr, arg).expect_unspaced(); + self.finish_ast(section, builder) + } else { + let opr = opr.expect_unspaced(); + self.finish_ast(ast::SectionSides { opr }, builder) + } + } + tree::Variant::Assignment(tree::Assignment { pattern, equals, expr }) => + self.opr_app(pattern, equals, expr).expect_unspaced(), + tree::Variant::OperatorBlockApplication(app) => { + let tree::OperatorBlockApplication { lhs, expressions, excess } = app; + let func = lhs.as_ref().map(|lhs| self.translate(lhs)).unwrap_or_default(); + let block = self.translate_operator_block(expressions, excess); + let app = maybe_prefix(func, block).expect_unspaced()?; + self.finish_ast(app, builder) + } + tree::Variant::TemplateFunction(tree::TemplateFunction { ast, .. }) => + self.translate(ast).expect_unspaced()?, + tree::Variant::Wildcard(tree::Wildcard { token, .. }) => { + self.visit_token(token).expect_unspaced(); + self.finish_ast(ast::Blank {}, builder) + } + tree::Variant::ArgumentBlockApplication(app) => { + let tree::ArgumentBlockApplication { lhs, arguments } = app; + let func = lhs.as_ref().map(|lhs| self.translate(lhs)).unwrap_or_default(); + let arg = self + .translate_block(arguments) + .map(|arg| arg.map(|arg| Some(Ast::from(arg)))) + .unwrap_or_default(); + let app = maybe_prefix(func, arg).expect_unspaced()?; + self.finish_ast(app, builder) + } + tree::Variant::DefaultApp(tree::DefaultApp { func, default }) => { + let func = self.translate(func); + let arg_builder = self.start_ast(); + let default = self.visit_token(default); + let arg = default.map(|name| self.finish_ast(ast::Var { name }, arg_builder)); + let app = maybe_prefix(func, arg).expect_unspaced()?; + self.finish_ast(app, builder) + } + tree::Variant::NamedApp(tree::NamedApp { func, open, name, equals, arg, close }) => { + let func = self.translate(func); + let open = open.as_ref().map(|token| self.visit_token(token)); + let name = self.visit_token(name); + let larg = name.map(|name| Ast::from(ast::Var { name })); + let opr = self.translate_operator(equals); + let non_block_operand = "Named-application operand cannot be an (empty) block."; + let rarg = self.translate(arg).expect(non_block_operand); + let mut arg = infix(larg, opr, rarg).map(Ast::from); + let close = close.as_ref().map(|token| self.visit_token(token)); + if let Some(open) = open && let Some(close) = close { + arg = open.map(|open| group(open, arg, close)); + } + let app = maybe_prefix(func, arg).expect_unspaced()?; + self.finish_ast(app, builder) + } + tree::Variant::TypeSignature(tree::TypeSignature { variable, operator, type_ }) => + self.opr_app(variable, operator, type_).expect_unspaced(), + tree::Variant::TypeAnnotated(tree::TypeAnnotated { expression, operator, type_ }) => + self.opr_app(expression, operator, type_).expect_unspaced(), + tree::Variant::AnnotatedBuiltin(tree::AnnotatedBuiltin { + token, + annotation, + newlines, + expression, + }) => { + let at = self.visit_token(token).expect_unspaced(); + let func = self.visit_token(annotation); + let func = + func.map(|func| Ast::from(ast::Annotation { name: format!("{at}{func}") })); + let arg = expression.as_ref().map(|e| self.translate(e)).unwrap_or_default(); + if !newlines.is_empty() { + error!("Multiline expression must be handled in translate_lines."); + } + let app = maybe_prefix(func, arg).expect_unspaced()?; + self.finish_ast(app, builder) + } + tree::Variant::Documented(tree::Documented { documentation: _, expression }) => { + warn!("Multiline expression should have been handled in translate_lines."); + self.translate(expression.as_ref()?).without_space()? + } + tree::Variant::Import(import) => { + let span_info = self.translate_items(tree); + let type_info = analyze_import(import).unwrap_or_default(); + let ast = ast::Tree::expression(span_info).with_type_info(type_info); + self.finish_ast(ast, builder) + } + tree::Variant::TextLiteral(_) => { + self.translate_items(tree); + let ast = ast::Tree::text(tree.trimmed_code()); + self.finish_ast(ast, builder) + } + tree::Variant::Group(_) => { + let span_info = self.translate_items(tree); + let ast = ast::Tree::group(span_info); + self.finish_ast(ast, builder) + } + _ => { + let ast = ast::Tree::expression(self.translate_items(tree)); + self.finish_ast(ast, builder) + } + }) + } + + /// Translate [`Tree`]s to [`Ast`]s in a multi-line context (i.e. when building a block or + /// module). + fn translate_lines(&mut self, tree: &Tree, out: &mut Vec>>) { + match &*tree.variant { + tree::Variant::AnnotatedBuiltin(tree::AnnotatedBuiltin { + token, + annotation, + newlines, + expression, + }) if !newlines.is_empty() => { + let space = self.visit_space(&tree.span); + let at = self.visit_token(token).expect_unspaced(); + let annotation = self.visit_token(annotation).expect_unspaced(); + let annotation = Ast::from(ast::Annotation { name: format!("{at}{annotation}") }); + out.extend_one(WithInitialSpace { space, body: Some(annotation) }); + self.translate_linebreaks(out, newlines); + if let Some(expression) = expression { + self.translate_lines(expression, out); + } + } + tree::Variant::Annotated(tree::Annotated { + token, + annotation, + argument, + newlines, + expression, + }) => { + let space = self.visit_space(&tree.span); + let at = self.visit_token(token).expect_unspaced(); + let annotation = self.visit_token(annotation).expect_unspaced(); + let body = Ast::from(ast::Annotation { name: format!("{at}{annotation}") }); + let mut annotation = WithInitialSpace { space, body }; + let argument = + argument.as_ref().and_then(|arg| ignore_space_if_empty(self.translate(arg))); + if let Some(argument) = argument { + annotation = prefix(annotation, argument).map(Ast::from); + } + out.extend_one(annotation.map(Some)); + self.translate_linebreaks(out, newlines); + if let Some(expression) = expression { + self.translate_lines(expression, out); + } + } + tree::Variant::Documented(tree::Documented { documentation, expression }) => { + let space = self.visit_space(&tree.span); + self.translate_doc(space, documentation, out); + if let Some(expression) = expression { + self.translate_lines(expression, out); + } + } + _ => out.extend_one(self.translate(tree)), + } + } + + /// Translate a sequence of line breaks and trailing comments to [`Ast`] representation. + fn translate_linebreaks( + &mut self, + out: &mut Vec>>, + newlines: &[syntax::token::Newline], + ) { + // In the [`Ast`] representation, each block line has one implicit newline. + let out_newlines = newlines.len().saturating_sub(1); + out.reserve(out_newlines); + let mut prev = None; + for token in newlines { + let next = self.visit_token(token).split(); + if let Some((space, token)) = prev.replace(next) { + if let Some(text) = into_comment(&token) { + append_comment(out, space, text); + } else { + out.push(WithInitialSpace { space, body: None }); + } + } + } + } + + /// Translate a documentation comment to [`Ast`] representation. + fn translate_doc( + &mut self, + space: usize, + documentation: &tree::DocComment, + out: &mut Vec>>, + ) { + let open = self.visit_token(&documentation.open); + let mut span_info = SpanSeedBuilder::new(); + span_info.token(open); + for element in &documentation.elements { + span_info.token(match element { + tree::TextElement::Section { text } => self.visit_token(text), + tree::TextElement::Escape { token } => self.visit_token(token), + tree::TextElement::Newline { newline } => self.visit_token(newline), + tree::TextElement::Splice { .. } => { + let error = "Lexer must not emit splices in documentation comments."; + debug_assert!(false, "{error}"); + error!("{error}"); + continue; + } + }) + } + let rendered = documentation.content().into(); + let type_info = ast::TreeType::Documentation { rendered }; + let span_info = span_info.build().expect_unspaced(); + let body = Some(Ast::from(ast::Tree::expression(span_info).with_type_info(type_info))); + out.extend_one(WithInitialSpace { space, body }); + self.translate_linebreaks(out, &documentation.newlines); + } + + /// Lower a function definition to [`Ast`] representation. + fn translate_function(&mut self, function: &tree::Function) -> ast::Shape { + let tree::Function { name, args, equals, body } = function; + let non_empty_name = "A function name cannot be an (empty) block."; + let name = self.translate(name).expect(non_empty_name); + let mut lhs_terms = vec![name]; + lhs_terms.extend(args.iter().map(|a| self.translate_argument_definition(a))); + let larg = lhs_terms + .into_iter() + .reduce(|func, arg| prefix(func, arg).map(Ast::from)) + .expect("`lhs_terms` has at least one value, because it was initialized with a value."); + let opr = self.translate_operator(equals); + let body = body.as_ref().map(|body| self.translate(body)).unwrap_or_default(); + infix(larg, opr, body).expect_unspaced() + } + + /// Lower a foreign-function definition to [`Ast`] representation. + fn translate_foreign_function(&mut self, func: &tree::ForeignFunction) -> ast::Shape { + let tree::ForeignFunction { foreign, language, name, args, equals, body } = func; + let mut lhs_terms: Vec<_> = [foreign, language, name] + .into_iter() + .map(|ident| self.visit_token(ident).map(|name| Ast::from(ast::Var { name }))) + .collect(); + lhs_terms.extend(args.iter().map(|a| self.translate_argument_definition(a))); + let lhs = lhs_terms + .into_iter() + .reduce(|func, arg| prefix(func, arg).map(Ast::from)) + .expect("`lhs_terms` has at least one value, because it was initialized with values."); + let equals = self.translate_operator(equals); + let body = self.translate(body); + infix(lhs, equals, body).expect_unspaced() + } + + /// Construct an operator application from [`Tree`] operands and a specific operator. + fn opr_app( + &mut self, + lhs: &Tree, + opr: &syntax::token::Operator, + rhs: &Tree, + ) -> WithInitialSpace { + let builder = self.start_ast(); + let lhs = self.translate(lhs); + let opr = self.translate_operator(opr); + let rhs = self.translate(rhs); + infix(lhs, opr, rhs).map(|opr_app| self.finish_ast(opr_app, builder)) + } + + /// Translate an operator or multiple-operator erorr into the [`Ast`] representation. + fn translate_operators(&mut self, opr: &tree::OperatorOrError) -> WithInitialSpace { + match opr { + Ok(name) => match name.code.repr.strip_suffix('=') { + Some(mod_name) if mod_name.contains(|c| c != '=') => { + let opr_builder = self.start_ast(); + let token = self.visit_token(name); + token.map(|_| { + let name = mod_name.to_string(); + let opr = ast::Mod { name }; + self.finish_ast(opr, opr_builder) + }) + } + _ => self.translate_operator(name), + }, + Err(names) => { + let opr_builder = self.start_ast(); + let mut span_info = SpanSeedBuilder::new(); + for token in &names.operators { + span_info.token(self.visit_token(token)); + } + let opr = span_info + .build() + .map(|span_info| ast::Shape::from(ast::Tree::expression(span_info))); + opr.map(|opr| self.finish_ast(opr, opr_builder)) + } + } + } + + /// Translate an operator token into its [`Ast`] representation. + fn translate_operator(&mut self, token: &syntax::token::Operator) -> WithInitialSpace { + let opr_builder = self.start_ast(); + let right_assoc = token.properties.associativity() == syntax::token::Associativity::Right; + self.visit_token(token) + .map(|name| self.finish_ast(ast::Opr { name, right_assoc }, opr_builder)) + } + + /// Translate a [`tree::BodyBlock`] into an [`Ast`] module. + fn translate_module(&mut self, block: &tree::BodyBlock) -> Ast { + let (lines, _) = + self.translate_block_lines(&block.statements).unwrap_or_default().expect_unspaced(); + Ast::new_no_id(ast::Module { lines }) + } + + /// Translate the lines of [`Tree`] block into the [`Ast`] block representation. + fn translate_block<'a, 's: 'a>( + &mut self, + tree_lines: impl IntoIterator>, + ) -> Option>> { + let (space, (ast_lines, indent)) = self.translate_block_lines(tree_lines)?.split(); + let mut empty_lines = vec![]; + let mut first_line = None; + let mut lines = vec![]; + for line in ast_lines { + if first_line.is_none() { + if let Some(elem) = line.elem { + first_line = Some(ast::BlockLine { elem, off: line.off }); + } else { + empty_lines.push(line.off); + } + } else { + lines.push(line); + } + } + let first_line = first_line?; + let body = ast::Block { indent, empty_lines, first_line, lines }; + Some(WithInitialSpace { space, body }) + } + + /// Translate the lines of [`Tree`] block into [`Ast`] block lines. + fn translate_block_lines<'a, 's: 'a>( + &mut self, + tree_lines: impl IntoIterator>, + ) -> Option>>, usize)>> { + let tree_lines = tree_lines.into_iter(); + let mut ast_lines: Vec>> = + Vec::with_capacity(tree_lines.size_hint().0); + let mut statement_lines = vec![]; + let mut initial_indent = None; + let mut space = 0; + for tree::block::Line { newline, expression } in tree_lines { + // Mapping from [`Tree`]'s leading offsets to [`Ast`]'s trailing offsets: + // Initially, we create each line with no trailing offset. + let off = 0; + // We write each line's leading offset into the trailing offset of the previous line + // (or, for the first line, the initial offset). + let (trailing_space, newline) = self.visit_token(newline).split(); + if let Some(prev_line_comment) = into_comment(&newline) { + append_comment_ast(&mut ast_lines, trailing_space, prev_line_comment); + continue; + } + *ast_lines.last_mut().map(|line| &mut line.off).unwrap_or(&mut space) = trailing_space; + match &expression { + Some(statement) => { + self.translate_lines(statement, &mut statement_lines); + if initial_indent.is_none() && let Some(first) = statement_lines.first() { + initial_indent = Some(first.space); + } + let new_lines = statement_lines + .drain(..) + .map(|elem| ast::BlockLine { elem: elem.without_space(), off }); + ast_lines.extend(new_lines); + } + None => ast_lines.push(ast::BlockLine { elem: None, off }), + } + } + let body = (ast_lines, initial_indent?); + Some(WithInitialSpace { space, body }) + } + + /// Lower an operator block into the [`Ast`] block representation. + fn translate_operator_block<'a, 's: 'a>( + &mut self, + operator_lines: impl IntoIterator>, + excess: impl IntoIterator>, + ) -> WithInitialSpace { + let mut ast_lines: Vec> = vec![]; + let mut indent = None; + let mut space = 0; + let off = 0; + for tree::block::OperatorLine { newline, expression } in operator_lines { + let (trailing_space, newline) = self.visit_token(newline).split(); + if let Some(prev_line_comment) = into_comment(&newline) { + append_comment_ast(&mut ast_lines, trailing_space, prev_line_comment); + continue; + } + *ast_lines.last_mut().map(|line| &mut line.off).unwrap_or(&mut space) = trailing_space; + let elem = expression.as_ref().map(|expression| { + let opr = self.translate_operators(&expression.operator); + let non_block_operand = "Expression in operand-line cannot be an (empty) block."; + let arg = self.translate(&expression.expression).expect(non_block_operand); + let (space, elem) = section_right(opr, arg).split(); + if indent.is_none() { + indent = Some(space); + } + Ast::from(elem) + }); + ast_lines.push(ast::BlockLine { elem, off }); + } + let non_empty_block = "An operator block must have at least one operator line."; + let indent = indent.expect(non_empty_block); + let mut statement_lines = vec![]; + for tree::block::Line { newline, expression } in excess { + let (trailing_space, newline) = self.visit_token(newline).split(); + if let Some(prev_line_comment) = into_comment(&newline) { + append_comment_ast(&mut ast_lines, trailing_space, prev_line_comment); + continue; + } + *ast_lines.last_mut().map(|line| &mut line.off).unwrap_or(&mut space) = trailing_space; + match &expression { + Some(statement) => { + self.translate_lines(statement, &mut statement_lines); + let new_lines = statement_lines + .drain(..) + .map(|elem| ast::BlockLine { elem: elem.without_space(), off }); + ast_lines.extend(new_lines); + } + None => ast_lines.push(ast::BlockLine { elem: None, off }), + } + } + let mut first_line = None; + let mut empty_lines = vec![]; + let mut lines = vec![]; + for ast::BlockLine { elem, off } in ast_lines { + match (elem, &mut first_line) { + (None, None) => empty_lines.push(off), + (Some(elem), None) => first_line = Some(ast::BlockLine { elem, off }), + (elem, Some(_)) => lines.push(ast::BlockLine { elem, off }), + } + } + let first_line = first_line.expect(non_empty_block); + let body = Ast::from(ast::Block { indent, empty_lines, first_line, lines }); + WithInitialSpace { space, body } + } + + /// Lower an argument definition into the [`Ast`] representation. + fn translate_argument_definition( + &mut self, + arg: &tree::ArgumentDefinition, + ) -> WithInitialSpace { + let tree::ArgumentDefinition { + open, + open2, + suspension, + pattern, + type_, + close2, + default, + close, + } = arg; + let open = open.as_ref().map(|token| self.visit_token(token)); + let open2 = open2.as_ref().map(|token| self.visit_token(token)); + let suspension = suspension.as_ref().map(|token| self.translate_operator(token)); + let non_block_operand = "Pattern in argument definition cannot be an (empty) block."; + let mut term = self.translate(pattern).expect(non_block_operand); + if let Some(opr) = suspension { + term = section_right(opr, term).map(Ast::from); + } + if let Some(tree::ArgumentType { operator, type_ }) = type_ { + let opr = self.translate_operator(operator); + let rarg = self.translate(type_); + term = infix(term, opr, rarg).map(Ast::from); + } + let close2 = close2.as_ref().map(|token| self.visit_token(token)); + if let Some(open) = open2 && let Some(close) = close2 { + term = open.map(|open| group(open, term, close)); + } + if let Some(tree::ArgumentDefault { equals, expression }) = default { + let opr = self.translate_operator(equals); + let rarg = self.translate(expression); + term = infix(term, opr, rarg).map(Ast::from); + } + let close = close.as_ref().map(|token| self.visit_token(token)); + if let Some(open) = open && let Some(close) = close { + term = open.map(|open| group(open, term, close)); + } + term + } + + /// Analyze a [`Tree`] and produce a representation used by the graph editor. + fn translate_items(&mut self, tree: &syntax::tree::Tree<'_>) -> Vec> { + let mut span_info = SpanSeedBuilder::new(); + tree.visit_items(|item| match item { + syntax::item::Ref::Token(token) => span_info.token(self.visit_token_ref(token)), + syntax::item::Ref::Tree(tree) => { + if let Some(ast) = ignore_space_if_empty(self.translate(tree)) { + span_info.child(ast); + } + } + }); + span_info.build().expect_unspaced() + } +} + + +// === Span-tracking === + +/// Tracks what input bytes are visited during the construction of a particular [`Ast`], and uses +/// that information to assign an ID from the ID map. +struct AstBuilder { + start: usize, +} + +impl Translate { + /// Marks the beginning of the input byte range that will be included in a particular [`Ast`] + /// node. + fn start_ast(&mut self) -> AstBuilder { + AstBuilder { start: self.offset } + } + + /// Marks the end of the input byte range that will be included in a particular [`Ast`] node, + /// and constructs the node from an [`ast::Shape`], using the calculated byte range to assign + /// an ID. + fn finish_ast>>(&mut self, shape: S, builder: AstBuilder) -> Ast { + let AstBuilder { start } = builder; + let start = start + self.space_after.get(&start).copied().unwrap_or_default(); + let end = self.offset; + let id = self.ids.remove(&(start, end)); + if DEBUG { + match id { + Some(id) => self.ids_assigned.push(((start, end), id)), + None => self.ids_missed.push((start, end)), + } + } + Ast::new(shape, id) + } +} + + +// === Semantic Analysis === + +/// Analyze an import statement to identify the module referenced and the names imported. +// TODO: In place of this analysis (and a similar analysis in Java [`TreeToIr`]), +// refactor [`tree::Import`] to a higher-level representation resembling +// [`ast::ImportedNames`] (but with concrete tokens). +fn analyze_import(import: &tree::Import) -> Option { + let tree::Import { polyglot, from, import, all, as_, hiding } = import; + fn parse_module(tree: &Tree) -> Option> { + let mut segments = vec![]; + for tree in tree.left_assoc_rev(".") { + match &*tree.variant { + tree::Variant::Ident(tree::Ident { token }) => + segments.push(token.code.to_string().into()), + _ => return None, + } + } + segments.reverse(); + Some(segments) + } + fn parse_ident(tree: &Tree) -> Option { + match &*tree.variant { + tree::Variant::Ident(tree::Ident { token }) => Some(token.code.to_string()), + _ => None, + } + } + fn parse_idents(tree: &Tree) -> Option> { + let mut names = std::collections::BTreeSet::new(); + for tree in tree.left_assoc_rev(",") { + match &*tree.variant { + tree::Variant::Ident(tree::Ident { token }) => { + names.insert(token.code.to_string()); + } + _ => return None, + } + } + Some(names) + } + let module; + let imported; + match (polyglot, from, all, as_, hiding) { + (None, None, None, None, None) => { + module = import.body.as_ref().and_then(parse_module)?; + imported = ast::ImportedNames::Module { alias: None }; + } + (None, None, None, Some(as_), None) => { + module = import.body.as_ref().and_then(parse_module)?; + let alias = as_.body.as_ref().and_then(parse_ident); + imported = ast::ImportedNames::Module { alias }; + } + (None, Some(from), None, None, None) => { + module = from.body.as_ref().and_then(parse_module)?; + let names = import.body.as_ref().and_then(parse_idents)?; + imported = ast::ImportedNames::List { names }; + } + (None, Some(from), Some(_), None, None) => { + module = from.body.as_ref().and_then(parse_module)?; + imported = ast::ImportedNames::All { except: Default::default() }; + } + (None, Some(from), Some(_), None, Some(hiding)) => { + module = from.body.as_ref().and_then(parse_module)?; + let except = hiding.body.as_ref().and_then(parse_idents)?; + imported = ast::ImportedNames::All { except }; + } + _ => return None, + } + Some(ast::TreeType::Import { module, imported }) +} + +/// Distinguish a plain newline from a trailing comment. +fn into_comment(mut newline: &str) -> Option { + if let Some(text) = newline.strip_suffix('\n') { + newline = text; + } + if let Some(text) = newline.strip_suffix('\r') { + newline = text; + } + (!newline.is_empty()).then(|| newline.to_string()) +} + + +// === WithInitialSpace === + +/// Helper for propagating spacing from children (Tree-style left offsets) to parents (Ast-style +/// top-down spacing). +#[derive(Debug, Default)] +struct WithInitialSpace { + space: usize, + body: T, +} + +impl WithInitialSpace { + /// If any initial space is present, emit a warning; forget the space and return the value. + fn expect_unspaced(self) -> T { + if DEBUG { + debug_assert_eq!(self.space, 0, "Expected no space before term: {:?}", &self.body); + } else if self.space != 0 { + warn!("Expected no space before term: {:?}", &self.body); + } + self.body + } +} + +impl WithInitialSpace { + /// Return the value, ignoring any initial space. + fn without_space(self) -> T { + self.body + } + + /// Return the initial space and the value. + fn split(self) -> (usize, T) { + (self.space, self.body) + } + + fn map(self, f: impl FnOnce(T) -> U) -> WithInitialSpace { + let WithInitialSpace { space, body } = self; + let body = f(body); + WithInitialSpace { space, body } + } +} + +impl WithInitialSpace> { + /// Convenience function that applies [`Option::expect`] to the inner value. + fn expect(self, message: &str) -> WithInitialSpace { + let WithInitialSpace { space, body } = self; + WithInitialSpace { space, body: body.expect(message) } + } +} + +/// Convenience function that transposes an optional value that always has initial space count, to +/// an optional value that, if present, has an initial space count. Note that this is a lossy +/// operation. +fn ignore_space_if_empty(spaced: WithInitialSpace>) -> Option> { + spaced.body.map(|body| WithInitialSpace { space: spaced.space, body }) +} + + +// === Shape-building helpers === + +/// Construct an [`ast::Prefix`], representing the initial spaces of the inputs appropriately. +fn prefix( + func: WithInitialSpace, + arg: WithInitialSpace, +) -> WithInitialSpace> { + func.map(|func| { + let (off, arg) = arg.split(); + (ast::Prefix { func, off, arg }).into() + }) +} + +/// Construct an [`ast::Prefix`] if both operands are present; otherwise, returns a single operand +/// if present. +fn maybe_prefix>, U: Into>>( + func: WithInitialSpace, + arg: WithInitialSpace, +) -> WithInitialSpace>> { + func.map(|func| { + let arg = ignore_space_if_empty(arg.map(|arg| arg.into())); + match (func.into(), arg) { + (Some(func), Some(arg)) => { + let (off, arg) = arg.split(); + Some((ast::Prefix { func, off, arg }).into()) + } + (Some(func), None) => Some(func.shape().clone()), + (None, Some(arg)) => Some(arg.expect_unspaced().shape().clone()), + (None, None) => None, + } + }) +} + +/// Constructs an operator section for an operator with only a right operand. +fn section_right( + opr: WithInitialSpace, + arg: WithInitialSpace, +) -> WithInitialSpace> { + opr.map(|opr| { + let (off, arg) = arg.split(); + (ast::SectionRight { opr, off, arg }).into() + }) +} + +/// Constructs an infix-application node. If any of the inputs are [`Option`] types, then an +/// operator section will be produced if appropriate. +fn infix>, U: Into>>( + larg: WithInitialSpace, + opr: WithInitialSpace, + rarg: WithInitialSpace, +) -> WithInitialSpace> { + larg.map(|larg| { + let (opr_off, opr) = opr.split(); + let rarg = ignore_space_if_empty(rarg.map(|arg| arg.into())); + match (larg.into(), rarg) { + (Some(larg), Some(rarg)) => { + let (roff, rarg) = rarg.split(); + (ast::Infix { larg, loff: opr_off, opr, roff, rarg }).into() + } + (Some(arg), None) => (ast::SectionLeft { arg, off: opr_off, opr }).into(), + (None, Some(arg)) => { + let (off, arg) = arg.split(); + (ast::SectionRight { opr, off, arg }).into() + } + (None, None) => (ast::SectionSides { opr }).into(), + } + }) +} + +/// Wrap an input in a parenthesized-group node. +fn group(open: String, body: WithInitialSpace, close: WithInitialSpace) -> Ast { + let (body_space, body) = body.split(); + let (close_space, close) = close.split(); + let min_elements = 3; // There are always at least 3 elements: open, close, and body + let mut span_info = Vec::with_capacity(min_elements); + span_info.push(ast::SpanSeed::Token(ast::SpanSeedToken { token: open })); + span_info.extend(ast::SpanSeed::space(body_space)); + span_info.push(ast::SpanSeed::Child(ast::SpanSeedChild { node: body })); + span_info.extend(ast::SpanSeed::space(close_space)); + span_info.push(ast::SpanSeed::Token(ast::SpanSeedToken { token: close })); + Ast::from(ast::Tree::expression(span_info)) +} + +/// Append a trailing-comment to the last line of the given block, creating a line to hold it if the +/// block is empty. +fn append_comment(lines: &mut Vec>>, space: usize, comment: String) { + let prev = lines.pop(); + let space_after_expression = match prev.as_ref().and_then(|spaced| spaced.body.as_ref()) { + Some(_) => space, + None => 0, + }; + let prev = prev.unwrap_or(WithInitialSpace { space, body: None }); + let line = prev.map(|prev| { + Some(Ast::from(ast::Tree::expression_with_comment(prev, space_after_expression, comment))) + }); + lines.push(line); +} + +/// Append a trailing-comment to the last line of the given block, creating a line to hold it if the +/// block is empty. +fn append_comment_ast(lines: &mut Vec>>, space: usize, comment: String) { + let prev = lines.pop(); + let off = prev.as_ref().map(|line| line.off).unwrap_or_default(); + let prev = prev.and_then(|line| line.elem); + // If there's no expression before the comment, the space to the left of the comment + // is already represented as the indent. + let trailing_space = match prev { + Some(_) => space, + None => 0, + }; + let elem = ast::Tree::expression_with_comment(prev, trailing_space, comment); + lines.push(ast::BlockLine { elem: Some(Ast::from(elem)), off }); +} + + +// === SpanSeedBuilder === + +/// Constructs a sequence of [`ast::SpanSeed`] values. +#[derive(Debug, Default)] +pub struct SpanSeedBuilder { + space: Option, + spans: Vec>, +} + +impl SpanSeedBuilder { + fn new() -> Self { + Self::default() + } + + /// Append a token. + fn token(&mut self, value: WithInitialSpace) { + let (space, value) = value.split(); + if self.space.is_none() { + self.space = Some(space); + } else { + self.spans.extend(ast::SpanSeed::space(space)); + } + self.spans.push(ast::SpanSeed::Token(ast::SpanSeedToken { token: value })); + } + + /// Append a node. + fn child(&mut self, node: WithInitialSpace) { + let (space, node) = node.split(); + if self.space.is_none() { + self.space = Some(space); + } else { + self.spans.extend(ast::SpanSeed::space(space)); + } + self.spans.push(ast::SpanSeed::Child(ast::SpanSeedChild { node })); + } + + /// Construct the sequence. + fn build(self) -> WithInitialSpace>> { + let space = self.space.unwrap_or_default(); + let body = self.spans; + WithInitialSpace { space, body } + } +} diff --git a/app/gui/language/parser/tests/ast.rs b/app/gui/language/parser/tests/ast.rs deleted file mode 100644 index e839cac4e43f..000000000000 --- a/app/gui/language/parser/tests/ast.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! Tests specific to Ast rather than parser itself but placed here because they depend on parser -//! to easily generate test input. - -// TODO: [mwu] -// That means that likely either `parser` should be merged with `ast` or that we should have a -// separate `ast_ops` crate that depends on both. Now it is better to tests here than none but -// a decision should be made as to which way we want to go. - -// === Non-Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] - -use parser_scala::prelude::*; - -use ast::opr; -use ast::opr::GeneralizedInfix; -use ast::prefix; -use ast::test_utils::expect_single_line; -use ast::HasRepr; -use wasm_bindgen_test::wasm_bindgen_test; - - - -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - -#[wasm_bindgen_test] -pub fn to_assignment_test() { - let parser = parser_scala::Parser::new_or_panic(); - let is_assignment = |code: &str| { - let ast = parser.parse(code.to_string(), default()).unwrap(); - let line = expect_single_line(&ast); - ast::opr::to_assignment(line).is_some() - }; - - let expected_assignments = vec!["a = 5", "a=5", "foo bar = a b c", "(x,y) = pos"]; - let expected_not_assignments = vec!["= 5", "a=", "=", "foo", "a->b", "a+b"]; - - for code in expected_assignments { - assert!(is_assignment(code), "{code} expected to be recognized as assignment"); - } - for code in expected_not_assignments { - assert!(!is_assignment(code), "{code} expected to not be recognized as assignment"); - } -} - -#[wasm_bindgen_test] -pub fn generalized_infix_test() { - let parser = parser_scala::Parser::new_or_panic(); - let make_gen_infix = |code: &str| { - let ast = parser.parse(code.to_string(), default()).unwrap(); - let line = expect_single_line(&ast); - GeneralizedInfix::try_new(line) - }; - - let infix = make_gen_infix("a+b").unwrap(); - assert_eq!(infix.name(), "+"); - assert_eq!(infix.left.map(|op| op.arg).repr(), "a"); - assert_eq!(infix.right.map(|op| op.arg).repr(), "b"); - - let right = make_gen_infix("+b").unwrap(); - assert_eq!(right.name(), "+"); - assert_eq!(right.right.map(|op| op.arg).repr(), "b"); - - let left = make_gen_infix("a+").unwrap(); - assert_eq!(left.name(), "+"); - assert_eq!(left.left.map(|op| op.arg).repr(), "a"); - - let sides = make_gen_infix("+").unwrap(); - assert_eq!(sides.name(), "+"); - - let var_as_infix = make_gen_infix("a"); - assert!(var_as_infix.is_none()); -} - -#[wasm_bindgen_test] -pub fn flatten_prefix_test() { - fn expect_pieces(flattened: &prefix::Chain, pieces: Vec<&str>) { - let mut piece_itr = pieces.iter(); - assert_eq!(flattened.args.len() + 1, pieces.len()); // +1 because `func` piece is separate field - assert_eq!(&flattened.func.repr(), piece_itr.next().unwrap()); - flattened.args.iter().zip(piece_itr).for_each(|(lhs, rhs)| { - assert_eq!(&lhs.repr(), rhs); - }) - } - - let parser = parser_scala::Parser::new_or_panic(); - let case = |code: &str, expected_pieces: Vec<&str>| { - let ast = parser.parse(code.into(), default()).unwrap(); - let ast = ast::test_utils::expect_single_line(&ast); - let flattened = prefix::Chain::from_ast_non_strict(ast); - expect_pieces(&flattened, expected_pieces); - assert_eq!(flattened.repr(), code); - }; - - case("a", vec!["a"]); - case("a b c d", vec!["a", " b", " c", " d"]); - case("+ a b c", vec!["+", " a", " b", " c"]); - case("a b + c d", vec!["a b + c d"]); // nothing to flatten, this is infix, not prefix -} - -#[wasm_bindgen_test] -pub fn flatten_infix_test() { - fn expect_pieces(flattened: &opr::Chain, target: &str, pieces: Vec<&str>) { - assert_eq!(flattened.target.as_ref().map(|a| &a.arg).repr(), target); - - let piece_itr = pieces.iter(); - assert_eq!(flattened.args.len(), pieces.len()); - flattened.args.iter().zip(piece_itr).for_each(|(lhs, rhs)| { - assert_eq!(lhs.operand.as_ref().map(|a| &a.arg).repr(), *rhs); - }) - } - - let parser = parser_scala::Parser::new_or_panic(); - let case = |code: &str, target: &str, expected_pieces: Vec<&str>| { - let ast = parser.parse(code.into(), default()).unwrap(); - let ast = ast::test_utils::expect_single_line(&ast); - let flattened = opr::Chain::try_new(ast).unwrap(); - expect_pieces(&flattened, target, expected_pieces); - }; - - case("a+b+c", "a", vec!["b", "c"]); - case("a,b,c", "c", vec!["b", "a"]); - case("a+b*c+d", "a", vec!["b*c", "d"]); -} diff --git a/app/gui/language/parser/tests/bugs.rs b/app/gui/language/parser/tests/bugs.rs deleted file mode 100644 index 6ce28fd6b9bd..000000000000 --- a/app/gui/language/parser/tests/bugs.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Tests for cases where parser currently fails. They are ignored, should be removed and placed -//! elsewhere, as the parser gets fixed. - -// === Non-Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] - -use wasm_bindgen_test::wasm_bindgen_test; -use wasm_bindgen_test::wasm_bindgen_test_configure; - - - -wasm_bindgen_test_configure!(run_in_browser); - -#[wasm_bindgen_test] -fn no_doc_found() { - let input = String::from("type Foo\n type Bar"); - let program = std::env::args().nth(1).unwrap_or(input); - let parser = parser_scala::DocParser::new_or_panic(); - let gen_code = parser.generate_html_docs(program).unwrap(); - // gen_code should be empty. - assert_eq!(gen_code.len(), 22, "Generated length differs from the expected\"{gen_code}\""); -} - -#[wasm_bindgen_test] -fn extension_operator_methods() { - let ast = parser_scala::Parser::new_or_panic().parse_line_ast("Int.+").unwrap(); - - use ast::*; - if let Shape::Infix(Infix { larg: _larg, loff: _loff, opr, roff: _roff, rarg }, ..) = - ast.shape() - { - if let Shape::Opr(Opr { .. }) = opr.shape() { - // TODO: should be Opr(+). https://github.com/enso-org/enso/issues/565 - if let Shape::Var(Var { .. }) = rarg.shape() { - return; - } - } - } - panic!("Should have matched into return."); -} diff --git a/app/gui/language/parser/tests/crumbs.rs b/app/gui/language/parser/tests/crumbs.rs deleted file mode 100644 index 7ad905912b54..000000000000 --- a/app/gui/language/parser/tests/crumbs.rs +++ /dev/null @@ -1,33 +0,0 @@ -// === Non-Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] - -use enso_prelude::*; - -use ast::crumbs::Crumbable; -use ast::HasRepr; -use parser_scala::Parser; -use wasm_bindgen_test::wasm_bindgen_test; -use wasm_bindgen_test::wasm_bindgen_test_configure; - - - -wasm_bindgen_test_configure!(run_in_browser); - - -#[wasm_bindgen_test] -fn macro_crumb_test() { - let ast = Parser::new_or_panic().parse_line_ast("foo -> bar").unwrap(); - let crumbs = ast.iter_subcrumbs().collect_vec(); - - assert_eq!(ast.get(&crumbs[0]).unwrap().repr(), "foo"); - assert_eq!(ast.get(&crumbs[1]).unwrap().repr(), "->"); - assert_eq!(ast.get(&crumbs[2]).unwrap().repr(), "bar"); - - let ast = Parser::new_or_panic().parse_line_ast("( foo bar )").unwrap(); - let crumbs = ast.iter_subcrumbs().collect_vec(); - - assert_eq!(ast.get(&crumbs[0]).unwrap().repr(), "("); - assert_eq!(ast.get(&crumbs[1]).unwrap().repr(), "foo bar"); - assert_eq!(ast.get(&crumbs[2]).unwrap().repr(), ")"); -} diff --git a/app/gui/language/parser/tests/doc-gen.rs b/app/gui/language/parser/tests/doc-gen.rs deleted file mode 100644 index 0ed454bc1992..000000000000 --- a/app/gui/language/parser/tests/doc-gen.rs +++ /dev/null @@ -1,33 +0,0 @@ -// === Non-Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] - -use parser_scala::DocParser; -use wasm_bindgen_test::wasm_bindgen_test; -use wasm_bindgen_test::wasm_bindgen_test_configure; - - - -wasm_bindgen_test_configure!(run_in_browser); - -#[wasm_bindgen_test] -fn doc_gen_test() { - // Case of pure documentation code. - let input = String::from("Foo *Bar* Baz"); - let program = std::env::args().nth(1).unwrap_or(input); - let parser = DocParser::new_or_panic(); - let gen_code = parser.generate_html_doc_pure(program).unwrap(); - assert_ne!(gen_code.len(), 0); - - let input = String::from("##\n foo\ntype Foo\n"); - let program = std::env::args().nth(1).unwrap_or(input); - let parser = DocParser::new_or_panic(); - let gen_code = parser.generate_html_docs(program).unwrap(); - assert_ne!(gen_code.len(), 0); - - let input = String::from("##\n DEPRECATED\n Foo bar baz\ntype Foo\n type Bar"); - let program = std::env::args().nth(1).unwrap_or(input); - let parser = DocParser::new_or_panic(); - let gen_code = parser.generate_html_docs(program).unwrap(); - assert_ne!(gen_code.len(), 0); -} diff --git a/app/gui/language/parser/tests/id_map.rs b/app/gui/language/parser/tests/id_map.rs deleted file mode 100644 index 42e9de66d543..000000000000 --- a/app/gui/language/parser/tests/id_map.rs +++ /dev/null @@ -1,35 +0,0 @@ -// === Non-Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] - -use parser_scala::prelude::*; - -use ast::HasIdMap; -use parser_scala::Parser; -use wasm_bindgen_test::wasm_bindgen_test; -use wasm_bindgen_test::wasm_bindgen_test_configure; - - - -wasm_bindgen_test_configure!(run_in_browser); - -#[wasm_bindgen_test] -fn id_map_round_tripping() { - let cases = [ - "main =\n 2 + 2", - "main = \n \n 2 + 2\n foo = bar \n baz", - "main = \n foo\n\n bar", - "main = \n foo\n \n bar", - "main = \n foo\n \n bar", - "main = \n foo\n baz \n bar", - ]; - - let parser = Parser::new().unwrap(); - for case in cases.iter().copied() { - let id_map = default(); - let ast1 = parser.parse_module(case, id_map).unwrap(); - let id_map = ast1.id_map(); - let ast2 = parser.parse_module(case, id_map).unwrap(); - assert_eq!(ast1, ast2) - } -} diff --git a/app/gui/language/parser/tests/macros.rs b/app/gui/language/parser/tests/macros.rs deleted file mode 100644 index afc9ae71294c..000000000000 --- a/app/gui/language/parser/tests/macros.rs +++ /dev/null @@ -1,82 +0,0 @@ -// === Non-Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] - -use parser_scala::prelude::*; - -use parser_scala::Parser; -use wasm_bindgen_test::wasm_bindgen_test; -use wasm_bindgen_test::wasm_bindgen_test_configure; - - - -wasm_bindgen_test_configure!(run_in_browser); - -#[wasm_bindgen_test] -fn import_utilities() { - use ast::macros::ast_as_import_match; - use ast::macros::is_ast_import; - use ast::macros::is_match_import; - - let parser = Parser::new_or_panic(); - let expect_import = |code: &str| { - let ast = parser.parse_line_ast(code).unwrap(); - assert!(is_ast_import(&ast), "Not Ast import: {ast:?}"); - let ast_match = ast_as_import_match(&ast).unwrap(); - assert_eq!(&ast, ast_match.ast()); - assert!(is_match_import(&ast_match)); - }; - - let expect_not_import = |code: &str| { - let ast = parser.parse_line_ast(code).unwrap(); - assert!(!is_ast_import(&ast)); - assert!(ast_as_import_match(&ast).is_none()); - }; - - expect_import("import"); - expect_import("import Foo"); - expect_import("import foo.Foo.Bar"); - expect_import("import foo.Foo.Bar"); - expect_import("import Foo.Bar"); - expect_import("import Foo.Bar.Baz"); - expect_import("from Foo import Bar"); - expect_import("from foo.Foo import all hiding Bar"); - expect_import("from Base.Data.List import all hiding Cons, Nil"); - - expect_not_import("type Foo"); - expect_not_import("type Foo as Bar"); - expect_not_import("if Foo then Bar else Baz"); - expect_not_import("Foo.Bar.Baz"); - expect_not_import("->"); - expect_not_import("export"); - expect_not_import("export Foo"); - expect_not_import("from Foo export all hiding Bar"); -} - -#[wasm_bindgen_test] -fn recognizing_lambdas() { - let parser = Parser::new_or_panic(); - - let expect_lambda = |code: &str, arg: &str, body: &str| { - let ast = parser.parse_line_ast(code).unwrap(); - let lambda = ast::macros::as_lambda(&ast).expect("failed to recognize lambda"); - assert_eq!(lambda.arg.repr(), arg); - assert_eq!(lambda.body.repr(), body); - assert_eq!(*lambda.arg, ast.get_traversing(&lambda.arg.crumbs).unwrap()); - assert_eq!(*lambda.body, ast.get_traversing(&lambda.body.crumbs).unwrap()); - }; - let expect_not_lambda = |code: &str| { - let ast = parser.parse_line_ast(code).unwrap(); - assert!(ast::macros::as_lambda_match(&ast).is_none(), "wrongly recognized a lambda"); - }; - - expect_lambda("a->b", "a", "b"); - expect_lambda("foo->4+(4)", "foo", "4+(4)"); - expect_lambda("a->b->c", "a", "b->c"); - expect_lambda("(a->b)->c", "(a->b)", "c"); - - expect_not_lambda("(a->b)"); - expect_not_lambda("a+b"); - expect_not_lambda("'a+b'"); - expect_not_lambda("497"); -} diff --git a/app/gui/language/parser/tests/parsing.rs b/app/gui/language/parser/tests/parsing.rs deleted file mode 100644 index 3c2e79169db9..000000000000 --- a/app/gui/language/parser/tests/parsing.rs +++ /dev/null @@ -1,550 +0,0 @@ -// === Features === -#![feature(generators, generator_trait)] -// === Non-Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] - -use ast::*; -use parser_scala::prelude::*; - -use ast::test_utils::expect_shape; -use parser_scala::api::Metadata; -use parser_scala::api::ParsedSourceFile; -use parser_scala::api::PruneUnusedIds; -use serde::de::DeserializeOwned; -use serde::Deserialize; -use serde::Serialize; -use wasm_bindgen_test::wasm_bindgen_test; -use wasm_bindgen_test::wasm_bindgen_test_configure; - - - -wasm_bindgen_test_configure!(run_in_browser); - - - -// =============== -// === Helpers === -// =============== - -/// Asserts that given AST is a Var with given name. -fn assert_var>(ast: &Ast, name: StringLike) { - let actual: &Var = expect_shape(ast); - let expected = Var { name: name.into() }; - assert_eq!(*actual, expected); -} - -/// Asserts that given AST is an Opr with given name. -fn assert_opr>(ast: &Ast, name: StringLike) { - let actual: &Opr = expect_shape(ast); - let expected = Opr { name: name.into() }; - assert_eq!(*actual, expected); -} - -fn roundtrip_program_with(parser: &parser_scala::Parser, program: &str) { - let ast = parser.parse(program.to_string(), Default::default()).unwrap(); - assert_eq!(ast.repr(), program, "{ast:#?}"); -} - -fn roundtrip_program(program: &str) { - let parser = parser_scala::Parser::new_or_panic(); - roundtrip_program_with(&parser, program); -} - - - -// ================ -// === Metadata === -// ================ - -/// Wrapper for using any serializable type as metadata. -#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] -struct FauxMetadata(T); - -impl PruneUnusedIds for FauxMetadata {} -impl Metadata for FauxMetadata {} - - - -// =============== -// === Fixture === -// =============== - -/// Persists parser (which is expensive to construct, so we want to reuse it -/// between tests. Additionally, hosts a number of helper methods. -struct Fixture { - parser: parser_scala::Parser, -} - -impl Fixture { - // === Helper methods === - - /// Create a new fixture, obtaining a default parser. - fn new() -> Fixture { - Fixture { parser: parser_scala::Parser::new_or_panic() } - } - - /// Program is expected to be single line module. The line's Shape subtype - /// is obtained and passed to `tester`. - fn test_shape(&mut self, program: &str, tester: F) - where - for<'t> &'t Shape: TryInto<&'t T>, - F: FnOnce(&T), { - let ast = self.parser.parse_line_ast(program).unwrap(); - let shape = expect_shape(&ast); - tester(shape); - } - - - // === Test Methods === - - fn blank_line_round_trip(&mut self) { - let program = "main = \n foo\n \n bar"; - let ast = self.parser.parse_module(program, default()).unwrap(); - assert_eq!(ast.repr(), program); - } - - fn deserialize_metadata(&mut self) { - let term = ast::Module { lines: vec![ast::BlockLine { elem: None, off: 0 }] }; - let ast = known::KnownAst::new_no_id(term); - let file = ParsedSourceFile { ast, metadata: serde_json::json!({}) }; - let code = String::try_from(&file).unwrap(); - assert_eq!(self.parser.parse_with_metadata(code).unwrap(), file); - } - - fn deserialize_unrecognized(&mut self) { - let unfinished = "`"; - self.test_shape(unfinished, |shape: &Unrecognized| { - assert_eq!(shape.str, "`"); - }); - } - - #[allow(dead_code)] // TODO [mwu] https://github.com/enso-org/enso/issues/1016 - fn deserialize_unexpected(&mut self) { - let unexpected = "import"; - let ast = self.parser.parse_line_ast(unexpected).unwrap(); - // This does not deserialize to "Unexpected" but to a very complex macro match tree that has - // Unexpected somewhere within. We just make sure that it is somewhere, and that confirms - // that we are able to deserialize such node. - let has_unexpected = - ast.iter_recursive().find(|ast| matches!(ast.shape(), Shape::Unexpected(_))); - assert!(has_unexpected.is_some()); - } - - fn deserialize_invalid_quote(&mut self) { - let unfinished = "'a''"; - self.test_shape(unfinished, |shape: &Prefix| { - // ignore shape.func, being TextUnclosed tested elsewhere - let arg: &InvalidQuote = expect_shape(&shape.arg); - let expected_quote = Text { str: "''".into() }; - assert_eq!(arg.quote, expected_quote.into()); - }); - } - - fn deserialize_inline_block(&mut self) { - let unfinished = "'''a"; - self.test_shape(unfinished, |shape: &Prefix| { - let func: &InlineBlock = expect_shape(&shape.func); - let expected_quote = Text { str: "'''".into() }; - assert_eq!(func.quote, expected_quote.into()); - assert_var(&shape.arg, "a"); - }); - } - - fn deserialize_blank(&mut self) { - let expect_blank = |_: &Blank| {}; - self.test_shape("_", expect_blank); - } - - fn deserialize_var(&mut self) { - self.test_shape("foo", |var: &Var| { - let expected_var = Var { name: "foo".into() }; - assert_eq!(var, &expected_var); - }); - } - - fn deserialize_cons(&mut self) { - let name = "FooBar"; - self.test_shape(name, |shape: &Cons| { - assert_eq!(shape.name, name); - }); - } - - fn deserialize_mod(&mut self) { - self.test_shape("+=", |shape: &Mod| { - assert_eq!(shape.name, "+"); - }); - } - - fn deserialize_invalid_suffix(&mut self) { - self.test_shape("foo'bar", |shape: &InvalidSuffix| { - assert_var(&shape.elem, "foo'"); - assert_eq!(shape.suffix, "bar"); - }); - } - - fn deserialize_number(&mut self) { - self.test_shape("127", |shape: &Number| { - assert_eq!(shape.base, None); - assert_eq!(shape.int, "127"); - }); - - self.test_shape("16_ff", |shape: &Number| { - assert_eq!(shape.base.as_ref().unwrap(), "16"); - assert_eq!(shape.int, "ff"); - }); - } - - fn deserialize_text_line_raw(&mut self) { - self.test_shape("\"foo\"", |shape: &TextLineRaw| { - let (segment,) = (&shape.text).expect_tuple(); - let expected = SegmentPlain { value: "foo".to_string() }; - assert_eq!(*segment, expected.into()); - }); - - let tricky_raw = r#""\\\'\n""#; - self.test_shape(tricky_raw, |shape: &TextLineRaw| { - let segments: (_,) = (&shape.text).expect_tuple(); - assert_eq!(*segments.0, SegmentPlain { value: r"\\\'\n".to_string() }.into()); - }); - } - - fn test_text_fmt_segment(&mut self, program: &str, tester: F) - where F: FnOnce(&SegmentFmt) { - self.test_shape(program, |shape: &TextLineFmt| { - let (segment,) = (&shape.text).expect_tuple(); - tester(segment) - }); - } - - fn deserialize_text_line_fmt(&mut self) { - use SegmentFmt::SegmentExpr; - - // plain segment - self.test_shape("'foo'", |shape: &TextLineFmt| { - let (segment,) = (&shape.text).expect_tuple(); - let expected = SegmentPlain { value: "foo".into() }; - assert_eq!(*segment, expected.into()); - }); - - // escapes - let tricky_fmt = r#"'\\\'\"'"#; - self.test_shape(tricky_fmt, |shape: &TextLineFmt| { - let segments: (_, _, _) = (&shape.text).expect_tuple(); - assert_eq!(*segments.0, Slash {}.into()); - assert_eq!(*segments.1, Quote {}.into()); - assert_eq!(*segments.2, Invalid { str: '"' }.into()); - }); - - // expression empty - let expr_fmt = r#"'``'"#; - self.test_text_fmt_segment(expr_fmt, |segment| match segment { - SegmentExpr(expr) => assert_eq!(expr.value, None), - _ => panic!("wrong segment type received"), - }); - - // expression non-empty - let expr_fmt = r#"'`foo`'"#; - self.test_text_fmt_segment(expr_fmt, |segment| match segment { - SegmentExpr(expr) => assert_var(expr.value.as_ref().unwrap(), "foo"), - _ => panic!("wrong segment type received"), - }); - - self.test_text_fmt_segment(r#"'\n'"#, |segment| { - let expected = EscapeCharacter { c: 'n' }; - assert_eq!(*segment, expected.into()); - }); - self.test_text_fmt_segment(r#"'\u0394'"#, |segment| { - let expected = EscapeUnicode16 { digits: "0394".into() }; - assert_eq!(*segment, expected.into()); - }); - // TODO [MWU] We don't test Unicode21 as it is not yet supported by the - // parser. - self.test_text_fmt_segment(r#"'\U0001f34c'"#, |segment| { - let expected = EscapeUnicode32 { digits: "0001f34c".into() }; - assert_eq!(*segment, expected.into()); - }); - } - - fn deserialize_text_block_raw(&mut self) { - let program = "\"\"\" \n \n X"; - self.test_shape(program, |shape: &TextBlockRaw| { - assert_eq!(shape.spaces, 1); - assert_eq!(shape.offset, 0); - - let (line,) = (&shape.text).expect_tuple(); - let (empty_line,) = (&line.empty_lines).expect_tuple(); - assert_eq!(*empty_line, 2); - - let (segment,) = (&line.text).expect_tuple(); - let expected_segment = SegmentPlain { value: " X".into() }; - assert_eq!(*segment, expected_segment.into()); - }); - } - - fn deserialize_text_block_fmt(&mut self) { - let program = "''' \n\n X\n Y"; - self.test_shape(program, |shape: &TextBlockFmt| { - assert_eq!(shape.spaces, 2); - assert_eq!(shape.offset, 0); - assert_eq!(shape.text.len(), 2); - - let (line1, line2) = (&shape.text).expect_tuple(); - let (empty_line,) = (&line1.empty_lines).expect_tuple(); - assert_eq!(*empty_line, 0); - let (segment,) = (&line1.text).expect_tuple(); - let expected_segment = SegmentPlain { value: " X".into() }; - assert_eq!(*segment, expected_segment.into()); - - assert!(line2.empty_lines.is_empty()); - let (segment,) = (&line2.text).expect_tuple(); - let expected_segment = SegmentPlain { value: " Y".into() }; - assert_eq!(*segment, expected_segment.into()); - }); - } - - - fn deserialize_unfinished_text(&mut self) { - let unfinished = r#""\"#; - self.test_shape(unfinished, |shape: &TextUnclosed| { - let line = &shape.line; - let line: &TextLineRaw = line.try_into().unwrap(); - - let (segment,) = (&line.text).expect_tuple(); - let expected = SegmentPlain { value: r"\".into() }; - assert_eq!(*segment, expected.into()); - }); - } - - fn deserialize_dangling_base(&mut self) { - self.test_shape("16_", |shape: &DanglingBase| { - assert_eq!(shape.base, "16"); - }); - } - - fn deserialize_prefix(&mut self) { - self.test_shape("foo bar", |shape: &Prefix| { - assert_var(&shape.func, "foo"); - assert_eq!(shape.off, 3); - assert_var(&shape.arg, "bar"); - }); - } - - fn deserialize_infix(&mut self) { - self.test_shape("foo + bar", |shape: &Infix| { - assert_var(&shape.larg, "foo"); - assert_eq!(shape.loff, 1); - assert_opr(&shape.opr, "+"); - assert_eq!(shape.roff, 2); - assert_var(&shape.rarg, "bar"); - }); - } - fn deserialize_left(&mut self) { - self.test_shape("foo +", |shape: &SectionLeft| { - assert_var(&shape.arg, "foo"); - assert_eq!(shape.off, 1); - assert_opr(&shape.opr, "+"); - }); - } - fn deserialize_right(&mut self) { - self.test_shape("+ bar", |shape: &SectionRight| { - assert_opr(&shape.opr, "+"); - assert_eq!(shape.off, 1); - assert_var(&shape.arg, "bar"); - }); - } - fn deserialize_sides(&mut self) { - self.test_shape("+", |shape: &SectionSides| { - assert_opr(&shape.opr, "+"); - }); - } - - fn deserialize_block(&mut self) { - self.test_shape(" foo\n bar", |block: &Block| { - assert_eq!(block.ty, BlockType::Continuous {}); - assert_eq!(block.indent, 1); - assert_eq!(block.empty_lines.len(), 0); - assert!(block.is_orphan); - - let first_line = &block.first_line; - assert_eq!(first_line.off, 0); - assert_var(&first_line.elem, "foo"); - - let (second_line,) = (&block.lines).expect_tuple(); - assert_eq!(second_line.off, 0); - assert_var(second_line.elem.as_ref().unwrap(), "bar"); - }); - } - - fn deserialize_annotation(&mut self) { - self.test_shape("@Tail_call", |annotation: &Annotation| { - let expected_annotation = Annotation { name: "@Tail_call".into() }; - assert_eq!(annotation, &expected_annotation); - }); - } - - /// Tests parsing a number of sample macro usages. - /// - /// As macros generate usually really huge ASTs, this test only checks - /// that we are able to deserialize the response and that it is a macro - /// match node. Node contents is not covered. - fn deserialize_macro_matches(&mut self) { - let macro_usages = vec![ - "[]", - "[1,2,3]", - "{x}", - "polyglot java import com.example.MyClass", - "foo -> bar", - "()", - "(foo -> bar)", - "a b c -> bar", - "type Maybe a\n Just val:a", - "if foo > 8 then 10 else 9", - "skip bar", - "freeze bar", - "case foo of\n bar", - "import foo", - "import", - "export bar", - "from bar import all", - "from bar export bo", - "a ->", - "-> a", - "(a -> b) -> c", - ]; - - for macro_usage in macro_usages.iter() { - println!(">>>>>>>>>> {macro_usage}"); - let ast = self.parser.parse_line_ast(*macro_usage).unwrap(); - println!("{ast:?}"); - expect_shape::>(&ast); - } - } - - fn deserialize_macro_ambiguous(&mut self) { - self.test_shape("if foo", |shape: &Ambiguous| { - let segment = &shape.segs.head; - assert_var(&segment.head, "if"); - - let segment_body = segment.body.as_ref().unwrap(); - assert_eq!(segment_body.off, 2); - assert_var(&segment_body.wrapped, "foo"); - }); - } - - fn run(&mut self) { - // Shapes not covered by separate test: - // * Opr (doesn't parse on its own, covered by Infix and other) - // * Module (covered by every single test, as parser wraps everything into module) - self.blank_line_round_trip(); - self.deserialize_metadata(); - self.deserialize_unrecognized(); - //self.deserialize_unexpected(); // TODO [mwu] https://github.com/enso-org/enso/issues/1016 - self.deserialize_invalid_quote(); - self.deserialize_inline_block(); - self.deserialize_blank(); - self.deserialize_var(); - self.deserialize_cons(); - self.deserialize_mod(); - self.deserialize_invalid_suffix(); - self.deserialize_number(); - self.deserialize_text_line_raw(); - self.deserialize_text_line_fmt(); - self.deserialize_text_block_raw(); - self.deserialize_text_block_fmt(); - self.deserialize_unfinished_text(); - self.deserialize_dangling_base(); - self.deserialize_prefix(); - self.deserialize_infix(); - self.deserialize_left(); - self.deserialize_right(); - self.deserialize_sides(); - self.deserialize_block(); - self.deserialize_annotation(); - self.deserialize_macro_matches(); - self.deserialize_macro_ambiguous(); - } -} - -/// A single entry point for all the tests here using external parser. -/// -/// Setting up the parser is costly, so we run all tests as a single batch. -/// Until proper CI solution for calling external parser is devised, this -/// test is marked with `#[ignore]`. -#[wasm_bindgen_test] -fn parser_tests() { - Fixture::new().run() -} - -/// Test case for https://github.com/enso-org/ide/issues/296 -#[wasm_bindgen_test] -fn block_roundtrip() { - let programs = vec![ - "main = 10 + 10", - "main =\n a = 10\n b = 20\n a * b", - "main =\n foo a =\n a * 10\n foo 10\n print \"hello\"", - "main =\n foo\n \n bar", - "main =\n \n foo\n \n bar", - ]; - for program in programs { - roundtrip_program(program); - } -} - -/// Test case for https://github.com/enso-org/ide/issues/296 -#[wasm_bindgen_test] -fn nested_macros() { - let parser = parser_scala::Parser::new_or_panic(); - - // Generate nested brackets. Stop at 8 because it gets slower and slower. - // At 12 the deserialization fails on WASM. - // At 14 the parsing fails in parser-service. - for i in 0..8 { - let program = format!("{}{}{}", "[".repeat(i), "foo", "]".repeat(i)); - roundtrip_program_with(&parser, &program); - } - - // Cases from https://github.com/enso-org/ide/issues/1351 - let program = r#"from Standard.Base import all - -main = - operator13 = Json.from_pairs [["a", 42], ["foo", [1,2,3]]] - var1 = [operator13, operator13]"#; - roundtrip_program_with(&parser, program); - - let program = r#"triplets n = 1.up_to n . to_vector . flat_map a-> - a+1 . up_to n . to_vector . flat_map b-> - b+1 . up_to n . to_vector . flat_map c-> - if a+b+c == n then [[a,b,c]] else [] -n = 10 -here.triplets n -IO.println(here.triplets n)"#; - roundtrip_program_with(&parser, program); -} - -#[wasm_bindgen_test] -fn dealing_with_invalid_metadata() { - let f = Fixture::new(); - - let id = ast::Id::from_str("52233542-5c73-430b-a2b7-a68aaf81341b").unwrap(); - let var = ast::Ast::new(ast::Var { name: "variable1".into() }, Some(id)); - let module = ast::Module::from_line(var); - let ast = known::Module::new_no_id(module); - let metadata = FauxMetadata("certainly_not_a_number".to_string()); - - // Make sure that out metadata cannot be deserialized as `FauxMetadata`. - let serialized_text_metadata = serde_json::to_string(&metadata).unwrap(); - assert!(serde_json::from_str::>(&serialized_text_metadata).is_err()); - - let parsed_file = parser_scala::api::ParsedSourceFile { ast, metadata }; - let generated = parsed_file.serialize().unwrap(); - let expected_generated = r#"variable1 - - -#### METADATA #### -[[{"index":{"value":0},"size":{"value":9}},"52233542-5c73-430b-a2b7-a68aaf81341b"]] -"certainly_not_a_number""#; - assert_eq!(generated.content, expected_generated); - let r = f.parser.parse_with_metadata::>(generated.content).unwrap(); - assert_eq!(r.metadata, default()); -} diff --git a/app/gui/language/parser/tests/web.rs b/app/gui/language/parser/tests/web.rs deleted file mode 100644 index 27683dc1cc9b..000000000000 --- a/app/gui/language/parser/tests/web.rs +++ /dev/null @@ -1,44 +0,0 @@ -// === Non-Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] - -use enso_prelude::*; - -use ast::Ast; -use parser_scala::api::ParsedSourceFile; -use parser_scala::Parser; -use uuid::Uuid; -use wasm_bindgen_test::wasm_bindgen_test; -use wasm_bindgen_test::wasm_bindgen_test_configure; - - - -wasm_bindgen_test_configure!(run_in_browser); - -#[wasm_bindgen_test] -fn web_test() { - let uuid = Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap(); - - let parser = Parser::new_or_panic(); - - let parse = |input| parser.parse_with_metadata(input).unwrap(); - let file = |term| ParsedSourceFile { - metadata: serde_json::json!({}), - ast: ast::known::KnownAst::new_no_id(term), - }; - - - let line = |term| ast::Module { lines: vec![ast::BlockLine { elem: term, off: 0 }] }; - - let app = ast::Prefix { func: Ast::var("x"), off: 3, arg: Ast::var("y") }; - let var = ast::Var { name: "x".into() }; - - let ast = file(line(None)); - assert_eq!(parse(String::try_from(&ast).unwrap()), ast); - - let ast = file(line(Some(Ast::new(var, Some(uuid))))); - assert_eq!(parse(String::try_from(&ast).unwrap()), ast); - - let ast = file(line(Some(Ast::new(app, Some(uuid))))); - assert_eq!(parse(String::try_from(&ast).unwrap()), ast); -} diff --git a/app/gui/language/span-tree/Cargo.toml b/app/gui/language/span-tree/Cargo.toml index 9315d59f029f..cae066ccfaef 100644 --- a/app/gui/language/span-tree/Cargo.toml +++ b/app/gui/language/span-tree/Cargo.toml @@ -13,5 +13,5 @@ enso-profiler = { path = "../../../../lib/rust/profiler" } failure = { workspace = true } [dev-dependencies] -parser-scala = { path = "../parser" } +parser = { path = "../parser" } wasm-bindgen-test = { workspace = true } diff --git a/app/gui/language/span-tree/example/src/lib.rs b/app/gui/language/span-tree/example/src/lib.rs index 4f6b16750073..71230c7ea1da 100644 --- a/app/gui/language/span-tree/example/src/lib.rs +++ b/app/gui/language/span-tree/example/src/lib.rs @@ -4,7 +4,6 @@ #![allow(clippy::bool_to_int_with_if)] #![allow(clippy::let_and_return)] -use ast::crumbs::PatternMatchCrumb::*; use ast::crumbs::*; use enso_prelude::*; use enso_text::traits::*; diff --git a/app/gui/language/span-tree/src/action.rs b/app/gui/language/span-tree/src/action.rs index 439b22b7c9ba..61e1b85510df 100644 --- a/app/gui/language/span-tree/src/action.rs +++ b/app/gui/language/span-tree/src/action.rs @@ -239,10 +239,9 @@ mod test { use crate::SpanTree; use ast::HasRepr; - use parser_scala::Parser; - use wasm_bindgen_test::wasm_bindgen_test; + use parser::Parser; - #[wasm_bindgen_test] + #[test] fn actions_in_span_tree() { #[derive(Debug)] struct Case { @@ -262,11 +261,12 @@ mod test { panic!("Invalid case {:?}: no node with span {:?}", self, self.span) }); let arg = Ast::new(ast::Var { name: "foo".to_string() }, None); + let case = format!("{self:?}"); let result = match &self.action { Set => node.set(&ast, arg), Erase => node.erase(&ast), } - .unwrap(); + .expect(&case); let result_repr = result.repr(); assert_eq!(result_repr, self.expected, "Wrong answer for case {self:?}"); assert_eq!(ast_id, result.id, "Changed AST ID in case {self:?}"); @@ -280,9 +280,6 @@ mod test { , Case{expr:"a + b" , span:4..5 , action:Set , expected:"a + foo" } , Case{expr:"a + b + c" , span:0..1 , action:Set , expected:"foo + b + c" } , Case{expr:"a + b + c" , span:4..5 , action:Set , expected:"a + foo + c" } - , Case{expr:"a , b , c" , span:0..1 , action:Set , expected:"foo , b , c" } - , Case{expr:"a , b , c" , span:4..5 , action:Set , expected:"a , foo , c" } - , Case{expr:"a , b , c" , span:8..9 , action:Set , expected:"a , b , foo" } , Case{expr:"f a b" , span:0..1 , action:Set , expected:"foo a b" } , Case{expr:"f a b" , span:2..3 , action:Set , expected:"f foo b" } , Case{expr:"f a b" , span:4..5 , action:Set , expected:"f a foo" } @@ -298,10 +295,6 @@ mod test { , Case{expr:"+ b" , span:3..3 , action:Set , expected:"+ b + foo" } , Case{expr:"a + b + c" , span:0..0 , action:Set , expected:"foo + a + b + c"} , Case{expr:"a + b + c" , span:5..5 , action:Set , expected:"a + b + foo + c"} - , Case{expr:"a , b , c" , span:0..0 , action:Set , expected:"foo , a , b , c"} - , Case{expr:"a , b , c" , span:4..4 , action:Set , expected:"a , foo , b , c"} - , Case{expr:"a , b , c" , span:8..8 , action:Set , expected:"a , b , foo , c"} - , Case{expr:"a , b , c" , span:9..9 , action:Set , expected:"a , b , c , foo"} , Case{expr:", b" , span:3..3 , action:Set , expected:", b , foo" } , Case{expr:"f a b" , span:2..2 , action:Set , expected:"f foo a b" } , Case{expr:"f a b" , span:3..3 , action:Set , expected:"f a foo b" } @@ -314,21 +307,18 @@ mod test { , Case{expr:"a + b + c" , span:0..1 , action:Erase, expected:"b + c" } , Case{expr:"a + b + c" , span:4..5 , action:Erase, expected:"a + c" } , Case{expr:"a + b + c" , span:8..9 , action:Erase, expected:"a + b" } - , Case{expr:"a , b , c" , span:0..1 , action:Erase, expected:"b , c" } - , Case{expr:"a , b , c" , span:4..5 , action:Erase, expected:"a , c" } - , Case{expr:"a , b , c" , span:8..9 , action:Erase, expected:"a , b" } , Case{expr:"f a b" , span:2..3 , action:Erase, expected:"f b" } , Case{expr:"f a b" , span:4..5 , action:Erase, expected:"f a" } , Case{expr:"(a + b + c)", span:5..6 , action:Erase, expected: "(a + c)" } , Case{expr:"(a + b + c" , span:5..6 , action:Erase, expected: "(a + c" } ]; - let parser = Parser::new_or_panic(); + let parser = Parser::new(); for case in cases { case.run(&parser); } } - #[wasm_bindgen_test] + #[test] fn possible_actions_in_span_tree() { #[derive(Debug)] struct Case { @@ -385,10 +375,9 @@ mod test { Case { expr: "[a,b]", span: 4..5, expected: &[] }, Case { expr: "(a + b + c)", span: 5..6, expected: &[Set, Erase] }, Case { expr: "(a", span: 1..2, expected: &[Set] }, - Case { expr: "(a", span: 0..1, expected: &[] }, Case { expr: "(a + b + c", span: 5..6, expected: &[Set, Erase] }, ]; - let parser = Parser::new_or_panic(); + let parser = Parser::new(); for case in cases { case.run(&parser); } diff --git a/app/gui/language/span-tree/src/generate.rs b/app/gui/language/span-tree/src/generate.rs index 2364c0d5f58d..17839a53de35 100644 --- a/app/gui/language/span-tree/src/generate.rs +++ b/app/gui/language/span-tree/src/generate.rs @@ -11,13 +11,11 @@ use crate::ArgumentInfo; use crate::Node; use crate::SpanTree; -use ast::assoc::Assoc; use ast::crumbs::Located; use ast::opr::GeneralizedInfix; use ast::Ast; use ast::HasRepr; -use ast::MacroAmbiguousSegment; -use ast::MacroMatchSegment; +use ast::SpanSeed; use std::collections::VecDeque; @@ -26,7 +24,6 @@ use std::collections::VecDeque; // ============== pub mod context; -pub mod macros; pub use context::Context; @@ -39,7 +36,7 @@ pub use context::Context; /// A trait for all types from which we can generate referred SpanTree. Meant to be implemented for /// all AST-like structures. pub trait SpanTreeGenerator { - /// Generate node with it's whole subtree. + /// Generate node with its whole subtree. fn generate_node( &self, kind: impl Into, @@ -272,13 +269,8 @@ fn generate_node_for_ast( match ast.shape() { ast::Shape::Prefix(_) => ast::prefix::Chain::from_ast(ast).unwrap().generate_node(kind, context), - // Lambdas should fall in _ case, because we don't want to create subports for - // them - ast::Shape::Match(_) if ast::macros::as_lambda_match(ast).is_none() => - ast::known::Match::try_new(ast.clone_ref()).unwrap().generate_node(kind, context), - ast::Shape::Ambiguous(_) => ast::known::Ambiguous::try_new(ast.clone_ref()) - .unwrap() - .generate_node(kind, context), + ast::Shape::Tree(tree) if tree.type_info != ast::TreeType::Lambda => + tree_generate_node(tree, kind, context, ast.id), _ => { let size = (ast.len().value as i32).byte_diff(); let ast_id = ast.id; @@ -374,7 +366,7 @@ fn generate_node_for_opr_chain( } gen.generate_empty_node(InsertionPointType::Append); - if ast::opr::assoc(&this.operator) == Assoc::Right { + if this.operator.right_assoc { gen.reverse_children(); } @@ -461,135 +453,6 @@ fn generate_node_for_prefix_chain( } -// === Match === - -impl SpanTreeGenerator for ast::known::Match { - fn generate_node( - &self, - kind: impl Into, - context: &impl Context, - ) -> FallibleResult> { - generate_node_for_known_match(self, kind.into(), context) - } -} - -fn generate_node_for_known_match( - this: &ast::known::Match, - kind: node::Kind, - context: &impl Context, -) -> FallibleResult> { - let removable = false; - let children_kind = node::Kind::argument().with_removable(removable); - let mut gen = ChildGenerator::default(); - if let Some(pat) = &this.pfx { - for macros::AstInPattern { ast, crumbs } in macros::all_ast_nodes_in_pattern(pat) { - let ast_crumb = ast::crumbs::MatchCrumb::Pfx { val: crumbs }; - let located_ast = Located::new(ast_crumb, ast.wrapped); - gen.generate_ast_node(located_ast, children_kind.clone(), context)?; - gen.spacing(ast.off); - } - } - let first_segment_index = 0; - generate_children_from_segment(&mut gen, first_segment_index, &this.segs.head, context)?; - for (index, segment) in this.segs.tail.iter().enumerate() { - gen.spacing(segment.off); - generate_children_from_segment(&mut gen, index + 1, &segment.wrapped, context)?; - } - Ok(Node { - kind, - size: gen.current_offset, - children: gen.children, - ast_id: this.id(), - payload: default(), - }) -} - -fn generate_children_from_segment( - gen: &mut ChildGenerator, - index: usize, - segment: &MacroMatchSegment, - context: &impl Context, -) -> FallibleResult { - // generate child for head - let ast = segment.head.clone_ref(); - let segment_crumb = ast::crumbs::SegmentMatchCrumb::Head; - let ast_crumb = ast::crumbs::MatchCrumb::Segs { val: segment_crumb, index }; - let located_ast = Located::new(ast_crumb, ast); - gen.generate_ast_node(located_ast, node::Kind::Token, context)?; - - for macros::AstInPattern { ast, crumbs } in macros::all_ast_nodes_in_pattern(&segment.body) { - let child_kind = match crumbs.last() { - Some(ast::crumbs::PatternMatchCrumb::Tok) => node::Kind::Token, - _ => node::Kind::argument().into(), - }; - gen.spacing(ast.off); - let segment_crumb = ast::crumbs::SegmentMatchCrumb::Body { val: crumbs }; - let ast_crumb = ast::crumbs::MatchCrumb::Segs { val: segment_crumb, index }; - let located_ast = Located::new(ast_crumb, ast.wrapped); - gen.generate_ast_node(located_ast, child_kind, context)?; - } - Ok(()) -} - - -// === Ambiguous == - -impl SpanTreeGenerator for ast::known::Ambiguous { - fn generate_node( - &self, - kind: impl Into, - context: &impl Context, - ) -> FallibleResult> { - generate_node_for_known_ambiguous(self, kind.into(), context) - } -} - -fn generate_node_for_known_ambiguous( - this: &ast::known::Ambiguous, - kind: node::Kind, - context: &impl Context, -) -> FallibleResult> { - let mut gen = ChildGenerator::default(); - let first_segment_index = 0; - generate_children_from_ambiguous(&mut gen, first_segment_index, &this.segs.head, context)?; - for (index, segment) in this.segs.tail.iter().enumerate() { - gen.spacing(segment.off); - generate_children_from_ambiguous(&mut gen, index + 1, &segment.wrapped, context)?; - } - Ok(Node { - kind, - size: gen.current_offset, - children: gen.children, - ast_id: this.id(), - payload: default(), - }) -} - -fn generate_children_from_ambiguous( - gen: &mut ChildGenerator, - index: usize, - segment: &MacroAmbiguousSegment, - context: &impl Context, -) -> FallibleResult { - let children_kind = node::Kind::argument(); - // generate child for head - let ast = segment.head.clone_ref(); - let segment_crumb = ast::crumbs::AmbiguousSegmentCrumb::Head; - let ast_crumb = ast::crumbs::AmbiguousCrumb { field: segment_crumb, index }; - let located_ast = Located::new(ast_crumb, ast); - gen.generate_ast_node(located_ast, node::Kind::Token, context)?; - - if let Some(sast) = &segment.body { - gen.spacing(sast.off); - let field = ast::crumbs::AmbiguousSegmentCrumb::Body; - let located_ast = - Located::new(ast::crumbs::AmbiguousCrumb { index, field }, sast.clone_ref()); - gen.generate_ast_node(located_ast, children_kind, context)?; - } - Ok(()) -} - - // === Common Utility == /// Build a prefix application-like span tree structure where the prefix argument has not been @@ -632,6 +495,61 @@ fn generate_expected_arguments( +// ========================= +// === SpanTree for Tree === +// ========================= + +fn tree_generate_node( + tree: &ast::Tree, + kind: impl Into, + context: &impl Context, + ast_id: Option, +) -> FallibleResult> { + let kind = match &tree.type_info { + ast::TreeType::Group => node::Kind::Group, + _ => kind.into(), + }; + let mut children = vec![]; + let size; + if let Some(leaf_info) = &tree.leaf_info { + size = ByteDiff::from(leaf_info.len()); + } else { + let mut offset = ByteDiff::from(0); + for (index, raw_span_info) in tree.span_info.iter().enumerate() { + match raw_span_info { + SpanSeed::Space(ast::SpanSeedSpace { space }) => offset += ByteDiff::from(space), + SpanSeed::Token(ast::SpanSeedToken { token }) => { + let kind = node::Kind::Token; + let size = ByteDiff::from(token.len()); + let ast_crumbs = vec![ast::crumbs::TreeCrumb { index }.into()]; + let node = Node { kind, size, ..default() }; + children.push(node::Child { node, offset, ast_crumbs }); + offset += size; + } + SpanSeed::Child(ast::SpanSeedChild { node }) => { + let kind = node::Kind::Argument(node::Argument { + removable: false, + name: None, + tp: None, + call_id: None, + tag_values: vec![], + }); + let node = node.generate_node(kind, context)?; + let child_size = node.size; + let ast_crumbs = vec![ast::crumbs::TreeCrumb { index }.into()]; + children.push(node::Child { node, offset, ast_crumbs }); + offset += child_size; + } + } + } + size = offset; + } + let payload = default(); + Ok(Node { kind, size, children, ast_id, payload }) +} + + + // =================== // === MockContext === // =================== @@ -676,21 +594,13 @@ mod test { use crate::node::Payload; use crate::ArgumentInfo; - use ast::crumbs::AmbiguousCrumb; - use ast::crumbs::AmbiguousSegmentCrumb; use ast::crumbs::InfixCrumb; - use ast::crumbs::PatternMatchCrumb; use ast::crumbs::PrefixCrumb; use ast::crumbs::SectionLeftCrumb; use ast::crumbs::SectionRightCrumb; - use ast::crumbs::SectionSidesCrumb; use ast::Crumbs; use ast::IdMap; - use parser_scala::Parser; - use wasm_bindgen_test::wasm_bindgen_test; - use wasm_bindgen_test::wasm_bindgen_test_configure; - - wasm_bindgen_test_configure!(run_in_browser); + use parser::Parser; /// A helper function which removes information about expression id from thw tree rooted at @@ -716,9 +626,9 @@ mod test { } } - #[wasm_bindgen_test] + #[test] fn generating_span_tree() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let mut id_map = IdMap::default(); id_map.generate(0..15); id_map.generate(0..11); @@ -733,7 +643,7 @@ mod test { let (span, id) = id_map_entry; let node = tree.root_ref().find_by_span(&span); assert!(node.is_some(), "Node with span {span} not found"); - assert_eq!(node.unwrap().node.ast_id, Some(id)); + assert_eq!(node.unwrap().node.ast_id, Some(id), "Span: {span}"); } // Check the other fields: @@ -763,9 +673,9 @@ mod test { assert_eq!(expected, tree) } - #[wasm_bindgen_test] + #[test] fn generate_span_tree_with_chains() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let ast = parser.parse_line_ast("2 + 3 + foo bar baz 13 + 5").unwrap(); let mut tree: SpanTree = ast.generate_tree(&context::Empty).unwrap(); clear_expression_ids(&mut tree.root); @@ -806,198 +716,54 @@ mod test { assert_eq!(expected, tree); } - #[wasm_bindgen_test] + #[test] + #[ignore] fn generating_span_tree_from_right_assoc_operator() { - let parser = Parser::new_or_panic(); - let ast = parser.parse_line_ast("1,2,3").unwrap(); + let parser = Parser::new(); + let ast = parser.parse_line_ast("1<|2<|3").unwrap(); let mut tree: SpanTree = ast.generate_tree(&context::Empty).unwrap(); clear_expression_ids(&mut tree.root); clear_parameter_infos(&mut tree.root); - - let expected = TreeBuilder::new(5) + let expected = TreeBuilder::new(7) .add_empty_child(0, Append) .add_leaf(0, 1, node::Kind::argument().removable(), InfixCrumb::LeftOperand) - .add_leaf(1, 1, node::Kind::operation(), InfixCrumb::Operator) - .add_child(2, 3, node::Kind::Chained, InfixCrumb::RightOperand) + .add_leaf(1, 2, node::Kind::operation(), InfixCrumb::Operator) + .add_child(3, 3, node::Kind::Chained, InfixCrumb::RightOperand) .add_empty_child(0, Append) .add_leaf(0, 1, node::Kind::argument().removable(), InfixCrumb::LeftOperand) - .add_leaf(1, 1, node::Kind::operation(), InfixCrumb::Operator) - .add_empty_child(2, AfterTarget) - .add_leaf(2, 1, node::Kind::this().removable(), InfixCrumb::RightOperand) - .add_empty_child(3, BeforeTarget) + .add_leaf(1, 2, node::Kind::operation(), InfixCrumb::Operator) + .add_empty_child(3, AfterTarget) + .add_leaf(3, 1, node::Kind::this().removable(), InfixCrumb::RightOperand) + .add_empty_child(4, BeforeTarget) .done() .build(); - assert_eq!(expected, tree) } - #[wasm_bindgen_test] - fn generating_span_tree_from_section() { - let parser = Parser::new_or_panic(); - // The star makes `SectionSides` ast being one of the parameters of + chain. First + makes - // SectionRight, and last + makes SectionLeft. - let ast = parser.parse_line_ast("+ * + + 2 +").unwrap(); - let mut tree: SpanTree = ast.generate_tree(&context::Empty).unwrap(); - clear_expression_ids(&mut tree.root); - clear_parameter_infos(&mut tree.root); - - let expected = TreeBuilder::new(11) - .add_child(0, 9, node::Kind::Chained, SectionLeftCrumb::Arg) - .add_child(0, 5, node::Kind::Chained, InfixCrumb::LeftOperand) - .add_child(0, 3, node::Kind::Chained, SectionLeftCrumb::Arg) - .add_empty_child(0, BeforeTarget) - .add_leaf(0, 1, node::Kind::operation(), SectionRightCrumb::Opr) - .add_child(2, 1, node::Kind::argument().removable(), SectionRightCrumb::Arg) - .add_empty_child(0, BeforeTarget) - .add_leaf(0, 1, node::Kind::operation(), SectionSidesCrumb) - .add_empty_child(1, Append) - .done() - .add_empty_child(3, Append) - .done() - .add_leaf(4, 1, node::Kind::operation(), SectionLeftCrumb::Opr) - .add_empty_child(5, Append) - .done() - .add_leaf(6, 1, node::Kind::operation(), InfixCrumb::Operator) - .add_leaf(8, 1, node::Kind::argument().removable(), InfixCrumb::RightOperand) - .add_empty_child(9, Append) - .done() - .add_leaf(10, 1, node::Kind::operation(), SectionLeftCrumb::Opr) - .add_empty_child(11, Append) - .build(); - - assert_eq!(expected, tree); - } - - #[wasm_bindgen_test] + #[test] + #[ignore] fn generating_span_tree_from_right_assoc_section() { - let parser = Parser::new_or_panic(); - let ast = parser.parse_line_ast(",2,").unwrap(); + let parser = Parser::new(); + let ast = parser.parse_line_ast("<|2<|").unwrap(); let mut tree: SpanTree = ast.generate_tree(&context::Empty).unwrap(); clear_expression_ids(&mut tree.root); clear_parameter_infos(&mut tree.root); - - let expected = TreeBuilder::new(3) + let expected = TreeBuilder::new(5) .add_empty_child(0, Append) - .add_leaf(0, 1, node::Kind::operation(), SectionRightCrumb::Opr) - .add_child(1, 2, node::Kind::Chained, SectionRightCrumb::Arg) + .add_leaf(0, 2, node::Kind::operation(), SectionRightCrumb::Opr) + .add_child(2, 2, node::Kind::Chained, SectionRightCrumb::Arg) .add_empty_child(0, Append) .add_leaf(0, 1, node::Kind::argument().removable(), SectionLeftCrumb::Arg) .add_leaf(1, 1, node::Kind::operation(), SectionLeftCrumb::Opr) .add_empty_child(2, BeforeTarget) .done() .build(); - - assert_eq!(expected, tree); - } - - #[wasm_bindgen_test] - fn generating_span_tree_from_matched_macros() { - use PatternMatchCrumb::*; - - let parser = Parser::new_or_panic(); - let mut id_map = IdMap::default(); - let expected_id = id_map.generate(0..29); - let expression = "if foo then (a + b) x else ()"; - let ast = parser.parse_line_ast_with_id_map(expression, id_map).unwrap(); - let mut tree: SpanTree = ast.generate_tree(&context::Empty).unwrap(); - - // Check if expression id is set - assert_eq!(tree.root_ref().ast_id, Some(expected_id)); - - // Check the other fields - clear_expression_ids(&mut tree.root); - clear_parameter_infos(&mut tree.root); - let seq = Seq { right: false }; - let if_then_else_cr = vec![seq, Or, Build]; - let parens_cr = vec![seq, Or, Or, Build]; - - let expected = TreeBuilder::new(29) - .add_leaf(0, 2, node::Kind::Token, segment_head_crumbs(0)) - .add_leaf(3, 3, node::Kind::argument(), segment_body_crumbs(0, &if_then_else_cr)) - .add_leaf(7, 4, node::Kind::Token, segment_head_crumbs(1)) - .add_child(12, 9, node::Kind::argument(), segment_body_crumbs(1, &if_then_else_cr)) - .add_child(0, 7, node::Kind::operation(), PrefixCrumb::Func) - .add_leaf(0, 1, node::Kind::Token, segment_head_crumbs(0)) - .add_child(1, 5, node::Kind::argument(), segment_body_crumbs(0, &parens_cr)) - .add_empty_child(0, BeforeTarget) - .add_leaf(0, 1, node::Kind::this(), InfixCrumb::LeftOperand) - .add_empty_child(1, AfterTarget) - .add_leaf(2, 1, node::Kind::operation(), InfixCrumb::Operator) - .add_leaf(4, 1, node::Kind::argument(), InfixCrumb::RightOperand) - .add_empty_child(5, Append) - .done() - .add_leaf(6, 1, node::Kind::Token, segment_head_crumbs(1)) - .done() - .add_empty_child(8, BeforeTarget) - .add_leaf(8, 1, node::Kind::this(), PrefixCrumb::Arg) - .add_empty_child(9, Append) - .done() - .add_leaf(22, 4, node::Kind::Token, segment_head_crumbs(2)) - .add_child(27, 2, node::Kind::argument(), segment_body_crumbs(2, &if_then_else_cr)) - .add_leaf(0, 1, node::Kind::Token, segment_head_crumbs(0)) - .add_leaf(1, 1, node::Kind::Token, segment_head_crumbs(1)) - .done() - .build(); - assert_eq!(expected, tree); } - #[wasm_bindgen_test] - fn generating_span_tree_from_matched_list_macro() { - use PatternMatchCrumb::*; - - let parser = Parser::new_or_panic(); - let expression = "[a,b]"; - let ast = parser.parse_line_ast(expression).unwrap(); - let mut tree: SpanTree = ast.generate_tree(&context::Empty).unwrap(); - - // Check the other fields - clear_expression_ids(&mut tree.root); - let left_seq = Seq { right: false }; - let right_seq = Seq { right: true }; - let many = Many { index: 0 }; - let first_element_cr = vec![left_seq, Or, Or, left_seq, Build]; - let second_element_cr = vec![left_seq, Or, Or, right_seq, many, right_seq, Build]; - let comma_cr = vec![left_seq, Or, Or, right_seq, many, left_seq, Tok]; - - let expected = TreeBuilder::new(5) - .add_leaf(0, 1, node::Kind::Token, segment_head_crumbs(0)) - .add_leaf(1, 1, node::Kind::argument(), segment_body_crumbs(0, &first_element_cr)) - .add_leaf(2, 1, node::Kind::Token, segment_body_crumbs(0, &comma_cr)) - .add_leaf(3, 1, node::Kind::argument(), segment_body_crumbs(0, &second_element_cr)) - .add_leaf(4, 1, node::Kind::Token, segment_head_crumbs(1)) - .build(); - - assert_eq!(expected, tree); - } - - #[wasm_bindgen_test] - fn generating_span_tree_from_ambiguous_macros() { - let parser = Parser::new_or_panic(); - let mut id_map = IdMap::default(); - id_map.generate(0..2); - let ast = parser.parse_line_ast_with_id_map("(4", id_map.clone()).unwrap(); - let mut tree: SpanTree = ast.generate_tree(&context::Empty).unwrap(); - - // Check the expression id: - let (_, expected_id) = id_map.vec.first().unwrap(); - assert_eq!(tree.root_ref().ast_id, Some(*expected_id)); - - // Check the other fields: - clear_expression_ids(&mut tree.root); - let head_crumb = AmbiguousCrumb { index: 0, field: AmbiguousSegmentCrumb::Head }; - let body_crumb = AmbiguousCrumb { index: 0, field: AmbiguousSegmentCrumb::Body }; - let expected = TreeBuilder::new(2) - .add_leaf(0, 1, node::Kind::Token, head_crumb) - .add_leaf(1, 1, node::Kind::argument(), body_crumb) - .build(); - - assert_eq!(expected, tree); - } - - #[wasm_bindgen_test] + #[test] fn generating_span_tree_for_lambda() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let ast = parser.parse_line_ast("foo a-> b + c").unwrap(); let mut tree: SpanTree = ast.generate_tree(&context::Empty).unwrap(); clear_expression_ids(&mut tree.root); @@ -1013,9 +779,9 @@ mod test { assert_eq!(expected, tree); } - #[wasm_bindgen_test] + #[test] fn generating_span_tree_for_unfinished_call() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let this_param = |call_id| ArgumentInfo { name: Some("self".to_owned()), tp: Some("Any".to_owned()), @@ -1143,17 +909,4 @@ mod test { clear_parameter_infos(&mut tree.root); assert_eq!(tree, expected); } - - fn segment_body_crumbs( - index: usize, - pattern_crumb: &[PatternMatchCrumb], - ) -> ast::crumbs::MatchCrumb { - let val = ast::crumbs::SegmentMatchCrumb::Body { val: pattern_crumb.to_vec() }; - ast::crumbs::MatchCrumb::Segs { val, index } - } - - fn segment_head_crumbs(index: usize) -> ast::crumbs::MatchCrumb { - let val = ast::crumbs::SegmentMatchCrumb::Head; - ast::crumbs::MatchCrumb::Segs { val, index } - } } diff --git a/app/gui/language/span-tree/src/generate/macros.rs b/app/gui/language/span-tree/src/generate/macros.rs deleted file mode 100644 index 1fd6ed591639..000000000000 --- a/app/gui/language/span-tree/src/generate/macros.rs +++ /dev/null @@ -1,139 +0,0 @@ -//! A module with utilities for generating SpanTree from macros (Match and Ambiguous). - -// TODO[ao] Duplicated with `pattern_subcrumbs` function in `ast::crumbs`, but adds information -// about spacing. All the 'crumblike' utilities should be merged to one solution - -use crate::prelude::*; - -use ast::crumbs::PatternMatchCrumb; -use ast::Ast; -use ast::MacroPatternMatch; -use ast::Shifted; - - - -// ====================== -// === LocatedPattern === -// ====================== - -/// A fragment of MacroPatternMatch localized by PatternMatchCrumbs. -#[allow(missing_docs)] -#[derive(Debug)] -pub struct LocatedPattern<'a> { - pub pattern: &'a MacroPatternMatch>, - pub crumbs: Vec, -} - - - -// ================== -// === PatternDfs === -// ================== - -/// A iterator over all nodes in MacroPatternMatch tree, traversing it with DFS algorithm. -struct PatternDfs<'a> { - /// The FILO queue of nodes to visit. - to_visit: Vec>, -} - -impl<'a> Iterator for PatternDfs<'a> { - type Item = LocatedPattern<'a>; - - fn next(&mut self) -> Option { - let to_return = self.to_visit.pop(); - if let Some(pattern) = &to_return { - self.push_children_to_visit(pattern); - } - to_return - } -} - -impl<'a> PatternDfs<'a> { - /// Create iterator which start from `root` node. - pub fn new(root: &'a MacroPatternMatch>) -> Self { - let first_to_visit = LocatedPattern { pattern: root, crumbs: vec![] }; - PatternDfs { to_visit: vec![first_to_visit] } - } - - /// Obtain all children of `pattern` and push them to `to_visit` queue. - fn push_children_to_visit(&mut self, pattern: &LocatedPattern<'a>) { - use ast::MacroPatternMatchRaw::*; - match pattern.pattern.deref() { - Except(pat) => self.push_child_to_visit(pattern, &pat.elem, PatternMatchCrumb::Except), - Tag(pat) => self.push_child_to_visit(pattern, &pat.elem, PatternMatchCrumb::Tag), - Cls(pat) => self.push_child_to_visit(pattern, &pat.elem, PatternMatchCrumb::Cls), - Or(pat) => self.push_child_to_visit(pattern, &pat.elem, PatternMatchCrumb::Or), - Seq(pat) => { - let (left_elem, right_elem) = &pat.elem; - self.push_child_to_visit(pattern, right_elem, PatternMatchCrumb::Seq { - right: true, - }); - self.push_child_to_visit(pattern, left_elem, PatternMatchCrumb::Seq { - right: false, - }); - } - Many(pat) => - for (index, elem) in pat.elem.iter().enumerate().rev() { - self.push_child_to_visit(pattern, elem, PatternMatchCrumb::Many { index }); - }, - // Other patterns does not have children. - _ => {} - } - } - - fn push_child_to_visit( - &mut self, - pattern: &LocatedPattern<'a>, - child: &'a MacroPatternMatch>, - crumb: PatternMatchCrumb, - ) { - let loc_pattern = LocatedPattern { - pattern: child, - crumbs: pattern.crumbs.iter().cloned().chain(std::iter::once(crumb)).collect(), - }; - self.to_visit.push(loc_pattern); - } -} - - -// ========================================== -// === Retrieving AST Nodes From Patterns === -// ========================================== - -/// An AST node being inside a Match node -#[allow(missing_docs)] -#[derive(Debug)] -pub struct AstInPattern { - pub ast: Shifted, - pub crumbs: Vec, -} - -/// Helper function that returns all AST nodes being on leaves of MacroPatternMatch. -pub fn all_ast_nodes_in_pattern( - pattern: &MacroPatternMatch>, -) -> impl Iterator + '_ { - use ast::MacroPatternMatchRaw::*; - - PatternDfs::new(pattern).filter_map(|pattern| { - let opt_ast_and_crumb = match pattern.pattern.deref() { - Build(pat) => Some((&pat.elem, PatternMatchCrumb::Build)), - Err(pat) => Some((&pat.elem, PatternMatchCrumb::Err)), - Tok(pat) => Some((&pat.elem, PatternMatchCrumb::Tok)), - Blank(pat) => Some((&pat.elem, PatternMatchCrumb::Blank)), - Var(pat) => Some((&pat.elem, PatternMatchCrumb::Var)), - Cons(pat) => Some((&pat.elem, PatternMatchCrumb::Cons)), - Opr(pat) => Some((&pat.elem, PatternMatchCrumb::Opr)), - Mod(pat) => Some((&pat.elem, PatternMatchCrumb::Mod)), - Num(pat) => Some((&pat.elem, PatternMatchCrumb::Num)), - Text(pat) => Some((&pat.elem, PatternMatchCrumb::Text)), - Block(pat) => Some((&pat.elem, PatternMatchCrumb::Block)), - Macro(pat) => Some((&pat.elem, PatternMatchCrumb::Macro)), - Invalid(pat) => Some((&pat.elem, PatternMatchCrumb::Invalid)), - _ => None, - }; - opt_ast_and_crumb.map(|(ast, crumb)| AstInPattern { - ast: ast.clone(), - crumbs: pattern.crumbs.into_iter().chain(std::iter::once(crumb)).collect(), - }) - }) -} diff --git a/app/gui/language/span-tree/src/node.rs b/app/gui/language/span-tree/src/node.rs index 1490ea5c3c58..9c2e6d3e08fc 100644 --- a/app/gui/language/span-tree/src/node.rs +++ b/app/gui/language/span-tree/src/node.rs @@ -8,7 +8,6 @@ use crate::iter::LeafIterator; use crate::iter::TreeFragment; use crate::ArgumentInfo; -use ast::crumbs::IntoCrumbs; use enso_text as text; @@ -65,12 +64,6 @@ impl Node { default() } - /// Define a new child by using the `ChildBuilder` pattern. Consumes self. - pub fn new_child(mut self, f: impl FnOnce(ChildBuilder) -> ChildBuilder) -> Self { - ChildBuilder::apply_to_node(&mut self, f); - self - } - /// Payload mapping utility. pub fn map(self, f: impl Copy + Fn(T) -> S) -> Node { let kind = self.kind; @@ -86,15 +79,9 @@ impl Node { #[allow(missing_docs)] impl Node { - // FIXME[WD]: This is a hack, which just checks token placement, not a real solution. - /// Check whether the node is a parensed expression. pub fn is_parensed(&self) -> bool { - let check = |t: Option<&Child>| { - t.map(|t| t.kind == Kind::Token && t.size.value == 1) == Some(true) - }; - check(self.children.first()) && check(self.children.last()) && self.children.len() == 3 + self.kind == Kind::Group } - pub fn is_root(&self) -> bool { self.kind.is_root() } @@ -216,116 +203,6 @@ impl DerefMut for Child { -// ==================== -// === ChildBuilder === -// ==================== - -/// A builder pattern for `SpanTree`. A think wrapper for `Child` which adds useful methods for -/// building properties of the current node. -/// -/// This builder exposes two main functions - `new_child`, and `add_child`. The former provides a -/// nice, user-friendly interface for building a `SpanTree`, while the later provides a very -/// explicit argument setting interface meant for building `SpanTree` for shape testing purposes. -#[derive(Debug)] -#[allow(missing_docs)] -pub struct ChildBuilder { - pub child: Child, -} - -impl Deref for ChildBuilder { - type Target = Child; - fn deref(&self) -> &Self::Target { - &self.child - } -} - -impl DerefMut for ChildBuilder { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.child - } -} - -impl ChildBuilder { - /// Constructor. - pub fn new(child: Child) -> Self { - Self { child } - } - - /// Add new child and use the `ChildBuilder` pattern to define its properties. This is a smart - /// child constructor. This function will automatically compute all not provided properties, - /// such as span or offset. Moreover, it will default all other not provided fields. - pub fn new_child(mut self, f: impl FnOnce(Self) -> Self) -> Self { - Self::apply_to_node(&mut self.node, f); - self - } - - /// Define a new child by using the `ChildBuilder` pattern. - fn apply_to_node(node: &mut Node, f: impl FnOnce(ChildBuilder) -> ChildBuilder) { - let mut new_child = Child::default(); - let offset = node.size; - new_child.offset = offset; - let builder = ChildBuilder::new(new_child); - let child = f(builder).child; - let offset_diff = child.offset - offset; - node.size = node.size + child.size + offset_diff; - node.children.push(child); - } - - /// Add new child and use the `ChildBuilder` pattern to define its properties. This function - /// accepts explicit list of arguments and disables all automatic computation of spans and - /// offsets. It is useful for testing purposes. - pub fn add_child( - mut self, - offset: usize, - size: usize, - kind: Kind, - crumbs: impl IntoCrumbs, - f: impl FnOnce(Self) -> Self, - ) -> Self { - let child: ChildBuilder = ChildBuilder::new(default()); - let child = f(child.offset(offset.into()).size(size.into()).kind(kind).crumbs(crumbs)); - self.node.children.push(child.child); - self - } - - /// Offset setter. - pub fn offset(mut self, offset: ByteDiff) -> Self { - self.offset = offset; - self - } - - /// Crumbs setter. - pub fn crumbs(mut self, crumbs: impl IntoCrumbs) -> Self { - self.ast_crumbs = crumbs.into_crumbs(); - self - } - - /// Kind setter. - pub fn kind(mut self, kind: impl Into) -> Self { - self.node.kind = kind.into(); - self - } - - /// Size setter. - pub fn size(mut self, size: ByteDiff) -> Self { - self.node.size = size; - self - } - - /// Expression ID setter. - pub fn ast_id(mut self, id: ast::Id) -> Self { - self.node.ast_id = Some(id); - self - } - - /// Expression ID generator. - pub fn new_ast_id(self) -> Self { - self.ast_id(ast::Id::new_v4()) - } -} - - - // ============== // === Crumbs === // ============== @@ -545,7 +422,7 @@ impl<'a, T: Payload> Ref<'a, T> { !ch.ast_crumbs.is_empty() && ast_crumbs.starts_with(&ch.ast_crumbs) }) .or_else(|| { - // We try to find appriopriate node second time, this time expecting case of + // We try to find appropriate node second time, this time expecting case of // "prefix-like" nodes with `InsertionPoint(ExpectedArgument(_))`. See also docs // for `generate::generate_expected_argument`. // TODO[ao]: As implementation of SpanTree will extend there may be some day @@ -561,7 +438,7 @@ impl<'a, T: Payload> Ref<'a, T> { } } - /// Get the node which exactly matches the given Span. If there many such node's, it pick first + /// Get the node which exactly matches the given Span. If there are many such nodes, pick first /// found by DFS. pub fn find_by_span(self, span: &text::Range) -> Option> { if self.span() == *span { diff --git a/app/gui/language/span-tree/src/node/kind.rs b/app/gui/language/span-tree/src/node/kind.rs index 07126c71ee30..9542ac089ea0 100644 --- a/app/gui/language/span-tree/src/node/kind.rs +++ b/app/gui/language/span-tree/src/node/kind.rs @@ -31,6 +31,8 @@ pub enum Kind { /// between AST tokens. For example, given expression `foo bar`, the span assigned to the /// `InsertionPoint` between `foo` and `bar` should be set to 3. InsertionPoint(InsertionPoint), + /// A parenthesized expression. + Group, } @@ -197,6 +199,7 @@ impl Kind { Self::Argument(_) => "Argument", Self::Token => "Token", Self::InsertionPoint(_) => "InsertionPoint", + Self::Group => "Group", } } } diff --git a/app/gui/src/controller/graph.rs b/app/gui/src/controller/graph.rs index 956b7051bedd..710c59afdf5c 100644 --- a/app/gui/src/controller/graph.rs +++ b/app/gui/src/controller/graph.rs @@ -23,7 +23,7 @@ use double_representation::node::MainLine; use double_representation::node::NodeInfo; use double_representation::node::NodeLocation; use engine_protocol::language_server; -use parser_scala::Parser; +use parser::Parser; use span_tree::action::Action; use span_tree::action::Actions; use span_tree::generate::context::CalledMethodInfo; @@ -334,15 +334,10 @@ impl Connections { pub fn name_for_ast(ast: &Ast) -> String { use ast::*; match ast.shape() { + Shape::Tree(tree) if let Some(name) = &tree.descriptive_name => name.to_string(), Shape::Var(ident) => ident.name.clone(), Shape::Cons(ident) => ident.name.to_lowercase(), Shape::Number(_) => "number".into(), - Shape::DanglingBase(_) => "number".into(), - Shape::TextLineRaw(_) => "text".into(), - Shape::TextLineFmt(_) => "text".into(), - Shape::TextBlockRaw(_) => "text".into(), - Shape::TextBlockFmt(_) => "text".into(), - Shape::TextUnclosed(_) => "text".into(), Shape::Opr(opr) => match opr.name.as_ref() { "+" => "sum", "*" => "product", @@ -1041,8 +1036,7 @@ pub mod tests { use double_representation::name::project; use engine_protocol::language_server::MethodPointer; use enso_text::index::*; - use parser_scala::Parser; - use wasm_bindgen_test::wasm_bindgen_test; + use parser::Parser; @@ -1097,7 +1091,7 @@ pub mod tests { /// Create a graph controller from the current mock data. pub fn graph(&self) -> Handle { - let parser = Parser::new().unwrap(); + let parser = Parser::new(); let urm = Rc::new(model::undo_redo::Repository::new()); let module = self.module_data().plain(&parser, urm); let id = self.graph_id.clone(); @@ -1109,7 +1103,6 @@ pub mod tests { self.module_path.method_pointer(self.project_name.clone(), self.graph_id.to_string()) } - #[profile(Debug)] pub fn suggestion_db(&self) -> Rc { use model::suggestion_database::SuggestionDatabase; let entries = self.suggestions.iter(); @@ -1147,7 +1140,7 @@ pub mod tests { } } - #[wasm_bindgen_test] + #[test] fn node_operations() { Fixture::set_up().run(|graph| async move { let uid = graph.all_node_infos().unwrap()[0].id(); @@ -1158,7 +1151,7 @@ pub mod tests { }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_notification_relay() { Fixture::set_up().run(|graph| async move { let mut sub = graph.subscribe(); @@ -1168,7 +1161,7 @@ pub mod tests { }); } - #[wasm_bindgen_test] + #[test] fn suggestion_db_updates_graph_values() { Fixture::set_up().run(|graph| async move { let mut sub = graph.subscribe(); @@ -1181,7 +1174,7 @@ pub mod tests { }); } - #[wasm_bindgen_test] + #[test] fn graph_controller_inline_definition() { let mut test = Fixture::set_up(); const EXPRESSION: &str = "2+2"; @@ -1196,7 +1189,7 @@ pub mod tests { }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_block_definition() { let mut test = Fixture::set_up(); test.data.code = r" @@ -1212,7 +1205,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_parse_expression() { let mut test = Fixture::set_up(); test.run(|graph| async move { @@ -1227,7 +1220,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn span_tree_context_handling_metadata_and_name() { let entry = crate::test::mock::data::suggestion_entry_foo(); let mut test = Fixture::set_up(); @@ -1266,7 +1259,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_used_names_in_inline_def() { let mut test = Fixture::set_up(); test.data.code = "main = foo".into(); @@ -1277,7 +1270,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_nested_definition() { let mut test = Fixture::set_up(); test.data.code = r"main = @@ -1298,7 +1291,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn collapsing_nodes_avoids_name_conflicts() { // Checks that generated name avoid collision with other methods defined in the module // and with symbols used that could be shadowed by the extracted method's name. @@ -1335,7 +1328,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn collapsing_nodes() { let mut test = Fixture::set_up(); let code = r" @@ -1385,7 +1378,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_doubly_nested_definition() { // Tests editing nested definition that requires transforming inline expression into // into a new block. @@ -1404,7 +1397,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_node_operations_node() { let mut test = Fixture::set_up(); const PROGRAM: &str = r" @@ -1483,7 +1476,8 @@ main = }) } - #[wasm_bindgen_test] + #[test] + #[ignore] // FIXME (https://github.com/enso-org/enso/issues/5574) fn graph_controller_connections_listing() { let mut test = Fixture::set_up(); const PROGRAM: &str = r" @@ -1532,7 +1526,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_create_connection() { /// A case for creating connection test. The field's names are short to be able to write /// nice-to-read table of cases without very long lines (see `let cases` below). @@ -1573,22 +1567,18 @@ main = } } - let cases = &[ - Case { src: "x", dst: "foo", expected: "x", ports: (&[], &[]) }, - Case { src: "x,y", dst: "foo a", expected: "foo y", ports: (&[4], &[2]) }, - Case { - src: "Vec x y", - dst: "1 + 2 + 3", - expected: "x + 2 + 3", - ports: (&[0, 2], &[0, 1]), - }, - ]; + let cases = &[Case { src: "x", dst: "foo", expected: "x", ports: (&[], &[]) }, Case { + src: "Vec x y", + dst: "1 + 2 + 3", + expected: "x + 2 + 3", + ports: (&[0, 2], &[0, 1]), + }]; for case in cases { case.run() } } - #[wasm_bindgen_test] + #[test] fn graph_controller_create_connection_reordering() { let mut test = Fixture::set_up(); const PROGRAM: &str = r"main = @@ -1621,7 +1611,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_create_connection_reordering_with_dependency() { let mut test = Fixture::set_up(); const PROGRAM: &str = r"main = @@ -1660,7 +1650,7 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn graph_controller_create_connection_introducing_var() { let mut test = Fixture::set_up(); const PROGRAM: &str = r"main = @@ -1697,9 +1687,9 @@ main = }) } - #[wasm_bindgen_test] + #[test] fn suggested_names() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let cases = [ ("a+b", "sum"), ("a-b", "difference"), @@ -1722,7 +1712,7 @@ main = } } - #[wasm_bindgen_test] + #[test] fn disconnect() { #[derive(Clone, Debug)] struct Case { @@ -1756,11 +1746,6 @@ main = Case { dest_node_expr: "var + b + c", dest_node_expected: "_ + b + c" }, Case { dest_node_expr: "a + var + c", dest_node_expected: "a + _ + c" }, Case { dest_node_expr: "a + b + var", dest_node_expected: "a + b" }, - Case { dest_node_expr: "var , a", dest_node_expected: "_ , a" }, - Case { dest_node_expr: "a , var", dest_node_expected: "a , _" }, - Case { dest_node_expr: "var , b , c", dest_node_expected: "_ , b , c" }, - Case { dest_node_expr: "a , var , c", dest_node_expected: "a , _ , c" }, - Case { dest_node_expr: "a , b , var", dest_node_expected: "a , b" }, Case { dest_node_expr: "f\n bar a var", dest_node_expected: "f\n bar a _", diff --git a/app/gui/src/controller/graph/executed.rs b/app/gui/src/controller/graph/executed.rs index 44f94c0d3367..a3a05695a721 100644 --- a/app/gui/src/controller/graph/executed.rs +++ b/app/gui/src/controller/graph/executed.rs @@ -385,7 +385,7 @@ pub mod tests { impl MockData { pub fn controller(&self) -> Handle { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let repository = Rc::new(model::undo_redo::Repository::new()); let module = self.module.plain(&parser, repository); let method = self.graph.method(); diff --git a/app/gui/src/controller/graph/widget.rs b/app/gui/src/controller/graph/widget.rs index f8945595d1c2..8dbdc957d5fe 100644 --- a/app/gui/src/controller/graph/widget.rs +++ b/app/gui/src/controller/graph/widget.rs @@ -400,9 +400,7 @@ impl QueryData { /// Escape a string to be used as a visualization argument. Transforms the string into an enso /// expression with string literal. fn escape_visualization_argument(arg: &str) -> String { - let segment = ast::SegmentPlain { value: arg.into() }; - let text = ast::TextLineRaw { text: vec![segment.into()] }; - text.repr() + Ast::raw_text_literal(arg).repr() } /// Escape a list of strings to be used as a visualization argument. Transforms the strings into diff --git a/app/gui/src/controller/ide.rs b/app/gui/src/controller/ide.rs index 96543df8be61..fe0609856d9e 100644 --- a/app/gui/src/controller/ide.rs +++ b/app/gui/src/controller/ide.rs @@ -7,7 +7,7 @@ use crate::prelude::*; use double_representation::name::project; use mockall::automock; -use parser_scala::Parser; +use parser::Parser; // ============== diff --git a/app/gui/src/controller/ide/desktop.rs b/app/gui/src/controller/ide/desktop.rs index f3690248b4c8..ac41570b7b9c 100644 --- a/app/gui/src/controller/ide/desktop.rs +++ b/app/gui/src/controller/ide/desktop.rs @@ -15,7 +15,7 @@ use engine_protocol::project_manager; use engine_protocol::project_manager::MissingComponentAction; use engine_protocol::project_manager::ProjectMetadata; use engine_protocol::project_manager::ProjectName; -use parser_scala::Parser; +use parser::Parser; @@ -69,7 +69,7 @@ impl Handle { ) -> Self { let current_project = Rc::new(CloneCell::new(project)); let status_notifications = default(); - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let notifications = default(); Self { current_project, project_manager, status_notifications, parser, notifications } } diff --git a/app/gui/src/controller/ide/plain.rs b/app/gui/src/controller/ide/plain.rs index 6c89b4ae1388..4c4d3d619f8f 100644 --- a/app/gui/src/controller/ide/plain.rs +++ b/app/gui/src/controller/ide/plain.rs @@ -11,7 +11,7 @@ use crate::model::project::synchronized::Properties; use double_representation::name::project; use engine_protocol::project_manager::ProjectName; -use parser_scala::Parser; +use parser::Parser; @@ -46,7 +46,7 @@ impl Handle { /// Create IDE Controller for a given opened project. pub fn new(project: model::Project) -> Self { let status_notifications = default(); - let parser = Parser::new_or_panic(); + let parser = Parser::new(); Self { status_notifications, parser, project } } @@ -73,7 +73,7 @@ impl Handle { ) .await?; let status_notifications = default(); - let parser = Parser::new_or_panic(); + let parser = Parser::new(); Ok(Self { status_notifications, parser, project }) } } diff --git a/app/gui/src/controller/module.rs b/app/gui/src/controller/module.rs index b44f6c595d8e..987e5afa33b9 100644 --- a/app/gui/src/controller/module.rs +++ b/app/gui/src/controller/module.rs @@ -14,7 +14,7 @@ use double_representation::name::QualifiedName; use double_representation::text::apply_code_change_to_id_map; use engine_protocol::language_server; use engine_protocol::types::Sha3_224; -use parser_scala::Parser; +use parser::Parser; @@ -89,7 +89,7 @@ impl Handle { "The module controller ast was not synchronized with text editor \ content!\n >>> Module: {my_code}\n >>> Editor: {code}" ); - let actual_ast = self.parser.parse(code, default())?.try_into()?; + let actual_ast = self.parser.parse(code, default()).try_into()?; self.model.update_ast(actual_ast)?; } Ok(()) @@ -171,7 +171,7 @@ impl Handle { parser: Parser, repository: Rc, ) -> FallibleResult { - let ast = parser.parse(code.to_string(), id_map)?.try_into()?; + let ast = parser.parse(code.to_string(), id_map).try_into()?; let metadata = default(); let model = Rc::new(model::module::Plain::new(path, ast, metadata, repository)); Ok(Handle { model, language_server, parser }) @@ -200,15 +200,14 @@ mod test { use ast::Ast; use ast::BlockLine; use enso_text::index::*; - use parser_scala::Parser; + use parser::Parser; use uuid::Uuid; - use wasm_bindgen_test::wasm_bindgen_test; - #[wasm_bindgen_test] + #[test] fn update_ast_after_text_change() { TestWithLocalPoolExecutor::set_up().run_task(async { let ls = language_server::Connection::new_mock_rc(default()); - let parser = Parser::new().unwrap(); + let parser = Parser::new(); let location = Path::from_mock_module_name("Test"); let code = "2+2"; let uuid1 = Uuid::new_v4(); @@ -236,7 +235,10 @@ mod test { Some(uuid1), ), loff: 0, - opr: Ast::new(ast::Opr { name: "+".to_string() }, Some(uuid2)), + opr: Ast::new( + ast::Opr { name: "+".to_string(), right_assoc: false }, + Some(uuid2), + ), roff: 0, rarg: Ast::new( ast::Number { base: None, int: "2".to_string() }, diff --git a/app/gui/src/controller/project.rs b/app/gui/src/controller/project.rs index c3cd7dc62890..80649279c530 100644 --- a/app/gui/src/controller/project.rs +++ b/app/gui/src/controller/project.rs @@ -12,7 +12,7 @@ use engine_protocol::language_server::MethodPointer; use engine_protocol::language_server::Path; use enso_frp::web::platform; use enso_frp::web::platform::Platform; -use parser_scala::Parser; +use parser::Parser; @@ -276,7 +276,7 @@ mod tests { #[wasm_bindgen_test] fn adding_missing_main() { let _ctx = TestWithLocalPoolExecutor::set_up(); - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); let mut data = crate::test::mock::Unified::new(); let module_name = data.module_path.module_name().to_owned(); let main_ptr = main_method_ptr(data.project_name.clone(), &data.module_path); diff --git a/app/gui/src/controller/searcher.rs b/app/gui/src/controller/searcher.rs index 1e783739e633..5c198bcb896f 100644 --- a/app/gui/src/controller/searcher.rs +++ b/app/gui/src/controller/searcher.rs @@ -26,7 +26,7 @@ use enso_text::Byte; use enso_text::Location; use enso_text::Rope; use flo_stream::Subscriber; -use parser_scala::Parser; +use parser::Parser; // ============== @@ -1778,7 +1778,7 @@ pub mod test { project.expect_qualified_name().returning_st(move || project_qname.clone()); project.expect_name().returning_st(move || project_name.clone()); let project = Rc::new(project); - ide.expect_parser().return_const(Parser::new_or_panic()); + ide.expect_parser().return_const(Parser::new()); let current_project = project.clone_ref(); ide.expect_current_project().returning_st(move || Some(current_project.clone_ref())); ide.expect_manage_projects() @@ -2123,7 +2123,7 @@ pub mod test { #[wasm_bindgen_test] fn parsed_input() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); fn args_reprs(prefix: &ast::prefix::Chain) -> Vec { prefix.args.iter().map(|arg| arg.repr()).collect() @@ -2186,9 +2186,7 @@ pub mod test { let expression = parsed.expression.unwrap(); assert_eq!(expression.off, 0); assert_eq!(expression.func.repr(), "foo"); - assert_eq!(args_reprs(&expression), vec![" bar".to_string()]); - assert_eq!(parsed.pattern_offset, 1); - assert_eq!(parsed.pattern.as_str(), "(baz "); + assert_eq!(args_reprs(&expression), vec![" bar".to_string(), " (baz".to_string()]); } fn are_same( @@ -2272,7 +2270,7 @@ pub mod test { } fn run(&self) { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let ast = parser.parse_line_ast(self.before).unwrap(); let new_ast = apply_this_argument("foo", &ast); assert_eq!(new_ast.repr(), self.after, "Case {self:?} failed: {ast:?}"); @@ -2424,7 +2422,7 @@ pub mod test { let module = searcher.graph.graph().module.clone_ref(); // Setup searcher. - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let picked_method = FragmentAddedByPickingSuggestion { id: CompletedFragmentId::Function, picked_suggestion: action::Suggestion::FromDatabase(entry4), @@ -2507,7 +2505,7 @@ pub mod test { #[wasm_bindgen_test] fn simple_function_call_parsing() { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let ast = parser.parse_line_ast("foo").unwrap(); let call = SimpleFunctionCall::try_new(&ast).expect("Returned None for \"foo\""); diff --git a/app/gui/src/controller/text.rs b/app/gui/src/controller/text.rs index 8e31837a7fa5..e2d3324ec38a 100644 --- a/app/gui/src/controller/text.rs +++ b/app/gui/src/controller/text.rs @@ -152,7 +152,7 @@ mod test { use crate::executor::test_utils::TestWithLocalPoolExecutor; use enso_text::index::*; - use parser_scala::Parser; + use parser::Parser; use wasm_bindgen_test::wasm_bindgen_test; fn setup_mock_project(setup: impl FnOnce(&mut model::project::MockAPI)) -> model::Project { @@ -171,7 +171,7 @@ mod test { test.run_task(async move { let ls = language_server::Connection::new_mock_rc(default()); let path = model::module::Path::from_mock_module_name("Test"); - let parser = Parser::new().unwrap(); + let parser = Parser::new(); let module_res = controller::Module::new_mock(path, "main = 2+2", default(), ls, parser, default()); let module = module_res.unwrap(); @@ -204,7 +204,7 @@ mod test { #[wasm_bindgen_test] fn obtain_text_controller_for_module() { - let parser = parser_scala::Parser::new_or_panic(); + let parser = parser::Parser::new(); TestWithLocalPoolExecutor::set_up().run_task(async move { let code = "2 + 2".to_string(); let undo = default(); diff --git a/app/gui/src/lib.rs b/app/gui/src/lib.rs index e9dc8f897451..0b3f0f9c9266 100644 --- a/app/gui/src/lib.rs +++ b/app/gui/src/lib.rs @@ -43,6 +43,7 @@ #![feature(assert_matches)] #![feature(hash_drain_filter)] #![feature(unwrap_infallible)] +#![feature(if_let_guard)] // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] diff --git a/app/gui/src/model/module.rs b/app/gui/src/model/module.rs index bae060bb424e..ec91379f1e4d 100644 --- a/app/gui/src/model/module.rs +++ b/app/gui/src/model/module.rs @@ -13,10 +13,10 @@ use double_representation::name::project; use double_representation::name::QualifiedName; use engine_protocol::language_server::MethodPointer; use flo_stream::Subscriber; -use parser_scala::api::ParsedSourceFile; -use parser_scala::api::PruneUnusedIds; -use parser_scala::api::SourceFile; -use parser_scala::Parser; +use parser::api::ParsedSourceFile; +use parser::api::PruneUnusedIds; +use parser::api::SourceFile; +use parser::Parser; use serde::Deserialize; use serde::Serialize; @@ -340,7 +340,7 @@ impl PruneUnusedIds for Metadata { } } -impl parser_scala::api::Metadata for Metadata {} +impl parser::api::Metadata for Metadata {} impl Default for Metadata { fn default() -> Self { @@ -738,7 +738,7 @@ pub mod test { parser: &Parser, repository: Rc, ) -> Module { - let ast = parser.parse_module(self.code.clone(), self.id_map.clone()).unwrap(); + let ast = parser.parse_module(&self.code, self.id_map.clone()).unwrap(); let module = Plain::new(self.path.clone(), ast, self.metadata.clone(), repository); Rc::new(module) } @@ -746,8 +746,7 @@ pub mod test { pub fn plain_from_code(code: impl Into) -> Module { let urm = default(); - MockData { code: code.into(), ..default() } - .plain(&parser_scala::Parser::new_or_panic(), urm) + MockData { code: code.into(), ..default() }.plain(&parser::Parser::new(), urm) } #[test] @@ -783,7 +782,7 @@ pub mod test { assert_eq!(qualified.to_string(), "n.P.Foo.Bar"); } - #[wasm_bindgen_test] + #[test] fn outdated_metadata_parses() { // Metadata here will fail to serialize because `File` is not a valid qualified name. // Expected behavior is that invalid metadata parts will be filled with defaults. @@ -794,8 +793,7 @@ main = 5 #### METADATA #### [[{"index":{"value":7},"size":{"value":8}},"04f2bbe4-6291-4bad-961c-146228f3aee4"],[{"index":{"value":15},"size":{"value":1}},"20f4e5e3-3ab4-4c68-ae7a-d261d3f23af0"],[{"index":{"value":16},"size":{"value":13}},"746b453a-3fed-4128-86ce-a3853ef684b0"],[{"index":{"value":0},"size":{"value":29}},"aead2cca-c429-47f2-85ef-fe090433990b"],[{"index":{"value":30},"size":{"value":4}},"063ab796-e79b-4037-bf94-1f24c9545b9a"],[{"index":{"value":35},"size":{"value":1}},"4b4992bd-7d8e-401b-aebf-42b30a4a5cae"],[{"index":{"value":37},"size":{"value":1}},"1d6660c6-a70b-4eeb-b5f7-82f05a51df25"],[{"index":{"value":30},"size":{"value":8}},"ad5b88bf-0cdb-4eba-90fe-07afc37e3953"],[{"index":{"value":0},"size":{"value":39}},"602dfcea-2321-48fa-95b1-1f58fb028099"]] {"ide":{"node":{"1d6660c6-a70b-4eeb-b5f7-82f05a51df25":{"position":{"vector":[-75.5,52]},"intended_method":{"module":"Base.System.File","defined_on_type":"File","name":"read"}}}}}"#; - let result = Parser::new_or_panic().parse_with_metadata::(code.into()); - let file = result.unwrap(); + let file = Parser::new().parse_with_metadata::(code); assert_eq!(file.ast.repr(), "import Standard.Visualization\nmain = 5"); assert_eq!(file.metadata.ide.node.len(), 1); let id = ast::Id::from_str("1d6660c6-a70b-4eeb-b5f7-82f05a51df25").unwrap(); diff --git a/app/gui/src/model/module/plain.rs b/app/gui/src/model/module/plain.rs index 3873127a5422..8dd8c50c368b 100644 --- a/app/gui/src/model/module/plain.rs +++ b/app/gui/src/model/module/plain.rs @@ -19,9 +19,9 @@ use double_representation::definition::DefinitionInfo; use double_representation::definition::DefinitionProvider; use double_representation::import; use flo_stream::Subscriber; -use parser_scala::api::ParsedSourceFile; -use parser_scala::api::SourceFile; -use parser_scala::Parser; +use parser::api::ParsedSourceFile; +use parser::api::SourceFile; +use parser::Parser; use std::collections::hash_map::Entry; @@ -178,7 +178,7 @@ impl model::module::API for Module { let replaced_end = code.offset_to_location_snapped(change.range.end); let replaced_location = enso_text::Range::new(replaced_start, replaced_end); code.apply_change(change.as_ref()); - let new_ast = parser.parse(code.into(), new_id_map)?.try_into()?; + let new_ast = parser.parse(code.to_string(), new_id_map).try_into()?; let notification = NotificationKind::CodeChanged { change, replaced_location }; self.update_content(notification, |content| content.ast = new_ast) } @@ -318,7 +318,7 @@ fn restore_edited_node_in_graph( "Restoring edited node {node_id} to original expression \ \"{previous_expression}\"." ); - graph.edit_node(node_id, Parser::new()?.parse_line_ast(previous_expression)?)?; + graph.edit_node(node_id, Parser::new().parse_line_ast(previous_expression)?)?; md_entry.get_mut().intended_method = previous_intended_method; } None => {} @@ -360,7 +360,7 @@ mod test { range: enso_text::Range::new(2.byte(), 5.byte()), text: "- abc".to_string(), }; - module.apply_code_change(change, &Parser::new_or_panic(), default()).unwrap(); + module.apply_code_change(change, &Parser::new(), default()).unwrap(); assert_eq!("2 - abc", module.ast().repr()); } @@ -391,7 +391,7 @@ mod test { range: enso_text::Range::new(0.byte(), 1.byte()), text: "foo".to_string(), }; - module.apply_code_change(change.clone(), &Parser::new_or_panic(), default()).unwrap(); + module.apply_code_change(change.clone(), &Parser::new(), default()).unwrap(); let replaced_location = enso_text::Range { start: enso_text::Location { line: 0.into(), offset: 0.byte() }, end: enso_text::Location { line: 0.into(), offset: 1.byte() }, diff --git a/app/gui/src/model/module/synchronized.rs b/app/gui/src/model/module/synchronized.rs index 129f62ef8aef..0e17dcbe48d1 100644 --- a/app/gui/src/model/module/synchronized.rs +++ b/app/gui/src/model/module/synchronized.rs @@ -24,8 +24,8 @@ use enso_text::text; use enso_text::Location; use enso_text::Range; use flo_stream::Subscriber; -use parser_scala::api::SourceFile; -use parser_scala::Parser; +use parser::api::SourceFile; +use parser::Parser; @@ -172,9 +172,7 @@ impl Module { info!("Read content of the module {path}, digest is {:?}", opened.current_version); let end_of_file_byte = content.last_line_end_location(); let end_of_file = content.utf16_code_unit_location_of_location(end_of_file_byte); - // TODO[ao] We should not fail here when metadata are malformed, but discard them and set - // default instead. - let source = parser.parse_with_metadata(opened.content)?; + let source = parser.parse_with_metadata(opened.content); let digest = opened.current_version; let summary = ContentSummary { digest, end_of_file }; let model = model::module::Plain::new(path, source.ast, source.metadata, repository); @@ -726,12 +724,12 @@ pub mod test { let parser = data.parser.clone(); let module = fixture.synchronized_module(); - let new_content = "main =\n println \"Test\"".to_string(); + let new_content = "main =\n println \"Test\""; let new_ast = parser.parse_module(new_content, default()).unwrap(); module.update_ast(new_ast).unwrap(); runner.perhaps_run_until_stalled(&mut fixture); let change = TextChange { range: (20..24).into(), text: "Test 2".to_string() }; - module.apply_code_change(change, &Parser::new_or_panic(), default()).unwrap(); + module.apply_code_change(change, &Parser::new(), default()).unwrap(); runner.perhaps_run_until_stalled(&mut fixture); }; diff --git a/app/gui/src/model/project.rs b/app/gui/src/model/project.rs index c92a836ee41e..2d6fcd699884 100644 --- a/app/gui/src/model/project.rs +++ b/app/gui/src/model/project.rs @@ -14,7 +14,7 @@ use engine_protocol::language_server; use engine_protocol::language_server::ContentRoot; use flo_stream::Subscriber; use mockall::automock; -use parser_scala::Parser; +use parser::Parser; use uuid::Uuid; diff --git a/app/gui/src/model/project/synchronized.rs b/app/gui/src/model/project/synchronized.rs index 35c4eceb0e1e..00822a6a9d12 100644 --- a/app/gui/src/model/project/synchronized.rs +++ b/app/gui/src/model/project/synchronized.rs @@ -25,7 +25,7 @@ use engine_protocol::project_manager::MissingComponentAction; use engine_protocol::project_manager::ProjectName; use flo_stream::Subscriber; use json_rpc::error::RpcError; -use parser_scala::Parser; +use parser::Parser; @@ -287,9 +287,9 @@ impl Project { let language_server = language_server_rpc.clone(); let module_registry = default(); let execution_contexts = default(); + let parser = Parser::new(); let visualization = controller::Visualization::new(language_server, embedded_visualizations); - let parser = Parser::new_or_panic(); let language_server = &*language_server_rpc; let suggestion_db = SuggestionDatabase::create_synchronized(language_server); let suggestion_db = Rc::new(suggestion_db.await.map_err(&wrap)?); diff --git a/app/gui/src/presenter/graph/state.rs b/app/gui/src/presenter/graph/state.rs index 58aa4e4551ff..69b774ecff13 100644 --- a/app/gui/src/presenter/graph/state.rs +++ b/app/gui/src/presenter/graph/state.rs @@ -152,7 +152,7 @@ impl Nodes { removed_views } - /// Remove node represented by given view (if any) and return it's AST ID. + /// Remove node represented by given view (if any) and return its AST ID. pub fn remove_node(&mut self, node: ViewNodeId) -> Option { let ast_id = self.ast_node_by_view_id.remove(&node)?; self.nodes.remove(&ast_id); @@ -827,10 +827,10 @@ impl<'a> ViewChange<'a> { mod tests { use super::*; use engine_protocol::language_server::MethodPointer; - use parser_scala::Parser; + use parser::Parser; fn create_test_node(expression: &str) -> controller::graph::Node { - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let ast = parser.parse_line_ast(expression).unwrap(); controller::graph::Node { info: double_representation::node::NodeInfo { @@ -969,7 +969,7 @@ mod tests { fn refreshing_node_expression() { let Fixture { state, nodes } = Fixture::setup_nodes(&["foo bar"]); let node_id = nodes[0].node.id(); - let new_ast = Parser::new_or_panic().parse_line_ast("foo baz").unwrap().with_id(node_id); + let new_ast = Parser::new().parse_line_ast("foo baz").unwrap().with_id(node_id); let new_node = controller::graph::Node { info: double_representation::node::NodeInfo { documentation: None, diff --git a/app/gui/src/test.rs b/app/gui/src/test.rs index 6f3a9f728d48..81e884810aba 100644 --- a/app/gui/src/test.rs +++ b/app/gui/src/test.rs @@ -134,7 +134,7 @@ pub mod mock { pub module_path: model::module::Path, pub suggestions: HashMap, pub context_id: model::execution_context::Id, - pub parser: parser_scala::Parser, + pub parser: parser::Parser, code: String, id_map: ast::IdMap, metadata: crate::model::module::Metadata, @@ -171,7 +171,7 @@ pub mod mock { metadata: default(), context_id: CONTEXT_ID, root_definition: definition_name(), - parser: parser_scala::Parser::new_or_panic(), + parser: parser::Parser::new(), } } @@ -180,7 +180,7 @@ pub mod mock { } pub fn module(&self, urm: Rc) -> crate::model::Module { - let ast = self.parser.parse_module(self.code.clone(), self.id_map.clone()).unwrap(); + let ast = self.parser.parse_module(&self.code, self.id_map.clone()).unwrap(); let path = self.module_path.clone(); let metadata = self.metadata.clone(); let repository = urm.repository.clone_ref(); diff --git a/app/gui/suggestion-database/Cargo.toml b/app/gui/suggestion-database/Cargo.toml index 9304da6dada7..fc3f7cfc6029 100644 --- a/app/gui/suggestion-database/Cargo.toml +++ b/app/gui/suggestion-database/Cargo.toml @@ -12,7 +12,8 @@ enso-prelude = { path = "../../../lib/rust/prelude" } convert_case = { workspace = true } span-tree = { path = "../language/span-tree" } ast = { path = "../language/ast/impl" } -parser-scala = { path = "../language/parser" } +parser = { path = "../language/parser" } +parser-scala = { path = "../language/parser-scala" } enso-text = { path = "../../../lib/rust/text" } double-representation = { path = "../controller/double-representation" } engine-protocol = { path = "../controller/engine-protocol" } diff --git a/app/gui/suggestion-database/src/example.rs b/app/gui/suggestion-database/src/example.rs index 377e1b3b0f1d..de90ac523961 100644 --- a/app/gui/suggestion-database/src/example.rs +++ b/app/gui/suggestion-database/src/example.rs @@ -5,7 +5,7 @@ use crate::prelude::*; use double_representation::definition; use double_representation::definition::DefinitionName; use double_representation::module; -use parser_scala::Parser; +use parser::Parser; @@ -74,7 +74,7 @@ impl Example { ) -> FallibleResult { let base_name = self.function_name(); let name = DefinitionName::new_plain(module.generate_name(&base_name)?); - let code_ast = parser.parse_module(self.code.clone(), default())?; + let code_ast = parser.parse_module(&self.code, default())?; let body_block = code_ast.shape().as_block(0).ok_or(InvalidExample)?; let body_ast = Ast::new(body_block, None); Ok(definition::ToAdd::new_with_body(name, default(), body_ast)) diff --git a/app/gui/view/Cargo.toml b/app/gui/view/Cargo.toml index 37e5107a108c..70b913a34616 100644 --- a/app/gui/view/Cargo.toml +++ b/app/gui/view/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] ast = { path = "../language/ast/impl" } +parser = { path = "../language/parser" } enso-config = { path = "../config" } enso-frp = { path = "../../../lib/rust/frp" } enso-prelude = { path = "../../../lib/rust/prelude" } @@ -23,7 +24,6 @@ ensogl-hardcoded-theme = { path = "../../../lib/rust/ensogl/app/theme/hardcoded" ide-view-component-browser = { path = "component-browser" } ide-view-documentation = { path = "documentation" } ide-view-graph-editor = { path = "graph-editor" } -parser-scala = { path = "../language/parser" } span-tree = { path = "../language/span-tree" } js-sys = { workspace = true } multi-map = { workspace = true } diff --git a/app/gui/view/debug_scene/interface/Cargo.toml b/app/gui/view/debug_scene/interface/Cargo.toml index 22d7c4a6f671..132fca244d80 100644 --- a/app/gui/view/debug_scene/interface/Cargo.toml +++ b/app/gui/view/debug_scene/interface/Cargo.toml @@ -9,12 +9,12 @@ crate-type = ["cdylib", "rlib"] [dependencies] ast = { path = "../../../language/ast/impl" } +parser = { path = "../../../language/parser" } enso-frp = { path = "../../../../../lib/rust/frp" } ensogl = { path = "../../../../../lib/rust/ensogl" } ensogl-hardcoded-theme = { path = "../../../../../lib/rust/ensogl/app/theme/hardcoded" } ensogl-text-msdf = { path = "../../../../../lib/rust/ensogl/component/text/src/font/msdf" } ide-view = { path = "../.." } -parser-scala = { path = "../../../language/parser" } span-tree = { path = "../../../language/span-tree" } uuid = { version = "0.8", features = ["v4", "wasm-bindgen"] } wasm-bindgen = { workspace = true } diff --git a/app/gui/view/debug_scene/interface/src/lib.rs b/app/gui/view/debug_scene/interface/src/lib.rs index ace730fb663a..8edf1f80500d 100644 --- a/app/gui/view/debug_scene/interface/src/lib.rs +++ b/app/gui/view/debug_scene/interface/src/lib.rs @@ -15,10 +15,7 @@ #![warn(unused_import_braces)] #![warn(unused_qualifications)] -use ast::crumbs::PatternMatchCrumb::*; -use ast::crumbs::*; use ensogl::prelude::*; -use span_tree::traits::*; use enso_frp as frp; use ensogl::application::Application; @@ -37,8 +34,7 @@ use ide_view::graph_editor::Type; use ide_view::project; use ide_view::root; use ide_view::status_bar; -use parser_scala::Parser; -use uuid::Uuid; +use parser::Parser; @@ -323,7 +319,7 @@ fn init(app: &Application) { pub fn expression_mock_string(label: &str) -> Expression { let pattern = Some(label.to_string()); let code = format!("\"{label}\""); - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let parameters = vec![]; let ast = parser.parse_line_ast(&code).unwrap(); let invocation_info = span_tree::generate::context::CalledMethodInfo { parameters }; @@ -338,7 +334,7 @@ pub fn expression_mock_string(label: &str) -> Expression { pub fn expression_mock() -> Expression { let pattern = Some("var1".to_string()); let code = "[1,2,3]".to_string(); - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let this_param = span_tree::ArgumentInfo { name: Some("self".to_owned()), tp: Some("Text".to_owned()), @@ -355,52 +351,11 @@ pub fn expression_mock() -> Expression { Expression { pattern, code, whole_expression_id, input_span_tree, output_span_tree } } -// TODO[ao] This expression mocks results in panic. If you want to use it, please fix it first. -pub fn expression_mock2() -> Expression { - let pattern = Some("var1".to_string()); - let pattern_cr = vec![Seq { right: false }, Or, Or, Build]; - let val = ast::crumbs::SegmentMatchCrumb::Body { val: pattern_cr }; - let parens_cr = ast::crumbs::MatchCrumb::Segs { val, index: 0 }; - let code = "make_maps size (distribution normal)".to_string(); - let output_span_tree = span_tree::SpanTree::default(); - let input_span_tree = span_tree::builder::TreeBuilder::new(36) - .add_child(0, 14, span_tree::node::Kind::Chained, PrefixCrumb::Func) - .add_child(0, 9, span_tree::node::Kind::operation(), PrefixCrumb::Func) - .set_ast_id(Uuid::new_v4()) - .done() - .add_empty_child(10, span_tree::node::InsertionPointType::BeforeTarget) - .add_child(10, 4, span_tree::node::Kind::this().removable(), PrefixCrumb::Arg) - .set_ast_id(Uuid::new_v4()) - .done() - .add_empty_child(14, span_tree::node::InsertionPointType::Append) - .set_ast_id(Uuid::new_v4()) - .done() - .add_child(15, 21, span_tree::node::Kind::argument().removable(), PrefixCrumb::Arg) - .set_ast_id(Uuid::new_v4()) - .add_child(1, 19, span_tree::node::Kind::argument(), parens_cr) - .set_ast_id(Uuid::new_v4()) - .add_child(0, 12, span_tree::node::Kind::operation(), PrefixCrumb::Func) - .set_ast_id(Uuid::new_v4()) - .done() - .add_empty_child(13, span_tree::node::InsertionPointType::BeforeTarget) - .add_child(13, 6, span_tree::node::Kind::this(), PrefixCrumb::Arg) - .set_ast_id(Uuid::new_v4()) - .done() - .add_empty_child(19, span_tree::node::InsertionPointType::Append) - .done() - .done() - .add_empty_child(36, span_tree::node::InsertionPointType::Append) - .build(); - let whole_expression_id = default(); - let code = code.into(); - Expression { pattern, code, whole_expression_id, input_span_tree, output_span_tree } -} - pub fn expression_mock3() -> Expression { let pattern = Some("Vector x y z".to_string()); // let code = "image.blur ((foo bar) baz)".to_string(); let code = "Vector x y z".to_string(); - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let this_param = span_tree::ArgumentInfo { name: Some("self".to_owned()), tp: Some("Image".to_owned()), @@ -440,7 +395,7 @@ pub fn expression_mock3() -> Expression { pub fn expression_mock_trim() -> Expression { let pattern = Some("trim_node".to_string()); let code = "\" hello \".trim".to_string(); - let parser = Parser::new_or_panic(); + let parser = Parser::new(); let this_param = span_tree::ArgumentInfo { name: Some("self".to_owned()), tp: Some("Text".to_owned()), diff --git a/app/gui/view/graph-editor/src/component/node/input/area.rs b/app/gui/view/graph-editor/src/component/node/input/area.rs index e74fd9a17abc..9dd2c69dab58 100644 --- a/app/gui/view/graph-editor/src/component/node/input/area.rs +++ b/app/gui/view/graph-editor/src/component/node/input/area.rs @@ -104,6 +104,7 @@ impl Debug for Expression { } } + // === Pretty printing debug adapter === /// Debug adapter used for pretty-printing the `Expression` span tree. Can be used to print the diff --git a/engine/runtime/src/test/java/org/enso/compiler/ErrorCompilerTest.java b/engine/runtime/src/test/java/org/enso/compiler/ErrorCompilerTest.java index c27fdf5eaf3f..94cfe17ecfae 100644 --- a/engine/runtime/src/test/java/org/enso/compiler/ErrorCompilerTest.java +++ b/engine/runtime/src/test/java/org/enso/compiler/ErrorCompilerTest.java @@ -171,13 +171,13 @@ public void malformedImport6() throws Exception { @Test public void malformedImport7() throws Exception { var ir = parse("import Foo hiding"); - assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 17, 17); + assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 7, 17); } @Test public void malformedImport8() throws Exception { var ir = parse("import Foo hiding X,"); - assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 18, 20); + assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 7, 20); } @Test diff --git a/lib/rust/parser/debug/tests/parse.rs b/lib/rust/parser/debug/tests/parse.rs index 02b2a7997b67..1bc04ea797a2 100644 --- a/lib/rust/parser/debug/tests/parse.rs +++ b/lib/rust/parser/debug/tests/parse.rs @@ -840,6 +840,9 @@ fn export() { fn metadata_raw() { let code = [ "x", + "", + "", + "", "#### METADATA ####", r#"[[{"index":{"value":7},"size":{"value":8}},"5bad897e-099b-4b00-9348-64092636746d"]]"#, ]; diff --git a/lib/rust/parser/src/macros/built_in.rs b/lib/rust/parser/src/macros/built_in.rs index 76886f5793d2..1f789687af81 100644 --- a/lib/rust/parser/src/macros/built_in.rs +++ b/lib/rust/parser/src/macros/built_in.rs @@ -47,15 +47,10 @@ fn register_import_macros(macros: &mut resolver::SegmentMap<'_>) { let defs = [ macro_definition! {("import", everything()) import_body}, macro_definition! {("import", everything(), "as", everything()) import_body}, - macro_definition! {("import", everything(), "hiding", everything()) import_body}, macro_definition! {("polyglot", everything(), "import", everything()) import_body}, macro_definition! { ("polyglot", everything(), "import", everything(), "as", everything()) import_body}, macro_definition! { - ("polyglot", everything(), "import", everything(), "hiding", everything()) import_body}, - macro_definition! { - ("from", everything(), "import", everything(), "hiding", everything()) import_body}, - macro_definition! { ("from", everything(), "import", nothing(), "all", nothing()) import_body}, macro_definition! { ("from", everything(), "import", nothing(), "all", nothing(), "hiding", everything()) diff --git a/lib/rust/parser/src/metadata.rs b/lib/rust/parser/src/metadata.rs index d6ed8837aa4d..4b89653420bf 100644 --- a/lib/rust/parser/src/metadata.rs +++ b/lib/rust/parser/src/metadata.rs @@ -9,7 +9,7 @@ use uuid::Uuid; -const MARKER: &str = "#### METADATA ####\n"; +const MARKER: &str = "\n\n\n#### METADATA ####\n"; @@ -47,6 +47,14 @@ impl From for Metadata { } } +/// Split input source file into the code and the metadata section, if any was found. +pub fn extract(input: &str) -> (&str, Option<&str>) { + match input.rsplit_once(MARKER) { + Some((code, metadata)) => (code, Some(metadata)), + None => (input, None), + } +} + /// Given source code, if a metadata section is found: Attempt to parse it; return the result, and /// the non-metadata portion of the input. pub fn parse(input: &str) -> Option<(Result, &str)> { @@ -54,6 +62,18 @@ pub fn parse(input: &str) -> Option<(Result, &str)> { Some((metadata.parse().map(|data: MetadataFormat| data.into()), code)) } +/// Parse just the metadata section. +pub fn parse_metadata(input: &str) -> Option> { + Some( + MetadataFormat::from_str(input) + .ok()? + .id_map + .into_iter() + .map(|(location, id)| ((location.index.value, location.size.value), id)) + .collect(), + ) +} + /// Result of parsing metadata. pub type Result = std::result::Result; diff --git a/lib/rust/parser/src/syntax/tree.rs b/lib/rust/parser/src/syntax/tree.rs index 19929b93bbd8..72110bf386ba 100644 --- a/lib/rust/parser/src/syntax/tree.rs +++ b/lib/rust/parser/src/syntax/tree.rs @@ -498,6 +498,33 @@ pub struct DocComment<'s> { pub newlines: Vec>, } +impl<'s> DocComment<'s> { + /// Return the contents of the comment, with leading whitespace, the `##` token, and following + /// empty lines removed; newlines will be normalized. + pub fn content(&self) -> String { + let mut buf = String::new(); + macro_rules! emit_token { + ($buf:expr, $token:expr) => {{ + $buf.push_str(&$token.left_offset.code.repr); + $buf.push_str(&$token.code.repr); + }}; + } + for element in &self.elements { + match element { + TextElement::Section { text } => buf.push_str(&text.code.repr), + TextElement::Escape { token } => emit_token!(buf, token), + TextElement::Newline { newline } => { + buf.push_str(&newline.left_offset.code.repr); + buf.push('\n'); + } + // Unreachable. + TextElement::Splice { .. } => continue, + } + } + buf + } +} + impl<'s> span::Builder<'s> for DocComment<'s> { fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> { span.add(&mut self.open).add(&mut self.elements).add(&mut self.newlines) @@ -1320,6 +1347,13 @@ impl<'s> Tree<'s> { self.visit_item(&mut visitor); visitor.code } + + /// Return source code of this AST, excluding initial whitespace. + pub fn trimmed_code(&self) -> String { + let mut visitor = CodePrinterVisitor::default(); + self.variant.visit_item(&mut visitor); + visitor.code + } } @@ -1391,3 +1425,61 @@ impl<'s> Tree<'s> { self.visit_mut(&mut visitor); } } + + +// === ItemFnVisitor === + +impl<'s> Tree<'s> { + /// Apply the provided function to each [`Token`] or [`Tree`] that is a child of the node. + pub fn visit_items(&self, f: F) + where F: for<'a> FnMut(item::Ref<'s, 'a>) { + struct ItemFnVisitor { + f: F, + } + impl Visitor for ItemFnVisitor {} + impl<'a, 's: 'a, F> ItemVisitor<'s, 'a> for ItemFnVisitor + where F: FnMut(item::Ref<'s, 'a>) + { + fn visit_item(&mut self, item: item::Ref<'s, 'a>) -> bool { + (self.f)(item); + false + } + } + self.variant.visit_item(&mut ItemFnVisitor { f }); + } +} + + + +// ================= +// === Traversal === +// ================= + +impl<'s> Tree<'s> { + /// Return an iterator over the operands of the given left-associative operator, in reverse + /// order. + pub fn left_assoc_rev<'t, 'o>(&'t self, operator: &'o str) -> LeftAssocRev<'o, 't, 's> { + let tree = Some(self); + LeftAssocRev { operator, tree } + } +} + +/// Iterator over the operands of a particular left-associative operator, in reverse order. +#[derive(Debug)] +pub struct LeftAssocRev<'o, 't, 's> { + operator: &'o str, + tree: Option<&'t Tree<'s>>, +} + +impl<'o, 't, 's> Iterator for LeftAssocRev<'o, 't, 's> { + type Item = &'t Tree<'s>; + fn next(&mut self) -> Option { + if let box Variant::OprApp(OprApp { lhs, opr: Ok(opr), rhs }) = &self.tree?.variant + && opr.code == self.operator { + self.tree = lhs.into(); + rhs.into() + } else { + self.tree.take() + } + } +} diff --git a/lib/rust/prelude/src/data/non_empty_vec.rs b/lib/rust/prelude/src/data/non_empty_vec.rs index b090e7b6c9b3..2b4940c6258e 100644 --- a/lib/rust/prelude/src/data/non_empty_vec.rs +++ b/lib/rust/prelude/src/data/non_empty_vec.rs @@ -284,7 +284,7 @@ where I: vec_indexed_by::Index } /// Get the tail reference. - pub fn tail(&mut self) -> &[T] + pub fn tail(&self) -> &[T] where I: From { &self.elems[I::from(1_u8)..] }