From e11df7c49a8bfc68f411b0013f43112961af449f Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 6 Mar 2021 12:14:55 +0100 Subject: [PATCH 01/74] Updated Cargo.lock using --- Cargo.lock | 437 ++++++++++++++++++++++++++++------------------------- 1 file changed, 229 insertions(+), 208 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef8b47a77e..e6ddba9981 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "ab_glyph" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b1f87418ab9d7e1ee2a783aa167767ebeb316d0a9fb10c347aec28a5008acb" +checksum = "7a933731feda8b460bdad9a9e43bb386baba6ec593d2bc19716ef3c75c09085c" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -27,9 +27,9 @@ dependencies = [ [[package]] name = "adler" -version = "0.2.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" @@ -115,7 +115,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c69a8137596e84c22d57f3da1b5de1d4230b1742a710091c85f4d7ce50f00f38" dependencies = [ - "libloading", + "libloading 0.6.7", ] [[package]] @@ -226,9 +226,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byte-tools" @@ -238,9 +238,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a4bad0c5981acc24bc09e532f35160f952e35422603f0563cd7a73c2c2e65a0" +checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58" dependencies = [ "bytemuck_derive", ] @@ -252,8 +252,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" dependencies = [ "jobserver", ] @@ -356,8 +356,8 @@ dependencies = [ "heck", "proc-macro-error", "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -420,12 +420,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "const_fn" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" - [[package]] name = "const_format" version = "0.2.13" @@ -442,7 +436,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df496e1bbc93814d728a8036ff054cd95830afe9cf2275c9326688c02eff936" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", + "quote 1.0.9", "unicode-xid 0.2.1", ] @@ -611,7 +605,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.3", ] [[package]] @@ -632,8 +626,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ "cfg-if 1.0.0", - "crossbeam-epoch 0.9.1", - "crossbeam-utils 0.8.1", + "crossbeam-epoch 0.9.3", + "crossbeam-utils 0.8.3", ] [[package]] @@ -653,13 +647,12 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" +checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" dependencies = [ "cfg-if 1.0.0", - "const_fn", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.3", "lazy_static", "memoffset 0.6.1", "scopeguard", @@ -689,9 +682,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" dependencies = [ "autocfg 1.0.1", "cfg-if 1.0.0", @@ -727,7 +720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a60cceb22c7c53035f8980524fdc7f17cf49681a3c154e6757d30afbec6ec4" dependencies = [ "bitflags", - "libloading", + "libloading 0.6.7", "winapi 0.3.9", ] @@ -738,8 +731,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -796,7 +789,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" dependencies = [ - "libloading", + "libloading 0.6.7", +] + +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading 0.7.0", ] [[package]] @@ -950,9 +952,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" +checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" dependencies = [ "futures-channel", "futures-core", @@ -965,9 +967,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" dependencies = [ "futures-core", "futures-sink", @@ -975,15 +977,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" +checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" [[package]] name = "futures-executor" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" +checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1" dependencies = [ "futures-core", "futures-task", @@ -992,42 +994,39 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" +checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" [[package]] name = "futures-macro" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" +checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" dependencies = [ "proc-macro-hack", "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] name = "futures-sink" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" +checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" [[package]] name = "futures-task" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" -dependencies = [ - "once_cell", -] +checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" [[package]] name = "futures-util" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" dependencies = [ "futures-channel", "futures-core", @@ -1036,7 +1035,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.4", + "pin-project-lite 0.2.6", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -1054,9 +1053,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" dependencies = [ "typenum", ] @@ -1104,7 +1103,7 @@ dependencies = [ "bitflags", "gfx-auxil", "gfx-hal", - "libloading", + "libloading 0.6.7", "log", "parking_lot 0.11.1", "range-alloc", @@ -1282,9 +1281,9 @@ checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" [[package]] name = "handlebars" -version = "3.5.2" +version = "3.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964d0e99a61fe9b1b347389b77ebf8b7e1587b70293676aaca7d27e59b9073b2" +checksum = "cdb0867bbc5a3da37a753e78021d5fcf8a4db00e18dd2dd90fd36e24190e162d" dependencies = [ "log", "pest", @@ -1357,9 +1356,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ "autocfg 1.0.1", "hashbrown", @@ -1383,8 +1382,8 @@ checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" dependencies = [ "proc-macro-hack", "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", "unindent", ] @@ -1408,8 +1407,8 @@ version = "0.2.0" source = "git+https://github.com/rtfeldman/inkwell?tag=llvm10-0.release3#57e9f00d98fc99486e737c314e56a59498c5dbbb" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -1483,9 +1482,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.47" +version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" +checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" dependencies = [ "wasm-bindgen", ] @@ -1520,9 +1519,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.85" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" +checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" [[package]] name = "libloading" @@ -1534,6 +1533,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "libloading" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" +dependencies = [ + "cfg-if 1.0.0", + "winapi 0.3.9", +] + [[package]] name = "line_drawing" version = "0.7.0" @@ -1551,9 +1560,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "llvm-sys" -version = "100.2.0" +version = "100.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9109e19fbfac3458f2970189719fa19f1007c6fd4e08c44fdebf4be0ddbe261d" +checksum = "15d9c00ce56221b2150e2d4d51887ff139fce5a0e50346c744861d1e66d2f7c4" dependencies = [ "cc", "lazy_static", @@ -1669,9 +1678,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", "autocfg 1.0.1", @@ -1814,10 +1823,22 @@ dependencies = [ ] [[package]] -name = "nom" -version = "6.1.0" +name = "nix" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab6f70b46d6325aa300f1c7bb3d470127dfc27806d8ea6bf294ee0ce643ce2b1" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", +] + +[[package]] +name = "nom" +version = "6.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" dependencies = [ "memchr", "version_check", @@ -1860,8 +1881,8 @@ checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -1923,9 +1944,9 @@ checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "once_cell" -version = "1.5.2" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "oorandom" @@ -1959,9 +1980,9 @@ dependencies = [ [[package]] name = "owned_ttf_parser" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "948b27637ba56144c62d3415929ef18986b3a383137ebcbe97d9362a968bf997" +checksum = "3a3c7a20e3f122223e68eef6ca58e39bc1ea8a1d83418ba4c2c1ba189d2ee355" dependencies = [ "ttf-parser", ] @@ -1996,8 +2017,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b4b5f600e60dd3a147fb57b4547033d382d1979eb087af310e91cb45a63b1f4" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -2018,7 +2039,7 @@ checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", "lock_api 0.4.2", - "parking_lot_core 0.8.2", + "parking_lot_core 0.8.3", ] [[package]] @@ -2037,16 +2058,16 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ "backtrace", "cfg-if 1.0.0", "instant", "libc", "petgraph", - "redox_syscall 0.1.57", + "redox_syscall 0.2.5", "smallvec", "thread-id", "winapi 0.3.9", @@ -2086,8 +2107,8 @@ dependencies = [ "pest", "pest_meta", "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -2151,15 +2172,15 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" [[package]] name = "pin-utils" @@ -2234,8 +2255,8 @@ checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" dependencies = [ "proc-macro-error-attr", "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", "version_check", ] @@ -2246,8 +2267,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", "syn-mid", "version_check", ] @@ -2339,9 +2360,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2 1.0.24", ] @@ -2387,7 +2408,7 @@ checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", "rand_chacha 0.3.0", - "rand_core 0.6.1", + "rand_core 0.6.2", "rand_hc 0.3.0", ] @@ -2418,7 +2439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", - "rand_core 0.6.1", + "rand_core 0.6.2", ] [[package]] @@ -2447,9 +2468,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom 0.2.2", ] @@ -2478,7 +2499,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core 0.6.1", + "rand_core 0.6.2", ] [[package]] @@ -2587,7 +2608,7 @@ checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" dependencies = [ "crossbeam-channel 0.5.0", "crossbeam-deque 0.8.0", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.3", "lazy_static", "num_cpus", ] @@ -2609,9 +2630,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" dependencies = [ "bitflags", ] @@ -2623,7 +2644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom 0.2.2", - "redox_syscall 0.2.4", + "redox_syscall 0.2.5", ] [[package]] @@ -2672,7 +2693,7 @@ dependencies = [ "indoc", "inkwell", "inlinable_string", - "libloading", + "libloading 0.6.7", "maplit", "pretty_assertions", "quickcheck", @@ -2746,7 +2767,7 @@ dependencies = [ "inkwell", "inlinable_string", "libc", - "libloading", + "libloading 0.6.7", "maplit", "pretty_assertions", "quickcheck", @@ -2918,7 +2939,7 @@ dependencies = [ "inlinable_string", "itertools 0.9.0", "libc", - "libloading", + "libloading 0.6.7", "maplit", "object 0.22.0", "pretty_assertions", @@ -3218,8 +3239,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db9dfbf470021de34cfaf6983067f460ea19164934a7c2d4b92eec0968eb95f1" dependencies = [ - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -3266,9 +3287,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.123" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" dependencies = [ "serde_derive", ] @@ -3297,20 +3318,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.123" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] name = "serde_json" -version = "1.0.62" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" dependencies = [ "itoa", "ryu", @@ -3335,8 +3356,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -3387,7 +3408,7 @@ checksum = "421c8dc7acf5cb205b88160f8b4cc2c5cfabe210e43b2f80f009f4c1ef910f1d" dependencies = [ "andrew", "bitflags", - "dlib", + "dlib 0.4.2", "lazy_static", "memmap", "nix 0.14.1", @@ -3397,19 +3418,19 @@ dependencies = [ [[package]] name = "smithay-client-toolkit" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "316e13a3eb853ce7bf72ad3530dc186cb2005c57c521ef5f4ada5ee4eed74de6" +checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" dependencies = [ "bitflags", - "dlib", + "dlib 0.4.2", "lazy_static", "log", "memmap2", "nix 0.18.0", - "wayland-client 0.28.3", + "wayland-client 0.28.5", "wayland-cursor", - "wayland-protocols 0.28.3", + "wayland-protocols 0.28.5", ] [[package]] @@ -3418,8 +3439,8 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06384dfaf645908220d976ae24ed39f6cf92efecb0225ea0a948e403014de527" dependencies = [ - "smithay-client-toolkit 0.12.2", - "wayland-client 0.28.3", + "smithay-client-toolkit 0.12.3", + "wayland-client 0.28.5", ] [[package]] @@ -3440,8 +3461,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -3517,12 +3538,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "ed22b90a0e734a23a7610f4283ac9e5acfb96cbb30dfefa540d66f866f1c09c5" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", + "quote 1.0.9", "unicode-xid 0.2.1", ] @@ -3533,8 +3554,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baa8e7560a164edb1621a55d18a0c59abf49d360f47aa7b821061dd7eea7fac9" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -3544,8 +3565,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", "unicode-xid 0.2.1", ] @@ -3564,7 +3585,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "rand 0.8.3", - "redox_syscall 0.2.4", + "redox_syscall 0.2.5", "remove_dir_all", "winapi 0.3.9", ] @@ -3590,7 +3611,7 @@ dependencies = [ "inkwell", "inlinable_string", "libc", - "libloading", + "libloading 0.6.7", "maplit", "quickcheck", "quickcheck_macros", @@ -3626,22 +3647,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", ] [[package]] @@ -3672,9 +3693,9 @@ checksum = "7572415bd688d401c52f6e36f4c8e805b9ae1622619303b9fa835d531db0acae" [[package]] name = "tinytemplate" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ada8616fad06a2d0c455adc530de4ef57605a8120cc65da9653e0e9623ca74" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", @@ -3690,7 +3711,7 @@ dependencies = [ "fnv", "memchr", "num_cpus", - "pin-project-lite 0.1.11", + "pin-project-lite 0.1.12", "slab", ] @@ -3705,12 +3726,12 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.23" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3" +checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" dependencies = [ "cfg-if 1.0.0", - "pin-project-lite 0.2.4", + "pin-project-lite 0.2.6", "tracing-core", ] @@ -3725,9 +3746,9 @@ dependencies = [ [[package]] name = "ttf-parser" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e7994fc4aed0ee366a4b0d01562c8a7cd5a5017088bceb6921b0c8c538f34e" +checksum = "85e00391c1f3d171490a3f8bd79999b0002ae38d3da0d6a3a306c754b053d71b" [[package]] name = "twox-hash" @@ -3887,9 +3908,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.70" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" +checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -3897,24 +3918,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.70" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" +checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" dependencies = [ "bumpalo", "lazy_static", "log", "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" +checksum = "8e67a5806118af01f0d9045915676b22aaebecf4178ae7021bc171dab0b897ab" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -3924,32 +3945,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.70" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" +checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" dependencies = [ - "quote 1.0.8", + "quote 1.0.9", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.70" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" +checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", - "syn 1.0.60", + "quote 1.0.9", + "syn 1.0.61", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.70" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" +checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" [[package]] name = "wasmparser" @@ -3976,18 +3997,18 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.28.3" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbdbe01d03b2267809f3ed99495b37395387fde789e0f2ebb78e8b43f75b6d7" +checksum = "06ca44d86554b85cf449f1557edc6cc7da935cc748c8e4bf1c507cbd43bae02c" dependencies = [ "bitflags", "downcast-rs", "libc", - "nix 0.18.0", + "nix 0.20.0", "scoped-tls", - "wayland-commons 0.28.3", - "wayland-scanner 0.28.3", - "wayland-sys 0.28.3", + "wayland-commons 0.28.5", + "wayland-scanner 0.28.5", + "wayland-sys 0.28.5", ] [[package]] @@ -4002,24 +4023,24 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.28.3" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480450f76717edd64ad04a4426280d737fc3d10a236b982df7b1aee19f0e2d56" +checksum = "8bd75ae380325dbcff2707f0cd9869827ea1d2d6d534cff076858d3f0460fd5a" dependencies = [ - "nix 0.18.0", + "nix 0.20.0", "once_cell", "smallvec", - "wayland-sys 0.28.3", + "wayland-sys 0.28.5", ] [[package]] name = "wayland-cursor" -version = "0.28.3" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6eb122c160223a7660feeaf949d0100281d1279acaaed3720eb3c9894496e5f" +checksum = "b37e5455ec72f5de555ec39b5c3704036ac07c2ecd50d0bffe02d5fe2d4e65ab" dependencies = [ - "nix 0.18.0", - "wayland-client 0.28.3", + "nix 0.20.0", + "wayland-client 0.28.5", "xcursor", ] @@ -4037,14 +4058,14 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.28.3" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "319a82b4d3054dd25acc32d9aee0f84fa95b63bc983fffe4703b6b8d47e01a30" +checksum = "95df3317872bcf9eec096c864b69aa4769a1d5d6291a5b513f8ba0af0efbd52c" dependencies = [ "bitflags", - "wayland-client 0.28.3", - "wayland-commons 0.28.3", - "wayland-scanner 0.28.3", + "wayland-client 0.28.5", + "wayland-commons 0.28.5", + "wayland-scanner 0.28.5", ] [[package]] @@ -4060,12 +4081,12 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.28.3" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7010ba5767b3fcd350decc59055390b4ebe6bd1b9279a9feb1f1888987f1133d" +checksum = "389d680d7bd67512dc9c37f39560224327038deb0f0e8d33f870900441b68720" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.8", + "quote 1.0.9", "xml-rs", ] @@ -4075,26 +4096,26 @@ version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4" dependencies = [ - "dlib", + "dlib 0.4.2", "lazy_static", ] [[package]] name = "wayland-sys" -version = "0.28.3" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6793834e0c35d11fd96a97297abe03d37be627e1847da52e17d7e0e3b51cc099" +checksum = "2907bd297eef464a95ba9349ea771611771aa285b932526c633dc94d5400a8e2" dependencies = [ - "dlib", + "dlib 0.5.0", "lazy_static", "pkg-config", ] [[package]] name = "web-sys" -version = "0.3.47" +version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" +checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" dependencies = [ "js-sys", "wasm-bindgen", @@ -4358,6 +4379,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb" dependencies = [ "proc-macro2 1.0.24", - "syn 1.0.60", + "syn 1.0.61", "synstructure", ] From 8aa0006e891943a9a806df8a0444cab090690af1 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 8 Mar 2021 12:25:55 +0100 Subject: [PATCH 02/74] done updating editor dependencies to latest version --- Cargo.lock | 974 ++++++++++++---------- editor/Cargo.toml | 22 +- editor/editor-ideas.md | 3 +- editor/src/editor/main.rs | 38 +- editor/src/graphics/lowlevel/ortho.rs | 7 +- editor/src/graphics/lowlevel/pipelines.rs | 42 +- editor/src/graphics/lowlevel/vertex.rs | 8 +- 7 files changed, 588 insertions(+), 506 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6ddba9981..2994b76f3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a933731feda8b460bdad9a9e43bb386baba6ec593d2bc19716ef3c75c09085c" dependencies = [ "ab_glyph_rasterizer", - "owned_ttf_parser", + "owned_ttf_parser 0.12.0", ] [[package]] @@ -31,6 +31,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" + [[package]] name = "aho-corasick" version = "0.7.15" @@ -42,24 +48,17 @@ dependencies = [ [[package]] name = "andrew" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" +checksum = "8c4afb09dd642feec8408e33f92f3ffc4052946f6b20f32fb99c1f58cd4fa7cf" dependencies = [ "bitflags", - "line_drawing", - "rusttype 0.7.9", + "rusttype", "walkdir", "xdg", "xml-rs", ] -[[package]] -name = "android_log-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8052e2d8aabbb8d556d6abbcce2a22b9590996c5f849b9c7ce4544a2e3b984e" - [[package]] name = "ansi_term" version = "0.11.0" @@ -91,7 +90,7 @@ dependencies = [ name = "arena-pool" version = "0.1.0" dependencies = [ - "pretty_assertions", + "pretty_assertions 0.5.1", ] [[package]] @@ -270,13 +269,12 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "calloop" -version = "0.4.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160" +checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c" dependencies = [ - "mio", - "mio-extras", - "nix 0.14.1", + "log", + "nix 0.18.0", ] [[package]] @@ -310,14 +308,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "cgmath" -version = "0.17.0" +name = "cfg_aliases" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cgmath" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" dependencies = [ - "approx 0.3.2", + "approx 0.4.0", "num-traits", - "rand 0.6.5", ] [[package]] @@ -381,14 +384,15 @@ dependencies = [ [[package]] name = "cocoa" -version = "0.20.2" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c49e86fc36d5704151f5996b7b3795385f50ce09e3be0f47a0cfde869681cf8" +checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" dependencies = [ "bitflags", "block", - "core-foundation 0.7.0", - "core-graphics", + "cocoa-foundation", + "core-foundation 0.9.1", + "core-graphics 0.22.2", "foreign-types", "libc", "objc", @@ -504,6 +508,19 @@ dependencies = [ "libc", ] +[[package]] +name = "core-graphics" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" +dependencies = [ + "bitflags", + "core-foundation 0.9.1", + "core-graphics-types", + "foreign-types", + "libc", +] + [[package]] name = "core-graphics-types" version = "0.1.1" @@ -524,7 +541,7 @@ checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" dependencies = [ "cfg-if 0.1.10", "core-foundation-sys 0.7.0", - "core-graphics", + "core-graphics 0.19.2", "libc", "objc", ] @@ -713,6 +730,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctor" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19" +dependencies = [ + "quote 1.0.9", + "syn 1.0.61", +] + [[package]] name = "d3d12" version = "0.3.2" @@ -724,6 +751,41 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.24", + "quote 1.0.9", + "strsim", + "syn 1.0.61", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote 1.0.9", + "syn 1.0.61", +] + [[package]] name = "derivative" version = "2.2.0" @@ -815,7 +877,7 @@ dependencies = [ "fs_extra", "handlebars", "maplit", - "pretty_assertions", + "pretty_assertions 0.5.1", "pulldown-cmark", "roc_builtins", "roc_can", @@ -856,9 +918,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.7.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" dependencies = [ "atty", "humantime", @@ -1084,9 +1146,9 @@ dependencies = [ [[package]] name = "gfx-auxil" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cd956b592970f08545b9325b87580eb95a51843b6f39da27b8667fec1a1216" +checksum = "e7b33ecf067f2117668d91c9b0f2e5f223ebd1ffec314caa2f3de27bb580186d" dependencies = [ "fxhash", "gfx-hal", @@ -1095,9 +1157,9 @@ dependencies = [ [[package]] name = "gfx-backend-dx11" -version = "0.6.17" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b43f06089866bdffe59b5a6801022c86b74d2c1dd28940a9cf301d3d014fbc" +checksum = "f851d03c2e8f117e3702bf41201a4fafa447d5cb1276d5375870ae7573d069dd" dependencies = [ "arrayvec", "bitflags", @@ -1105,7 +1167,7 @@ dependencies = [ "gfx-hal", "libloading 0.6.7", "log", - "parking_lot 0.11.1", + "parking_lot", "range-alloc", "raw-window-handle", "smallvec", @@ -1117,9 +1179,9 @@ dependencies = [ [[package]] name = "gfx-backend-dx12" -version = "0.6.13" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375014deed24d76b03604736dd899f0925158a1a96db90cbefb9cce070f71af7" +checksum = "36dc6ba2b7647e2c2b27b8f74ff5ccdd53c703776588eee5b1de515fdcbd6bc9" dependencies = [ "arrayvec", "bit-set", @@ -1128,6 +1190,7 @@ dependencies = [ "gfx-auxil", "gfx-hal", "log", + "parking_lot", "range-alloc", "raw-window-handle", "smallvec", @@ -1137,9 +1200,9 @@ dependencies = [ [[package]] name = "gfx-backend-empty" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2085227c12b78f6657a900c829f2d0deb46a9be3eaf86844fde263cdc218f77c" +checksum = "9f07ef26a65954cfdd7b4c587f485100d1bb3b0bd6a51b02d817d6c87cca7a91" dependencies = [ "gfx-hal", "log", @@ -1147,10 +1210,33 @@ dependencies = [ ] [[package]] -name = "gfx-backend-metal" -version = "0.6.5" +name = "gfx-backend-gl" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273d60d5207f96d99e0d11d0718995f67e56533a9df1444d83baf787f4c3cb32" +checksum = "c6717c50ab601efe4a669bfb44db615e3888695ac8263222aeaa702642b9fbc2" +dependencies = [ + "arrayvec", + "bitflags", + "gfx-auxil", + "gfx-hal", + "glow", + "js-sys", + "khronos-egl", + "libloading 0.6.7", + "log", + "naga", + "parking_lot", + "raw-window-handle", + "spirv_cross", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gfx-backend-metal" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc54b456ece69ef49f8893269ebf24ac70969ed34ba2719c3f3abcc8fbff14e" dependencies = [ "arrayvec", "bitflags", @@ -1160,23 +1246,22 @@ dependencies = [ "foreign-types", "gfx-auxil", "gfx-hal", - "lazy_static", "log", "metal", + "naga", "objc", - "parking_lot 0.11.1", + "parking_lot", "range-alloc", "raw-window-handle", - "smallvec", "spirv_cross", "storage-map", ] [[package]] name = "gfx-backend-vulkan" -version = "0.6.5" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3a63cf61067a09b7d1ac480af3cb2ae0c5ede5bed294607bbd814cb1666c45" +checksum = "dabe88b1a5c91e0f969b441cc57e70364858066e4ba937deeb62065654ef9bd9" dependencies = [ "arrayvec", "ash", @@ -1184,48 +1269,25 @@ dependencies = [ "core-graphics-types", "gfx-hal", "inplace_it", - "lazy_static", "log", + "naga", "objc", + "parking_lot", "raw-window-handle", "smallvec", "winapi 0.3.9", - "x11", -] - -[[package]] -name = "gfx-descriptor" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8c7afcd000f279d541a490e27117e61037537279b9342279abf4938fe60c6b" -dependencies = [ - "arrayvec", - "fxhash", - "gfx-hal", - "log", ] [[package]] name = "gfx-hal" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d0754f5b7a43915fd7466883b2d1bb0800d7cc4609178d0b27bf143b9e5123" +checksum = "c1d9cc8d3b573dda62d0baca4f02e0209786e22c562caff001d77c389008781d" dependencies = [ "bitflags", + "naga", "raw-window-handle", -] - -[[package]] -name = "gfx-memory" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dccdda5d2b39412f4ca2cb15c70b5a82783a86b0606f5e985342754c8ed88f05" -dependencies = [ - "bit-set", - "fxhash", - "gfx-hal", - "log", - "slab", + "thiserror", ] [[package]] @@ -1234,6 +1296,18 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" +[[package]] +name = "glow" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "072136d2c3783f3a92f131acb227bc806d3886278e2a4dc1e9990ec89ef9e70b" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "glyph_brush" version = "0.7.2" @@ -1243,7 +1317,7 @@ dependencies = [ "glyph_brush_draw_cache", "glyph_brush_layout", "log", - "ordered-float 2.1.1", + "ordered-float", "rustc-hash", "twox-hash", ] @@ -1273,6 +1347,47 @@ dependencies = [ "xi-unicode", ] +[[package]] +name = "gpu-alloc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7724b9aef57ea36d70faf54e0ee6265f86e41de16bed8333efdeab5b00e16b" +dependencies = [ + "bitflags", + "gpu-alloc-types", + "tracing", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5" +dependencies = [ + "bitflags", +] + +[[package]] +name = "gpu-descriptor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a70f1e87a3840ed6a3e99e02c2b861e4dbdf26f0d07e38f42ea5aff46cfce2" +dependencies = [ + "bitflags", + "gpu-descriptor-types", + "hashbrown", + "tracing", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126" +dependencies = [ + "bitflags", +] + [[package]] name = "half" version = "1.7.1" @@ -1288,7 +1403,7 @@ dependencies = [ "log", "pest", "pest_derive", - "quick-error 2.0.0", + "quick-error", "serde", "serde_json", ] @@ -1298,6 +1413,9 @@ name = "hashbrown" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash", +] [[package]] name = "heck" @@ -1319,12 +1437,15 @@ dependencies = [ [[package]] name = "humantime" -version = "1.3.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error 1.2.3", -] +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "im" @@ -1335,7 +1456,21 @@ dependencies = [ "bitmaps", "rand_core 0.5.1", "rand_xoshiro", - "sized-chunks", + "sized-chunks 0.5.3", + "typenum", + "version_check", +] + +[[package]] +name = "im" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "111c1983f3c5bb72732df25cddacee9b546d08325fb584b5ebd38148be7b0246" +dependencies = [ + "bitmaps", + "rand_core 0.5.1", + "rand_xoshiro", + "sized-chunks 0.6.4", "typenum", "version_check", ] @@ -1349,7 +1484,21 @@ dependencies = [ "bitmaps", "rand_core 0.5.1", "rand_xoshiro", - "sized-chunks", + "sized-chunks 0.5.3", + "typenum", + "version_check", +] + +[[package]] +name = "im-rc" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ca8957e71f04a205cb162508f9326aea04676c8dfd0711220190d6b83664f3f" +dependencies = [ + "bitmaps", + "rand_core 0.5.1", + "rand_xoshiro", + "sized-chunks 0.6.4", "typenum", "version_check", ] @@ -1374,6 +1523,15 @@ dependencies = [ "proc-macro-hack", ] +[[package]] +name = "indoc" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136" +dependencies = [ + "unindent", +] + [[package]] name = "indoc-impl" version = "0.3.6" @@ -1397,7 +1555,7 @@ dependencies = [ "libc", "llvm-sys", "once_cell", - "parking_lot 0.11.1", + "parking_lot", "regex", ] @@ -1482,9 +1640,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.48" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" dependencies = [ "wasm-bindgen", ] @@ -1499,6 +1657,16 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "khronos-egl" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b19cc4a81304db2a0ad69740e83cdc3a9364e3f9bd6d88a87288a4c2deec927b" +dependencies = [ + "libc", + "libloading 0.6.7", +] + [[package]] name = "lazy-bytes-cast" version = "5.0.1" @@ -1543,15 +1711,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "line_drawing" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" -dependencies = [ - "num-traits", -] - [[package]] name = "linked-hash-map" version = "0.5.4" @@ -1571,15 +1730,6 @@ dependencies = [ "semver", ] -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard", -] - [[package]] name = "lock_api" version = "0.4.2" @@ -1625,16 +1775,6 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" -[[package]] -name = "memmap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -dependencies = [ - "libc", - "winapi 0.3.9", -] - [[package]] name = "memmap2" version = "0.1.0" @@ -1664,9 +1804,9 @@ dependencies = [ [[package]] name = "metal" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4e8a431536529327e28c9ba6992f2cb0c15d4222f0602a16e6d7695ff3bccf" +checksum = "4598d719460ade24c7d91f335daf055bf2a7eec030728ce751814c50cdd6a26c" dependencies = [ "bitflags", "block", @@ -1731,48 +1871,64 @@ dependencies = [ [[package]] name = "naga" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0873deb76cf44b7454fba7b2ba6a89d3de70c08aceffd2c489379b3d9d08e661" +checksum = "05089b2acdf0e6a962cdbf5e328402345a27f59fcde1a59fe97a73e8149d416f" dependencies = [ + "bit-set", "bitflags", "fxhash", "log", "num-traits", + "petgraph", "spirv_headers", "thiserror", ] [[package]] name = "ndk" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a356cafe20aee088789830bfea3a61336e84ded9e545e00d3869ce95dcb80c" +checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" dependencies = [ "jni-sys", "ndk-sys", "num_enum", + "thiserror", ] [[package]] name = "ndk-glue" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1730ee2e3de41c3321160a6da815f008c4006d71b095880ea50e17cf52332b8" +checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" dependencies = [ - "android_log-sys", "lazy_static", "libc", "log", "ndk", + "ndk-macro", "ndk-sys", ] [[package]] -name = "ndk-sys" -version = "0.1.0" +name = "ndk-macro" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2820aca934aba5ed91c79acc72b6a44048ceacc5d36c035ed4e051f12d887d" +checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2 1.0.24", + "quote 1.0.9", + "syn 1.0.61", +] + +[[package]] +name = "ndk-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" [[package]] name = "net2" @@ -1785,19 +1941,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "nix" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" -dependencies = [ - "bitflags", - "cc", - "cfg-if 0.1.10", - "libc", - "void", -] - [[package]] name = "nix" version = "0.18.0" @@ -1960,15 +2103,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -[[package]] -name = "ordered-float" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" -dependencies = [ - "num-traits", -] - [[package]] name = "ordered-float" version = "2.1.1" @@ -1978,13 +2112,31 @@ dependencies = [ "num-traits", ] +[[package]] +name = "output_vt100" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" +dependencies = [ + "ttf-parser 0.6.2", +] + [[package]] name = "owned_ttf_parser" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3c7a20e3f122223e68eef6ca58e39bc1ea8a1d83418ba4c2c1ba189d2ee355" dependencies = [ - "ttf-parser", + "ttf-parser 0.12.0", ] [[package]] @@ -2021,16 +2173,6 @@ dependencies = [ "syn 1.0.61", ] -[[package]] -name = "parking_lot" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" -dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.7.2", -] - [[package]] name = "parking_lot" version = "0.11.1" @@ -2038,22 +2180,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", - "lock_api 0.4.2", - "parking_lot_core 0.8.3", -] - -[[package]] -name = "parking_lot_core" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" -dependencies = [ - "cfg-if 0.1.10", - "cloudabi", - "libc", - "redox_syscall 0.1.57", - "smallvec", - "winapi 0.3.9", + "lock_api", + "parking_lot_core", ] [[package]] @@ -2238,6 +2366,18 @@ dependencies = [ "difference", ] +[[package]] +name = "pretty_assertions" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" +dependencies = [ + "ansi_term", + "ctor", + "difference", + "output_vt100", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -2314,12 +2454,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quick-error" version = "2.0.0" @@ -2338,6 +2472,17 @@ dependencies = [ "rand_core 0.4.2", ] +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger 0.8.3", + "log", + "rand 0.8.3", +] + [[package]] name = "quickcheck_macros" version = "0.8.0" @@ -2349,6 +2494,17 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "quickcheck_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.9", + "syn 1.0.61", +] + [[package]] name = "quote" version = "0.6.13" @@ -2688,16 +2844,16 @@ name = "roc_build" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", - "indoc", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", "inkwell", "inlinable_string", "libloading 0.6.7", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_builtins", "roc_can", "roc_collections", @@ -2721,11 +2877,11 @@ dependencies = [ name = "roc_builtins" version = "0.1.0" dependencies = [ - "indoc", + "indoc 0.3.6", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_collections", "roc_module", "roc_region", @@ -2737,14 +2893,14 @@ name = "roc_can" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", - "indoc", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", "inlinable_string", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_collections", "roc_module", "roc_parse", @@ -2761,17 +2917,17 @@ dependencies = [ "bumpalo", "clap 3.0.0-beta.1", "const_format", - "im", - "im-rc", - "indoc", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", "inkwell", "inlinable_string", "libc", "libloading 0.6.7", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_build", "roc_builtins", "roc_can", @@ -2805,8 +2961,8 @@ name = "roc_collections" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", + "im 14.3.0", + "im-rc 14.3.0", "wyhash", ] @@ -2814,11 +2970,11 @@ dependencies = [ name = "roc_constrain" version = "0.1.0" dependencies = [ - "indoc", + "indoc 0.3.6", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_builtins", "roc_can", "roc_collections", @@ -2839,12 +2995,12 @@ dependencies = [ "colored", "copypasta", "criterion", - "env_logger 0.7.1", + "env_logger 0.8.3", "futures", "glyph_brush", - "im", - "im-rc", - "indoc", + "im 15.0.0", + "im-rc 15.0.0", + "indoc 1.0.3", "inlinable_string", "libc", "log", @@ -2853,9 +3009,9 @@ dependencies = [ "palette", "pest", "pest_derive", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.6.1", + "quickcheck 1.0.3", + "quickcheck_macros 1.0.0", "rand 0.8.3", "roc_can", "roc_collections", @@ -2879,14 +3035,14 @@ name = "roc_fmt" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", - "indoc", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", "inlinable_string", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_collections", "roc_module", "roc_parse", @@ -2899,16 +3055,16 @@ version = "0.1.0" dependencies = [ "bumpalo", "either", - "im", - "im-rc", - "indoc", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", "inkwell", "inlinable_string", "libc", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_build", "roc_builtins", "roc_can", @@ -2933,18 +3089,18 @@ name = "roc_gen_dev" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", - "indoc", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", "inlinable_string", "itertools 0.9.0", "libc", "libloading 0.6.7", "maplit", "object 0.22.0", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_build", "roc_builtins", "roc_can", @@ -2972,14 +3128,14 @@ version = "0.1.0" dependencies = [ "bumpalo", "crossbeam", - "indoc", + "indoc 0.3.6", "inlinable_string", "maplit", "num_cpus", - "parking_lot 0.11.1", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "parking_lot", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_builtins", "roc_can", "roc_collections", @@ -3002,11 +3158,11 @@ name = "roc_module" version = "0.1.0" dependencies = [ "bumpalo", - "indoc", + "indoc 0.3.6", "inlinable_string", "lazy_static", "maplit", - "pretty_assertions", + "pretty_assertions 0.5.1", "roc_collections", "roc_region", ] @@ -3016,12 +3172,12 @@ name = "roc_mono" version = "0.1.0" dependencies = [ "bumpalo", - "indoc", + "indoc 0.3.6", "linked-hash-map", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_builtins", "roc_can", "roc_collections", @@ -3043,11 +3199,11 @@ version = "0.1.0" dependencies = [ "bumpalo", "encode_unicode", - "indoc", + "indoc 0.3.6", "inlinable_string", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_collections", "roc_module", "roc_region", @@ -3057,12 +3213,12 @@ dependencies = [ name = "roc_problem" version = "0.1.0" dependencies = [ - "indoc", + "indoc 0.3.6", "inlinable_string", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_collections", "roc_module", "roc_parse", @@ -3079,14 +3235,14 @@ version = "0.1.0" dependencies = [ "bumpalo", "distance", - "im", - "im-rc", - "indoc", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", "inlinable_string", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_builtins", "roc_can", "roc_collections", @@ -3106,11 +3262,11 @@ name = "roc_solve" version = "0.1.0" dependencies = [ "bumpalo", - "indoc", + "indoc 0.3.6", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_builtins", "roc_can", "roc_collections", @@ -3136,12 +3292,12 @@ dependencies = [ name = "roc_types" version = "0.1.0" dependencies = [ - "indoc", + "indoc 0.3.6", "inlinable_string", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_collections", "roc_module", "roc_region", @@ -3152,11 +3308,11 @@ dependencies = [ name = "roc_unify" version = "0.1.0" dependencies = [ - "indoc", + "indoc 0.3.6", "maplit", - "pretty_assertions", - "quickcheck", - "quickcheck_macros", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_collections", "roc_module", "roc_types", @@ -3194,22 +3350,12 @@ dependencies = [ [[package]] name = "rusttype" -version = "0.7.9" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" +checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59" dependencies = [ - "rusttype 0.8.3", -] - -[[package]] -name = "rusttype" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f61411055101f7b60ecf1041d87fb74205fb20b0c7a723f07ef39174cf6b4c0" -dependencies = [ - "approx 0.3.2", - "ordered-float 1.1.1", - "stb_truetype", + "ab_glyph_rasterizer", + "owned_ttf_parser 0.6.0", ] [[package]] @@ -3345,7 +3491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" dependencies = [ "lazy_static", - "parking_lot 0.11.1", + "parking_lot", "serial_test_derive", ] @@ -3388,49 +3534,51 @@ dependencies = [ "typenum", ] +[[package]] +name = "sized-chunks" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e65d6a9f13cd78f361ea5a2cf53a45d67cdda421ba0316b9be101560f3d207" +dependencies = [ + "bitmaps", + "typenum", +] + [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +[[package]] +name = "slotmap" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c46a3482db8f247956e464d783693ece164ca056e6e67563ee5505bdb86452cd" + [[package]] name = "smallvec" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" -[[package]] -name = "smithay-client-toolkit" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421c8dc7acf5cb205b88160f8b4cc2c5cfabe210e43b2f80f009f4c1ef910f1d" -dependencies = [ - "andrew", - "bitflags", - "dlib 0.4.2", - "lazy_static", - "memmap", - "nix 0.14.1", - "wayland-client 0.23.6", - "wayland-protocols 0.23.6", -] - [[package]] name = "smithay-client-toolkit" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" dependencies = [ + "andrew", "bitflags", + "calloop", "dlib 0.4.2", "lazy_static", "log", "memmap2", "nix 0.18.0", - "wayland-client 0.28.5", + "wayland-client", "wayland-cursor", - "wayland-protocols 0.28.5", + "wayland-protocols", ] [[package]] @@ -3439,8 +3587,8 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06384dfaf645908220d976ae24ed39f6cf92efecb0225ea0a948e403014de527" dependencies = [ - "smithay-client-toolkit 0.12.3", - "wayland-client 0.28.5", + "smithay-client-toolkit", + "wayland-client", ] [[package]] @@ -3467,9 +3615,9 @@ dependencies = [ [[package]] name = "spirv_cross" -version = "0.22.2" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ebd49af36be83ecd6290b57147e2a0e26145b832634b17146d934b197ca3713" +checksum = "60647fadbf83c4a72f0d7ea67a7ca3a81835cf442b8deae5c134c3e0055b2e14" dependencies = [ "cc", "js-sys", @@ -3492,22 +3640,13 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stb_truetype" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51" -dependencies = [ - "byteorder", -] - [[package]] name = "storage-map" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "418bb14643aa55a7841d5303f72cf512cfb323b8cc221d51580500a1ca75206c" dependencies = [ - "lock_api 0.4.2", + "lock_api", ] [[package]] @@ -3605,16 +3744,16 @@ version = "0.1.0" dependencies = [ "bumpalo", "either", - "im", - "im-rc", - "indoc", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", "inkwell", "inlinable_string", "libc", "libloading 0.6.7", "maplit", - "quickcheck", - "quickcheck_macros", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", "roc_build", "roc_builtins", "roc_can", @@ -3732,9 +3871,21 @@ checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" dependencies = [ "cfg-if 1.0.0", "pin-project-lite 0.2.6", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.9", + "syn 1.0.61", +] + [[package]] name = "tracing-core" version = "0.1.17" @@ -3744,6 +3895,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "ttf-parser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" + [[package]] name = "ttf-parser" version = "0.12.0" @@ -3868,12 +4025,6 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "vte" version = "0.3.3" @@ -3908,9 +4059,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.71" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -3918,9 +4069,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.71" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" dependencies = [ "bumpalo", "lazy_static", @@ -3933,9 +4084,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.21" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e67a5806118af01f0d9045915676b22aaebecf4178ae7021bc171dab0b897ab" +checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -3945,9 +4096,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.71" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" dependencies = [ "quote 1.0.9", "wasm-bindgen-macro-support", @@ -3955,9 +4106,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.71" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", @@ -3968,9 +4119,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.71" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" [[package]] name = "wasmparser" @@ -3978,23 +4129,6 @@ version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6" -[[package]] -name = "wayland-client" -version = "0.23.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda" -dependencies = [ - "bitflags", - "calloop", - "downcast-rs", - "libc", - "mio", - "nix 0.14.1", - "wayland-commons 0.23.6", - "wayland-scanner 0.23.6", - "wayland-sys 0.23.6", -] - [[package]] name = "wayland-client" version = "0.28.5" @@ -4006,19 +4140,9 @@ dependencies = [ "libc", "nix 0.20.0", "scoped-tls", - "wayland-commons 0.28.5", - "wayland-scanner 0.28.5", - "wayland-sys 0.28.5", -] - -[[package]] -name = "wayland-commons" -version = "0.23.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" -dependencies = [ - "nix 0.14.1", - "wayland-sys 0.23.6", + "wayland-commons", + "wayland-scanner", + "wayland-sys", ] [[package]] @@ -4030,7 +4154,7 @@ dependencies = [ "nix 0.20.0", "once_cell", "smallvec", - "wayland-sys 0.28.5", + "wayland-sys", ] [[package]] @@ -4040,22 +4164,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b37e5455ec72f5de555ec39b5c3704036ac07c2ecd50d0bffe02d5fe2d4e65ab" dependencies = [ "nix 0.20.0", - "wayland-client 0.28.5", + "wayland-client", "xcursor", ] -[[package]] -name = "wayland-protocols" -version = "0.23.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" -dependencies = [ - "bitflags", - "wayland-client 0.23.6", - "wayland-commons 0.23.6", - "wayland-scanner 0.23.6", -] - [[package]] name = "wayland-protocols" version = "0.28.5" @@ -4063,20 +4175,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95df3317872bcf9eec096c864b69aa4769a1d5d6291a5b513f8ba0af0efbd52c" dependencies = [ "bitflags", - "wayland-client 0.28.5", - "wayland-commons 0.28.5", - "wayland-scanner 0.28.5", -] - -[[package]] -name = "wayland-scanner" -version = "0.23.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "xml-rs", + "wayland-client", + "wayland-commons", + "wayland-scanner", ] [[package]] @@ -4090,16 +4191,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "wayland-sys" -version = "0.23.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4" -dependencies = [ - "dlib 0.4.2", - "lazy_static", -] - [[package]] name = "wayland-sys" version = "0.28.5" @@ -4113,9 +4204,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.48" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" dependencies = [ "js-sys", "wasm-bindgen", @@ -4123,20 +4214,17 @@ dependencies = [ [[package]] name = "wgpu" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991903e4c9f5b7319732b30a3d0339e27a51ea992cea22769b5f6c7f7076af6d" +checksum = "c60007fc3748278a36b458d96f86105f43aa5f0e412b15a5f934950d61ec26a9" dependencies = [ "arrayvec", - "futures", - "gfx-backend-vulkan", "js-sys", - "objc", - "parking_lot 0.11.1", + "naga", + "parking_lot", "raw-window-handle", "smallvec", "tracing", - "typed-arena", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -4146,24 +4234,26 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea487deeae90e06d77eb8e6cef945247774e7c0a0a226d238b31e90633594365" +checksum = "c89fa2cc5d72236461ac09c5be967012663e29cb62f1a972654cbf35e49dffa8" dependencies = [ "arrayvec", "bitflags", + "cfg_aliases", "copyless", "fxhash", "gfx-backend-dx11", "gfx-backend-dx12", "gfx-backend-empty", + "gfx-backend-gl", "gfx-backend-metal", "gfx-backend-vulkan", - "gfx-descriptor", "gfx-hal", - "gfx-memory", + "gpu-alloc", + "gpu-descriptor", "naga", - "parking_lot 0.11.1", + "parking_lot", "raw-window-handle", "smallvec", "thiserror", @@ -4173,23 +4263,23 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3529528e608b54838ee618c3923b0f46e6db0334cfc6c42a16cf4ceb3bdb57" +checksum = "72fa9ba80626278fd87351555c363378d08122d7601e58319be3d6fa85a87747" dependencies = [ "bitflags", ] [[package]] name = "wgpu_glyph" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27812a263e1298d3330795af62faf5daf5852beb632794acf93e4494234fc9f4" +checksum = "354c1f79e09923724a6006a6953c3946522b84f7a9ec202b7e8001f274fd4bd5" dependencies = [ + "bytemuck", "glyph_brush", "log", "wgpu", - "zerocopy", ] [[package]] @@ -4237,14 +4327,14 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winit" -version = "0.22.2" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4ccbf7ddb6627828eace16cacde80fc6bf4dbb3469f88487262a02cf8e7862" +checksum = "da4eda6fce0eb84bd0a33e3c8794eb902e1033d0a1d5a31bc4f19b1b4bbff597" dependencies = [ "bitflags", "cocoa", - "core-foundation 0.7.0", - "core-graphics", + "core-foundation 0.9.1", + "core-graphics 0.22.2", "core-video-sys", "dispatch", "instant", @@ -4257,11 +4347,11 @@ dependencies = [ "ndk-glue", "ndk-sys", "objc", - "parking_lot 0.10.2", + "parking_lot", "percent-encoding", "raw-window-handle", - "smithay-client-toolkit 0.6.6", - "wayland-client 0.23.6", + "smithay-client-toolkit", + "wayland-client", "winapi 0.3.9", "x11-dl", ] @@ -4294,16 +4384,6 @@ dependencies = [ "rand_core 0.4.2", ] -[[package]] -name = "x11" -version = "2.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ecd092546cb16f25783a5451538e73afc8d32e242648d54f4ae5459ba1e773" -dependencies = [ - "libc", - "pkg-config", -] - [[package]] name = "x11-clipboard" version = "0.5.1" diff --git a/editor/Cargo.toml b/editor/Cargo.toml index 6adf1f8e77..b2a89fa2ac 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -19,29 +19,29 @@ roc_fmt = { path = "../compiler/fmt" } # TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it ven_graph = { path = "../vendor/pathfinding" } -im = "14" # im and im-rc should always have the same version! -im-rc = "14" # im and im-rc should always have the same version! +im = "15" # im and im-rc should always have the same version! +im-rc = "15" # im and im-rc should always have the same version! bumpalo = { version = "3.2", features = ["collections"] } inlinable_string = "0.1" arraystring = "0.3.0" libc = "0.2" page_size = "0.4" -winit = "0.22" -wgpu = "0.6" +winit = "0.24" +wgpu = "0.7" glyph_brush = "0.7" log = "0.4" zerocopy = "0.3" -env_logger = "0.7" +env_logger = "0.8" futures = "0.3" -wgpu_glyph = "0.10" -cgmath = "0.17.0" +wgpu_glyph = "0.11" +cgmath = "0.18.0" snafu = { version = "0.6", features = ["backtraces"] } colored = "2" pest = "2.1" pest_derive = "2.1" ropey = "1.2.0" copypasta = "0.7.1" -indoc = "0.3.3" +indoc = "1.0" palette = "0.5" [dependencies.bytemuck] @@ -49,10 +49,10 @@ version = "1.4" features = ["derive"] [dev-dependencies] -pretty_assertions = "0.5.1" +pretty_assertions = "0.6" maplit = "1.0.1" -quickcheck = "0.8" -quickcheck_macros = "0.8" +quickcheck = "1.0" +quickcheck_macros = "1.0" criterion = "0.3" rand = "0.8.2" diff --git a/editor/editor-ideas.md b/editor/editor-ideas.md index dadfc4c4ac..af93a2be73 100644 --- a/editor/editor-ideas.md +++ b/editor/editor-ideas.md @@ -64,7 +64,8 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe * When refactoring; - Cutting and pasting code to a new file should automatically add imports to the new file and delete them from the old file. - - Ability to link e.g. variable name in comments to actual variable name. Comment is automatically updated when variable name is changed. + - Ability to link e.g. variable name in comments to actual variable name. Comment is automatically updated when variable name is changed. + - When updating dependencies with breaking changes; show similar diffs from github projects that have succesfully updated that dependency. * Automatically create all "arms" when pattern matching after entering `when var is` based on the type. - All `when ... is` should be updated if the type is changed, e.g. adding Indigo to the Color type should add an arm everywhere where `when color is` is used. * When a function is called like `foo(false)`, the name of the boolean argument should be shown automatically; `foo(`*is_active:*`false)`. This should be done for booleans and numbers. diff --git a/editor/src/editor/main.rs b/editor/src/editor/main.rs index 5ddc264a02..cfaca9baaa 100644 --- a/editor/src/editor/main.rs +++ b/editor/src/editor/main.rs @@ -88,9 +88,9 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { adapter .request_device( &wgpu::DeviceDescriptor { + label: None, features: wgpu::Features::empty(), limits: wgpu::Limits::default(), - shader_validation: false, }, None, ) @@ -108,12 +108,12 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { let mut size = window.inner_size(); let swap_chain_descr = wgpu::SwapChainDescriptor { - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + usage: wgpu::TextureUsage::RENDER_ATTACHMENT, format: render_format, width: size.width, height: size.height, - //Immediate may cause tearing, change present_mode if this becomes a problem - present_mode: wgpu::PresentMode::Immediate, + // TODO go back to Immediate + present_mode: wgpu::PresentMode::Fifo, }; let mut swap_chain = gpu_device.create_swap_chain(&surface, &swap_chain_descr); @@ -182,12 +182,12 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { swap_chain = gpu_device.create_swap_chain( &surface, &wgpu::SwapChainDescriptor { - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + usage: wgpu::TextureUsage::RENDER_ATTACHMENT, format: render_format, width: size.width, height: size.height, - //Immediate may cause tearing, change present_mode if this becomes a problem - present_mode: wgpu::PresentMode::Immediate, + // TODO go back to Immediate + present_mode: wgpu::PresentMode::Fifo, }, ); @@ -376,7 +376,10 @@ fn draw_all_rects( render_pass.set_pipeline(&rect_resources.pipeline); render_pass.set_bind_group(0, &rect_resources.ortho.bind_group, &[]); render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..)); - render_pass.set_index_buffer(rect_buffers.index_buffer.slice(..)); + render_pass.set_index_buffer( + rect_buffers.index_buffer.slice(..), + wgpu::IndexFormat::Uint32, + ); render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1); } else { // need to begin render pass to clear screen @@ -403,25 +406,26 @@ fn begin_render_pass<'a>( }, }], depth_stencil_attachment: None, + label: None, }) } fn queue_editor_text( size: &PhysicalSize, - _editor_lines: &str, + editor_lines: &str, caret_pos: TextPos, config: &Config, glyph_brush: &mut GlyphBrush<()>, ) { let area_bounds = (size.width as f32, size.height as f32).into(); - // let code_text = Text { - // position: CODE_TXT_XY.into(), - // area_bounds, - // text: editor_lines, - // size: settings.code_font_size, - // ..Default::default() - // }; + let code_text = Text { + position: CODE_TXT_XY.into(), + area_bounds, + text: editor_lines, + size: config.code_font_size, + ..Default::default() + }; let s = format!("Ln {}, Col {}", caret_pos.line, caret_pos.column); let text = s.as_str(); @@ -438,7 +442,7 @@ fn queue_editor_text( queue_text_draw(&caret_pos_label, glyph_brush); // TODO convert to ast and render with render_ast::render_expr2 - //queue_code_text_draw(&code_text, &ed_theme.syntax_high_map, settings, glyph_brush); + queue_text_draw(&code_text, glyph_brush); } fn _queue_no_file_text( diff --git a/editor/src/graphics/lowlevel/ortho.rs b/editor/src/graphics/lowlevel/ortho.rs index 115fd24a11..c3bcab80bb 100644 --- a/editor/src/graphics/lowlevel/ortho.rs +++ b/editor/src/graphics/lowlevel/ortho.rs @@ -91,8 +91,9 @@ pub fn init_ortho( entries: &[BindGroupLayoutEntry { binding: 0, visibility: ShaderStage::VERTEX, - ty: wgpu::BindingType::UniformBuffer { - dynamic: false, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, min_binding_size: None, }, count: None, @@ -104,7 +105,7 @@ pub fn init_ortho( layout: &ortho_bind_group_layout, entries: &[wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(ortho_buffer.slice(..)), + resource: ortho_buffer.as_entire_binding(), }], label: Some("Ortho bind group"), }); diff --git a/editor/src/graphics/lowlevel/pipelines.rs b/editor/src/graphics/lowlevel/pipelines.rs index 98b692fe30..9c5084b207 100644 --- a/editor/src/graphics/lowlevel/pipelines.rs +++ b/editor/src/graphics/lowlevel/pipelines.rs @@ -21,9 +21,8 @@ pub fn make_rect_pipeline( &gpu_device, &pipeline_layout, swap_chain_descr.format, - &[Vertex::DESC], - wgpu::include_spirv!("../shaders/rect.vert.spv"), - wgpu::include_spirv!("../shaders/rect.frag.spv"), + &wgpu::include_spirv!("../shaders/rect.vert.spv"), + &wgpu::include_spirv!("../shaders/rect.frag.spv"), ); RectResources { pipeline, ortho } @@ -33,9 +32,8 @@ pub fn create_render_pipeline( device: &wgpu::Device, layout: &wgpu::PipelineLayout, color_format: wgpu::TextureFormat, - vertex_descs: &[wgpu::VertexBufferDescriptor], - vs_src: wgpu::ShaderModuleSource, - fs_src: wgpu::ShaderModuleSource, + vs_src: &wgpu::ShaderModuleDescriptor, + fs_src: &wgpu::ShaderModuleDescriptor, ) -> wgpu::RenderPipeline { let vs_module = device.create_shader_module(vs_src); let fs_module = device.create_shader_module(fs_src); @@ -43,29 +41,27 @@ pub fn create_render_pipeline( device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Render pipeline"), layout: Some(&layout), - vertex_stage: wgpu::ProgrammableStageDescriptor { + vertex: wgpu::VertexState { module: &vs_module, entry_point: "main", + buffers: &[Vertex::DESC], }, - fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + fragment: Some(wgpu::FragmentState { module: &fs_module, entry_point: "main", + targets: &[wgpu::ColorTargetState { + format: color_format, + color_blend: wgpu::BlendState::REPLACE, + alpha_blend: wgpu::BlendState::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }], }), - rasterization_state: None, - primitive_topology: wgpu::PrimitiveTopology::TriangleList, - color_states: &[wgpu::ColorStateDescriptor { - format: color_format, - color_blend: wgpu::BlendDescriptor::REPLACE, - alpha_blend: wgpu::BlendDescriptor::REPLACE, - write_mask: wgpu::ColorWrite::ALL, - }], - depth_stencil_state: None, - sample_count: 1, - sample_mask: !0, - alpha_to_coverage_enabled: false, - vertex_state: wgpu::VertexStateDescriptor { - index_format: wgpu::IndexFormat::Uint32, - vertex_buffers: vertex_descs, + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, }, }) } diff --git a/editor/src/graphics/lowlevel/vertex.rs b/editor/src/graphics/lowlevel/vertex.rs index 3b5274ce3f..1b6dd40c56 100644 --- a/editor/src/graphics/lowlevel/vertex.rs +++ b/editor/src/graphics/lowlevel/vertex.rs @@ -13,18 +13,18 @@ unsafe impl bytemuck::Zeroable for Vertex {} impl Vertex { pub const SIZE: wgpu::BufferAddress = std::mem::size_of::() as wgpu::BufferAddress; - pub const DESC: wgpu::VertexBufferDescriptor<'static> = wgpu::VertexBufferDescriptor { - stride: Self::SIZE, + pub const DESC: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout { + array_stride: Self::SIZE, step_mode: wgpu::InputStepMode::Vertex, attributes: &[ // position - wgpu::VertexAttributeDescriptor { + wgpu::VertexAttribute { offset: 0, shader_location: 0, format: wgpu::VertexFormat::Float2, }, // color - wgpu::VertexAttributeDescriptor { + wgpu::VertexAttribute { offset: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress, shader_location: 1, format: wgpu::VertexFormat::Float4, From 502946982554ad7a3c8848d668f6d97c0b447754 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 8 Mar 2021 15:48:22 +0100 Subject: [PATCH 03/74] optimize keyword parsing --- compiler/parse/src/parser.rs | 40 ++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 56c93a324b..dc3c479e3c 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -1128,31 +1128,27 @@ where ToError: Fn(Row, Col) -> E, E: 'a, { - move |arena, state: State<'a>| { - let initial_state = state.clone(); - // first parse the keyword characters - let (_, _, after_keyword_state) = ascii_string(keyword) - .parse(arena, state) - .map_err(|(_, _, state)| (NoProgress, if_error(state.line, state.column), state))?; + move |_, mut state: State<'a>| { + let width = keyword.len(); - // then we must have at least one space character - // TODO this is potentially wasteful if there are a lot of spaces - match peek_utf8_char(&after_keyword_state) { - Ok((next, _width)) if next == ' ' || next == '#' || next == '\n' => { - // give back the state after parsing the keyword, but before the whitespace - // that way we can attach the whitespace to whatever follows - Ok((MadeProgress, (), after_keyword_state)) + if !state.bytes.starts_with(keyword.as_bytes()) { + return Err((NoProgress, if_error(state.line, state.column), state)); + } + + // the next character should not be an identifier character + // to prevent treating `whence` or `iffy` as keywords + match state.bytes.get(width) { + Some(next) if *next == b' ' || *next == b'#' || *next == b'\n' => { + state.column += width as u16; + state.bytes = &state.bytes[width..]; + Ok((MadeProgress, (), state)) } - _ => { - // this is not a keyword, maybe it's `whence` or `iffy` - // anyway, make no progress and return the initial state - // so we can try something else - Err(( - NoProgress, - if_error(initial_state.line, initial_state.column), - initial_state, - )) + None => { + state.column += width as u16; + state.bytes = &state.bytes[width..]; + Ok((MadeProgress, (), state)) } + Some(_) => Err((NoProgress, if_error(state.line, state.column), state)), } } } From 905301bf963ef4261b021dfa99a0b6b6c8d2608d Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 8 Mar 2021 15:48:40 +0100 Subject: [PATCH 04/74] start giving header parsing good messages --- compiler/parse/src/expr.rs | 1 + compiler/parse/src/module.rs | 50 +++++++++++++++++++++----- compiler/parse/src/parser.rs | 58 ++++++++++++++++++++++++++++++ compiler/parse/src/test_helpers.rs | 1 - 4 files changed, 100 insertions(+), 10 deletions(-) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 18e70a736d..5780967d54 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -20,6 +20,7 @@ use roc_region::all::{Located, Region}; use crate::parser::Progress::{self, *}; +// public for testing purposes pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> { // Recursive parsers must not directly invoke functions which return (impl Parser), // as this causes rustc to stack overflow. Thus, parse_expr must be a diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 2a48a885fb..6b8ae2af12 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -1,5 +1,5 @@ use crate::ast::{Attempting, CommentOrNewline, Def, Module}; -use crate::blankspace::{space0, space0_around, space0_before, space1}; +use crate::blankspace::{space0, space0_around, space0_before, space0_e, space1}; use crate::expr::def; use crate::header::{ package_entry, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry, @@ -10,7 +10,8 @@ use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ self, ascii_char, ascii_string, backtrackable, end_of_file, loc, optional, peek_utf8_char, - peek_utf8_char_at, unexpected, unexpected_eof, Either, ParseResult, Parser, State, SyntaxError, + peek_utf8_char_at, specialize, unexpected, unexpected_eof, word1, EHeader, EProvides, Either, + ParseResult, Parser, State, SyntaxError, }; use crate::string_literal; use crate::type_annotation; @@ -382,18 +383,49 @@ fn provides_without_to<'a>() -> impl Parser< ), SyntaxError<'a>, > { + specialize( + |e, r, c| SyntaxError::Header(EHeader::Provides(e, r, c)), + provides_without_to_help(), + ) +} + +#[inline(always)] +fn provides_without_to_help<'a>() -> impl Parser< + 'a, + ( + (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), + Vec<'a, Located>>, + ), + EProvides, +> { + let min_indent = 1; and!( - and!(skip_second!(space1(1), ascii_string("provides")), space1(1)), - collection!( - ascii_char(b'['), - loc!(map!(unqualified_ident(), ExposesEntry::Exposed)), - ascii_char(b','), - ascii_char(b']'), - 1 + and!( + skip_second!( + space0_e(min_indent, EProvides::Space, EProvides::IndentKeyword), + crate::parser::keyword_e("provides", EProvides::Keyword) + ), + space0_e(min_indent, EProvides::Space, EProvides::IndentListStart) + ), + collection_e!( + word1(b'[', EProvides::ListStart), + exposes_entry(), + word1(b',', EProvides::ListEnd), + word1(b']', EProvides::ListEnd), + min_indent, + EProvides::Space, + EProvides::IndentListEnd ) ) } +fn exposes_entry<'a>() -> impl Parser<'a, Located>, EProvides> { + loc!(map!( + specialize(|_, r, c| EProvides::Identifier(r, c), unqualified_ident()), + ExposesEntry::Exposed + )) +} + #[inline(always)] fn requires<'a>() -> impl Parser< 'a, diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index dc3c479e3c..5fb31c1f0d 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -334,9 +334,27 @@ pub enum SyntaxError<'a> { Type(Type<'a>), Pattern(EPattern<'a>), Expr(EExpr<'a>), + Header(EHeader), Space(BadInputError), } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EHeader { + Provides(EProvides, Row, Col), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EProvides { + Keyword(Row, Col), + IndentKeyword(Row, Col), + IndentListStart(Row, Col), + IndentListEnd(Row, Col), + ListStart(Row, Col), + ListEnd(Row, Col), + Identifier(Row, Col), + Space(BadInputError, Row, Col), +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BadInputError { HasTab, @@ -1585,6 +1603,46 @@ macro_rules! collection { }; } +#[macro_export] +macro_rules! collection_e { + ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $space_problem:expr, $indent_problem:expr) => { + skip_first!( + $opening_brace, + skip_first!( + // We specifically allow space characters inside here, so that + // `[ ]` can be successfully parsed as an empty list, and then + // changed by the formatter back into `[]`. + // + // We don't allow newlines or comments in the middle of empty + // roc_collections because those are normally stored in an Expr, + // and there's no Expr in which to store them in an empty collection! + // + // We could change the AST to add extra storage specifically to + // support empty literals containing newlines or comments, but this + // does not seem worth even the tiniest regression in compiler performance. + zero_or_more!($crate::parser::word1(b' ', |row, col| $space_problem( + crate::parser::BadInputError::LineTooLong, + row, + col + ))), + skip_second!( + $crate::parser::sep_by0( + $delimiter, + $crate::blankspace::space0_around_ee( + $elem, + $min_indent, + $space_problem, + $indent_problem, + $indent_problem + ) + ), + $closing_brace + ) + ) + ) + }; +} + /// Parse zero or more elements between two braces (e.g. square braces). /// Elements can be optionally surrounded by spaces, and are separated by a /// delimiter (e.g comma-separated) with optionally a trailing delimiter. diff --git a/compiler/parse/src/test_helpers.rs b/compiler/parse/src/test_helpers.rs index e0ca22e765..ebfb5bb1af 100644 --- a/compiler/parse/src/test_helpers.rs +++ b/compiler/parse/src/test_helpers.rs @@ -7,7 +7,6 @@ use bumpalo::collections::Vec; use bumpalo::Bump; use roc_region::all::Located; -#[allow(dead_code)] pub fn parse_expr_with<'a>( arena: &'a Bump, input: &'a str, From 784e3ddac4a5dedee8bb2ede64426f254ffb6491 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 8 Mar 2021 16:15:38 +0100 Subject: [PATCH 05/74] change provides ... to --- compiler/parse/src/module.rs | 69 +++++++++++++++++------------------- compiler/parse/src/parser.rs | 8 +++-- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 6b8ae2af12..7815ce9615 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -3,14 +3,13 @@ use crate::blankspace::{space0, space0_around, space0_before, space0_e, space1}; use crate::expr::def; use crate::header::{ package_entry, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry, - InterfaceHeader, ModuleName, PackageEntry, PackageName, PackageOrPath, PlatformHeader, To, - TypedIdent, + InterfaceHeader, ModuleName, PackageEntry, PackageName, PlatformHeader, To, TypedIdent, }; use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ self, ascii_char, ascii_string, backtrackable, end_of_file, loc, optional, peek_utf8_char, - peek_utf8_char_at, specialize, unexpected, unexpected_eof, word1, EHeader, EProvides, Either, + peek_utf8_char_at, specialize, unexpected, unexpected_eof, word1, EHeader, EProvides, ParseResult, Parser, State, SyntaxError, }; use crate::string_literal; @@ -322,46 +321,44 @@ struct ProvidesTo<'a> { #[inline(always)] fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, SyntaxError<'a>> { + specialize( + |e, r, c| SyntaxError::Header(EHeader::Provides(e, r, c)), + provides_to_help(), + ) +} + +fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, SyntaxError<'a>> { + one_of![ + map!(lowercase_ident(), To::ExistingPackage), + map!(package_or_path(), To::NewPackage) + ] +} + +#[inline(always)] +fn provides_to_help<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> { + let min_indent = 1; + map!( and!( + provides_without_to_help(), and!( - skip_second!(backtrackable(space1(1)), ascii_string("provides")), - space1(1) - ), - and!( - collection!( - ascii_char(b'['), - loc!(map!(unqualified_ident(), ExposesEntry::Exposed)), - ascii_char(b','), - ascii_char(b']'), - 1 - ), - and!( - space1(1), - skip_first!( - ascii_string("to"), - and!( - space1(1), - loc!(either!(lowercase_ident(), package_or_path())) - ) + space0_e(min_indent, EProvides::Space, EProvides::IndentTo), + skip_first!( + crate::parser::keyword_e("to", EProvides::To), + and!( + space0_e(min_indent, EProvides::Space, EProvides::IndentPackage), + loc!(specialize( + |_, r, c| EProvides::Package(r, c), + provides_to_package() + )) ) ) ) ), |( - (before_provides_keyword, after_provides_keyword), - (entries, (before_to_keyword, (after_to_keyword, loc_to))), + ((before_provides_keyword, after_provides_keyword), entries), + (before_to_keyword, (after_to_keyword, to)), )| { - let loc_to: Located>> = loc_to; - let to_val = match loc_to.value { - Either::First(pkg) => To::ExistingPackage(pkg), - Either::Second(pkg) => To::NewPackage(pkg), - }; - let to = Located { - value: to_val, - region: loc_to.region, - }; - ProvidesTo { entries, to, @@ -402,8 +399,8 @@ fn provides_without_to_help<'a>() -> impl Parser< and!( and!( skip_second!( - space0_e(min_indent, EProvides::Space, EProvides::IndentKeyword), - crate::parser::keyword_e("provides", EProvides::Keyword) + space0_e(min_indent, EProvides::Space, EProvides::IndentProvides), + crate::parser::keyword_e("provides", EProvides::Provides) ), space0_e(min_indent, EProvides::Space, EProvides::IndentListStart) ), diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 5fb31c1f0d..a16cb3bd9f 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -345,13 +345,17 @@ pub enum EHeader { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EProvides { - Keyword(Row, Col), - IndentKeyword(Row, Col), + Provides(Row, Col), + To(Row, Col), + IndentProvides(Row, Col), + IndentTo(Row, Col), IndentListStart(Row, Col), IndentListEnd(Row, Col), + IndentPackage(Row, Col), ListStart(Row, Col), ListEnd(Row, Col), Identifier(Row, Col), + Package(Row, Col), Space(BadInputError, Row, Col), } From 65cee3904127752591e8455ae4bfbd0465138290 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 8 Mar 2021 16:33:45 +0100 Subject: [PATCH 06/74] remove attempting argument from parser state --- compiler/load/src/file.rs | 6 +++--- compiler/parse/src/parser.rs | 2 +- compiler/parse/src/test_helpers.rs | 8 ++++---- editor/src/lang/expr.rs | 4 ++-- editor/src/lang/roc_file.rs | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index dc0dc9ec12..7ae0aa7833 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -22,7 +22,7 @@ use roc_mono::ir::{ CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs, }; use roc_mono::layout::{Layout, LayoutCache, LayoutProblem}; -use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation}; +use roc_parse::ast::{self, StrLiteral, TypeAnnotation}; use roc_parse::header::{ ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent, }; @@ -2304,7 +2304,7 @@ fn load_pkg_config<'a>( Ok(bytes_vec) => { let parse_start = SystemTime::now(); let bytes = arena.alloc(bytes_vec); - let parse_state = parser::State::new_in(arena, bytes, Attempting::Module); + let parse_state = parser::State::new_in(arena, bytes); let parsed = roc_parse::module::header().parse(&arena, parse_state); let parse_header_duration = parse_start.elapsed().unwrap(); @@ -2474,7 +2474,7 @@ fn parse_header<'a>( start_time: SystemTime, ) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> { let parse_start = SystemTime::now(); - let parse_state = parser::State::new_in(arena, src_bytes, Attempting::Module); + let parse_state = parser::State::new_in(arena, src_bytes); let parsed = roc_parse::module::header().parse(&arena, parse_state); let parse_header_duration = parse_start.elapsed().unwrap(); diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index a16cb3bd9f..1ab906ea70 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -43,7 +43,7 @@ pub enum Either { } impl<'a> State<'a> { - pub fn new_in(arena: &'a Bump, bytes: &'a [u8], _attempting: Attempting) -> State<'a> { + pub fn new_in(arena: &'a Bump, bytes: &'a [u8]) -> State<'a> { State { bytes, line: 0, diff --git a/compiler/parse/src/test_helpers.rs b/compiler/parse/src/test_helpers.rs index ebfb5bb1af..7cfddd8d8c 100644 --- a/compiler/parse/src/test_helpers.rs +++ b/compiler/parse/src/test_helpers.rs @@ -1,4 +1,4 @@ -use crate::ast::{self, Attempting}; +use crate::ast; use crate::blankspace::space0_before; use crate::expr::expr; use crate::module::{header, module_defs}; @@ -18,7 +18,7 @@ pub fn parse_header_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module); + let state = State::new_in(arena, input.trim().as_bytes()); let answer = header().parse(arena, state); answer @@ -31,7 +31,7 @@ pub fn parse_defs_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>>, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module); + let state = State::new_in(arena, input.trim().as_bytes()); let answer = module_defs().parse(arena, state); answer .map(|(_, loc_expr, _)| loc_expr) @@ -43,7 +43,7 @@ pub fn parse_loc_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module); + let state = State::new_in(arena, input.trim().as_bytes()); let parser = space0_before(loc(expr(0)), 0); let answer = parser.parse(&arena, state); diff --git a/editor/src/lang/expr.rs b/editor/src/lang/expr.rs index 061fc21495..410009bc2e 100644 --- a/editor/src/lang/expr.rs +++ b/editor/src/lang/expr.rs @@ -16,8 +16,8 @@ use roc_module::ident::ModuleName; use roc_module::low_level::LowLevel; use roc_module::operator::CalledVia; use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol}; +use roc_parse::ast; use roc_parse::ast::StrLiteral; -use roc_parse::ast::{self, Attempting}; use roc_parse::blankspace::space0_before; use roc_parse::expr::expr; use roc_parse::parser::{loc, Parser, State, SyntaxError}; @@ -235,7 +235,7 @@ pub fn str_to_expr2<'a>( scope: &mut Scope, region: Region, ) -> Result<(Expr2, self::Output), SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module); + let state = State::new_in(arena, input.trim().as_bytes()); let parser = space0_before(loc(expr(0)), 0); let parse_res = parser.parse(&arena, state); diff --git a/editor/src/lang/roc_file.rs b/editor/src/lang/roc_file.rs index 676c74048b..f034a1f1b0 100644 --- a/editor/src/lang/roc_file.rs +++ b/editor/src/lang/roc_file.rs @@ -2,7 +2,7 @@ use bumpalo::collections::Vec; use bumpalo::Bump; use roc_fmt::def::fmt_def; use roc_fmt::module::fmt_module; -use roc_parse::ast::{Attempting, Def, Module}; +use roc_parse::ast::{Def, Module}; use roc_parse::module::module_defs; use roc_parse::parser; use roc_parse::parser::{Parser, SyntaxError}; @@ -36,7 +36,7 @@ impl<'a> File<'a> { let allocation = arena.alloc(bytes); - let module_parse_state = parser::State::new_in(arena, allocation, Attempting::Module); + let module_parse_state = parser::State::new_in(arena, allocation); let parsed_module = roc_parse::module::header().parse(&arena, module_parse_state); match parsed_module { From 25e3ab5a7d8879deedd46bbcb19707bca094face Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 8 Mar 2021 16:51:45 +0100 Subject: [PATCH 07/74] setup test infra --- compiler/reporting/tests/helpers/mod.rs | 4 +- compiler/reporting/tests/test_reporting.rs | 82 ++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/compiler/reporting/tests/helpers/mod.rs b/compiler/reporting/tests/helpers/mod.rs index 7bc49f0421..2247be164c 100644 --- a/compiler/reporting/tests/helpers/mod.rs +++ b/compiler/reporting/tests/helpers/mod.rs @@ -11,7 +11,7 @@ use roc_collections::all::{ImMap, MutMap, SendSet}; use roc_constrain::expr::constrain_expr; use roc_constrain::module::{constrain_imported_values, Import}; use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds}; -use roc_parse::ast::{self, Attempting}; +use roc_parse::ast; use roc_parse::blankspace::space0_before; use roc_parse::parser::{loc, Parser, State, SyntaxError}; use roc_problem::can::Problem; @@ -110,7 +110,7 @@ pub fn parse_loc_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module); + let state = State::new_in(arena, input.trim().as_bytes()); let parser = space0_before(loc(roc_parse::expr::expr(0)), 0); let answer = parser.parse(&arena, state); diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index f4bd0aca8b..c73c168c55 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -169,6 +169,36 @@ mod test_reporting { } } + fn list_header_reports(arena: &Bump, src: &str, buf: &mut String, callback: F) + where + F: FnOnce(RocDocBuilder<'_>, &mut String), + { + use ven_pretty::DocAllocator; + + use roc_parse::parser::State; + + let state = State::new_in(arena, src.as_bytes()); + + let filename = filename_from_string(r"\code\proj\Main.roc"); + let src_lines: Vec<&str> = src.split('\n').collect(); + + use roc_parse::parser::Parser; + match roc_parse::module::header().parse(arena, state) { + Err((_, fail, _)) => { + let interns = Interns::default(); + let home = crate::helpers::test_home(); + + let alloc = RocDocAllocator::new(&src_lines, home, &interns); + + let problem = fail.into_parse_problem(filename.clone(), src.as_bytes()); + let doc = parse_problem(&alloc, filename, 0, problem); + + callback(doc.pretty(&alloc).append(alloc.line()), buf) + } + Ok(_) => todo!(), + } + } + fn report_problem_as(src: &str, expected_rendering: &str) { let mut buf: String = String::new(); let arena = Bump::new(); @@ -193,6 +223,30 @@ mod test_reporting { assert_eq!(buf, expected_rendering); } + fn report_header_problem_as(src: &str, expected_rendering: &str) { + let mut buf: String = String::new(); + let arena = Bump::new(); + + let callback = |doc: RocDocBuilder<'_>, buf: &mut String| { + doc.1 + .render_raw(70, &mut roc_reporting::report::CiWrite::new(buf)) + .expect("list_reports") + }; + + list_header_reports(&arena, src, &mut buf, callback); + + // convenient to copy-paste the generated message + if true { + if buf != expected_rendering { + for line in buf.split("\n") { + println!(" {}", line); + } + } + } + + assert_eq!(buf, expected_rendering); + } + fn color_report_problem_as(src: &str, expected_rendering: &str) { let mut buf: String = String::new(); let arena = Bump::new(); @@ -5702,4 +5756,32 @@ mod test_reporting { ), ) } + + #[test] + fn provides_to() { + // this is still bad, but changing the order and progress of other parsers should improve it + // down the line + report_header_problem_as( + indoc!( + r#" + app "test-base64" + packages { base: "platform" } + imports [base.Task, Base64 ] + provides [ main, ## ] to base + "# + ), + indoc!( + r#" + ── MISSING EXPRESSION ────────────────────────────────────────────────────────── + + I am partway through parsing a definition, but I got stuck here: + + 1│ main = 5 -> 3 + ^ + + I was expecting to see an expression like 42 or "hello". + "# + ), + ) + } } From 845307f94eb0b8233d964dfdeb11cdc24eb94fe1 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 8 Mar 2021 19:20:11 +0100 Subject: [PATCH 08/74] first header parse report test --- compiler/parse/src/module.rs | 2 +- compiler/reporting/src/error/parse.rs | 57 ++++++++++++++++++++++ compiler/reporting/tests/test_reporting.rs | 21 ++++---- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 7815ce9615..40a6dd1014 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -308,7 +308,7 @@ pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located>>, SyntaxErr // force that we pare until the end of the input skip_second!(zero_or_more!(space0_around(loc(def(0)), 0)), end_of_file()) } - +#[derive(Debug)] struct ProvidesTo<'a> { entries: Vec<'a, Located>>, to: Located>, diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index c9a412aac7..8e2dca66a3 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -153,6 +153,7 @@ fn to_syntax_report<'a>( 0, 0, ), + Header(header) => to_header_report(alloc, filename, &header, 0, 0), _ => todo!("unhandled parse error: {:?}", parse_problem), } } @@ -2469,6 +2470,62 @@ fn to_tapply_report<'a>( } } +fn to_header_report<'a>( + alloc: &'a RocDocAllocator<'a>, + filename: PathBuf, + parse_problem: &roc_parse::parser::EHeader, + _start_row: Row, + _start_col: Col, +) -> Report<'a> { + use roc_parse::parser::EHeader; + + match *parse_problem { + EHeader::Provides(provides, row, col) => { + to_provides_report(alloc, filename, &provides, row, col) + } // _ => todo!("unhandled parse error {:?}", parse_problem), + } +} + +fn to_provides_report<'a>( + alloc: &'a RocDocAllocator<'a>, + filename: PathBuf, + parse_problem: &roc_parse::parser::EProvides, + start_row: Row, + start_col: Col, +) -> Report<'a> { + use roc_parse::parser::EProvides; + + match *parse_problem { + EProvides::Identifier(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow( + r"I am in the middle of parsing a provides list, but I got stuck here:", + ), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![alloc.reflow( + "I was expecting a type name, value name or function name next, like ", + )]), + alloc + .parser_suggestion("provides [ Animal, default, tame ]") + .indent(4), + ]); + + Report { + filename, + doc, + title: "WEIRD PROVIDES".to_string(), + } + } + + EProvides::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), + + _ => todo!("unhandled parse error {:?}", parse_problem), + } +} + fn to_space_report<'a>( alloc: &'a RocDocAllocator<'a>, filename: PathBuf, diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index c73c168c55..2980c65927 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -5767,19 +5767,22 @@ mod test_reporting { app "test-base64" packages { base: "platform" } imports [base.Task, Base64 ] - provides [ main, ## ] to base + provides [ main, @Foo ] to base "# ), indoc!( r#" - ── MISSING EXPRESSION ────────────────────────────────────────────────────────── - - I am partway through parsing a definition, but I got stuck here: - - 1│ main = 5 -> 3 - ^ - - I was expecting to see an expression like 42 or "hello". + ── WEIRD PROVIDES ────────────────────────────────────────────────────────────── + + I am in the middle of parsing a provides list, but I got stuck here: + + 3│ imports [base.Task, Base64 ] + 4│ provides [ main, @Foo ] to base + ^ + + I was expecting a type name, value name or function name next, like + + provides [ Animal, default, tame ] "# ), ) From 8b9804adc2c1b00b51c110298aafe6988ad94238 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 8 Mar 2021 21:14:47 +0100 Subject: [PATCH 09/74] generalize --- compiler/parse/src/module.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 40a6dd1014..fdc9aede9d 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -406,7 +406,7 @@ fn provides_without_to_help<'a>() -> impl Parser< ), collection_e!( word1(b'[', EProvides::ListStart), - exposes_entry(), + exposes_entry(EProvides::Identifier), word1(b',', EProvides::ListEnd), word1(b']', EProvides::ListEnd), min_indent, @@ -416,9 +416,16 @@ fn provides_without_to_help<'a>() -> impl Parser< ) } -fn exposes_entry<'a>() -> impl Parser<'a, Located>, EProvides> { +fn exposes_entry<'a, F, E>( + to_expectation: F, +) -> impl Parser<'a, Located>, E> +where + F: Fn(crate::parser::Row, crate::parser::Col) -> E, + F: Copy, + E: 'a, +{ loc!(map!( - specialize(|_, r, c| EProvides::Identifier(r, c), unqualified_ident()), + specialize(|_, r, c| to_expectation(r, c), unqualified_ident()), ExposesEntry::Exposed )) } From 268ab8241004c60ecaf9f494d7022b383e16dffc Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 8 Mar 2021 21:12:19 -0500 Subject: [PATCH 10/74] feat(List): add map3 --- compiler/builtins/bitcode/src/list.zig | 121 +++++++++++++++++++++++++ compiler/builtins/src/bitcode.rs | 1 + compiler/builtins/src/std.rs | 15 +++ compiler/can/src/builtins.rs | 39 ++++++++ compiler/gen/src/llvm/build.rs | 36 +++++++- compiler/gen/src/llvm/build_list.rs | 108 ++++++++++++++++++++++ compiler/module/src/low_level.rs | 1 + compiler/module/src/symbol.rs | 1 + compiler/mono/src/borrow.rs | 1 + compiler/test_gen/src/gen_list.rs | 30 ++++++ 10 files changed, 351 insertions(+), 2 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index fcd5f42cf8..95137d25bb 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -114,6 +114,7 @@ pub const RocList = extern struct { const Caller1 = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void; const Caller2 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void; +const Caller3 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void; pub fn listMap(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, old_element_width: usize, new_element_width: usize) callconv(.C) RocList { if (list.bytes) |source_ptr| { @@ -213,6 +214,126 @@ pub fn listMap2(list1: RocList, list2: RocList, transform: Opaque, caller: Calle } } +pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaque, caller: Caller3, alignment: usize, a_width: usize, b_width: usize, c_width: usize, d_width: usize, dec_a: Dec, dec_b: Dec, dec_c: Dec) callconv(.C) RocList { + const smaller_length = std.math.min(list1.len(), list2.len()); + const output_length = std.math.min(smaller_length, list3.len()); + + if (list1.bytes) |source_a| { + if (list2.bytes) |source_b| { + if (list3.bytes) |source_c| { + const output = RocList.allocate(std.heap.c_allocator, alignment, output_length, d_width); + const target_ptr = output.bytes orelse unreachable; + + var i: usize = 0; + while (i < output_length) : (i += 1) { + const element_a = source_a + i * a_width; + const element_b = source_b + i * b_width; + const element_c = source_c + i * c_width; + const target = target_ptr + i * d_width; + + caller(transform, element_a, element_b, element_c, target); + } + + // if the lists don't have equal length, we must consume the remaining elements + // In this case we consume by (recursively) decrementing the elements + if (list1.len() > output_length) { + while (i < list1.len()) : (i += 1) { + const element_a = source_a + i * a_width; + dec_a(element_a); + } + } + + if (list2.len() > output_length) { + while (i < list2.len()) : (i += 1) { + const element_b = source_b + i * b_width; + dec_b(element_b); + } + } + + if (list3.len() > output_length) { + while (i < list3.len()) : (i += 1) { + const element_c = source_c + i * c_width; + dec_b(element_c); + } + } + + utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width); + utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width); + utils.decref(std.heap.c_allocator, alignment, list3.bytes, list3.len() * c_width); + + return output; + } else { + // consume list1 elements (we know there is at least one because the list1.bytes pointer is non-null + var i: usize = 0; + while (i < list1.len()) : (i += 1) { + const element_a = source_a + i * a_width; + dec_a(element_a); + } + utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width); + + // consume list2 elements (we know there is at least one because the list1.bytes pointer is non-null + i = 0; + while (i < list2.len()) : (i += 1) { + const element_b = source_b + i * b_width; + dec_b(element_b); + } + utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width); + + return RocList.empty(); + } + } else { + // consume list1 elements (we know there is at least one because the list1.bytes pointer is non-null + var i: usize = 0; + while (i < list1.len()) : (i += 1) { + const element_a = source_a + i * a_width; + dec_a(element_a); + } + + utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width); + + // consume list3 elements (if any) + if (list3.bytes) |source_c| { + i = 0; + + while (i < list2.len()) : (i += 1) { + const element_c = source_c + i * c_width; + dec_c(element_c); + } + + utils.decref(std.heap.c_allocator, alignment, list3.bytes, list3.len() * c_width); + } + + return RocList.empty(); + } + } else { + // consume list2 elements (if any) + if (list2.bytes) |source_b| { + var i: usize = 0; + + while (i < list2.len()) : (i += 1) { + const element_b = source_b + i * b_width; + dec_b(element_b); + } + + utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width); + } + + // consume list3 elements (if any) + if (list3.bytes) |source_c| { + var i: usize = 0; + + while (i < list2.len()) : (i += 1) { + const element_c = source_c + i * c_width; + dec_c(element_c); + } + + utils.decref(std.heap.c_allocator, alignment, list3.bytes, list3.len() * c_width); + } + + return RocList.empty(); + } +} + pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, element_width: usize, inc: Inc, dec: Dec) callconv(.C) RocList { if (list.bytes) |source_ptr| { const size = list.len(); diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index 2905ec52df..0fd6cb1f2f 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -64,6 +64,7 @@ pub const SET_FROM_LIST: &str = "roc_builtins.dict.set_from_list"; pub const LIST_MAP: &str = "roc_builtins.list.map"; pub const LIST_MAP2: &str = "roc_builtins.list.map2"; +pub const LIST_MAP3: &str = "roc_builtins.list.map3"; pub const LIST_MAP_WITH_INDEX: &str = "roc_builtins.list.map_with_index"; pub const LIST_KEEP_IF: &str = "roc_builtins.list.keep_if"; pub const LIST_KEEP_OKS: &str = "roc_builtins.list.keep_oks"; diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 094fbac48f..a743afde77 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -817,6 +817,21 @@ pub fn types() -> MutMap { ) }); + // map3 : List a, List b, List c, (a, b, c -> d) -> List d + add_type(Symbol::LIST_MAP3, { + let_tvars! {a, b, c, d, cvar}; + + top_level_function( + vec![ + list_type(flex(a)), + list_type(flex(b)), + list_type(flex(c)), + closure(vec![flex(a), flex(b), flex(c)], cvar, Box::new(flex(d))), + ], + Box::new(list_type(flex(d))), + ) + }); + // append : List elem, elem -> List elem add_type( Symbol::LIST_APPEND, diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 6977eaa607..e31d1597f0 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -81,6 +81,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option LIST_JOIN => list_join, LIST_MAP => list_map, LIST_MAP2 => list_map2, + LIST_MAP3 => list_map3, LIST_MAP_WITH_INDEX => list_map_with_index, LIST_KEEP_IF => list_keep_if, LIST_KEEP_OKS => list_keep_oks, @@ -218,6 +219,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { Symbol::LIST_JOIN => list_join, Symbol::LIST_MAP => list_map, Symbol::LIST_MAP2 => list_map2, + Symbol::LIST_MAP3 => list_map3, Symbol::LIST_MAP_WITH_INDEX => list_map_with_index, Symbol::LIST_KEEP_IF => list_keep_if, Symbol::LIST_KEEP_OKS => list_keep_oks, @@ -370,6 +372,38 @@ fn lowlevel_3(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def { ) } +fn lowlevel_4(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def { + let arg1_var = var_store.fresh(); + let arg2_var = var_store.fresh(); + let arg3_var = var_store.fresh(); + let arg4_var = var_store.fresh(); + let ret_var = var_store.fresh(); + + let body = RunLowLevel { + op, + args: vec![ + (arg1_var, Var(Symbol::ARG_1)), + (arg2_var, Var(Symbol::ARG_2)), + (arg3_var, Var(Symbol::ARG_3)), + (arg4_var, Var(Symbol::ARG_4)), + ], + ret_var, + }; + + defn( + symbol, + vec![ + (arg1_var, Symbol::ARG_1), + (arg2_var, Symbol::ARG_2), + (arg3_var, Symbol::ARG_3), + (arg4_var, Symbol::ARG_4), + ], + var_store, + body, + ret_var, + ) +} + /// Num.maxInt : Int fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); @@ -2122,6 +2156,11 @@ fn list_map2(symbol: Symbol, var_store: &mut VarStore) -> Def { lowlevel_3(symbol, LowLevel::ListMap2, var_store) } +/// List.map3 : List a, List b, (a, b -> c) -> List c +fn list_map3(symbol: Symbol, var_store: &mut VarStore) -> Def { + lowlevel_4(symbol, LowLevel::ListMap3, var_store) +} + /// Dict.hashTestOnly : k, v -> Nat pub fn dict_hash_test_only(symbol: Symbol, var_store: &mut VarStore) -> Def { lowlevel_2(symbol, LowLevel::Hash, var_store) diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 98386c2270..903ec6acef 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -7,8 +7,8 @@ use crate::llvm::build_hash::generic_hash; use crate::llvm::build_list::{ allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains, list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, - list_map2, list_map_with_index, list_prepend, list_repeat, list_reverse, list_set, list_single, - list_sum, list_walk, list_walk_backwards, + list_map2, list_map3, list_map_with_index, list_prepend, list_repeat, list_reverse, list_set, + list_single, list_sum, list_walk, list_walk_backwards, }; use crate::llvm::build_str::{ str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8, @@ -3746,6 +3746,38 @@ fn run_low_level<'a, 'ctx, 'env>( _ => unreachable!("invalid list layout"), } } + ListMap3 => { + debug_assert_eq!(args.len(), 4); + + let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]); + let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]); + let (list3, list3_layout) = load_symbol_and_layout(scope, &args[2]); + + let (func, func_layout) = load_symbol_and_layout(scope, &args[3]); + + match (list1_layout, list2_layout, list3_layout) { + ( + Layout::Builtin(Builtin::List(_, element1_layout)), + Layout::Builtin(Builtin::List(_, element2_layout)), + Layout::Builtin(Builtin::List(_, element3_layout)), + ) => list_map3( + env, + layout_ids, + func, + func_layout, + list1, + list2, + list3, + element1_layout, + element2_layout, + element3_layout, + ), + (Layout::Builtin(Builtin::EmptyList), _, _) + | (_, Layout::Builtin(Builtin::EmptyList), _) + | (_, _, Layout::Builtin(Builtin::EmptyList)) => empty_list(env), + _ => unreachable!("invalid list layout"), + } + } ListMapWithIndex => { // List.map : List before, (before -> after) -> List after debug_assert_eq!(args.len(), 2); diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index 4124891cf0..fb3232e0b8 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -1305,6 +1305,114 @@ pub fn list_map2<'a, 'ctx, 'env>( ) } +pub fn list_map3<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + transform: BasicValueEnum<'ctx>, + transform_layout: &Layout<'a>, + list1: BasicValueEnum<'ctx>, + list2: BasicValueEnum<'ctx>, + list3: BasicValueEnum<'ctx>, + element1_layout: &Layout<'a>, + element2_layout: &Layout<'a>, + element3_layout: &Layout<'a>, +) -> BasicValueEnum<'ctx> { + let builder = env.builder; + + let return_layout = match transform_layout { + Layout::FunctionPointer(_, ret) => ret, + Layout::Closure(_, _, ret) => ret, + _ => unreachable!("not a callable layout"), + }; + + let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic); + + let list1_i128 = complex_bitcast( + env.builder, + list1, + env.context.i128_type().into(), + "to_i128", + ); + + let list2_i128 = complex_bitcast( + env.builder, + list2, + env.context.i128_type().into(), + "to_i128", + ); + + let list3_i128 = complex_bitcast( + env.builder, + list3, + env.context.i128_type().into(), + "to_i128", + ); + + let transform_ptr = builder.build_alloca(transform.get_type(), "transform_ptr"); + env.builder.build_store(transform_ptr, transform); + + let argument_layouts = [ + element1_layout.clone(), + element2_layout.clone(), + element3_layout.clone(), + ]; + let stepper_caller = + build_transform_caller(env, layout_ids, transform_layout, &argument_layouts) + .as_global_value() + .as_pointer_value(); + + let a_width = env + .ptr_int() + .const_int(element1_layout.stack_size(env.ptr_bytes) as u64, false); + + let b_width = env + .ptr_int() + .const_int(element2_layout.stack_size(env.ptr_bytes) as u64, false); + + let c_width = env + .ptr_int() + .const_int(element3_layout.stack_size(env.ptr_bytes) as u64, false); + + let d_width = env + .ptr_int() + .const_int(return_layout.stack_size(env.ptr_bytes) as u64, false); + + let alignment = return_layout.alignment_bytes(env.ptr_bytes); + let alignment_iv = env.ptr_int().const_int(alignment as u64, false); + + let dec_a = build_dec_wrapper(env, layout_ids, element1_layout); + let dec_b = build_dec_wrapper(env, layout_ids, element2_layout); + let dec_c = build_dec_wrapper(env, layout_ids, element3_layout); + + let output = call_bitcode_fn( + env, + &[ + list1_i128, + list2_i128, + list3_i128, + env.builder + .build_bitcast(transform_ptr, u8_ptr, "to_opaque"), + stepper_caller.into(), + alignment_iv.into(), + a_width.into(), + b_width.into(), + c_width.into(), + d_width.into(), + dec_a.as_global_value().as_pointer_value().into(), + dec_b.as_global_value().as_pointer_value().into(), + dec_c.as_global_value().as_pointer_value().into(), + ], + bitcode::LIST_MAP3, + ); + + complex_bitcast( + env.builder, + output, + collection(env.context, env.ptr_bytes).into(), + "from_i128", + ) +} + /// List.concat : List elem, List elem -> List elem pub fn list_concat<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index c597c42076..b8a2ed31c6 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -28,6 +28,7 @@ pub enum LowLevel { ListJoin, ListMap, ListMap2, + ListMap3, ListMapWithIndex, ListKeepIf, ListWalk, diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 9ecb241be0..652c8493e2 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -910,6 +910,7 @@ define_builtins! { 22 LIST_KEEP_ERRS: "keepErrs" 23 LIST_MAP_WITH_INDEX: "mapWithIndex" 24 LIST_MAP2: "map2" + 25 LIST_MAP3: "map3" } 5 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 486205fcb3..b3785ddaa9 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -652,6 +652,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { ListJoin => arena.alloc_slice_copy(&[irrelevant]), ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, irrelevant]), ListMap2 => arena.alloc_slice_copy(&[owned, owned, irrelevant]), + ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, irrelevant]), ListKeepIf | ListKeepOks | ListKeepErrs => arena.alloc_slice_copy(&[owned, borrowed]), ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListWalk => arena.alloc_slice_copy(&[owned, irrelevant, owned]), diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index dbf974988b..28986f682c 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -568,6 +568,36 @@ fn list_map_closure() { ); } +#[test] +fn list_map3_group() { + assert_evals_to!( + indoc!( + r#" + List.map3 [1,2,3] [3,2,1] [2,1,3] (\a, b, c -> Group a b c) + "# + ), + RocList::from_slice(&[(1, 3, 2), (2, 2, 1), (3, 1, 3)]), + RocList<(i64, i64, i64)> + ); +} + +#[test] +fn list_map3_different_length() { + assert_evals_to!( + indoc!( + r#" + List.map3 + ["a", "b", "d" ] + ["b"], + ["c"], + Str.concat + "# + ), + RocList::from_slice(&[RocStr::from_slice("abc".as_bytes()),]), + RocList + ); +} + #[test] fn list_map2_pair() { assert_evals_to!( From 9fc572ace95e02bdc17848467699615cdad82980 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 8 Mar 2021 21:14:15 -0500 Subject: [PATCH 11/74] fix(List): map3 ref count use dec_c on third list --- compiler/builtins/bitcode/src/list.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 95137d25bb..bd765e1eca 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -253,7 +253,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu if (list3.len() > output_length) { while (i < list3.len()) : (i += 1) { const element_c = source_c + i * c_width; - dec_b(element_c); + dec_c(element_c); } } From bebd96674b5364849e1beaa8cac18024a14cf8d2 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 8 Mar 2021 21:18:22 -0500 Subject: [PATCH 12/74] fix(List): export map3 from main.zig --- compiler/builtins/bitcode/src/main.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index e0f501654b..4cd57a84a5 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -8,6 +8,7 @@ const list = @import("list.zig"); comptime { exportListFn(list.listMap, "map"); exportListFn(list.listMap2, "map2"); + exportListFn(list.listMap3, "map3"); exportListFn(list.listMapWithIndex, "map_with_index"); exportListFn(list.listKeepIf, "keep_if"); exportListFn(list.listWalk, "walk"); From dfc36bd4a82f4ec064a9395fd51b63947a6bd48e Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 8 Mar 2021 21:42:11 -0500 Subject: [PATCH 13/74] fix(List): parse error in tests --- compiler/test_gen/src/gen_list.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 28986f682c..34244d06fb 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -588,8 +588,8 @@ fn list_map3_different_length() { r#" List.map3 ["a", "b", "d" ] - ["b"], - ["c"], + ["b"] + ["c"] Str.concat "# ), From afe9c27c19facc352ed53394b3d80c25dcc7f33f Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 8 Mar 2021 21:46:06 -0500 Subject: [PATCH 14/74] fix(List): in test, Str.concat only takes 2 args, need to pass lambda --- compiler/test_gen/src/gen_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 34244d06fb..83d93eebf2 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -590,7 +590,7 @@ fn list_map3_different_length() { ["a", "b", "d" ] ["b"] ["c"] - Str.concat + (\a, b, c -> Str.concat a (Str.concat b c)) "# ), RocList::from_slice(&[RocStr::from_slice("abc".as_bytes()),]), From 73365fe5fa0ab0a020a2cd1395ebd5087bc9c02a Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 9 Mar 2021 11:15:48 +0100 Subject: [PATCH 15/74] added g++ to dependencies for spirv_cross --- Earthfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Earthfile b/Earthfile index d4f41a014c..e4df77170f 100644 --- a/Earthfile +++ b/Earthfile @@ -8,7 +8,7 @@ install-other-libs: FROM +prep-debian RUN apt -y install wget git RUN apt -y install libxcb-shape0-dev libxcb-xfixes0-dev # for editor clipboard - RUN apt -y install libc++-dev libc++abi-dev libunwind-dev pkg-config libx11-dev zlib1g-dev + RUN apt -y install libc++-dev libc++abi-dev g++ libunwind-dev pkg-config libx11-dev zlib1g-dev install-zig-llvm-valgrind-clippy-rustfmt: FROM +install-other-libs From 71ab77b804192f765c3a66d54d430f7a78779d2f Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 14:39:25 +0100 Subject: [PATCH 16/74] start of imports --- compiler/parse/src/module.rs | 227 +++++++++++++++++---- compiler/parse/src/parser.rs | 47 +++++ compiler/reporting/src/error/parse.rs | 160 ++++++++++++++- compiler/reporting/tests/test_reporting.rs | 32 ++- 4 files changed, 418 insertions(+), 48 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index fdc9aede9d..ecac928a90 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -9,8 +9,8 @@ use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ self, ascii_char, ascii_string, backtrackable, end_of_file, loc, optional, peek_utf8_char, - peek_utf8_char_at, specialize, unexpected, unexpected_eof, word1, EHeader, EProvides, - ParseResult, Parser, State, SyntaxError, + peek_utf8_char_at, specialize, unexpected, unexpected_eof, word1, Col, EExposes, EHeader, + EImports, EProvides, ParseResult, Parser, Row, State, SyntaxError, }; use crate::string_literal; use crate::type_annotation; @@ -342,22 +342,23 @@ fn provides_to_help<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> { and!( provides_without_to_help(), and!( - space0_e(min_indent, EProvides::Space, EProvides::IndentTo), - skip_first!( - crate::parser::keyword_e("to", EProvides::To), - and!( - space0_e(min_indent, EProvides::Space, EProvides::IndentPackage), - loc!(specialize( - |_, r, c| EProvides::Package(r, c), - provides_to_package() - )) - ) - ) + spaces_around_keyword( + min_indent, + "to", + EProvides::To, + EProvides::Space, + EProvides::IndentTo, + EProvides::IndentListStart + ), + loc!(specialize( + |_, r, c| EProvides::Package(r, c), + provides_to_package() + )) ) ), |( ((before_provides_keyword, after_provides_keyword), entries), - (before_to_keyword, (after_to_keyword, to)), + ((before_to_keyword, after_to_keyword), to), )| { ProvidesTo { entries, @@ -397,12 +398,13 @@ fn provides_without_to_help<'a>() -> impl Parser< > { let min_indent = 1; and!( - and!( - skip_second!( - space0_e(min_indent, EProvides::Space, EProvides::IndentProvides), - crate::parser::keyword_e("provides", EProvides::Provides) - ), - space0_e(min_indent, EProvides::Space, EProvides::IndentListStart) + spaces_around_keyword( + min_indent, + "provides", + EProvides::Provides, + EProvides::Space, + EProvides::IndentProvides, + EProvides::IndentListStart ), collection_e!( word1(b'[', EProvides::ListStart), @@ -460,14 +462,40 @@ fn exposes_values<'a>() -> impl Parser< ), SyntaxError<'a>, > { + specialize( + |e, r, c| SyntaxError::Header(EHeader::Exposes(e, r, c)), + exposes_values_help(), + ) +} + +#[inline(always)] +fn exposes_values_help<'a>() -> impl Parser< + 'a, + ( + (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), + Vec<'a, Located>>, + ), + EExposes, +> { + let min_indent = 1; + and!( - and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)), - collection!( - ascii_char(b'['), - loc!(map!(unqualified_ident(), ExposesEntry::Exposed)), - ascii_char(b','), - ascii_char(b']'), - 1 + spaces_around_keyword( + min_indent, + "exposes", + EExposes::Exposes, + EExposes::Space, + EExposes::IndentExposes, + EExposes::IndentListStart + ), + collection_e!( + word1(b'[', EExposes::ListStart), + exposes_entry(EExposes::Identifier), + word1(b',', EExposes::ListEnd), + word1(b']', EExposes::ListEnd), + min_indent, + EExposes::Space, + EExposes::IndentListEnd ) ) } @@ -481,18 +509,78 @@ fn exposes_modules<'a>() -> impl Parser< ), SyntaxError<'a>, > { + specialize( + |e, r, c| SyntaxError::Header(EHeader::Exposes(e, r, c)), + exposes_modules_help(), + ) +} + +fn spaces_around_keyword<'a, E>( + min_indent: u16, + keyword: &'static str, + expectation: fn(Row, Col) -> E, + space_problem: fn(crate::parser::BadInputError, Row, Col) -> E, + indent_problem1: fn(Row, Col) -> E, + indent_problem2: fn(Row, Col) -> E, +) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), E> +where + E: 'a, +{ and!( - and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)), - collection!( - ascii_char(b'['), - loc!(map!(module_name(), ExposesEntry::Exposed)), - ascii_char(b','), - ascii_char(b']'), - 1 + skip_second!( + space0_e(min_indent, space_problem, indent_problem1), + crate::parser::keyword_e(keyword, expectation) + ), + space0_e(min_indent, space_problem, indent_problem2) + ) +} + +#[inline(always)] +fn exposes_modules_help<'a>() -> impl Parser< + 'a, + ( + (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), + Vec<'a, Located>>>, + ), + EExposes, +> { + let min_indent = 1; + + and!( + spaces_around_keyword( + min_indent, + "exposes", + EExposes::Exposes, + EExposes::Space, + EExposes::IndentExposes, + EExposes::IndentListStart + ), + collection_e!( + word1(b'[', EExposes::ListStart), + exposes_module(EExposes::Identifier), + word1(b',', EExposes::ListEnd), + word1(b']', EExposes::ListEnd), + min_indent, + EExposes::Space, + EExposes::IndentListEnd ) ) } +fn exposes_module<'a, F, E>( + to_expectation: F, +) -> impl Parser<'a, Located>>, E> +where + F: Fn(crate::parser::Row, crate::parser::Col) -> E, + F: Copy, + E: 'a, +{ + loc!(map!( + specialize(|_, r, c| to_expectation(r, c), module_name()), + ExposesEntry::Exposed + )) +} + #[derive(Debug)] struct Packages<'a> { entries: Vec<'a, Located>>, @@ -551,6 +639,30 @@ fn imports<'a>() -> impl Parser< ) } +#[inline(always)] +fn imports_help<'a>() -> impl Parser< + 'a, + ( + (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), + Vec<'a, Located>>, + ), + SyntaxError<'a>, +> { + and!( + and!( + skip_second!(backtrackable(space1(1)), ascii_string("imports")), + space1(1) + ), + collection!( + ascii_char(b'['), + loc!(imports_entry()), + ascii_char(b','), + ascii_char(b']'), + 1 + ) + ) +} + #[inline(always)] fn effects<'a>() -> impl Parser<'a, Effects<'a>, SyntaxError<'a>> { move |arena, state| { @@ -621,23 +733,50 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, SyntaxError<'a>> { #[inline(always)] #[allow(clippy::type_complexity)] fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, SyntaxError<'a>> { + specialize( + |e, r, c| SyntaxError::Header(EHeader::Imports(e, r, c)), + imports_entry_help(), + ) +} + +fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> { + specialize(|_, r, c| EImports::Shortname(r, c), lowercase_ident()) +} + +fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E> +where + F: Fn(crate::parser::Row, crate::parser::Col) -> E, + E: 'a, + F: 'a, +{ + specialize(move |_, r, c| to_expectation(r, c), module_name()) +} + +fn imports_entry_help<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> { + let min_indent = 1; + map_with_arena!( and!( and!( // e.g. `base.` - optional(skip_second!(lowercase_ident(), ascii_char(b'.'))), + maybe!(skip_second!( + shortname(), + word1(b'.', EImports::ShorthandDot) + )), // e.g. `Task` - module_name() + module_name_help(EImports::ModuleName) ), // e.g. `.{ Task, after}` - optional(skip_first!( - ascii_char(b'.'), - collection!( - ascii_char(b'{'), - loc!(map!(unqualified_ident(), ExposesEntry::Exposed)), - ascii_char(b','), - ascii_char(b'}'), - 1 + maybe!(skip_first!( + word1(b'.', EImports::ExposingDot), + collection_e!( + word1(b'{', EImports::SetStart), + exposes_entry(EImports::Identifier), + word1(b',', EImports::SetEnd), + word1(b'}', EImports::SetEnd), + min_indent, + EImports::Space, + EImports::IndentSetEnd ) )) ), diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 1ab906ea70..1d831bbf6b 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -341,6 +341,8 @@ pub enum SyntaxError<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EHeader { Provides(EProvides, Row, Col), + Exposes(EExposes, Row, Col), + Imports(EImports, Row, Col), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -359,6 +361,38 @@ pub enum EProvides { Space(BadInputError, Row, Col), } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EExposes { + Exposes(Row, Col), + IndentExposes(Row, Col), + IndentListStart(Row, Col), + IndentListEnd(Row, Col), + ListStart(Row, Col), + ListEnd(Row, Col), + Identifier(Row, Col), + Space(BadInputError, Row, Col), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EImports { + Imports(Row, Col), + IndentImports(Row, Col), + IndentListStart(Row, Col), + IndentListEnd(Row, Col), + ListStart(Row, Col), + ListEnd(Row, Col), + Identifier(Row, Col), + ExposingDot(Row, Col), + ShorthandDot(Row, Col), + Shortname(Row, Col), + ModuleName(Row, Col), + Space(BadInputError, Row, Col), + IndentSetStart(Row, Col), + IndentSetEnd(Row, Col), + SetStart(Row, Col), + SetEnd(Row, Col), +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BadInputError { HasTab, @@ -1777,6 +1811,19 @@ macro_rules! one_of { }; } +#[macro_export] +macro_rules! maybe { + ($p1:expr) => { + move |arena: &'a bumpalo::Bump, state: $crate::parser::State<'a>| match $p1 + .parse(arena, state) + { + Ok((progress, value, state)) => Ok((progress, Some(value), state)), + Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)), + Err((NoProgress, _, state)) => Ok((NoProgress, None, state)), + } + }; +} + #[macro_export] macro_rules! one_of_with_error { ($toerror:expr; $p1:expr) => { diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 8e2dca66a3..93eebfcdf8 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -2482,7 +2482,15 @@ fn to_header_report<'a>( match *parse_problem { EHeader::Provides(provides, row, col) => { to_provides_report(alloc, filename, &provides, row, col) - } // _ => todo!("unhandled parse error {:?}", parse_problem), + } + + EHeader::Exposes(exposes, row, col) => { + to_exposes_report(alloc, filename, &exposes, row, col) + } + + EHeader::Imports(imports, row, col) => { + to_imports_report(alloc, filename, &imports, row, col) + } } } @@ -2520,12 +2528,162 @@ fn to_provides_report<'a>( } } + EProvides::Provides(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am in the middle of a header, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting the "), + alloc.keyword("provides"), + alloc.reflow(" keyword next, like "), + ]), + alloc + .parser_suggestion("provides [ Animal, default, tame ]") + .indent(4), + ]); + + Report { + filename, + doc, + title: "WEIRD PROVIDES".to_string(), + } + } + EProvides::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), _ => todo!("unhandled parse error {:?}", parse_problem), } } +fn to_exposes_report<'a>( + alloc: &'a RocDocAllocator<'a>, + filename: PathBuf, + parse_problem: &roc_parse::parser::EExposes, + start_row: Row, + start_col: Col, +) -> Report<'a> { + use roc_parse::parser::EExposes; + + match *parse_problem { + EExposes::Identifier(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc + .reflow(r"I am in the middle of parsing a exposes list, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![alloc.reflow( + "I was expecting a type name, value name or function name next, like ", + )]), + alloc + .parser_suggestion("exposes [ Animal, default, tame ]") + .indent(4), + ]); + + Report { + filename, + doc, + title: "WEIRD EXPOSES".to_string(), + } + } + + EExposes::Exposes(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am in the middle of a header, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting the "), + alloc.keyword("exposes"), + alloc.reflow(" keyword next, like "), + ]), + alloc + .parser_suggestion("exposes [ Animal, default, tame ]") + .indent(4), + ]); + + Report { + filename, + doc, + title: "WEIRD EXPOSES".to_string(), + } + } + + EExposes::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), + + _ => todo!("unhandled parse error {:?}", parse_problem), + } +} + +fn to_imports_report<'a>( + alloc: &'a RocDocAllocator<'a>, + filename: PathBuf, + parse_problem: &roc_parse::parser::EImports, + start_row: Row, + start_col: Col, +) -> Report<'a> { + use roc_parse::parser::EImports; + + match *parse_problem { + EImports::Identifier(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc + .reflow(r"I am in the middle of parsing a imports list, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![alloc.reflow( + "I was expecting a type name, value name or function name next, like ", + )]), + alloc + .parser_suggestion("imports [ Animal, default, tame ]") + .indent(4), + ]); + + Report { + filename, + doc, + title: "WEIRD EXPOSES".to_string(), + } + } + + EImports::Imports(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am in the middle of a header, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting the "), + alloc.keyword("imports"), + alloc.reflow(" keyword next, like "), + ]), + alloc + .parser_suggestion("imports [ Animal, default, tame ]") + .indent(4), + ]); + + Report { + filename, + doc, + title: "WEIRD IMPORTS".to_string(), + } + } + + EImports::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), + + _ => todo!("unhandled parse error {:?}", parse_problem), + } +} + fn to_space_report<'a>( alloc: &'a RocDocAllocator<'a>, filename: PathBuf, diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 2980c65927..bef4e30107 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -5758,9 +5758,7 @@ mod test_reporting { } #[test] - fn provides_to() { - // this is still bad, but changing the order and progress of other parsers should improve it - // down the line + fn provides_to_identifier() { report_header_problem_as( indoc!( r#" @@ -5787,4 +5785,32 @@ mod test_reporting { ), ) } + + #[test] + fn exposes_identifier() { + report_header_problem_as( + indoc!( + r#" + interface Foobar + exposes [ main, @Foo ] + imports [base.Task, Base64 ] + "# + ), + indoc!( + r#" + ── WEIRD EXPOSES ─────────────────────────────────────────────────────────────── + + I am in the middle of parsing a exposes list, but I got stuck here: + + 1│ interface Foobar + 2│ exposes [ main, @Foo ] + ^ + + I was expecting a type name, value name or function name next, like + + exposes [ Animal, default, tame ] + "# + ), + ) + } } From f25e88397ae383ad2ae68a78943f45fcbde6c561 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 15:23:45 +0100 Subject: [PATCH 17/74] finish import --- compiler/parse/src/module.rs | 53 +++++++++++++++--------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index ecac928a90..eaa9b495e6 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -624,18 +624,9 @@ fn imports<'a>() -> impl Parser< ), SyntaxError<'a>, > { - and!( - and!( - skip_second!(backtrackable(space1(1)), ascii_string("imports")), - space1(1) - ), - collection!( - ascii_char(b'['), - loc!(imports_entry()), - ascii_char(b','), - ascii_char(b']'), - 1 - ) + specialize( + |e, r, c| SyntaxError::Header(EHeader::Imports(e, r, c)), + imports_help(), ) } @@ -646,19 +637,27 @@ fn imports_help<'a>() -> impl Parser< (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), Vec<'a, Located>>, ), - SyntaxError<'a>, + EImports, > { + let min_indent = 1; + and!( - and!( - skip_second!(backtrackable(space1(1)), ascii_string("imports")), - space1(1) + spaces_around_keyword( + min_indent, + "imports", + EImports::Imports, + EImports::Space, + EImports::IndentImports, + EImports::IndentListStart ), - collection!( - ascii_char(b'['), + collection_e!( + word1(b'[', EImports::ListStart), loc!(imports_entry()), - ascii_char(b','), - ascii_char(b']'), - 1 + word1(b',', EImports::ListEnd), + word1(b']', EImports::ListEnd), + min_indent, + EImports::Space, + EImports::IndentListEnd ) ) } @@ -730,15 +729,6 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, SyntaxError<'a>> { } } -#[inline(always)] -#[allow(clippy::type_complexity)] -fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, SyntaxError<'a>> { - specialize( - |e, r, c| SyntaxError::Header(EHeader::Imports(e, r, c)), - imports_entry_help(), - ) -} - fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> { specialize(|_, r, c| EImports::Shortname(r, c), lowercase_ident()) } @@ -752,7 +742,8 @@ where specialize(move |_, r, c| to_expectation(r, c), module_name()) } -fn imports_entry_help<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> { +#[inline(always)] +fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> { let min_indent = 1; map_with_arena!( From 322c645714d7b1dc22c47fe940911c662b19c2e5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 16:48:42 +0100 Subject: [PATCH 18/74] refactor --- compiler/parse/src/module.rs | 379 ++++++++++++++------------ compiler/parse/src/parser.rs | 30 +- compiler/parse/tests/test_parse.rs | 123 ++++----- compiler/reporting/src/error/parse.rs | 54 +++- 4 files changed, 334 insertions(+), 252 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index eaa9b495e6..6a51c34c67 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -1,5 +1,5 @@ use crate::ast::{Attempting, CommentOrNewline, Def, Module}; -use crate::blankspace::{space0, space0_around, space0_before, space0_e, space1}; +use crate::blankspace::{space0, space0_around, space0_before, space0_before_e, space0_e, space1}; use crate::expr::def; use crate::header::{ package_entry, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry, @@ -10,7 +10,7 @@ use crate::parser::Progress::{self, *}; use crate::parser::{ self, ascii_char, ascii_string, backtrackable, end_of_file, loc, optional, peek_utf8_char, peek_utf8_char_at, specialize, unexpected, unexpected_eof, word1, Col, EExposes, EHeader, - EImports, EProvides, ParseResult, Parser, Row, State, SyntaxError, + EImports, EProvides, ERequires, ETypedIdent, ParseResult, Parser, Row, State, SyntaxError, }; use crate::string_literal; use crate::type_annotation; @@ -19,55 +19,46 @@ use bumpalo::Bump; use roc_region::all::Located; pub fn header<'a>() -> impl Parser<'a, Module<'a>, SyntaxError<'a>> { - one_of!(interface_module(), app_module(), platform_module()) -} - -#[inline(always)] -fn app_module<'a>() -> impl Parser<'a, Module<'a>, SyntaxError<'a>> { - map!(app_header(), |header| { Module::App { header } }) -} - -#[inline(always)] -fn platform_module<'a>() -> impl Parser<'a, Module<'a>, SyntaxError<'a>> { - map!(platform_header(), |header| { Module::Platform { header } }) -} - -#[inline(always)] -fn interface_module<'a>() -> impl Parser<'a, Module<'a>, SyntaxError<'a>> { - map!(interface_header(), |header| { - Module::Interface { header } - }) -} - -#[inline(always)] -pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, SyntaxError<'a>> { - parser::map( - and!( - skip_first!( - ascii_string("interface"), - and!(space1(1), loc!(module_name())) - ), - and!(exposes_values(), imports()) + one_of![ + map!( + skip_first!(debug!(ascii_string("app")), app_header()), + |header| { Module::App { header } } ), - |( - (after_interface_keyword, name), - ( - ((before_exposes, after_exposes), exposes), - ((before_imports, after_imports), imports), - ), - )| { - InterfaceHeader { - name, - exposes, - imports, - after_interface_keyword, - before_exposes, - after_exposes, - before_imports, - after_imports, - } - }, - ) + map!( + skip_first!(ascii_string("platform"), platform_header()), + |header| { Module::Platform { header } } + ), + map!( + skip_first!(debug!(ascii_string("interface")), interface_header()), + |header| { Module::Interface { header } } + ) + ] +} + +#[inline(always)] +fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, SyntaxError<'a>> { + |arena, state| { + let (_, after_interface_keyword, state) = space1(1).parse(arena, state)?; + let (_, name, state) = loc!(module_name()).parse(arena, state)?; + + let (_, ((before_exposes, after_exposes), exposes), state) = + exposes_values().parse(arena, state)?; + let (_, ((before_imports, after_imports), imports), state) = + imports().parse(arena, state)?; + + let header = InterfaceHeader { + name, + exposes, + imports, + after_interface_keyword, + before_exposes, + after_exposes, + before_imports, + after_imports, + }; + + Ok((MadeProgress, header, state)) + } } #[inline(always)] @@ -87,10 +78,12 @@ pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'a>> { ) } -pub fn parse_package_part<'a>( +fn parse_package_part<'a>( arena: &'a Bump, mut state: State<'a>, ) -> ParseResult<'a, &'a str, SyntaxError<'a>> { + use encode_unicode::CharExt; + let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) while !state.bytes.is_empty() { @@ -184,123 +177,109 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError<'a>> { } #[inline(always)] -pub fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, SyntaxError<'a>> { - map_with_arena!( - and!( - skip_first!( - ascii_string("app"), - and!( - space1(1), - loc!(crate::parser::specialize( - |e, r, c| SyntaxError::Expr(crate::parser::EExpr::Str(e, r, c)), - string_literal::parse() - )) +fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, SyntaxError<'a>> { + |arena, state| { + let (_, after_app_keyword, state) = space1(1).parse(arena, state)?; + let (_, name, state) = loc!(crate::parser::specialize( + |e, r, c| SyntaxError::Expr(crate::parser::EExpr::Str(e, r, c)), + string_literal::parse() + )) + .parse(arena, state)?; + + let (_, opt_pkgs, state) = maybe!(packages()).parse(arena, state)?; + let (_, opt_imports, state) = maybe!(imports()).parse(arena, state)?; + let (_, provides, state) = provides_to().parse(arena, state)?; + + let (before_packages, after_packages, package_entries) = match opt_pkgs { + Some(pkgs) => { + let pkgs: Packages<'a> = pkgs; // rustc must be told the type here + + ( + pkgs.before_packages_keyword, + pkgs.after_packages_keyword, + pkgs.entries, ) - ), - and!( - optional(packages()), - and!(optional(imports()), provides_to()) - ) - ), - |arena, ((after_app_keyword, name), (opt_pkgs, (opt_imports, provides)))| { - let (before_packages, after_packages, package_entries) = match opt_pkgs { - Some(pkgs) => { - let pkgs: Packages<'a> = pkgs; // rustc must be told the type here - - ( - pkgs.before_packages_keyword, - pkgs.after_packages_keyword, - pkgs.entries, - ) - } - None => (&[] as _, &[] as _, Vec::new_in(arena)), - }; - - // rustc must be told the type here - #[allow(clippy::type_complexity)] - let opt_imports: Option<( - (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>, - )> = opt_imports; - - let ((before_imports, after_imports), imports) = - opt_imports.unwrap_or_else(|| ((&[] as _, &[] as _), Vec::new_in(arena))); - let provides: ProvidesTo<'a> = provides; // rustc must be told the type here - - AppHeader { - name, - packages: package_entries, - imports, - provides: provides.entries, - to: provides.to, - after_app_keyword, - before_packages, - after_packages, - before_imports, - after_imports, - before_provides: provides.before_provides_keyword, - after_provides: provides.after_provides_keyword, - before_to: provides.before_to_keyword, - after_to: provides.after_to_keyword, } - } - ) + None => (&[] as _, &[] as _, Vec::new_in(arena)), + }; + + // rustc must be told the type here + #[allow(clippy::type_complexity)] + let opt_imports: Option<( + (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), + Vec<'a, Located>>, + )> = opt_imports; + + let ((before_imports, after_imports), imports) = + opt_imports.unwrap_or_else(|| ((&[] as _, &[] as _), Vec::new_in(arena))); + let provides: ProvidesTo<'a> = provides; // rustc must be told the type here + + let header = AppHeader { + name, + packages: package_entries, + imports, + provides: provides.entries, + to: provides.to, + after_app_keyword, + before_packages, + after_packages, + before_imports, + after_imports, + before_provides: provides.before_provides_keyword, + after_provides: provides.after_provides_keyword, + before_to: provides.before_to_keyword, + after_to: provides.after_to_keyword, + }; + + Ok((MadeProgress, header, state)) + } } #[inline(always)] -pub fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, SyntaxError<'a>> { - parser::map( - and!( - skip_first!( - ascii_string("platform"), - and!(space1(1), loc!(package_name())) - ), - and!( - and!( - and!(requires(), and!(exposes_modules(), packages())), - and!(imports(), provides_without_to()) - ), - effects() - ) - ), - |( - (after_platform_keyword, name), - ( - ( - ( - ((before_requires, after_requires), requires), - (((before_exposes, after_exposes), exposes), packages), - ), - ( - ((before_imports, after_imports), imports), - ((before_provides, after_provides), provides), - ), - ), - effects, - ), - )| { - PlatformHeader { - name, - requires, - exposes, - packages: packages.entries, - imports, - provides, - effects, - after_platform_keyword, - before_requires, - after_requires, - before_exposes, - after_exposes, - before_packages: packages.before_packages_keyword, - after_packages: packages.after_packages_keyword, - before_imports, - after_imports, - before_provides, - after_provides, - } - }, - ) +fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, SyntaxError<'a>> { + |arena, state| { + let (_, after_platform_keyword, state) = space1(1).parse(arena, state)?; + let (_, name, state) = loc!(package_name()).parse(arena, state)?; + + let (_, ((before_requires, after_requires), requires), state) = + requires().parse(arena, state)?; + + let (_, ((before_exposes, after_exposes), exposes), state) = + exposes_modules().parse(arena, state)?; + + let (_, packages, state) = packages().parse(arena, state)?; + + let (_, ((before_imports, after_imports), imports), state) = + imports().parse(arena, state)?; + + let (_, ((before_provides, after_provides), provides), state) = + provides_without_to().parse(arena, state)?; + + let (_, effects, state) = effects().parse(arena, state)?; + + let header = PlatformHeader { + name, + requires, + exposes, + packages: packages.entries, + imports, + provides, + effects, + after_platform_keyword, + before_requires, + after_requires, + before_exposes, + after_exposes, + before_packages: packages.before_packages_keyword, + after_packages: packages.after_packages_keyword, + before_imports, + after_imports, + before_provides, + after_provides, + }; + + Ok((MadeProgress, header, state)) + } } #[inline(always)] @@ -441,14 +420,39 @@ fn requires<'a>() -> impl Parser< ), SyntaxError<'a>, > { + specialize( + |e, r, c| SyntaxError::Header(EHeader::Requires(e, r, c)), + requires_help(), + ) +} + +#[inline(always)] +fn requires_help<'a>() -> impl Parser< + 'a, + ( + (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), + Vec<'a, Located>>, + ), + ERequires<'a>, +> { + let min_indent = 1; and!( - and!(skip_second!(space1(1), ascii_string("requires")), space1(1)), - collection!( - ascii_char(b'{'), - loc!(typed_ident()), - ascii_char(b','), - ascii_char(b'}'), - 1 + spaces_around_keyword( + min_indent, + "requires", + ERequires::Requires, + ERequires::Space, + ERequires::IndentRequires, + ERequires::IndentListStart + ), + collection_e!( + word1(b'{', ERequires::ListStart), + specialize(ERequires::TypedIdent, loc!(typed_ident_help())), + word1(b',', ERequires::ListEnd), + word1(b'}', ERequires::ListEnd), + min_indent, + ERequires::Space, + ERequires::IndentListEnd ) ) } @@ -729,6 +733,41 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, SyntaxError<'a>> { } } +fn typed_ident_help<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> { + // e.g. + // + // printLine : Str -> Effect {} + let min_indent = 0; + + map!( + and!( + and!( + loc!(specialize( + |_, r, c| ETypedIdent::Identifier(r, c), + lowercase_ident() + )), + space0_e(min_indent, ETypedIdent::Space, ETypedIdent::IndentHasType) + ), + skip_first!( + word1(b':', ETypedIdent::HasType), + space0_before_e( + specialize(ETypedIdent::Type, type_annotation::located_help(min_indent)), + min_indent, + ETypedIdent::Space, + ETypedIdent::IndentType, + ) + ) + ), + |((ident, spaces_before_colon), ann)| { + TypedIdent::Entry { + ident, + spaces_before_colon, + ann, + } + } + ) +} + fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> { specialize(|_, r, c| EImports::Shortname(r, c), lowercase_ident()) } diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 1d831bbf6b..affb41567b 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -334,15 +334,16 @@ pub enum SyntaxError<'a> { Type(Type<'a>), Pattern(EPattern<'a>), Expr(EExpr<'a>), - Header(EHeader), + Header(EHeader<'a>), Space(BadInputError), } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum EHeader { +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EHeader<'a> { Provides(EProvides, Row, Col), Exposes(EExposes, Row, Col), Imports(EImports, Row, Col), + Requires(ERequires<'a>, Row, Col), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -373,6 +374,29 @@ pub enum EExposes { Space(BadInputError, Row, Col), } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ERequires<'a> { + Requires(Row, Col), + IndentRequires(Row, Col), + IndentListStart(Row, Col), + IndentListEnd(Row, Col), + ListStart(Row, Col), + ListEnd(Row, Col), + TypedIdent(ETypedIdent<'a>, Row, Col), + Space(BadInputError, Row, Col), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ETypedIdent<'a> { + Space(BadInputError, Row, Col), + HasType(Row, Col), + IndentHasType(Row, Col), + Name(Row, Col), + Type(Type<'a>, Row, Col), + IndentType(Row, Col), + Identifier(Row, Col), +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EImports { Imports(Row, Col), diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index f48d03df13..084f169f12 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -23,14 +23,12 @@ mod test_parse { use roc_parse::ast::Pattern::{self, *}; use roc_parse::ast::StrLiteral::{self, *}; use roc_parse::ast::StrSegment::*; - use roc_parse::ast::{ - self, Attempting, Def, EscapedChar, Spaceable, TypeAnnotation, WhenBranch, - }; + use roc_parse::ast::{self, Def, EscapedChar, Spaceable, TypeAnnotation, WhenBranch}; use roc_parse::header::{ AppHeader, Effects, ExposesEntry, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry, PackageName, PackageOrPath, PlatformHeader, To, }; - use roc_parse::module::{app_header, interface_header, module_defs, platform_header}; + use roc_parse::module::module_defs; use roc_parse::parser::{Parser, State, SyntaxError}; use roc_parse::test_helpers::parse_expr_with; use roc_region::all::{Located, Region}; @@ -43,10 +41,9 @@ mod test_parse { assert_eq!(Ok(expected_expr), actual); } - fn assert_parsing_fails<'a>(input: &'a str, _reason: SyntaxError, _attempting: Attempting) { + fn assert_parsing_fails<'a>(input: &'a str, _reason: SyntaxError) { let arena = Bump::new(); let actual = parse_expr_with(&arena, input); - // let expected_fail = Fail { reason, attempting }; assert!(actual.is_err()); } @@ -291,7 +288,7 @@ mod test_parse { #[test] fn empty_source_file() { - assert_parsing_fails("", SyntaxError::Eof(Region::zero()), Attempting::Module); + assert_parsing_fails("", SyntaxError::Eof(Region::zero())); } #[test] @@ -308,11 +305,7 @@ mod test_parse { // Make sure it's longer than our maximum line length assert_eq!(too_long_str.len(), max_line_length + 1); - assert_parsing_fails( - &too_long_str, - SyntaxError::LineTooLong(0), - Attempting::Module, - ); + assert_parsing_fails(&too_long_str, SyntaxError::LineTooLong(0)); } // INT LITERALS @@ -2416,7 +2409,7 @@ mod test_parse { let imports = Vec::new_in(&arena); let provides = Vec::new_in(&arena); let module_name = StrLiteral::PlainLine("test-app"); - let expected = AppHeader { + let header = AppHeader { name: Located::new(0, 0, 4, 14, module_name), packages, imports, @@ -2433,16 +2426,15 @@ mod test_parse { after_to: &[], }; + let expected = roc_parse::ast::Module::App { header }; + let src = indoc!( r#" app "test-app" packages {} imports [] provides [] to blah "# ); - let actual = app_header() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + let actual = roc_parse::module::header() + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert_eq!(Ok(expected), actual); @@ -2457,7 +2449,7 @@ mod test_parse { let imports = Vec::new_in(&arena); let provides = Vec::new_in(&arena); let module_name = StrLiteral::PlainLine("test-app"); - let expected = AppHeader { + let header = AppHeader { name: Located::new(0, 0, 4, 14, module_name), packages, imports, @@ -2474,16 +2466,16 @@ mod test_parse { after_to: &[], }; + let expected = roc_parse::ast::Module::App { header }; + let src = indoc!( r#" app "test-app" provides [] to "./blah" "# ); - let actual = app_header() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + + let actual = roc_parse::module::header() + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert_eq!(Ok(expected), actual); @@ -2509,7 +2501,8 @@ mod test_parse { let provide_entry = Located::new(3, 3, 15, 24, Exposed("quicksort")); let provides = bumpalo::vec![in &arena; provide_entry]; let module_name = StrLiteral::PlainLine("quicksort"); - let expected = AppHeader { + + let header = AppHeader { name: Located::new(0, 0, 4, 15, module_name), packages, imports, @@ -2526,6 +2519,8 @@ mod test_parse { after_to: &[], }; + let expected = roc_parse::ast::Module::App { header }; + let src = indoc!( r#" app "quicksort" @@ -2535,11 +2530,8 @@ mod test_parse { "# ); - let actual = app_header() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + let actual = roc_parse::module::header() + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert_eq!(Ok(expected), actual); @@ -2560,7 +2552,7 @@ mod test_parse { spaces_after_effects_keyword: &[], spaces_after_type_name: &[], }; - let expected = PlatformHeader { + let header = PlatformHeader { name: Located::new(0, 0, 9, 23, pkg_name), requires: Vec::new_in(&arena), exposes: Vec::new_in(&arena), @@ -2581,12 +2573,11 @@ mod test_parse { after_provides: &[], }; + let expected = roc_parse::ast::Module::Platform { header }; + let src = "platform rtfeldman/blah requires {} exposes [] packages {} imports [] provides [] effects fx.Blah {}"; - let actual = platform_header() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + let actual = roc_parse::module::header() + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert_eq!(Ok(expected), actual); @@ -2621,7 +2612,7 @@ mod test_parse { spaces_after_effects_keyword: &[], spaces_after_type_name: &[], }; - let expected = PlatformHeader { + let header = PlatformHeader { name: Located::new(0, 0, 9, 19, pkg_name), requires: Vec::new_in(&arena), exposes: Vec::new_in(&arena), @@ -2642,6 +2633,8 @@ mod test_parse { after_provides: &[], }; + let expected = roc_parse::ast::Module::Platform { header }; + let src = indoc!( r#" platform foo/barbaz @@ -2653,11 +2646,8 @@ mod test_parse { effects fx.Effect {} "# ); - let actual = platform_header() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + let actual = roc_parse::module::header() + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert_eq!(Ok(expected), actual); @@ -2669,7 +2659,7 @@ mod test_parse { let exposes = Vec::new_in(&arena); let imports = Vec::new_in(&arena); let module_name = ModuleName::new("Foo"); - let expected = InterfaceHeader { + let header = InterfaceHeader { name: Located::new(0, 0, 10, 13, module_name), exposes, imports, @@ -2680,16 +2670,16 @@ mod test_parse { before_imports: &[], after_imports: &[], }; + + let expected = roc_parse::ast::Module::Interface { header }; + let src = indoc!( r#" interface Foo exposes [] imports [] "# ); - let actual = interface_header() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + let actual = roc_parse::module::header() + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert_eq!(Ok(expected), actual); @@ -2701,7 +2691,7 @@ mod test_parse { let exposes = Vec::new_in(&arena); let imports = Vec::new_in(&arena); let module_name = ModuleName::new("Foo.Bar.Baz"); - let expected = InterfaceHeader { + let header = InterfaceHeader { name: Located::new(0, 0, 10, 21, module_name), exposes, imports, @@ -2712,16 +2702,16 @@ mod test_parse { before_imports: &[], after_imports: &[], }; + + let expected = roc_parse::ast::Module::Interface { header }; + let src = indoc!( r#" interface Foo.Bar.Baz exposes [] imports [] "# ); - let actual = interface_header() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + let actual = roc_parse::module::header() + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert_eq!(Ok(expected), actual); @@ -2748,10 +2738,7 @@ mod test_parse { "# ); let actual = module_defs() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); // It should occur twice in the debug output - once for the pattern, @@ -2810,10 +2797,7 @@ mod test_parse { ); let actual = module_defs() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert_eq!(Ok(expected), actual); @@ -2833,10 +2817,7 @@ mod test_parse { ); let actual = module_defs() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert!(actual.is_ok()); @@ -2858,10 +2839,7 @@ mod test_parse { ); let actual = module_defs() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); assert!(actual.is_ok()); @@ -2882,10 +2860,7 @@ mod test_parse { ); let actual = module_defs() - .parse( - &arena, - State::new_in(&arena, src.as_bytes(), Attempting::Module), - ) + .parse(&arena, State::new_in(&arena, src.as_bytes())) .map(|tuple| tuple.1); dbg!(&actual); diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 93eebfcdf8..732e261e17 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -2473,23 +2473,27 @@ fn to_tapply_report<'a>( fn to_header_report<'a>( alloc: &'a RocDocAllocator<'a>, filename: PathBuf, - parse_problem: &roc_parse::parser::EHeader, + parse_problem: &roc_parse::parser::EHeader<'a>, _start_row: Row, _start_col: Col, ) -> Report<'a> { use roc_parse::parser::EHeader; - match *parse_problem { + match parse_problem { EHeader::Provides(provides, row, col) => { - to_provides_report(alloc, filename, &provides, row, col) + to_provides_report(alloc, filename, &provides, *row, *col) } EHeader::Exposes(exposes, row, col) => { - to_exposes_report(alloc, filename, &exposes, row, col) + to_exposes_report(alloc, filename, &exposes, *row, *col) } EHeader::Imports(imports, row, col) => { - to_imports_report(alloc, filename, &imports, row, col) + to_imports_report(alloc, filename, &imports, *row, *col) + } + + EHeader::Requires(requires, row, col) => { + to_requires_report(alloc, filename, &requires, *row, *col) } } } @@ -2684,6 +2688,46 @@ fn to_imports_report<'a>( } } +fn to_requires_report<'a>( + alloc: &'a RocDocAllocator<'a>, + filename: PathBuf, + parse_problem: &roc_parse::parser::ERequires<'a>, + start_row: Row, + start_col: Col, +) -> Report<'a> { + use roc_parse::parser::ERequires; + + match *parse_problem { + ERequires::Requires(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am in the middle of a header, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting the "), + alloc.keyword("requires"), + alloc.reflow(" keyword next, like "), + ]), + alloc + .parser_suggestion("requires { main : Task I64 Str }") + .indent(4), + ]); + + Report { + filename, + doc, + title: "MISSING REQUIRES".to_string(), + } + } + + ERequires::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), + + _ => todo!("unhandled parse error {:?}", parse_problem), + } +} + fn to_space_report<'a>( alloc: &'a RocDocAllocator<'a>, filename: PathBuf, From a80c429a0a27b9f1ee260e475783447bb7a87170 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 9 Mar 2021 18:41:32 +0100 Subject: [PATCH 19/74] replace ; with && to prevent discarding of exit codes --- Earthfile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Earthfile b/Earthfile index d4f41a014c..dc8254fb15 100644 --- a/Earthfile +++ b/Earthfile @@ -30,10 +30,10 @@ install-zig-llvm-valgrind-clippy-rustfmt: RUN wget https://sourceware.org/pub/valgrind/valgrind-3.16.1.tar.bz2 RUN tar -xf valgrind-3.16.1.tar.bz2 # need to cd every time, every command starts at WORKDIR - RUN cd valgrind-3.16.1; ./autogen.sh - RUN cd valgrind-3.16.1; ./configure --disable-dependency-tracking - RUN cd valgrind-3.16.1; make -j`nproc` - RUN cd valgrind-3.16.1; make install + RUN cd valgrind-3.16.1 && ./autogen.sh + RUN cd valgrind-3.16.1 && ./configure --disable-dependency-tracking + RUN cd valgrind-3.16.1 && make -j`nproc` + RUN cd valgrind-3.16.1 && make install # clippy RUN rustup component add clippy # rustfmt @@ -75,16 +75,16 @@ save-cache: FROM +install-zig-llvm-valgrind-clippy-rustfmt COPY +prepare-cache/recipe.json ./ RUN --mount=type=cache,target=$SCCACHE_DIR \ - cargo chef cook; sccache --show-stats # for clippy + cargo chef cook && sccache --show-stats # for clippy RUN --mount=type=cache,target=$SCCACHE_DIR \ - cargo chef cook --release --tests; sccache --show-stats + cargo chef cook --release --tests && sccache --show-stats SAVE ARTIFACT target SAVE ARTIFACT $CARGO_HOME cargo_home test-zig: FROM +install-zig-llvm-valgrind-clippy-rustfmt COPY --dir compiler/builtins/bitcode ./ - RUN cd bitcode; ./run-tests.sh; + RUN cd bitcode && ./run-tests.sh check-clippy: FROM +copy-dirs-and-cache @@ -101,7 +101,7 @@ test-rust: FROM +copy-dirs-and-cache ENV RUST_BACKTRACE=1 RUN --mount=type=cache,target=$SCCACHE_DIR \ - cargo test --release; sccache --show-stats + cargo test --release && sccache --show-stats test-all: BUILD +test-zig From 3c70eca1452ec497510767e2525859af5dcd8c03 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 18:51:56 +0100 Subject: [PATCH 20/74] refactor --- compiler/parse/src/module.rs | 18 +++++++++++++----- compiler/parse/src/parser.rs | 4 ++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 6a51c34c67..cdd2b44af8 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -37,14 +37,22 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, SyntaxError<'a>> { #[inline(always)] fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, SyntaxError<'a>> { + specialize(|e, _, _| SyntaxError::Header(e), interface_header_help()) +} + +#[inline(always)] +fn interface_header_help<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> { |arena, state| { - let (_, after_interface_keyword, state) = space1(1).parse(arena, state)?; - let (_, name, state) = loc!(module_name()).parse(arena, state)?; + let min_indent = 1; + + let (_, after_interface_keyword, state) = + space0_e(min_indent, EHeader::Space, EHeader::IndentStart).parse(arena, state)?; + let (_, name, state) = loc!(module_name_help(EHeader::ModuleName)).parse(arena, state)?; let (_, ((before_exposes, after_exposes), exposes), state) = - exposes_values().parse(arena, state)?; + specialize(EHeader::Exposes, exposes_values_help()).parse(arena, state)?; let (_, ((before_imports, after_imports), imports), state) = - imports().parse(arena, state)?; + specialize(EHeader::Imports, imports_help()).parse(arena, state)?; let header = InterfaceHeader { name, @@ -532,7 +540,7 @@ where { and!( skip_second!( - space0_e(min_indent, space_problem, indent_problem1), + backtrackable(space0_e(min_indent, space_problem, indent_problem1)), crate::parser::keyword_e(keyword, expectation) ), space0_e(min_indent, space_problem, indent_problem2) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index affb41567b..0627fcf2d1 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -344,6 +344,10 @@ pub enum EHeader<'a> { Exposes(EExposes, Row, Col), Imports(EImports, Row, Col), Requires(ERequires<'a>, Row, Col), + + Space(BadInputError, Row, Col), + ModuleName(Row, Col), + IndentStart(Row, Col), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] From e96a0215bec0b6d01524a6b83ac7c6dbf326ca8e Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 20:38:58 +0100 Subject: [PATCH 21/74] more errors for headers --- compiler/reporting/src/error/parse.rs | 31 ++++++++++++++++++++-- compiler/reporting/tests/test_reporting.rs | 26 ++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 732e261e17..73f5829161 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -2474,8 +2474,8 @@ fn to_header_report<'a>( alloc: &'a RocDocAllocator<'a>, filename: PathBuf, parse_problem: &roc_parse::parser::EHeader<'a>, - _start_row: Row, - _start_col: Col, + start_row: Row, + start_col: Col, ) -> Report<'a> { use roc_parse::parser::EHeader; @@ -2495,6 +2495,33 @@ fn to_header_report<'a>( EHeader::Requires(requires, row, col) => { to_requires_report(alloc, filename, &requires, *row, *col) } + + EHeader::IndentStart(row, col) => todo!(), + + EHeader::ModuleName(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); + let region = Region::from_row_col(*row, *col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing a header, but got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting a module name next, like "), + alloc.parser_suggestion("BigNum"), + alloc.reflow(" or "), + alloc.parser_suggestion("Main"), + alloc.reflow(". Module names must start with an uppercase letter."), + ]), + ]); + + Report { + filename, + doc, + title: "WEIRD MODULE NAME".to_string(), + } + } + + EHeader::Space(error, row, col) => to_space_report(alloc, filename, &error, *row, *col), } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index bef4e30107..d24ff00022 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -5813,4 +5813,30 @@ mod test_reporting { ), ) } + + #[test] + fn invalid_module_name() { + report_header_problem_as( + indoc!( + r#" + interface foobar + exposes [ main, @Foo ] + imports [base.Task, Base64 ] + "# + ), + indoc!( + r#" + ── WEIRD MODULE NAME ─────────────────────────────────────────────────────────── + + I am partway through parsing a header, but got stuck here: + + 1│ interface foobar + ^ + + I am expecting a module name next, like BigNum or Main. Module names + must start with an uppercase letter. + "# + ), + ) + } } From caafcc28889d45dfb098fb03f52ad71a40dae4e7 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 20:40:19 +0100 Subject: [PATCH 22/74] cleanup --- compiler/parse/src/module.rs | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index cdd2b44af8..efa13d2cc3 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -8,9 +8,9 @@ use crate::header::{ use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ - self, ascii_char, ascii_string, backtrackable, end_of_file, loc, optional, peek_utf8_char, - peek_utf8_char_at, specialize, unexpected, unexpected_eof, word1, Col, EExposes, EHeader, - EImports, EProvides, ERequires, ETypedIdent, ParseResult, Parser, Row, State, SyntaxError, + ascii_char, ascii_string, backtrackable, end_of_file, loc, peek_utf8_char, peek_utf8_char_at, + specialize, unexpected, unexpected_eof, word1, Col, EExposes, EHeader, EImports, EProvides, + ERequires, ETypedIdent, ParseResult, Parser, Row, State, SyntaxError, }; use crate::string_literal; use crate::type_annotation; @@ -50,7 +50,7 @@ fn interface_header_help<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<' let (_, name, state) = loc!(module_name_help(EHeader::ModuleName)).parse(arena, state)?; let (_, ((before_exposes, after_exposes), exposes), state) = - specialize(EHeader::Exposes, exposes_values_help()).parse(arena, state)?; + specialize(EHeader::Exposes, exposes_values()).parse(arena, state)?; let (_, ((before_imports, after_imports), imports), state) = specialize(EHeader::Imports, imports_help()).parse(arena, state)?; @@ -90,8 +90,6 @@ fn parse_package_part<'a>( arena: &'a Bump, mut state: State<'a>, ) -> ParseResult<'a, &'a str, SyntaxError<'a>> { - use encode_unicode::CharExt; - let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) while !state.bytes.is_empty() { @@ -467,21 +465,6 @@ fn requires_help<'a>() -> impl Parser< #[inline(always)] fn exposes_values<'a>() -> impl Parser< - 'a, - ( - (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>, - ), - SyntaxError<'a>, -> { - specialize( - |e, r, c| SyntaxError::Header(EHeader::Exposes(e, r, c)), - exposes_values_help(), - ) -} - -#[inline(always)] -fn exposes_values_help<'a>() -> impl Parser< 'a, ( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), From 3eddedc56654d28a007860006fae6537913e49d3 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 21:53:23 +0100 Subject: [PATCH 23/74] port effects --- compiler/parse/src/header.rs | 58 ++++++- compiler/parse/src/module.rs | 186 ++++++++++++--------- compiler/parse/src/parser.rs | 32 +++- compiler/reporting/src/error/parse.rs | 142 +++++++++++++--- compiler/reporting/tests/test_reporting.rs | 26 +++ 5 files changed, 340 insertions(+), 104 deletions(-) diff --git a/compiler/parse/src/header.rs b/compiler/parse/src/header.rs index d90501564b..e289e15179 100644 --- a/compiler/parse/src/header.rs +++ b/compiler/parse/src/header.rs @@ -1,13 +1,14 @@ +use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation}; use crate::blankspace::space0; use crate::ident::lowercase_ident; -use crate::module::package_name; -use crate::parser::{ascii_char, optional, Either, Parser, Progress::*, State, SyntaxError}; -use crate::string_literal; -use crate::{ - ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation}, - parser::specialize, +use crate::parser::{ + ascii_char, optional, peek_utf8_char, specialize, unexpected_eof, Either, ParseResult, Parser, + Progress, Progress::*, State, SyntaxError, }; +use crate::string_literal; +use bumpalo::collections::String; use bumpalo::collections::Vec; +use bumpalo::Bump; use inlinable_string::InlinableString; use roc_region::all::Loc; @@ -296,3 +297,48 @@ pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>, SyntaxError<' fn package_version<'a>() -> impl Parser<'a, Version<'a>, SyntaxError<'a>> { move |_, _| todo!("TODO parse package version") } + +#[inline(always)] +pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'a>> { + // e.g. rtfeldman/blah + // + // Package names and accounts can be capitalized and can contain dashes. + // They cannot contain underscores or other special characters. + // They must be ASCII. + + map!( + and!( + parse_package_part, + skip_first!(ascii_char(b'/'), parse_package_part) + ), + |(account, pkg)| { PackageName { account, pkg } } + ) +} + +fn parse_package_part<'a>( + arena: &'a Bump, + mut state: State<'a>, +) -> ParseResult<'a, &'a str, SyntaxError<'a>> { + let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) + + while !state.bytes.is_empty() { + match peek_utf8_char(&state) { + Ok((ch, bytes_parsed)) => { + if ch == '-' || ch.is_ascii_alphanumeric() { + part_buf.push(ch); + + state = state.advance_without_indenting(bytes_parsed)?; + } else { + let progress = Progress::progress_when(!part_buf.is_empty()); + return Ok((progress, part_buf.into_bump_str(), state)); + } + } + Err(reason) => { + let progress = Progress::progress_when(!part_buf.is_empty()); + return state.fail(arena, progress, reason); + } + } + } + + Err(unexpected_eof(arena, state, 0)) +} diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index efa13d2cc3..7b25e80def 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -2,20 +2,19 @@ use crate::ast::{Attempting, CommentOrNewline, Def, Module}; use crate::blankspace::{space0, space0_around, space0_before, space0_before_e, space0_e, space1}; use crate::expr::def; use crate::header::{ - package_entry, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry, - InterfaceHeader, ModuleName, PackageEntry, PackageName, PlatformHeader, To, TypedIdent, + package_entry, package_name, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry, + InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, To, TypedIdent, }; use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ ascii_char, ascii_string, backtrackable, end_of_file, loc, peek_utf8_char, peek_utf8_char_at, - specialize, unexpected, unexpected_eof, word1, Col, EExposes, EHeader, EImports, EProvides, - ERequires, ETypedIdent, ParseResult, Parser, Row, State, SyntaxError, + specialize, unexpected, word1, Col, EEffects, EExposes, EHeader, EImports, EPackages, + EProvides, ERequires, ETypedIdent, Parser, Row, State, SyntaxError, }; use crate::string_literal; use crate::type_annotation; use bumpalo::collections::{String, Vec}; -use bumpalo::Bump; use roc_region::all::Located; pub fn header<'a>() -> impl Parser<'a, Module<'a>, SyntaxError<'a>> { @@ -69,51 +68,6 @@ fn interface_header_help<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<' } } -#[inline(always)] -pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'a>> { - // e.g. rtfeldman/blah - // - // Package names and accounts can be capitalized and can contain dashes. - // They cannot contain underscores or other special characters. - // They must be ASCII. - - map!( - and!( - parse_package_part, - skip_first!(ascii_char(b'/'), parse_package_part) - ), - |(account, pkg)| { PackageName { account, pkg } } - ) -} - -fn parse_package_part<'a>( - arena: &'a Bump, - mut state: State<'a>, -) -> ParseResult<'a, &'a str, SyntaxError<'a>> { - let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) - - while !state.bytes.is_empty() { - match peek_utf8_char(&state) { - Ok((ch, bytes_parsed)) => { - if ch == '-' || ch.is_ascii_alphanumeric() { - part_buf.push(ch); - - state = state.advance_without_indenting(bytes_parsed)?; - } else { - let progress = Progress::progress_when(!part_buf.is_empty()); - return Ok((progress, part_buf.into_bump_str(), state)); - } - } - Err(reason) => { - let progress = Progress::progress_when(!part_buf.is_empty()); - return state.fail(arena, progress, reason); - } - } - } - - Err(unexpected_eof(arena, state, 0)) -} - #[inline(always)] pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError<'a>> { move |arena, mut state: State<'a>| { @@ -184,17 +138,28 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError<'a>> { #[inline(always)] fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, SyntaxError<'a>> { + specialize(|e, _, _| SyntaxError::Header(e), app_header_help()) +} + +#[inline(always)] +fn app_header_help<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { |arena, state| { - let (_, after_app_keyword, state) = space1(1).parse(arena, state)?; + let min_indent = 1; + + let (_, after_app_keyword, state) = + space0_e(min_indent, EHeader::Space, EHeader::IndentStart).parse(arena, state)?; let (_, name, state) = loc!(crate::parser::specialize( - |e, r, c| SyntaxError::Expr(crate::parser::EExpr::Str(e, r, c)), + EHeader::AppName, string_literal::parse() )) .parse(arena, state)?; - let (_, opt_pkgs, state) = maybe!(packages()).parse(arena, state)?; - let (_, opt_imports, state) = maybe!(imports()).parse(arena, state)?; - let (_, provides, state) = provides_to().parse(arena, state)?; + let (_, opt_pkgs, state) = + maybe!(specialize(EHeader::Packages, packages_help())).parse(arena, state)?; + let (_, opt_imports, state) = + maybe!(specialize(EHeader::Imports, imports_help())).parse(arena, state)?; + let (_, provides, state) = + specialize(EHeader::Provides, provides_to()).parse(arena, state)?; let (before_packages, after_packages, package_entries) = match opt_pkgs { Some(pkgs) => { @@ -304,14 +269,6 @@ struct ProvidesTo<'a> { after_to_keyword: &'a [CommentOrNewline<'a>], } -#[inline(always)] -fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, SyntaxError<'a>> { - specialize( - |e, r, c| SyntaxError::Header(EHeader::Provides(e, r, c)), - provides_to_help(), - ) -} - fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, SyntaxError<'a>> { one_of![ map!(lowercase_ident(), To::ExistingPackage), @@ -320,7 +277,7 @@ fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, SyntaxError<'a>> { } #[inline(always)] -fn provides_to_help<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> { +fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> { let min_indent = 1; map!( @@ -584,6 +541,7 @@ struct Packages<'a> { after_packages_keyword: &'a [CommentOrNewline<'a>], } +/* #[inline(always)] fn packages<'a>() -> impl Parser<'a, Packages<'a>, SyntaxError<'a>> { map!( @@ -609,6 +567,52 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, SyntaxError<'a>> { } ) } +*/ + +#[inline(always)] +fn packages<'a>() -> impl Parser<'a, Packages<'a>, SyntaxError<'a>> { + specialize( + |e, r, c| SyntaxError::Header(EHeader::Packages(e, r, c)), + packages_help(), + ) +} + +#[inline(always)] +fn packages_help<'a>() -> impl Parser<'a, Packages<'a>, EPackages> { + let min_indent = 1; + + map!( + and!( + spaces_around_keyword( + min_indent, + "packages", + EPackages::Packages, + EPackages::Space, + EPackages::IndentPackages, + EPackages::IndentListStart + ), + collection_e!( + word1(b'{', EPackages::ListStart), + specialize( + |_, r, c| EPackages::PackageEntry(r, c), + loc!(package_entry()) + ), + word1(b',', EPackages::ListEnd), + word1(b'}', EPackages::ListEnd), + min_indent, + EPackages::Space, + EPackages::IndentListEnd + ) + ), + |((before_packages_keyword, after_packages_keyword), entries)| { + Packages { + entries, + before_packages_keyword, + after_packages_keyword, + } + } + ) +} #[inline(always)] fn imports<'a>() -> impl Parser< @@ -659,23 +663,49 @@ fn imports_help<'a>() -> impl Parser< #[inline(always)] fn effects<'a>() -> impl Parser<'a, Effects<'a>, SyntaxError<'a>> { + specialize( + |e, r, c| SyntaxError::Header(EHeader::Effects(e, r, c)), + effects_help(), + ) +} + +#[inline(always)] +fn effects_help<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> { move |arena, state| { - let (_, spaces_before_effects_keyword, state) = - skip_second!(space1(0), ascii_string("effects")).parse(arena, state)?; - let (_, spaces_after_effects_keyword, state) = space1(0).parse(arena, state)?; + let min_indent = 1; + + let (_, (spaces_before_effects_keyword, spaces_after_effects_keyword), state) = + spaces_around_keyword( + min_indent, + "effects", + EEffects::Effects, + EEffects::Space, + EEffects::IndentEffects, + EEffects::IndentListStart, + ) + .parse(arena, state)?; // e.g. `fx.` - let (_, type_shortname, state) = - skip_second!(lowercase_ident(), ascii_char(b'.')).parse(arena, state)?; + let (_, type_shortname, state) = skip_second!( + specialize(|_, r, c| EEffects::Shorthand(r, c), lowercase_ident()), + word1(b'.', EEffects::ShorthandDot) + ) + .parse(arena, state)?; - let (_, (type_name, spaces_after_type_name), state) = - and!(uppercase_ident(), space1(0)).parse(arena, state)?; - let (_, entries, state) = collection!( - ascii_char(b'{'), - loc!(typed_ident()), - ascii_char(b','), - ascii_char(b'}'), - 1 + // the type name, e.g. Effects + let (_, (type_name, spaces_after_type_name), state) = and!( + specialize(|_, r, c| EEffects::TypeName(r, c), uppercase_ident()), + space0_e(min_indent, EEffects::Space, EEffects::IndentListStart) + ) + .parse(arena, state)?; + let (_, entries, state) = collection_e!( + word1(b'{', EEffects::ListStart), + specialize(EEffects::TypedIdent, loc!(typed_ident_help())), + word1(b',', EEffects::ListEnd), + word1(b'}', EEffects::ListEnd), + min_indent, + EEffects::Space, + EEffects::IndentListEnd ) .parse(arena, state)?; @@ -760,7 +790,7 @@ fn typed_ident_help<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> { } fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> { - specialize(|_, r, c| EImports::Shortname(r, c), lowercase_ident()) + specialize(|_, r, c| EImports::Shorthand(r, c), lowercase_ident()) } fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E> diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 0627fcf2d1..786454ac73 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -344,9 +344,12 @@ pub enum EHeader<'a> { Exposes(EExposes, Row, Col), Imports(EImports, Row, Col), Requires(ERequires<'a>, Row, Col), + Packages(EPackages, Row, Col), + Effects(EEffects<'a>, Row, Col), Space(BadInputError, Row, Col), ModuleName(Row, Col), + AppName(EString<'a>, Row, Col), IndentStart(Row, Col), } @@ -401,6 +404,33 @@ pub enum ETypedIdent<'a> { Identifier(Row, Col), } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EPackages { + Space(BadInputError, Row, Col), + Packages(Row, Col), + IndentPackages(Row, Col), + ListStart(Row, Col), + ListEnd(Row, Col), + IndentListStart(Row, Col), + IndentListEnd(Row, Col), + PackageEntry(Row, Col), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EEffects<'a> { + Space(BadInputError, Row, Col), + Effects(Row, Col), + IndentEffects(Row, Col), + ListStart(Row, Col), + ListEnd(Row, Col), + IndentListStart(Row, Col), + IndentListEnd(Row, Col), + TypedIdent(ETypedIdent<'a>, Row, Col), + ShorthandDot(Row, Col), + Shorthand(Row, Col), + TypeName(Row, Col), +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EImports { Imports(Row, Col), @@ -412,7 +442,7 @@ pub enum EImports { Identifier(Row, Col), ExposingDot(Row, Col), ShorthandDot(Row, Col), - Shortname(Row, Col), + Shorthand(Row, Col), ModuleName(Row, Col), Space(BadInputError, Row, Col), IndentSetStart(Row, Col), diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 73f5829161..f5d9ea662e 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -205,7 +205,7 @@ fn to_expr_report<'a>( let region = *region; let doc = alloc.stack(vec![ - alloc.reflow(r"I am in the middle of parsing a definition, but I got stuck here:"), + alloc.reflow(r"I am partway through parsing a definition, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("Looks like you are trying to define a function. "), @@ -381,7 +381,7 @@ fn to_expr_report<'a>( let region = Region::from_row_col(*row, *col); let doc = alloc.stack(vec![ - alloc.reflow(r"I am in the middle of parsing a definition, but I got stuck here:"), + alloc.reflow(r"I am partway through parsing a definition, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("Looks like you are trying to define a function. "), @@ -420,7 +420,7 @@ fn to_lambda_report<'a>( let doc = alloc.stack(vec![ alloc - .reflow(r"I am in the middle of parsing a function argument list, but I got stuck here:"), + .reflow(r"I am partway through parsing a function argument list, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I was expecting a "), @@ -441,7 +441,7 @@ fn to_lambda_report<'a>( let doc = alloc.stack(vec![ alloc - .reflow(r"I am in the middle of parsing a function argument list, but I got stuck here:"), + .reflow(r"I am partway through parsing a function argument list, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I was expecting a "), @@ -465,7 +465,7 @@ fn to_lambda_report<'a>( let doc = alloc.stack(vec![ alloc - .reflow(r"I am in the middle of parsing a function argument list, but I got stuck here:"), + .reflow(r"I am partway through parsing a function argument list, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I was expecting a "), @@ -486,7 +486,7 @@ fn to_lambda_report<'a>( let doc = alloc.stack(vec![ alloc - .reflow(r"I am in the middle of parsing a function argument list, but I got stuck here:"), + .reflow(r"I am partway through parsing a function argument list, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I was expecting a "), @@ -510,7 +510,7 @@ fn to_lambda_report<'a>( let doc = alloc.stack(vec![ alloc - .reflow(r"I am in the middle of parsing a function argument list, but I got stuck at this comma:"), + .reflow(r"I am partway through parsing a function argument list, but I got stuck at this comma:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I was expecting an argument pattern before this, "), @@ -530,7 +530,7 @@ fn to_lambda_report<'a>( let doc = alloc.stack(vec![ alloc - .reflow(r"I am in the middle of parsing a function argument list, but I got stuck here:"), + .reflow(r"I am partway through parsing a function argument list, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I was expecting an argument pattern before this, "), @@ -2496,6 +2496,14 @@ fn to_header_report<'a>( to_requires_report(alloc, filename, &requires, *row, *col) } + EHeader::Packages(packages, row, col) => { + to_packages_report(alloc, filename, &packages, *row, *col) + } + + EHeader::Effects(effects, row, col) => { + to_effects_report(alloc, filename, &effects, *row, *col) + } + EHeader::IndentStart(row, col) => todo!(), EHeader::ModuleName(row, col) => { @@ -2521,6 +2529,29 @@ fn to_header_report<'a>( } } + EHeader::AppName(_, row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); + let region = Region::from_row_col(*row, *col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing a header, but got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting an application name next, like "), + alloc.parser_suggestion("app \"main\""), + alloc.reflow(" or "), + alloc.parser_suggestion("app \"editor\""), + alloc.reflow(". App names are surrounded by quotation marks."), + ]), + ]); + + Report { + filename, + doc, + title: "WEIRD APP NAME".to_string(), + } + } + EHeader::Space(error, row, col) => to_space_report(alloc, filename, &error, *row, *col), } } @@ -2540,9 +2571,8 @@ fn to_provides_report<'a>( let region = Region::from_row_col(row, col); let doc = alloc.stack(vec![ - alloc.reflow( - r"I am in the middle of parsing a provides list, but I got stuck here:", - ), + alloc + .reflow(r"I am partway through parsing a provides list, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![alloc.reflow( "I was expecting a type name, value name or function name next, like ", @@ -2564,7 +2594,7 @@ fn to_provides_report<'a>( let region = Region::from_row_col(row, col); let doc = alloc.stack(vec![ - alloc.reflow(r"I am in the middle of a header, but I got stuck here:"), + alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I am expecting the "), @@ -2604,8 +2634,7 @@ fn to_exposes_report<'a>( let region = Region::from_row_col(row, col); let doc = alloc.stack(vec![ - alloc - .reflow(r"I am in the middle of parsing a exposes list, but I got stuck here:"), + alloc.reflow(r"I am partway through parsing a exposes list, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![alloc.reflow( "I was expecting a type name, value name or function name next, like ", @@ -2627,7 +2656,7 @@ fn to_exposes_report<'a>( let region = Region::from_row_col(row, col); let doc = alloc.stack(vec![ - alloc.reflow(r"I am in the middle of a header, but I got stuck here:"), + alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I am expecting the "), @@ -2667,8 +2696,7 @@ fn to_imports_report<'a>( let region = Region::from_row_col(row, col); let doc = alloc.stack(vec![ - alloc - .reflow(r"I am in the middle of parsing a imports list, but I got stuck here:"), + alloc.reflow(r"I am partway through parsing a imports list, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![alloc.reflow( "I was expecting a type name, value name or function name next, like ", @@ -2690,7 +2718,7 @@ fn to_imports_report<'a>( let region = Region::from_row_col(row, col); let doc = alloc.stack(vec![ - alloc.reflow(r"I am in the middle of a header, but I got stuck here:"), + alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I am expecting the "), @@ -2730,7 +2758,7 @@ fn to_requires_report<'a>( let region = Region::from_row_col(row, col); let doc = alloc.stack(vec![ - alloc.reflow(r"I am in the middle of a header, but I got stuck here:"), + alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ alloc.reflow("I am expecting the "), @@ -2755,6 +2783,82 @@ fn to_requires_report<'a>( } } +fn to_packages_report<'a>( + alloc: &'a RocDocAllocator<'a>, + filename: PathBuf, + parse_problem: &roc_parse::parser::EPackages, + start_row: Row, + start_col: Col, +) -> Report<'a> { + use roc_parse::parser::EPackages; + + match *parse_problem { + EPackages::Packages(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting the "), + alloc.keyword("packages"), + alloc.reflow(" keyword next, like "), + ]), + alloc.parser_suggestion("packages {}").indent(4), + ]); + + Report { + filename, + doc, + title: "MISSING PACKAGES".to_string(), + } + } + + EPackages::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), + + _ => todo!("unhandled parse error {:?}", parse_problem), + } +} + +fn to_effects_report<'a>( + alloc: &'a RocDocAllocator<'a>, + filename: PathBuf, + parse_problem: &roc_parse::parser::EEffects, + start_row: Row, + start_col: Col, +) -> Report<'a> { + use roc_parse::parser::EEffects; + + match *parse_problem { + EEffects::Effects(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting the "), + alloc.keyword("effects"), + alloc.reflow(" keyword next, like "), + ]), + alloc.parser_suggestion("effects {}").indent(4), + ]); + + Report { + filename, + doc, + title: "MISSING PACKAGES".to_string(), + } + } + + EEffects::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), + + _ => todo!("unhandled parse error {:?}", parse_problem), + } +} + fn to_space_report<'a>( alloc: &'a RocDocAllocator<'a>, filename: PathBuf, diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index d24ff00022..84befac1a5 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -5839,4 +5839,30 @@ mod test_reporting { ), ) } + + #[test] + fn invalid_app_name() { + report_header_problem_as( + indoc!( + r#" + app foobar + exposes [ main, @Foo ] + imports [base.Task, Base64 ] + "# + ), + indoc!( + r#" + ── WEIRD APP NAME ────────────────────────────────────────────────────────────── + + I am partway through parsing a header, but got stuck here: + + 1│ app foobar + ^ + + I am expecting an application name next, like app "main" or + app "editor". App names are surrounded by quotation marks. + "# + ), + ) + } } From 25d7caa89e5e4c7e252f627a417414f41c2afcfe Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 22:00:00 +0100 Subject: [PATCH 24/74] port platform module --- compiler/parse/src/module.rs | 30 ++++++++++++++++++++------- compiler/parse/src/parser.rs | 1 + compiler/reporting/src/error/parse.rs | 21 +++++++++++++++++++ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 7b25e80def..be55df9694 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -208,25 +208,39 @@ fn app_header_help<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { #[inline(always)] fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, SyntaxError<'a>> { + specialize(|e, _, _| SyntaxError::Header(e), platform_header_help()) +} + +#[inline(always)] +fn platform_header_help<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { |arena, state| { - let (_, after_platform_keyword, state) = space1(1).parse(arena, state)?; - let (_, name, state) = loc!(package_name()).parse(arena, state)?; + let min_indent = 1; + + let (_, after_platform_keyword, state) = + space0_e(min_indent, EHeader::Space, EHeader::IndentStart).parse(arena, state)?; + let (_, name, state) = loc!(specialize( + |_, r, c| EHeader::PlatformName(r, c), + package_name() + )) + .parse(arena, state)?; let (_, ((before_requires, after_requires), requires), state) = - requires().parse(arena, state)?; + specialize(EHeader::Requires, requires_help()).parse(arena, state)?; let (_, ((before_exposes, after_exposes), exposes), state) = - exposes_modules().parse(arena, state)?; + specialize(EHeader::Exposes, exposes_modules_help()).parse(arena, state)?; - let (_, packages, state) = packages().parse(arena, state)?; + let (_, packages, state) = + specialize(EHeader::Packages, packages_help()).parse(arena, state)?; let (_, ((before_imports, after_imports), imports), state) = - imports().parse(arena, state)?; + specialize(EHeader::Imports, imports_help()).parse(arena, state)?; let (_, ((before_provides, after_provides), provides), state) = - provides_without_to().parse(arena, state)?; + specialize(EHeader::Provides, provides_without_to_help()).parse(arena, state)?; - let (_, effects, state) = effects().parse(arena, state)?; + let (_, effects, state) = + specialize(EHeader::Effects, effects_help()).parse(arena, state)?; let header = PlatformHeader { name, diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 786454ac73..6848480b55 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -350,6 +350,7 @@ pub enum EHeader<'a> { Space(BadInputError, Row, Col), ModuleName(Row, Col), AppName(EString<'a>, Row, Col), + PlatformName(Row, Col), IndentStart(Row, Col), } diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index f5d9ea662e..d3886c27ed 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -2552,6 +2552,27 @@ fn to_header_report<'a>( } } + EHeader::PlatformName(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); + let region = Region::from_row_col(*row, *col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing a header, but got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting a platform name next, like "), + alloc.parser_suggestion("roc/core"), + alloc.reflow("."), + ]), + ]); + + Report { + filename, + doc, + title: "WEIRD MODULE NAME".to_string(), + } + } + EHeader::Space(error, row, col) => to_space_report(alloc, filename, &error, *row, *col), } } From d6ff5540dca30467c0fedbd32507933d0e6ffda3 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 22:02:34 +0100 Subject: [PATCH 25/74] cleanup --- compiler/parse/src/module.rs | 175 ++++------------------------------- 1 file changed, 20 insertions(+), 155 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index be55df9694..f5bb27afb8 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -1,5 +1,5 @@ use crate::ast::{Attempting, CommentOrNewline, Def, Module}; -use crate::blankspace::{space0, space0_around, space0_before, space0_before_e, space0_e, space1}; +use crate::blankspace::{space0_around, space0_before_e, space0_e}; use crate::expr::def; use crate::header::{ package_entry, package_name, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry, @@ -8,9 +8,9 @@ use crate::header::{ use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ - ascii_char, ascii_string, backtrackable, end_of_file, loc, peek_utf8_char, peek_utf8_char_at, - specialize, unexpected, word1, Col, EEffects, EExposes, EHeader, EImports, EPackages, - EProvides, ERequires, ETypedIdent, Parser, Row, State, SyntaxError, + ascii_string, backtrackable, end_of_file, loc, peek_utf8_char, peek_utf8_char_at, specialize, + unexpected, word1, Col, EEffects, EExposes, EHeader, EImports, EPackages, EProvides, ERequires, + ETypedIdent, Parser, Row, State, SyntaxError, }; use crate::string_literal; use crate::type_annotation; @@ -51,7 +51,7 @@ fn interface_header_help<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<' let (_, ((before_exposes, after_exposes), exposes), state) = specialize(EHeader::Exposes, exposes_values()).parse(arena, state)?; let (_, ((before_imports, after_imports), imports), state) = - specialize(EHeader::Imports, imports_help()).parse(arena, state)?; + specialize(EHeader::Imports, imports()).parse(arena, state)?; let header = InterfaceHeader { name, @@ -155,9 +155,9 @@ fn app_header_help<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { .parse(arena, state)?; let (_, opt_pkgs, state) = - maybe!(specialize(EHeader::Packages, packages_help())).parse(arena, state)?; + maybe!(specialize(EHeader::Packages, packages())).parse(arena, state)?; let (_, opt_imports, state) = - maybe!(specialize(EHeader::Imports, imports_help())).parse(arena, state)?; + maybe!(specialize(EHeader::Imports, imports())).parse(arena, state)?; let (_, provides, state) = specialize(EHeader::Provides, provides_to()).parse(arena, state)?; @@ -225,22 +225,20 @@ fn platform_header_help<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a> .parse(arena, state)?; let (_, ((before_requires, after_requires), requires), state) = - specialize(EHeader::Requires, requires_help()).parse(arena, state)?; + specialize(EHeader::Requires, requires()).parse(arena, state)?; let (_, ((before_exposes, after_exposes), exposes), state) = - specialize(EHeader::Exposes, exposes_modules_help()).parse(arena, state)?; + specialize(EHeader::Exposes, exposes_modules()).parse(arena, state)?; - let (_, packages, state) = - specialize(EHeader::Packages, packages_help()).parse(arena, state)?; + let (_, packages, state) = specialize(EHeader::Packages, packages()).parse(arena, state)?; let (_, ((before_imports, after_imports), imports), state) = - specialize(EHeader::Imports, imports_help()).parse(arena, state)?; + specialize(EHeader::Imports, imports()).parse(arena, state)?; let (_, ((before_provides, after_provides), provides), state) = - specialize(EHeader::Provides, provides_without_to_help()).parse(arena, state)?; + specialize(EHeader::Provides, provides_without_to()).parse(arena, state)?; - let (_, effects, state) = - specialize(EHeader::Effects, effects_help()).parse(arena, state)?; + let (_, effects, state) = specialize(EHeader::Effects, effects()).parse(arena, state)?; let header = PlatformHeader { name, @@ -296,7 +294,7 @@ fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> { map!( and!( - provides_without_to_help(), + provides_without_to(), and!( spaces_around_keyword( min_indent, @@ -330,21 +328,6 @@ fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> { #[inline(always)] fn provides_without_to<'a>() -> impl Parser< - 'a, - ( - (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>, - ), - SyntaxError<'a>, -> { - specialize( - |e, r, c| SyntaxError::Header(EHeader::Provides(e, r, c)), - provides_without_to_help(), - ) -} - -#[inline(always)] -fn provides_without_to_help<'a>() -> impl Parser< 'a, ( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), @@ -390,21 +373,6 @@ where #[inline(always)] fn requires<'a>() -> impl Parser< - 'a, - ( - (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>, - ), - SyntaxError<'a>, -> { - specialize( - |e, r, c| SyntaxError::Header(EHeader::Requires(e, r, c)), - requires_help(), - ) -} - -#[inline(always)] -fn requires_help<'a>() -> impl Parser< 'a, ( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), @@ -424,7 +392,7 @@ fn requires_help<'a>() -> impl Parser< ), collection_e!( word1(b'{', ERequires::ListStart), - specialize(ERequires::TypedIdent, loc!(typed_ident_help())), + specialize(ERequires::TypedIdent, loc!(typed_ident())), word1(b',', ERequires::ListEnd), word1(b'}', ERequires::ListEnd), min_indent, @@ -466,21 +434,6 @@ fn exposes_values<'a>() -> impl Parser< ) } -#[inline(always)] -fn exposes_modules<'a>() -> impl Parser< - 'a, - ( - (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>>, - ), - SyntaxError<'a>, -> { - specialize( - |e, r, c| SyntaxError::Header(EHeader::Exposes(e, r, c)), - exposes_modules_help(), - ) -} - fn spaces_around_keyword<'a, E>( min_indent: u16, keyword: &'static str, @@ -502,7 +455,7 @@ where } #[inline(always)] -fn exposes_modules_help<'a>() -> impl Parser< +fn exposes_modules<'a>() -> impl Parser< 'a, ( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), @@ -555,44 +508,8 @@ struct Packages<'a> { after_packages_keyword: &'a [CommentOrNewline<'a>], } -/* #[inline(always)] -fn packages<'a>() -> impl Parser<'a, Packages<'a>, SyntaxError<'a>> { - map!( - and!( - and!( - skip_second!(backtrackable(space1(1)), ascii_string("packages")), - space1(1) - ), - collection!( - ascii_char(b'{'), - loc!(package_entry()), - ascii_char(b','), - ascii_char(b'}'), - 1 - ) - ), - |((before_packages_keyword, after_packages_keyword), entries)| { - Packages { - entries, - before_packages_keyword, - after_packages_keyword, - } - } - ) -} -*/ - -#[inline(always)] -fn packages<'a>() -> impl Parser<'a, Packages<'a>, SyntaxError<'a>> { - specialize( - |e, r, c| SyntaxError::Header(EHeader::Packages(e, r, c)), - packages_help(), - ) -} - -#[inline(always)] -fn packages_help<'a>() -> impl Parser<'a, Packages<'a>, EPackages> { +fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages> { let min_indent = 1; map!( @@ -630,21 +547,6 @@ fn packages_help<'a>() -> impl Parser<'a, Packages<'a>, EPackages> { #[inline(always)] fn imports<'a>() -> impl Parser< - 'a, - ( - (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>, - ), - SyntaxError<'a>, -> { - specialize( - |e, r, c| SyntaxError::Header(EHeader::Imports(e, r, c)), - imports_help(), - ) -} - -#[inline(always)] -fn imports_help<'a>() -> impl Parser< 'a, ( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), @@ -676,15 +578,7 @@ fn imports_help<'a>() -> impl Parser< } #[inline(always)] -fn effects<'a>() -> impl Parser<'a, Effects<'a>, SyntaxError<'a>> { - specialize( - |e, r, c| SyntaxError::Header(EHeader::Effects(e, r, c)), - effects_help(), - ) -} - -#[inline(always)] -fn effects_help<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> { +fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> { move |arena, state| { let min_indent = 1; @@ -714,7 +608,7 @@ fn effects_help<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> { .parse(arena, state)?; let (_, entries, state) = collection_e!( word1(b'{', EEffects::ListStart), - specialize(EEffects::TypedIdent, loc!(typed_ident_help())), + specialize(EEffects::TypedIdent, loc!(typed_ident())), word1(b',', EEffects::ListEnd), word1(b'}', EEffects::ListEnd), min_indent, @@ -739,36 +633,7 @@ fn effects_help<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> { } #[inline(always)] -fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, SyntaxError<'a>> { - move |arena, state| { - // You must have a field name, e.g. "email" - let (_, ident, state) = loc!(lowercase_ident()).parse(arena, state)?; - - let (_, spaces_before_colon, state) = space0(0).parse(arena, state)?; - - let (_, ann, state) = skip_first!( - ascii_char(b':'), - space0_before(type_annotation::located(0), 0) - ) - .parse(arena, state)?; - - // e.g. - // - // printLine : Str -> Effect {} - - Ok(( - MadeProgress, - TypedIdent::Entry { - ident, - spaces_before_colon, - ann, - }, - state, - )) - } -} - -fn typed_ident_help<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> { +fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> { // e.g. // // printLine : Str -> Effect {} From 932dcf6da7542b8bbbc2bfb37381f72a29cb5708 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 22:34:10 +0100 Subject: [PATCH 26/74] faster module name parsing --- compiler/parse/src/module.rs | 139 +++++++++++++++++------------------ compiler/parse/src/parser.rs | 1 + 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index f5bb27afb8..3083c01ad7 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -1,4 +1,4 @@ -use crate::ast::{Attempting, CommentOrNewline, Def, Module}; +use crate::ast::{CommentOrNewline, Def, Module}; use crate::blankspace::{space0_around, space0_before_e, space0_e}; use crate::expr::def; use crate::header::{ @@ -8,27 +8,38 @@ use crate::header::{ use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ - ascii_string, backtrackable, end_of_file, loc, peek_utf8_char, peek_utf8_char_at, specialize, - unexpected, word1, Col, EEffects, EExposes, EHeader, EImports, EPackages, EProvides, ERequires, - ETypedIdent, Parser, Row, State, SyntaxError, + backtrackable, end_of_file, loc, specialize, word1, Col, EEffects, EExposes, EHeader, EImports, + EPackages, EProvides, ERequires, ETypedIdent, Parser, Row, State, SyntaxError, }; use crate::string_literal; use crate::type_annotation; -use bumpalo::collections::{String, Vec}; +use bumpalo::collections::Vec; use roc_region::all::Located; pub fn header<'a>() -> impl Parser<'a, Module<'a>, SyntaxError<'a>> { + specialize(|e, _, _| SyntaxError::Header(e), header_help()) +} + +fn header_help<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> { + use crate::parser::keyword_e; + one_of![ map!( - skip_first!(debug!(ascii_string("app")), app_header()), + skip_first!(keyword_e("app", EHeader::Start), app_header_help()), |header| { Module::App { header } } ), map!( - skip_first!(ascii_string("platform"), platform_header()), + skip_first!( + keyword_e("platform", EHeader::Start), + platform_header_help() + ), |header| { Module::Platform { header } } ), map!( - skip_first!(debug!(ascii_string("interface")), interface_header()), + skip_first!( + keyword_e("interface", EHeader::Start), + interface_header_help() + ), |header| { Module::Interface { header } } ) ] @@ -68,72 +79,60 @@ fn interface_header_help<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<' } } -#[inline(always)] -pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError<'a>> { - move |arena, mut state: State<'a>| { - match peek_utf8_char(&state) { - Ok((first_letter, bytes_parsed)) => { - if !first_letter.is_uppercase() { - return Err(unexpected(0, Attempting::Module, state)); - }; +fn chomp_module_name<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { + use encode_unicode::CharExt; - let mut buf = String::with_capacity_in(4, arena); + let mut chomped = 0; - buf.push(first_letter); - - state = state.advance_without_indenting(bytes_parsed)?; - - while !state.bytes.is_empty() { - match peek_utf8_char(&state) { - Ok((ch, bytes_parsed)) => { - // After the first character, only these are allowed: - // - // * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers - // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() - // * A '.' separating module parts - if ch.is_alphabetic() || ch.is_ascii_digit() { - state = state.advance_without_indenting(bytes_parsed)?; - - buf.push(ch); - } else if ch == '.' { - match peek_utf8_char_at(&state, 1) { - Ok((next, next_bytes_parsed)) => { - if next.is_uppercase() { - // If we hit another uppercase letter, keep going! - buf.push('.'); - buf.push(next); - - state = state.advance_without_indenting( - bytes_parsed + next_bytes_parsed, - )?; - } else { - // We have finished parsing the module name. - // - // There may be an identifier after this '.', - // e.g. "baz" in `Foo.Bar.baz` - return Ok(( - MadeProgress, - ModuleName::new(buf.into_bump_str()), - state, - )); - } - } - Err(reason) => return state.fail(arena, MadeProgress, reason), - } - } else { - // This is the end of the module name. We're done! - break; - } - } - Err(reason) => return state.fail(arena, MadeProgress, reason), - } - } - - Ok((MadeProgress, ModuleName::new(buf.into_bump_str()), state)) - } - Err(reason) => state.fail(arena, MadeProgress, reason), + if let Ok((first_letter, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + if first_letter.is_uppercase() { + chomped += width; + } else { + return Err(Progress::NoProgress); } } + + while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + // After the first character, only these are allowed: + // + // * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers + // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() + // * A '.' separating module parts + if ch.is_alphabetic() || ch.is_ascii_digit() { + chomped += width; + } else if ch == '.' { + chomped += width; + + if let Ok((first_letter, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + if first_letter.is_uppercase() { + chomped += width; + } else { + return Err(Progress::MadeProgress); + } + } + } else { + // we're done + break; + } + } + + let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; + + Ok(name) +} + +#[inline(always)] +fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> { + |_, mut state: State<'a>| match chomp_module_name(state.bytes) { + Ok(name) => { + let width = name.len(); + state.column += width as u16; + state.bytes = &state.bytes[width..]; + + Ok((MadeProgress, ModuleName::new(name), state)) + } + Err(progress) => Err((progress, (), state)), + } } #[inline(always)] diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 6848480b55..de6bbd2ee1 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -348,6 +348,7 @@ pub enum EHeader<'a> { Effects(EEffects<'a>, Row, Col), Space(BadInputError, Row, Col), + Start(Row, Col), ModuleName(Row, Col), AppName(EString<'a>, Row, Col), PlatformName(Row, Col), From 86ef187d1cd2e9cb4afd8deda8b80fcaf2de3031 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 22:39:32 +0100 Subject: [PATCH 27/74] cleanup --- compiler/parse/src/module.rs | 33 ++++----------------- compiler/reporting/src/error/parse.rs | 42 ++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 3083c01ad7..7c270bcd67 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -25,33 +25,22 @@ fn header_help<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> { one_of![ map!( - skip_first!(keyword_e("app", EHeader::Start), app_header_help()), + skip_first!(keyword_e("app", EHeader::Start), app_header()), |header| { Module::App { header } } ), map!( - skip_first!( - keyword_e("platform", EHeader::Start), - platform_header_help() - ), + skip_first!(keyword_e("platform", EHeader::Start), platform_header()), |header| { Module::Platform { header } } ), map!( - skip_first!( - keyword_e("interface", EHeader::Start), - interface_header_help() - ), + skip_first!(keyword_e("interface", EHeader::Start), interface_header()), |header| { Module::Interface { header } } ) ] } #[inline(always)] -fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, SyntaxError<'a>> { - specialize(|e, _, _| SyntaxError::Header(e), interface_header_help()) -} - -#[inline(always)] -fn interface_header_help<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> { +fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> { |arena, state| { let min_indent = 1; @@ -136,12 +125,7 @@ fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> { } #[inline(always)] -fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, SyntaxError<'a>> { - specialize(|e, _, _| SyntaxError::Header(e), app_header_help()) -} - -#[inline(always)] -fn app_header_help<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { +fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { |arena, state| { let min_indent = 1; @@ -206,12 +190,7 @@ fn app_header_help<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { } #[inline(always)] -fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, SyntaxError<'a>> { - specialize(|e, _, _| SyntaxError::Header(e), platform_header_help()) -} - -#[inline(always)] -fn platform_header_help<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { +fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { |arena, state| { let min_indent = 1; diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index d3886c27ed..d485467733 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -2504,7 +2504,47 @@ fn to_header_report<'a>( to_effects_report(alloc, filename, &effects, *row, *col) } - EHeader::IndentStart(row, col) => todo!(), + EHeader::IndentStart(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); + let region = Region::from_row_col(*row, *col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing a header, but got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![alloc.reflow("I may be confused by indentation.")]), + ]); + + Report { + filename, + doc, + title: "INCOMPLETE HEADER".to_string(), + } + } + + EHeader::Start(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); + let region = Region::from_row_col(*row, *col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am expecting a header, but got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting a module keyword next, one of "), + alloc.keyword("interface"), + alloc.reflow(", "), + alloc.keyword("app"), + alloc.reflow(" or "), + alloc.keyword("platform"), + alloc.reflow("."), + ]), + ]); + + Report { + filename, + doc, + title: "MISSING HEADER".to_string(), + } + } EHeader::ModuleName(row, col) => { let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); From bd61a03ae045cc4b502bea912460ec5265bc1a8f Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 23:06:17 +0100 Subject: [PATCH 28/74] port headers! --- compiler/parse/src/module.rs | 8 ++++++-- compiler/reporting/src/error/parse.rs | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 7c270bcd67..5e7a8e5535 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -8,7 +8,7 @@ use crate::header::{ use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ - backtrackable, end_of_file, loc, specialize, word1, Col, EEffects, EExposes, EHeader, EImports, + backtrackable, end_of_file, specialize, word1, Col, EEffects, EExposes, EHeader, EImports, EPackages, EProvides, ERequires, ETypedIdent, Parser, Row, State, SyntaxError, }; use crate::string_literal; @@ -95,6 +95,10 @@ fn chomp_module_name<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { if let Ok((first_letter, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { if first_letter.is_uppercase() { chomped += width; + } else if first_letter == '{' { + // the .{ starting a `Foo.{ bar, baz }` importing clauses + chomped -= width; + break; } else { return Err(Progress::MadeProgress); } @@ -246,7 +250,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { #[inline(always)] pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located>>, SyntaxError<'a>> { // force that we pare until the end of the input - skip_second!(zero_or_more!(space0_around(loc(def(0)), 0)), end_of_file()) + skip_second!(zero_or_more!(space0_around(loc!(def(0)), 0)), end_of_file()) } #[derive(Debug)] struct ProvidesTo<'a> { diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index d485467733..cfa73606b7 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -2800,6 +2800,29 @@ fn to_imports_report<'a>( EImports::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), + EImports::ModuleName(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing a header, but got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting a module name next, like "), + alloc.parser_suggestion("BigNum"), + alloc.reflow(" or "), + alloc.parser_suggestion("Main"), + alloc.reflow(". Module names must start with an uppercase letter."), + ]), + ]); + + Report { + filename, + doc, + title: "WEIRD MODULE NAME".to_string(), + } + } + _ => todo!("unhandled parse error {:?}", parse_problem), } } From 91271d0308e0b88f573adcacf8bb4c64f7d48ffc Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 9 Mar 2021 23:20:31 +0100 Subject: [PATCH 29/74] don't leak implementation details --- compiler/load/src/file.rs | 24 ++++++++--------- compiler/parse/src/module.rs | 12 ++++++--- compiler/parse/src/test_helpers.rs | 14 +--------- compiler/parse/tests/test_parse.rs | 41 +++++++++++++----------------- editor/src/lang/roc_file.rs | 6 ++--- 5 files changed, 42 insertions(+), 55 deletions(-) diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 7ae0aa7833..a92d075677 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2305,7 +2305,7 @@ fn load_pkg_config<'a>( let parse_start = SystemTime::now(); let bytes = arena.alloc(bytes_vec); let parse_state = parser::State::new_in(arena, bytes); - let parsed = roc_parse::module::header().parse(&arena, parse_state); + let parsed = roc_parse::module::parse_header(&arena, parse_state); let parse_header_duration = parse_start.elapsed().unwrap(); // Insert the first entries for this module's timings @@ -2319,19 +2319,19 @@ fn load_pkg_config<'a>( effect_module_timing.parse_header = parse_header_duration; match parsed { - Ok((_, ast::Module::Interface { header }, _parse_state)) => { + Ok((ast::Module::Interface { header }, _parse_state)) => { Err(LoadingProblem::UnexpectedHeader(format!( "expected platform/package module, got Interface with header\n{:?}", header ))) } - Ok((_, ast::Module::App { header }, _parse_state)) => { + Ok((ast::Module::App { header }, _parse_state)) => { Err(LoadingProblem::UnexpectedHeader(format!( "expected platform/package module, got App with header\n{:?}", header ))) } - Ok((_, ast::Module::Platform { header }, parser_state)) => { + Ok((ast::Module::Platform { header }, parser_state)) => { // make a Pkg-Config module that ultimately exposes `main` to the host let pkg_config_module_msg = fabricate_pkg_config_module( arena, @@ -2359,8 +2359,8 @@ fn load_pkg_config<'a>( Ok(Msg::Many(vec![effects_module_msg, pkg_config_module_msg])) } - Err((_, fail, _)) => Err(LoadingProblem::ParsingFailed( - fail.into_parse_problem(filename, bytes), + Err(fail) => Err(LoadingProblem::ParsingFailed( + SyntaxError::Header(fail).into_parse_problem(filename, bytes), )), } } @@ -2475,7 +2475,7 @@ fn parse_header<'a>( ) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> { let parse_start = SystemTime::now(); let parse_state = parser::State::new_in(arena, src_bytes); - let parsed = roc_parse::module::header().parse(&arena, parse_state); + let parsed = roc_parse::module::parse_header(&arena, parse_state); let parse_header_duration = parse_start.elapsed().unwrap(); // Insert the first entries for this module's timings @@ -2485,7 +2485,7 @@ fn parse_header<'a>( module_timing.parse_header = parse_header_duration; match parsed { - Ok((_, ast::Module::Interface { header }, parse_state)) => { + Ok((ast::Module::Interface { header }, parse_state)) => { let header_src = unsafe { let chomped = src_bytes.len() - parse_state.bytes.len(); std::str::from_utf8_unchecked(&src_bytes[..chomped]) @@ -2514,7 +2514,7 @@ fn parse_header<'a>( module_timing, )) } - Ok((_, ast::Module::App { header }, parse_state)) => { + Ok((ast::Module::App { header }, parse_state)) => { let mut pkg_config_dir = filename.clone(); pkg_config_dir.pop(); @@ -2623,7 +2623,7 @@ fn parse_header<'a>( }, } } - Ok((_, ast::Module::Platform { header }, _parse_state)) => Ok(fabricate_effects_module( + Ok((ast::Module::Platform { header }, _parse_state)) => Ok(fabricate_effects_module( arena, &"", module_ids, @@ -2632,8 +2632,8 @@ fn parse_header<'a>( header, module_timing, )), - Err((_, fail, _)) => Err(LoadingProblem::ParsingFailed( - fail.into_parse_problem(filename, src_bytes), + Err(fail) => Err(LoadingProblem::ParsingFailed( + SyntaxError::Header(fail).into_parse_problem(filename, src_bytes), )), } } diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 5e7a8e5535..fab4c3ab4d 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -16,11 +16,17 @@ use crate::type_annotation; use bumpalo::collections::Vec; use roc_region::all::Located; -pub fn header<'a>() -> impl Parser<'a, Module<'a>, SyntaxError<'a>> { - specialize(|e, _, _| SyntaxError::Header(e), header_help()) +pub fn parse_header<'a>( + arena: &'a bumpalo::Bump, + state: State<'a>, +) -> Result<(Module<'a>, State<'a>), EHeader<'a>> { + match header().parse(arena, state) { + Ok((_, module, state)) => Ok((module, state)), + Err((_, fail, _)) => Err(fail), + } } -fn header_help<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> { +fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> { use crate::parser::keyword_e; one_of![ diff --git a/compiler/parse/src/test_helpers.rs b/compiler/parse/src/test_helpers.rs index 7cfddd8d8c..7fce100612 100644 --- a/compiler/parse/src/test_helpers.rs +++ b/compiler/parse/src/test_helpers.rs @@ -1,7 +1,7 @@ use crate::ast; use crate::blankspace::space0_before; use crate::expr::expr; -use crate::module::{header, module_defs}; +use crate::module::module_defs; use crate::parser::{loc, Parser, State, SyntaxError}; use bumpalo::collections::Vec; use bumpalo::Bump; @@ -14,18 +14,6 @@ pub fn parse_expr_with<'a>( parse_loc_with(arena, input).map(|loc_expr| loc_expr.value) } -pub fn parse_header_with<'a>( - arena: &'a Bump, - input: &'a str, -) -> Result, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes()); - let answer = header().parse(arena, state); - - answer - .map(|(_, loc_expr, _)| loc_expr) - .map_err(|(_, fail, _)| fail) -} - #[allow(dead_code)] pub fn parse_defs_with<'a>( arena: &'a Bump, diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 084f169f12..58dfb65174 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -2433,9 +2433,8 @@ mod test_parse { app "test-app" packages {} imports [] provides [] to blah "# ); - let actual = roc_parse::module::header() - .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); } @@ -2474,9 +2473,8 @@ mod test_parse { "# ); - let actual = roc_parse::module::header() - .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); } @@ -2530,9 +2528,8 @@ mod test_parse { "# ); - let actual = roc_parse::module::header() - .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); } @@ -2576,9 +2573,8 @@ mod test_parse { let expected = roc_parse::ast::Module::Platform { header }; let src = "platform rtfeldman/blah requires {} exposes [] packages {} imports [] provides [] effects fx.Blah {}"; - let actual = roc_parse::module::header() - .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); } @@ -2646,9 +2642,8 @@ mod test_parse { effects fx.Effect {} "# ); - let actual = roc_parse::module::header() - .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); } @@ -2678,9 +2673,8 @@ mod test_parse { interface Foo exposes [] imports [] "# ); - let actual = roc_parse::module::header() - .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); } @@ -2710,9 +2704,8 @@ mod test_parse { interface Foo.Bar.Baz exposes [] imports [] "# ); - let actual = roc_parse::module::header() - .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); } @@ -2818,7 +2811,7 @@ mod test_parse { let actual = module_defs() .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + .map(|tuple| tuple.0); assert!(actual.is_ok()); } @@ -2840,7 +2833,7 @@ mod test_parse { let actual = module_defs() .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + .map(|tuple| tuple.0); assert!(actual.is_ok()); } @@ -2861,7 +2854,7 @@ mod test_parse { let actual = module_defs() .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.1); + .map(|tuple| tuple.0); dbg!(&actual); diff --git a/editor/src/lang/roc_file.rs b/editor/src/lang/roc_file.rs index f034a1f1b0..f551d987e2 100644 --- a/editor/src/lang/roc_file.rs +++ b/editor/src/lang/roc_file.rs @@ -37,10 +37,10 @@ impl<'a> File<'a> { let allocation = arena.alloc(bytes); let module_parse_state = parser::State::new_in(arena, allocation); - let parsed_module = roc_parse::module::header().parse(&arena, module_parse_state); + let parsed_module = roc_parse::module::parse_header(&arena, module_parse_state); match parsed_module { - Ok((_, module, state)) => { + Ok((module, state)) => { let parsed_defs = module_defs().parse(&arena, state); match parsed_defs { @@ -52,7 +52,7 @@ impl<'a> File<'a> { Err((_, error, _)) => Err(ReadError::ParseDefs(error)), } } - Err((_, error, _)) => Err(ReadError::ParseHeader(error)), + Err(error) => Err(ReadError::ParseHeader(SyntaxError::Header(error))), } } From 4b8f5fb4777c9f8f89541fc9348aaddf57c3a1ad Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 00:26:54 +0100 Subject: [PATCH 30/74] cleanup --- compiler/parse/src/header.rs | 95 +++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/compiler/parse/src/header.rs b/compiler/parse/src/header.rs index e289e15179..3bd83988ab 100644 --- a/compiler/parse/src/header.rs +++ b/compiler/parse/src/header.rs @@ -2,13 +2,10 @@ use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation}; use crate::blankspace::space0; use crate::ident::lowercase_ident; use crate::parser::{ - ascii_char, optional, peek_utf8_char, specialize, unexpected_eof, Either, ParseResult, Parser, - Progress, Progress::*, State, SyntaxError, + ascii_char, optional, specialize, Either, Parser, Progress, Progress::*, State, SyntaxError, }; use crate::string_literal; -use bumpalo::collections::String; use bumpalo::collections::Vec; -use bumpalo::Bump; use inlinable_string::InlinableString; use roc_region::all::Loc; @@ -274,24 +271,22 @@ pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, SyntaxError<'a>> } pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>, SyntaxError<'a>> { - map!( - either!( + one_of![ + map!( specialize( |e, r, c| SyntaxError::Expr(crate::parser::EExpr::Str(e, r, c)), string_literal::parse() ), + PackageOrPath::Path + ), + map!( and!( package_name(), skip_first!(one_or_more!(ascii_char(b' ')), package_version()) - ) - ), - |answer| { - match answer { - Either::First(str_literal) => PackageOrPath::Path(str_literal), - Either::Second((name, version)) => PackageOrPath::Package(name, version), - } - } - ) + ), + |(name, version)| { PackageOrPath::Package(name, version) } + ) + ] } fn package_version<'a>() -> impl Parser<'a, Version<'a>, SyntaxError<'a>> { @@ -300,45 +295,57 @@ fn package_version<'a>() -> impl Parser<'a, Version<'a>, SyntaxError<'a>> { #[inline(always)] pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'a>> { + use encode_unicode::CharExt; // e.g. rtfeldman/blah // // Package names and accounts can be capitalized and can contain dashes. // They cannot contain underscores or other special characters. // They must be ASCII. - map!( - and!( - parse_package_part, - skip_first!(ascii_char(b'/'), parse_package_part) - ), - |(account, pkg)| { PackageName { account, pkg } } - ) -} + |_, mut state: State<'a>| match chomp_package_part(state.bytes) { + Err(progress) => Err((progress, SyntaxError::ConditionFailed, state)), + Ok(account) => { + let mut chomped = account.len(); + if let Ok(('/', width)) = char::from_utf8_slice_start(&state.bytes[chomped..]) { + chomped += width; + match chomp_package_part(&state.bytes[chomped..]) { + Err(progress) => Err((progress, SyntaxError::ConditionFailed, state)), + Ok(pkg) => { + chomped += pkg.len(); -fn parse_package_part<'a>( - arena: &'a Bump, - mut state: State<'a>, -) -> ParseResult<'a, &'a str, SyntaxError<'a>> { - let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) + state.column += chomped as u16; + state.bytes = &state.bytes[chomped..]; - while !state.bytes.is_empty() { - match peek_utf8_char(&state) { - Ok((ch, bytes_parsed)) => { - if ch == '-' || ch.is_ascii_alphanumeric() { - part_buf.push(ch); - - state = state.advance_without_indenting(bytes_parsed)?; - } else { - let progress = Progress::progress_when(!part_buf.is_empty()); - return Ok((progress, part_buf.into_bump_str(), state)); + let value = PackageName { account, pkg }; + Ok((MadeProgress, value, state)) + } } - } - Err(reason) => { - let progress = Progress::progress_when(!part_buf.is_empty()); - return state.fail(arena, progress, reason); + } else { + Err((MadeProgress, SyntaxError::ConditionFailed, state)) } } } - - Err(unexpected_eof(arena, state, 0)) +} + +fn chomp_package_part<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { + use encode_unicode::CharExt; + + let mut chomped = 0; + + while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + if ch == '-' || ch.is_ascii_alphanumeric() { + chomped += width; + } else { + // we're done + break; + } + } + + if chomped == 0 { + Err(Progress::NoProgress) + } else { + let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; + + Ok(name) + } } From 2773639c7b28dd231ac20a8a84351cb88000fdab Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 01:02:24 +0100 Subject: [PATCH 31/74] port header parsing --- compiler/parse/src/header.rs | 88 +++++++++++++++++++++------ compiler/parse/src/module.rs | 32 ++++------ compiler/parse/src/parser.rs | 46 +++++++++----- compiler/reporting/src/error/parse.rs | 2 +- 4 files changed, 113 insertions(+), 55 deletions(-) diff --git a/compiler/parse/src/header.rs b/compiler/parse/src/header.rs index 3bd83988ab..6b3d8d7089 100644 --- a/compiler/parse/src/header.rs +++ b/compiler/parse/src/header.rs @@ -1,8 +1,9 @@ use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation}; -use crate::blankspace::space0; +use crate::blankspace::space0_e; use crate::ident::lowercase_ident; +use crate::parser::Progress::{self, *}; use crate::parser::{ - ascii_char, optional, specialize, Either, Parser, Progress, Progress::*, State, SyntaxError, + specialize, word1, EPackageEntry, EPackageName, EPackageOrPath, Parser, State, }; use crate::string_literal; use bumpalo::collections::Vec; @@ -240,18 +241,32 @@ impl<'a> Spaceable<'a> for PackageEntry<'a> { } } -pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, SyntaxError<'a>> { +pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, EPackageEntry<'a>> { move |arena, state| { // You may optionally have a package shorthand, // e.g. "uc" in `uc: roc/unicode 1.0.0` // // (Indirect dependencies don't have a shorthand.) - let (_, opt_shorthand, state) = optional(and!( - skip_second!(lowercase_ident(), ascii_char(b':')), - space0(1) + let min_indent = 1; + + let (_, opt_shorthand, state) = maybe!(and!( + skip_second!( + specialize(|_, r, c| EPackageEntry::Shorthand(r, c), lowercase_ident()), + word1(b':', EPackageEntry::Colon) + ), + space0_e( + min_indent, + EPackageEntry::Space, + EPackageEntry::IndentPackageOrPath + ) + )) + .parse(arena, state)?; + + let (_, package_or_path, state) = loc!(specialize( + EPackageEntry::BadPackageOrPath, + package_or_path() )) .parse(arena, state)?; - let (_, package_or_path, state) = loc!(package_or_path()).parse(arena, state)?; let entry = match opt_shorthand { Some((shorthand, spaces_after_shorthand)) => PackageEntry::Entry { @@ -270,31 +285,54 @@ pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, SyntaxError<'a>> } } -pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>, SyntaxError<'a>> { +pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>, EPackageOrPath<'a>> { one_of![ map!( - specialize( - |e, r, c| SyntaxError::Expr(crate::parser::EExpr::Str(e, r, c)), - string_literal::parse() - ), + specialize(EPackageOrPath::BadPath, string_literal::parse()), PackageOrPath::Path ), map!( and!( - package_name(), - skip_first!(one_or_more!(ascii_char(b' ')), package_version()) + specialize(EPackageOrPath::BadPackage, package_name()), + skip_first!(skip_spaces(), package_version()) ), |(name, version)| { PackageOrPath::Package(name, version) } ) ] } -fn package_version<'a>() -> impl Parser<'a, Version<'a>, SyntaxError<'a>> { +fn skip_spaces<'a, T>() -> impl Parser<'a, (), T> +where + T: 'a, +{ + |_, mut state: State<'a>| { + let mut chomped = 0; + let mut it = state.bytes.iter(); + + while let Some(b' ') = it.next() { + chomped += 1; + } + + if chomped == 0 { + Ok((NoProgress, (), state)) + } else { + state.column += chomped; + state.bytes = it.as_slice(); + + Ok((MadeProgress, (), state)) + } + } +} + +fn package_version<'a, T>() -> impl Parser<'a, Version<'a>, T> +where + T: 'a, +{ move |_, _| todo!("TODO parse package version") } #[inline(always)] -pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'a>> { +pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName> { use encode_unicode::CharExt; // e.g. rtfeldman/blah // @@ -303,13 +341,21 @@ pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'a>> { // They must be ASCII. |_, mut state: State<'a>| match chomp_package_part(state.bytes) { - Err(progress) => Err((progress, SyntaxError::ConditionFailed, state)), + Err(progress) => Err(( + progress, + EPackageName::Account(state.line, state.column), + state, + )), Ok(account) => { let mut chomped = account.len(); if let Ok(('/', width)) = char::from_utf8_slice_start(&state.bytes[chomped..]) { chomped += width; match chomp_package_part(&state.bytes[chomped..]) { - Err(progress) => Err((progress, SyntaxError::ConditionFailed, state)), + Err(progress) => Err(( + progress, + EPackageName::Pkg(state.line, state.column + chomped as u16), + state, + )), Ok(pkg) => { chomped += pkg.len(); @@ -321,7 +367,11 @@ pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'a>> { } } } else { - Err((MadeProgress, SyntaxError::ConditionFailed, state)) + Err(( + MadeProgress, + EPackageName::MissingSlash(state.line, state.column + chomped as u16), + state, + )) } } } diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index fab4c3ab4d..710ce22bba 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -206,11 +206,8 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { let (_, after_platform_keyword, state) = space0_e(min_indent, EHeader::Space, EHeader::IndentStart).parse(arena, state)?; - let (_, name, state) = loc!(specialize( - |_, r, c| EHeader::PlatformName(r, c), - package_name() - )) - .parse(arena, state)?; + let (_, name, state) = + loc!(specialize(EHeader::PlatformName, package_name())).parse(arena, state)?; let (_, ((before_requires, after_requires), requires), state) = specialize(EHeader::Requires, requires()).parse(arena, state)?; @@ -269,15 +266,18 @@ struct ProvidesTo<'a> { after_to_keyword: &'a [CommentOrNewline<'a>], } -fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, SyntaxError<'a>> { +fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> { one_of![ - map!(lowercase_ident(), To::ExistingPackage), - map!(package_or_path(), To::NewPackage) + specialize( + |_, r, c| EProvides::Identifier(r, c), + map!(lowercase_ident(), To::ExistingPackage) + ), + specialize(EProvides::Package, map!(package_or_path(), To::NewPackage)) ] } #[inline(always)] -fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> { +fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> { let min_indent = 1; map!( @@ -292,10 +292,7 @@ fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> { EProvides::IndentTo, EProvides::IndentListStart ), - loc!(specialize( - |_, r, c| EProvides::Package(r, c), - provides_to_package() - )) + loc!(provides_to_package()) ) ), |( @@ -321,7 +318,7 @@ fn provides_without_to<'a>() -> impl Parser< (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), Vec<'a, Located>>, ), - EProvides, + EProvides<'a>, > { let min_indent = 1; and!( @@ -497,7 +494,7 @@ struct Packages<'a> { } #[inline(always)] -fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages> { +fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> { let min_indent = 1; map!( @@ -512,10 +509,7 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages> { ), collection_e!( word1(b'{', EPackages::ListStart), - specialize( - |_, r, c| EPackages::PackageEntry(r, c), - loc!(package_entry()) - ), + specialize(EPackages::PackageEntry, loc!(package_entry())), word1(b',', EPackages::ListEnd), word1(b'}', EPackages::ListEnd), min_indent, diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index de6bbd2ee1..51cb45bc6e 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -340,23 +340,23 @@ pub enum SyntaxError<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum EHeader<'a> { - Provides(EProvides, Row, Col), + Provides(EProvides<'a>, Row, Col), Exposes(EExposes, Row, Col), Imports(EImports, Row, Col), Requires(ERequires<'a>, Row, Col), - Packages(EPackages, Row, Col), + Packages(EPackages<'a>, Row, Col), Effects(EEffects<'a>, Row, Col), Space(BadInputError, Row, Col), Start(Row, Col), ModuleName(Row, Col), AppName(EString<'a>, Row, Col), - PlatformName(Row, Col), + PlatformName(EPackageName, Row, Col), IndentStart(Row, Col), } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum EProvides { +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EProvides<'a> { Provides(Row, Col), To(Row, Col), IndentProvides(Row, Col), @@ -367,7 +367,7 @@ pub enum EProvides { ListStart(Row, Col), ListEnd(Row, Col), Identifier(Row, Col), - Package(Row, Col), + Package(EPackageOrPath<'a>, Row, Col), Space(BadInputError, Row, Col), } @@ -407,7 +407,7 @@ pub enum ETypedIdent<'a> { } #[derive(Debug, Clone, PartialEq, Eq)] -pub enum EPackages { +pub enum EPackages<'a> { Space(BadInputError, Row, Col), Packages(Row, Col), IndentPackages(Row, Col), @@ -415,7 +415,29 @@ pub enum EPackages { ListEnd(Row, Col), IndentListStart(Row, Col), IndentListEnd(Row, Col), - PackageEntry(Row, Col), + PackageEntry(EPackageEntry<'a>, Row, Col), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EPackageName { + MissingSlash(Row, Col), + Account(Row, Col), + Pkg(Row, Col), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EPackageOrPath<'a> { + BadPath(EString<'a>, Row, Col), + BadPackage(EPackageName, Row, Col), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EPackageEntry<'a> { + BadPackageOrPath(EPackageOrPath<'a>, Row, Col), + Shorthand(Row, Col), + Colon(Row, Col), + IndentPackageOrPath(Row, Col), + Space(BadInputError, Row, Col), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -562,14 +584,6 @@ pub enum EString<'a> { Format(&'a SyntaxError<'a>, Row, Col), } -// #[derive(Debug, Clone, Copy, PartialEq, Eq)] -// pub enum Escape { -// EscapeUnknown, -// BadUnicodeFormat(u16), -// BadUnicodeCode(u16), -// BadUnicodeLength(u16, i32, i32), -// } - #[derive(Debug, Clone, PartialEq, Eq)] pub enum ERecord<'a> { End(Row, Col), diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index cfa73606b7..177707a186 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -2592,7 +2592,7 @@ fn to_header_report<'a>( } } - EHeader::PlatformName(row, col) => { + EHeader::PlatformName(_, row, col) => { let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); let region = Region::from_row_col(*row, *col); From d2482246f2957168bba11fb4c2bc4ff348a9bd28 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 01:08:49 +0100 Subject: [PATCH 32/74] clippy --- compiler/parse/src/header.rs | 2 +- compiler/parse/src/module.rs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/parse/src/header.rs b/compiler/parse/src/header.rs index 6b3d8d7089..5442509877 100644 --- a/compiler/parse/src/header.rs +++ b/compiler/parse/src/header.rs @@ -377,7 +377,7 @@ pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName> { } } -fn chomp_package_part<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { +fn chomp_package_part(buffer: &[u8]) -> Result<&str, Progress> { use encode_unicode::CharExt; let mut chomped = 0; diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 710ce22bba..32d2fb2919 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -74,7 +74,7 @@ fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> { } } -fn chomp_module_name<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { +fn chomp_module_name(buffer: &[u8]) -> Result<&str, Progress> { use encode_unicode::CharExt; let mut chomped = 0; @@ -667,6 +667,11 @@ where fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> { let min_indent = 1; + type Temp<'a> = ( + (Option<&'a str>, ModuleName<'a>), + Option>>>, + ); + map_with_arena!( and!( and!( @@ -692,11 +697,7 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> { ) )) ), - |arena, - ((opt_shortname, module_name), opt_values): ( - (Option<&'a str>, ModuleName<'a>), - Option>>> - )| { + |arena, ((opt_shortname, module_name), opt_values): Temp<'a>| { let exposed_values = opt_values.unwrap_or_else(|| Vec::new_in(arena)); match opt_shortname { From 181958f284ef20718b448ac0db253ca8f1b34882 Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 9 Mar 2021 19:50:01 -0500 Subject: [PATCH 33/74] fix(List): i in list.zig needs to be reset --- compiler/builtins/bitcode/src/list.zig | 3 +++ compiler/test_gen/src/gen_list.rs | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index bd765e1eca..139163af1d 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -237,6 +237,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu // if the lists don't have equal length, we must consume the remaining elements // In this case we consume by (recursively) decrementing the elements if (list1.len() > output_length) { + i = output_length; while (i < list1.len()) : (i += 1) { const element_a = source_a + i * a_width; dec_a(element_a); @@ -244,6 +245,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu } if (list2.len() > output_length) { + i = output_length; while (i < list2.len()) : (i += 1) { const element_b = source_b + i * b_width; dec_b(element_b); @@ -251,6 +253,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu } if (list3.len() > output_length) { + i = output_length; while (i < list3.len()) : (i += 1) { const element_c = source_c + i * c_width; dec_c(element_c); diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 83d93eebf2..047dcfae54 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -587,8 +587,8 @@ fn list_map3_different_length() { indoc!( r#" List.map3 - ["a", "b", "d" ] - ["b"] + ["a", "b", "d"] + ["b", "x"] ["c"] (\a, b, c -> Str.concat a (Str.concat b c)) "# From 8ee99fa6be52543f610a221eaef2d412bc160cfd Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 12:03:33 +0100 Subject: [PATCH 34/74] fix up tests --- compiler/can/tests/helpers/mod.rs | 4 +-- compiler/fmt/tests/test_fmt.rs | 8 ++--- compiler/reporting/tests/test_reporting.rs | 35 +++++++++++----------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/compiler/can/tests/helpers/mod.rs b/compiler/can/tests/helpers/mod.rs index 679d341204..b1e2fb888e 100644 --- a/compiler/can/tests/helpers/mod.rs +++ b/compiler/can/tests/helpers/mod.rs @@ -8,7 +8,7 @@ use roc_can::operator; use roc_can::scope::Scope; use roc_collections::all::MutMap; use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds}; -use roc_parse::ast::{self, Attempting}; +use roc_parse::ast; use roc_parse::blankspace::space0_before; use roc_parse::parser::{loc, Parser, State, SyntaxError}; use roc_problem::can::Problem; @@ -30,7 +30,7 @@ pub fn parse_loc_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module); + let state = State::new_in(arena, input.trim().as_bytes()); let parser = space0_before(loc(roc_parse::expr::expr(0)), 0); let answer = parser.parse(&arena, state); diff --git a/compiler/fmt/tests/test_fmt.rs b/compiler/fmt/tests/test_fmt.rs index ee17739af7..d2faf53847 100644 --- a/compiler/fmt/tests/test_fmt.rs +++ b/compiler/fmt/tests/test_fmt.rs @@ -14,13 +14,13 @@ mod test_fmt { use roc_fmt::annotation::{Formattable, Newlines, Parens}; use roc_fmt::def::fmt_def; use roc_fmt::module::fmt_module; - use roc_parse::ast::{Attempting, Expr}; + use roc_parse::ast::Expr; use roc_parse::blankspace::space0_before; use roc_parse::module::{self, module_defs}; use roc_parse::parser::{Parser, State, SyntaxError}; fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module); + let state = State::new_in(arena, input.trim().as_bytes()); let parser = space0_before(loc!(roc_parse::expr::expr(0)), 0); let answer = parser.parse(&arena, state); @@ -55,8 +55,8 @@ mod test_fmt { let src = src.trim_end(); let expected = expected.trim_end(); - match module::header().parse(&arena, State::new_in(&arena, src.as_bytes(), Attempting::Module)) { - Ok((_, actual, state)) => { + match module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) { + Ok((actual, state)) => { let mut buf = String::new_in(&arena); fmt_module(&mut buf, &actual); diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 84befac1a5..dfd2bee8b8 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -182,15 +182,16 @@ mod test_reporting { let filename = filename_from_string(r"\code\proj\Main.roc"); let src_lines: Vec<&str> = src.split('\n').collect(); - use roc_parse::parser::Parser; - match roc_parse::module::header().parse(arena, state) { - Err((_, fail, _)) => { + match roc_parse::module::parse_header(arena, state) { + Err(fail) => { let interns = Interns::default(); let home = crate::helpers::test_home(); let alloc = RocDocAllocator::new(&src_lines, home, &interns); - let problem = fail.into_parse_problem(filename.clone(), src.as_bytes()); + use roc_parse::parser::SyntaxError; + let problem = + SyntaxError::Header(fail).into_parse_problem(filename.clone(), src.as_bytes()); let doc = parse_problem(&alloc, filename, 0, problem); callback(doc.pretty(&alloc).append(alloc.line()), buf) @@ -3214,7 +3215,7 @@ mod test_reporting { r#" ── ARGUMENTS BEFORE EQUALS ───────────────────────────────────────────────────── - I am in the middle of parsing a definition, but I got stuck here: + I am partway through parsing a definition, but I got stuck here: 1│ f x y = x ^^^ @@ -5037,13 +5038,13 @@ mod test_reporting { indoc!( r#" ── UNFINISHED ARGUMENT LIST ──────────────────────────────────────────────────── - - I am in the middle of parsing a function argument list, but I got - stuck at this comma: - + + I am partway through parsing a function argument list, but I got stuck + at this comma: + 1│ \a,,b -> 1 ^ - + I was expecting an argument pattern before this, so try adding an argument before the comma and see if that helps? "# @@ -5062,13 +5063,13 @@ mod test_reporting { indoc!( r#" ── UNFINISHED ARGUMENT LIST ──────────────────────────────────────────────────── - - I am in the middle of parsing a function argument list, but I got - stuck at this comma: - + + I am partway through parsing a function argument list, but I got stuck + at this comma: + 1│ \,b -> 1 ^ - + I was expecting an argument pattern before this, so try adding an argument before the comma and see if that helps? "# @@ -5772,7 +5773,7 @@ mod test_reporting { r#" ── WEIRD PROVIDES ────────────────────────────────────────────────────────────── - I am in the middle of parsing a provides list, but I got stuck here: + I am partway through parsing a provides list, but I got stuck here: 3│ imports [base.Task, Base64 ] 4│ provides [ main, @Foo ] to base @@ -5800,7 +5801,7 @@ mod test_reporting { r#" ── WEIRD EXPOSES ─────────────────────────────────────────────────────────────── - I am in the middle of parsing a exposes list, but I got stuck here: + I am partway through parsing a exposes list, but I got stuck here: 1│ interface Foobar 2│ exposes [ main, @Foo ] From 5e2848d10c99ee1b6b3330d48c7f9f386b16bbaf Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 12:55:56 +0100 Subject: [PATCH 35/74] use the EExpr identifier parser --- compiler/parse/src/expr.rs | 12 +++++------ compiler/parse/src/ident.rs | 39 ++++++++++++++++++++++++++++++++--- compiler/parse/src/pattern.rs | 5 +++-- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 5780967d54..1f69407eca 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -3,7 +3,7 @@ use crate::blankspace::{ line_comment, space0_after_e, space0_around_ee, space0_before_e, space0_e, space1_e, spaces_exactly_e, }; -use crate::ident::{ident, lowercase_ident, Ident}; +use crate::ident::{lowercase_ident, parse_ident_help, Ident}; use crate::keyword; use crate::parser::{ self, allocated, and_then_with_indent_level, ascii_char, backtrackable, map, newline_char, @@ -1967,11 +1967,11 @@ fn ident_then_args<'a>( } fn ident_without_apply_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> { - specialize_ref( - EExpr::Syntax, - then(loc!(ident()), move |arena, state, progress, loc_ident| { + then( + loc!(parse_ident_help), + move |arena, state, progress, loc_ident| { Ok((progress, ident_to_expr(arena, loc_ident.value), state)) - }), + }, ) } @@ -2163,7 +2163,7 @@ fn record_field_help<'a>( fn record_updateable_identifier<'a>() -> impl Parser<'a, Expr<'a>, ERecord<'a>> { specialize( |_, r, c| ERecord::Updateable(r, c), - map_with_arena!(ident(), ident_to_expr), + map_with_arena!(parse_ident_help, ident_to_expr), ) } diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index bb3143b53d..d1819f7f36 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -61,11 +61,44 @@ impl<'a> Ident<'a> { } } -pub fn ident<'a>() -> impl Parser<'a, Ident<'a>, SyntaxError<'a>> { - crate::parser::specialize(|e, _, _| SyntaxError::Expr(e), parse_ident_help) +fn chomp_identifier<'a, F>(pred: F, buffer: &[u8]) -> Result<&str, Progress> +where + F: Fn(char) -> bool, +{ + use encode_unicode::CharExt; + + let mut chomped = 0; + + match char::from_utf8_slice_start(&buffer[chomped..]) { + Ok((ch, width)) if pred(ch) => { + chomped += width; + } + _ => { + // no parse + return Err(Progress::NoProgress); + } + } + + while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + // After the first character, only these are allowed: + // + // * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers + // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() + // * A ':' indicating the end of the field + if ch.is_alphabetic() || ch.is_ascii_digit() { + chomped += width; + } else { + // we're done + break; + } + } + + let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; + + Ok(name) } -pub fn global_tag_or_ident<'a, F>(pred: F) -> impl Parser<'a, &'a str, SyntaxError<'a>> +fn global_tag_or_ident<'a, F>(pred: F) -> impl Parser<'a, &'a str, SyntaxError<'a>> where F: Fn(char) -> bool, { diff --git a/compiler/parse/src/pattern.rs b/compiler/parse/src/pattern.rs index 5e073623d8..146940d790 100644 --- a/compiler/parse/src/pattern.rs +++ b/compiler/parse/src/pattern.rs @@ -1,6 +1,6 @@ use crate::ast::Pattern; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; -use crate::ident::{ident, lowercase_ident, Ident}; +use crate::ident::{lowercase_ident, parse_ident_help, Ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ backtrackable, optional, specialize, specialize_ref, word1, EPattern, PInParens, PRecord, @@ -179,7 +179,8 @@ fn loc_ident_pattern_help<'a>( let original_state = state.clone(); let (_, loc_ident, state) = - specialize(|_, r, c| EPattern::Start(r, c), loc!(ident())).parse(arena, state)?; + specialize(|_, r, c| EPattern::Start(r, c), loc!(parse_ident_help)) + .parse(arena, state)?; match loc_ident.value { Ident::GlobalTag(tag) => { From 397e579d882e672026ca0052680f48e9b6df768e Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 12:58:32 +0100 Subject: [PATCH 36/74] chomp from source string --- compiler/parse/src/ident.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index d1819f7f36..59350b5897 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -316,6 +316,9 @@ pub fn parse_ident_help_help<'a>( let is_accessor_fn; let mut is_private_tag = false; + let bytes = state.bytes; + let mut chomped_capitalized = 0; + // Identifiers and accessor functions must start with either a letter or a dot. // If this starts with neither, it must be something else! match peek_utf8_char(&state) { @@ -420,6 +423,7 @@ pub fn parse_ident_help_help<'a>( } if is_capitalized { + chomped_capitalized += part_buf.len() + (chomped_capitalized != 0) as usize; capitalized_parts.push(part_buf.into_bump_str()); } else { noncapitalized_parts.push(part_buf.into_bump_str()); @@ -478,6 +482,7 @@ pub fn parse_ident_help_help<'a>( // Record the final parts. if is_capitalized { + chomped_capitalized += part_buf.len() + (chomped_capitalized != 0) as usize; capitalized_parts.push(part_buf.into_bump_str()); } else { noncapitalized_parts.push(part_buf.into_bump_str()); @@ -536,8 +541,15 @@ pub fn parse_ident_help_help<'a>( )); } else { // We have multiple noncapitalized parts, so this must be field access. + let module_name = if capitalized_parts.len() == 0 { + "" + } else { + let chomped = chomped_capitalized; + unsafe { std::str::from_utf8_unchecked(&bytes[..chomped]) } + }; + Ident::Access { - module_name: join_module_parts(arena, capitalized_parts.into_bump_slice()), + module_name, parts: noncapitalized_parts.into_bump_slice(), } }; From fd82f1d45b48d66f9e4ea66e2394649ba56a7c74 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 13:02:32 +0100 Subject: [PATCH 37/74] cparts --- compiler/parse/src/ident.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 59350b5897..3929260de8 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -318,6 +318,7 @@ pub fn parse_ident_help_help<'a>( let bytes = state.bytes; let mut chomped_capitalized = 0; + let mut cparts = 0; // Identifiers and accessor functions must start with either a letter or a dot. // If this starts with neither, it must be something else! @@ -424,6 +425,7 @@ pub fn parse_ident_help_help<'a>( if is_capitalized { chomped_capitalized += part_buf.len() + (chomped_capitalized != 0) as usize; + cparts += 1; capitalized_parts.push(part_buf.into_bump_str()); } else { noncapitalized_parts.push(part_buf.into_bump_str()); @@ -468,7 +470,7 @@ pub fn parse_ident_help_help<'a>( // If we made it this far and don't have a next_char, then necessarily // we have consumed a '.' char previously. let fail = if noncapitalized_parts.is_empty() { - if capitalized_parts.is_empty() { + if cparts == 0 { BadIdent::StrayDot(state.line, state.column) } else { BadIdent::WeirdDotQualified(state.line, state.column) @@ -483,6 +485,7 @@ pub fn parse_ident_help_help<'a>( // Record the final parts. if is_capitalized { chomped_capitalized += part_buf.len() + (chomped_capitalized != 0) as usize; + cparts += 1; capitalized_parts.push(part_buf.into_bump_str()); } else { noncapitalized_parts.push(part_buf.into_bump_str()); @@ -491,7 +494,7 @@ pub fn parse_ident_help_help<'a>( let answer = if is_accessor_fn { // Handle accessor functions first because they have the strictest requirements. // Accessor functions may have exactly 1 noncapitalized part, and no capitalzed parts. - if capitalized_parts.is_empty() && noncapitalized_parts.len() == 1 && !is_private_tag { + if cparts == 0 && noncapitalized_parts.len() == 1 && !is_private_tag { let value = noncapitalized_parts.iter().next().unwrap(); Ident::AccessorFunction(value) @@ -506,7 +509,7 @@ pub fn parse_ident_help_help<'a>( // We have capitalized parts only, so this must be a tag. match capitalized_parts.first() { Some(value) => { - if capitalized_parts.len() == 1 { + if cparts == 1 { if is_private_tag { Ident::PrivateTag(value) } else { @@ -541,7 +544,7 @@ pub fn parse_ident_help_help<'a>( )); } else { // We have multiple noncapitalized parts, so this must be field access. - let module_name = if capitalized_parts.len() == 0 { + let module_name = if cparts == 0 { "" } else { let chomped = chomped_capitalized; From 058551a2246cff55bfcb1269b69c03473264cccd Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 13:37:35 +0100 Subject: [PATCH 38/74] remove capitalized_parts --- compiler/parse/src/ident.rs | 40 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 3929260de8..2b9e943131 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -310,7 +310,6 @@ pub fn parse_ident_help_help<'a>( mut state: State<'a>, ) -> ParseResult<'a, (Ident<'a>, Option), BadIdent> { let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) - let mut capitalized_parts: Vec<&'a str> = Vec::new_in(arena); let mut noncapitalized_parts: Vec<&'a str> = Vec::new_in(arena); let mut is_capitalized; let is_accessor_fn; @@ -426,7 +425,6 @@ pub fn parse_ident_help_help<'a>( if is_capitalized { chomped_capitalized += part_buf.len() + (chomped_capitalized != 0) as usize; cparts += 1; - capitalized_parts.push(part_buf.into_bump_str()); } else { noncapitalized_parts.push(part_buf.into_bump_str()); } @@ -486,7 +484,6 @@ pub fn parse_ident_help_help<'a>( if is_capitalized { chomped_capitalized += part_buf.len() + (chomped_capitalized != 0) as usize; cparts += 1; - capitalized_parts.push(part_buf.into_bump_str()); } else { noncapitalized_parts.push(part_buf.into_bump_str()); } @@ -507,24 +504,8 @@ pub fn parse_ident_help_help<'a>( } } else if noncapitalized_parts.is_empty() { // We have capitalized parts only, so this must be a tag. - match capitalized_parts.first() { - Some(value) => { - if cparts == 1 { - if is_private_tag { - Ident::PrivateTag(value) - } else { - Ident::GlobalTag(value) - } - } else { - // This is a qualified tag, which is not allowed! - return Err(( - MadeProgress, - BadIdent::QualifiedTag(state.line, state.column), - state, - )); - } - } - None => { + match cparts { + 0 => { // We had neither capitalized nor noncapitalized parts, // yet we made it this far. The only explanation is that this was // a stray '.' drifting through the cosmos. @@ -534,6 +515,23 @@ pub fn parse_ident_help_help<'a>( state, )); } + 1 => { + let chomped = chomped_capitalized; + let value = unsafe { std::str::from_utf8_unchecked(&bytes[..chomped]) }; + if is_private_tag { + Ident::PrivateTag(value) + } else { + Ident::GlobalTag(value) + } + } + _ => { + // This is a qualified tag, which is not allowed! + return Err(( + MadeProgress, + BadIdent::QualifiedTag(state.line, state.column), + state, + )); + } } } else if is_private_tag { // This is qualified field access with an '@' in front, which does not make sense! From edd54ab4ab1a6d1e13b77f8af49811584e96fd40 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 15:23:11 +0100 Subject: [PATCH 39/74] specialize accessor parsing --- compiler/parse/src/ident.rs | 93 +++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 2b9e943131..639fca2f3b 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -305,7 +305,44 @@ pub enum BadIdent { /// /// This is separate from the `ident` Parser because string interpolation /// wants to use it this way. -pub fn parse_ident_help_help<'a>( + +/// a `.foo` accessor function +fn chomp_accessor(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> { + // assumes the leading `.` has been chomped already + use encode_unicode::CharExt; + + let mut chomped = 0; + + if let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + if ch.is_lowercase() { + chomped += width; + } else { + return Err(BadIdent::StrayDot(row, col + 1)); + } + } + + while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + if ch.is_alphabetic() || ch.is_ascii_digit() { + chomped += width; + } else { + // we're done + break; + } + } + + if chomped == 0 { + Err(BadIdent::StrayDot(row, col + 1)) + } else if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[chomped..]) { + Err(BadIdent::WeirdAccessor(row, col)) + } else { + let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; + + dbg!(name); + Ok(name) + } +} + +fn parse_ident_help_help<'a>( arena: &'a Bump, mut state: State<'a>, ) -> ParseResult<'a, (Ident<'a>, Option), BadIdent> { @@ -319,22 +356,36 @@ pub fn parse_ident_help_help<'a>( let mut chomped_capitalized = 0; let mut cparts = 0; + let mut chomped_part_buf = 0; + let mut chomped = 0; + // Identifiers and accessor functions must start with either a letter or a dot. // If this starts with neither, it must be something else! match peek_utf8_char(&state) { Ok((first_ch, bytes_parsed)) => { if first_ch.is_alphabetic() { part_buf.push(first_ch); + chomped_part_buf += bytes_parsed; is_capitalized = first_ch.is_uppercase(); is_accessor_fn = false; state = advance_state!(state, bytes_parsed)?; } else if first_ch == '.' { - is_capitalized = false; - is_accessor_fn = true; + match chomp_accessor(&state.bytes[1..], state.line, state.column) { + Ok(accessor) => { + let bytes_parsed = 1 + accessor.len(); - state = advance_state!(state, bytes_parsed)?; + state = advance_state!(state, bytes_parsed)?; + + return Ok(( + MadeProgress, + (Ident::AccessorFunction(accessor), None), + state, + )); + } + Err(fail) => return Err((MadeProgress, fail, state)), + } } else if first_ch == '@' { state = advance_state!(state, bytes_parsed)?; @@ -346,6 +397,7 @@ pub fn parse_ident_help_help<'a>( part_buf.push('@'); part_buf.push(next_ch); + chomped_part_buf += 1 + next_bytes_parsed; is_private_tag = true; is_capitalized = true; @@ -377,7 +429,7 @@ pub fn parse_ident_help_help<'a>( while !state.bytes.is_empty() { match peek_utf8_char(&state) { - Ok((ch, bytes_parsed)) => { + Ok((ch, width)) => { // After the first character, only these are allowed: // // * Unicode alphabetic chars - you might name a variable `鹏` if that's clear to your readers @@ -390,6 +442,7 @@ pub fn parse_ident_help_help<'a>( } part_buf.push(ch); + chomped_part_buf += width; } else if ch.is_ascii_digit() { // Parts may not start with numbers! if part_buf.is_empty() { @@ -401,6 +454,7 @@ pub fn parse_ident_help_help<'a>( } part_buf.push(ch); + chomped_part_buf += width; } else if ch == '.' { // There are two posssible errors here: // @@ -423,19 +477,27 @@ pub fn parse_ident_help_help<'a>( } if is_capitalized { - chomped_capitalized += part_buf.len() + (chomped_capitalized != 0) as usize; + chomped_capitalized += + chomped_part_buf + (chomped_capitalized != 0) as usize; cparts += 1; } else { - noncapitalized_parts.push(part_buf.into_bump_str()); + let value = unsafe { + std::str::from_utf8_unchecked( + &bytes[chomped..chomped + chomped_part_buf], + ) + }; + noncapitalized_parts.push(value); } // Now that we've recorded the contents of the current buffer, reset it. part_buf = String::new_in(arena); + chomped += chomped_part_buf + 1; + chomped_part_buf = 0; } else if ch == '_' { // we don't allow underscores in the middle of an identifier // but still parse them (and generate a malformed identifier) // to give good error messages for this case - state = advance_state!(state, bytes_parsed)?; + state = advance_state!(state, width)?; return Err(( MadeProgress, BadIdent::Underscore(state.line, state.column), @@ -447,7 +509,7 @@ pub fn parse_ident_help_help<'a>( break; } - state = advance_state!(state, bytes_parsed)?; + state = advance_state!(state, width)?; } Err(_reason) => { // @@ -460,7 +522,7 @@ pub fn parse_ident_help_help<'a>( } } - if part_buf.is_empty() { + if chomped_part_buf == 0 { // We probably had a trailing dot, e.g. `Foo.bar.` - this is malformed! // // This condition might also occur if we encounter a malformed accessor like `.|` @@ -482,17 +544,22 @@ pub fn parse_ident_help_help<'a>( // Record the final parts. if is_capitalized { - chomped_capitalized += part_buf.len() + (chomped_capitalized != 0) as usize; + chomped_capitalized += chomped_part_buf + (chomped_capitalized != 0) as usize; cparts += 1; } else { - noncapitalized_parts.push(part_buf.into_bump_str()); + let value = + unsafe { std::str::from_utf8_unchecked(&bytes[chomped..chomped + chomped_part_buf]) }; + noncapitalized_parts.push(value); } let answer = if is_accessor_fn { // Handle accessor functions first because they have the strictest requirements. // Accessor functions may have exactly 1 noncapitalized part, and no capitalzed parts. if cparts == 0 && noncapitalized_parts.len() == 1 && !is_private_tag { - let value = noncapitalized_parts.iter().next().unwrap(); + // an accessor starts with a `.`, but we drop that from the name + let value = unsafe { + std::str::from_utf8_unchecked(&bytes[1 + chomped..1 + chomped + chomped_part_buf]) + }; Ident::AccessorFunction(value) } else { From ea32a3731550bb611e5096e21a934eb148b3fcfc Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 17:26:42 +0100 Subject: [PATCH 40/74] improved private tag parsing --- compiler/parse/src/ident.rs | 121 +++++++------ compiler/reporting/src/error/canonicalize.rs | 172 ++++++++++++++++++- compiler/reporting/src/error/parse.rs | 2 +- compiler/reporting/tests/test_reporting.rs | 17 +- 4 files changed, 242 insertions(+), 70 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 639fca2f3b..963f707fa5 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -299,25 +299,33 @@ pub enum BadIdent { WeirdDotQualified(Row, Col), DoubleDot(Row, Col), StrayDot(Row, Col), + StrayAt(Row, Col), + BadPrivateTag(Row, Col), } -/// Parse an identifier into a string. -/// -/// This is separate from the `ident` Parser because string interpolation -/// wants to use it this way. +fn chomp_lowercase_part(buffer: &[u8]) -> Result<&str, Progress> { + chomp_part(|c: char| c.is_lowercase(), buffer) +} -/// a `.foo` accessor function -fn chomp_accessor(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> { +fn chomp_uppercase_part(buffer: &[u8]) -> Result<&str, Progress> { + chomp_part(|c: char| c.is_uppercase(), buffer) +} + +#[inline(always)] +fn chomp_part(leading_is_good: F, buffer: &[u8]) -> Result<&str, Progress> +where + F: Fn(char) -> bool, +{ // assumes the leading `.` has been chomped already use encode_unicode::CharExt; let mut chomped = 0; if let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { - if ch.is_lowercase() { + if leading_is_good(ch) { chomped += width; } else { - return Err(BadIdent::StrayDot(row, col + 1)); + return Err(NoProgress); } } @@ -331,17 +339,56 @@ fn chomp_accessor(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> { } if chomped == 0 { - Err(BadIdent::StrayDot(row, col + 1)) - } else if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[chomped..]) { - Err(BadIdent::WeirdAccessor(row, col)) + Err(NoProgress) } else { let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; - dbg!(name); Ok(name) } } +/// a `.foo` accessor function +fn chomp_accessor(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> { + // assumes the leading `.` has been chomped already + use encode_unicode::CharExt; + + match chomp_lowercase_part(buffer) { + Ok(name) => { + let chomped = name.len(); + + if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[chomped..]) { + Err(BadIdent::WeirdAccessor(row, col)) + } else { + Ok(name) + } + } + Err(_) => { + // we've already made progress with the initial `.` + Err(BadIdent::StrayDot(row, col + 1)) + } + } +} + +/// a `@Token` private tag +fn chomp_private_tag(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> { + // assumes the leading `@` has NOT been chomped already + debug_assert_eq!(buffer.get(0), Some(&b'@')); + use encode_unicode::CharExt; + + match chomp_uppercase_part(&buffer[1..]) { + Ok(name) => { + let chomped = 1 + name.len(); + + if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[chomped..]) { + Err(BadIdent::BadPrivateTag(row, col + chomped as u16)) + } else { + Ok(name) + } + } + Err(_) => Err(BadIdent::BadPrivateTag(row, col + 1)), + } +} + fn parse_ident_help_help<'a>( arena: &'a Bump, mut state: State<'a>, @@ -350,7 +397,6 @@ fn parse_ident_help_help<'a>( let mut noncapitalized_parts: Vec<&'a str> = Vec::new_in(arena); let mut is_capitalized; let is_accessor_fn; - let mut is_private_tag = false; let bytes = state.bytes; let mut chomped_capitalized = 0; @@ -387,35 +433,17 @@ fn parse_ident_help_help<'a>( Err(fail) => return Err((MadeProgress, fail, state)), } } else if first_ch == '@' { - state = advance_state!(state, bytes_parsed)?; + match chomp_private_tag(state.bytes, state.line, state.column) { + Ok(tagname) => { + let bytes_parsed = 1 + tagname.len(); - // '@' must always be followed by a capital letter! - match peek_utf8_char(&state) { - Ok((next_ch, next_bytes_parsed)) => { - if next_ch.is_uppercase() { - state = advance_state!(state, next_bytes_parsed)?; + state = advance_state!(state, bytes_parsed)?; - part_buf.push('@'); - part_buf.push(next_ch); - chomped_part_buf += 1 + next_bytes_parsed; - - is_private_tag = true; - is_capitalized = true; - is_accessor_fn = false; - } else { - return Err(( - MadeProgress, - BadIdent::PrivateTagNotUppercase(state.line, state.column), - state, - )); - } + return Ok((MadeProgress, (Ident::PrivateTag(tagname), None), state)); } - Err(_reason) => { - return Err(( - MadeProgress, - BadIdent::PrivateTagNotUppercase(state.line, state.column), - state, - )); + Err(fail) => { + state = advance_state!(state, 1)?; + return Err((MadeProgress, fail, state)); } } } else { @@ -555,7 +583,7 @@ fn parse_ident_help_help<'a>( let answer = if is_accessor_fn { // Handle accessor functions first because they have the strictest requirements. // Accessor functions may have exactly 1 noncapitalized part, and no capitalzed parts. - if cparts == 0 && noncapitalized_parts.len() == 1 && !is_private_tag { + if cparts == 0 && noncapitalized_parts.len() == 1 { // an accessor starts with a `.`, but we drop that from the name let value = unsafe { std::str::from_utf8_unchecked(&bytes[1 + chomped..1 + chomped + chomped_part_buf]) @@ -585,11 +613,7 @@ fn parse_ident_help_help<'a>( 1 => { let chomped = chomped_capitalized; let value = unsafe { std::str::from_utf8_unchecked(&bytes[..chomped]) }; - if is_private_tag { - Ident::PrivateTag(value) - } else { - Ident::GlobalTag(value) - } + Ident::GlobalTag(value) } _ => { // This is a qualified tag, which is not allowed! @@ -600,13 +624,6 @@ fn parse_ident_help_help<'a>( )); } } - } else if is_private_tag { - // This is qualified field access with an '@' in front, which does not make sense! - return Err(( - MadeProgress, - BadIdent::PrivateTagFieldAccess(state.line, state.column), - state, - )); } else { // We have multiple noncapitalized parts, so this must be field access. let module_name = if cparts == 0 { diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index 85fc4da8d1..f19637f2c7 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -1,4 +1,5 @@ use roc_collections::all::MutSet; +use roc_parse::parser::{Col, Row}; use roc_problem::can::PrecedenceProblem::BothNonAssociative; use roc_problem::can::{FloatErrorKind, IntErrorKind, Problem, RuntimeError}; use roc_region::all::Region; @@ -450,14 +451,106 @@ fn to_bad_ident_expr_report<'b>( ]) } - PrivateTagFieldAccess(_row, _col) => alloc.stack(vec![ - alloc.reflow("I am very confused by this field access:"), - alloc.region(surroundings), - alloc.concat(vec![ - alloc.reflow(r"It looks like a record field access on a private tag.") - ]), - ]), - _ => todo!(), + PrivateTagFieldAccess(row, col) => { + let region = + Region::from_rows_cols(surroundings.start_line, surroundings.start_col, row, col); + alloc.stack(vec![ + alloc.reflow("I am very confused by this field access:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow(r"It looks like a record field access on a private tag.") + ]), + ]) + } + + Underscore(row, col) => { + let region = + Region::from_rows_cols(surroundings.start_line, surroundings.start_col, row, col); + alloc.stack(vec![ + alloc.reflow("Underscores are not allowed in identifier names:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![alloc.reflow( + r"I recommend using camelCase, it is the standard in the Roc ecosystem.", + )]), + ]) + } + + DoubleDot(row, col) => { + let region = + Region::from_rows_cols(surroundings.start_line, surroundings.start_col, row, col); + alloc.stack(vec![ + alloc.reflow("I am very confused by these two dots in a row:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow(r"There always needs to be a name after a dot.") + ]), + ]) + } + + StrayAt(row, col) => { + let region = + Region::from_rows_cols(surroundings.start_line, surroundings.start_col, row, col); + alloc.stack(vec![ + alloc.reflow("I am very confused by this @ symbol"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![alloc.reflow(r"I expected a private tag.")]), + ]) + } + + BadPrivateTag(row, col) => { + use BadIdentNext::*; + match what_is_next(alloc.src_lines, row, col) { + LowercaseAccess(width) => { + let region = Region::from_rows_cols(row, col, row, col + width); + alloc.stack(vec![ + alloc.reflow("I am very confused by this field access:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow(r"It looks like a record field access on a private tag.") + ]), + ]) + } + UppercaseAccess(width) => { + let region = Region::from_rows_cols(row, col, row, col + width); + alloc.stack(vec![ + alloc.reflow("I am very confused by this expression:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow( + r"Looks like a private tag is treated like a module name. ", + ), + alloc.reflow(r"Maybe you wanted a qualified name, like "), + alloc.parser_suggestion("Json.Decode.string"), + alloc.text("?"), + ]), + ]) + } + Other(Some(c)) if c.is_lowercase() => { + let region = Region::from_rows_cols( + surroundings.start_line, + surroundings.start_col + 1, + row, + col + 1, + ); + alloc.stack(vec![ + alloc.reflow("I am trying to parse a private tag here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow(r"But after the "), + alloc.keyword("@"), + alloc.reflow(r" symbol I found a lowercase letter. "), + alloc.reflow(r"All tag names (global and private)"), + alloc.reflow(r" must start with an uppercase letter, like "), + alloc.parser_suggestion("@UUID"), + alloc.reflow(" or "), + alloc.parser_suggestion("@Secrets"), + alloc.reflow("."), + ]), + ]) + } + other => todo!("{:?}", other), + } + } } } @@ -591,6 +684,69 @@ fn to_bad_ident_pattern_report<'b>( } } +#[derive(Debug)] +enum BadIdentNext<'a> { + LowercaseAccess(u16), + UppercaseAccess(u16), + NumberAccess(u16), + Keyword(&'a str), + DanglingDot, + Other(Option), +} + +fn what_is_next<'a>(source_lines: &'a [&'a str], row: Row, col: Col) -> BadIdentNext<'a> { + let row_index = row as usize; + let col_index = col as usize; + match source_lines.get(row_index) { + None => BadIdentNext::Other(None), + Some(line) => { + let chars = &line[col_index..]; + let mut it = chars.chars(); + + match roc_parse::keyword::KEYWORDS + .iter() + .find(|keyword| crate::error::parse::starts_with_keyword(chars, keyword)) + { + Some(keyword) => BadIdentNext::Keyword(keyword), + None => match it.next() { + None => BadIdentNext::Other(None), + Some('.') => match it.next() { + Some(c) if c.is_lowercase() => { + BadIdentNext::LowercaseAccess(2 + till_whitespace(it) as u16) + } + Some(c) if c.is_uppercase() => { + BadIdentNext::UppercaseAccess(2 + till_whitespace(it) as u16) + } + Some(c) if c.is_ascii_digit() => { + BadIdentNext::NumberAccess(2 + till_whitespace(it) as u16) + } + _ => BadIdentNext::DanglingDot, + }, + Some(c) => BadIdentNext::Other(Some(c)), + }, + } + } + } +} + +fn till_whitespace(mut it: I) -> usize +where + I: Iterator, +{ + let mut chomped = 0; + + while let Some(c) = it.next() { + if c.is_ascii_whitespace() || c == '#' { + break; + } else { + chomped += 1; + continue; + } + } + + chomped +} + fn pretty_runtime_error<'b>( alloc: &'b RocDocAllocator<'b>, runtime_error: RuntimeError, diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 177707a186..a1a5a687b3 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -3012,7 +3012,7 @@ fn what_is_next<'a>(source_lines: &'a [&'a str], row: Row, col: Col) -> Next<'a> } } -fn starts_with_keyword(rest_of_line: &str, keyword: &str) -> bool { +pub fn starts_with_keyword(rest_of_line: &str, keyword: &str) -> bool { if let Some(stripped) = rest_of_line.strip_prefix(keyword) { match stripped.chars().next() { None => true, diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index dfd2bee8b8..9e8efc54db 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -4147,15 +4147,14 @@ mod test_reporting { indoc!( r#" ── SYNTAX PROBLEM ────────────────────────────────────────────────────────────── - - I am trying to parse a qualified name here: - + + I am very confused by this expression: + 1│ @Foo.Bar - ^ - - This looks like a qualified tag name to me, but tags cannot be - qualified! Maybe you wanted a qualified name, something like - Json.Decode.string? + ^^^^ + + Looks like a private tag is treated like a module name. Maybe you + wanted a qualified name, like Json.Decode.string? "# ), ) @@ -5523,7 +5522,7 @@ mod test_reporting { I am very confused by this field access: 1│ @UUID.bar - ^^^^^^^^^ + ^^^^ It looks like a record field access on a private tag. "# From 796ec0032375ae7305ff3e794bc3871cfacc9217 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 20:21:51 +0100 Subject: [PATCH 41/74] wip faster ident parsing --- compiler/parse/src/ident.rs | 186 ++++++++++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 9 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 963f707fa5..bd2f7c06a2 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -220,7 +220,7 @@ pub fn parse_ident_help<'a>( let initial = state.clone(); match parse_ident_help_help(arena, state) { - Ok((progress, (ident, _), state)) => { + Ok((progress, ident, state)) => { if let Ident::Access { module_name, parts } = ident { if module_name.is_empty() { if let Some(first) = parts.first() { @@ -389,10 +389,182 @@ fn chomp_private_tag(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent } } +fn chomp_identifier_chain<'a>( + arena: &'a Bump, + buffer: &'a [u8], + row: Row, + col: Col, +) -> Result<(u16, Ident<'a>), (u16, BadIdent)> { + use encode_unicode::CharExt; + + let first_is_uppercase; + let mut chomped = 0; + + match char::from_utf8_slice_start(&buffer[chomped..]) { + Ok((ch, width)) => match ch { + '.' => match chomp_accessor(&buffer[1..], row, col) { + Ok(accessor) => { + let bytes_parsed = 1 + accessor.len(); + + return Ok((bytes_parsed as u16, Ident::AccessorFunction(accessor))); + } + Err(fail) => return Err((1, fail)), + }, + '@' => match chomp_private_tag(buffer, row, col) { + Ok(tagname) => { + let bytes_parsed = tagname.len(); + + return Ok((bytes_parsed as u16, Ident::PrivateTag(tagname))); + } + Err(fail) => return Err((1, fail)), + }, + c if c.is_alphabetic() => { + // fall through + chomped += width; + first_is_uppercase = c.is_uppercase(); + } + _ => { + return Err((0, BadIdent::Start(row, col))); + } + }, + Err(_) => return Err((0, BadIdent::Start(row, col))), + } + + while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + if ch.is_alphabetic() || ch.is_ascii_digit() { + chomped += width; + } else { + // we're done + break; + } + } + + if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[chomped..]) { + let module_name = if first_is_uppercase { + match chomp_module_chain(&buffer[chomped..]) { + Ok(width) => { + chomped += width as usize; + unsafe { std::str::from_utf8_unchecked(&buffer[..chomped + width as usize]) } + } + Err(MadeProgress) => todo!(), + Err(NoProgress) => { + chomped += 1; + "" + } + } + } else { + "" + }; + + let mut parts = Vec::with_capacity_in(4, arena); + + if !first_is_uppercase { + let first_part = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; + parts.push(first_part); + } + + match chomp_access_chain(&buffer[chomped..], &mut parts) { + Ok(width) => { + chomped += width as usize; + + let ident = Ident::Access { + module_name, + parts: parts.into_bump_slice(), + }; + + Ok((chomped as u16, ident)) + } + Err(MadeProgress) => todo!(), + Err(NoProgress) => Err((chomped as u16 + 1, BadIdent::StrayDot(row, col))), + } + } else if first_is_uppercase { + // just one segment, starting with an uppercase letter; that's a global tag + let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; + Ok((chomped as u16, Ident::GlobalTag(value))) + } else { + // just one segment, starting with a lowercase letter; that's a normal identifier + let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; + let ident = Ident::Access { + module_name: "", + parts: arena.alloc([value]), + }; + Ok((chomped as u16, ident)) + } +} + +fn chomp_module_chain<'a>(buffer: &'a [u8]) -> Result { + let mut chomped = 0; + // number of dots (equivalently parts) we've seen + let mut i = 1; + + loop { + match chomp_uppercase_part(&buffer[chomped + i..]) { + Ok(name) => { + chomped += name.len(); + i += 1; + } + Err(MadeProgress) => return Err(MadeProgress), + Err(NoProgress) => break, + } + } + + if chomped == 0 { + Err(NoProgress) + } else { + Ok((chomped + i) as u16) + } +} + +fn chomp_access_chain<'a>(buffer: &'a [u8], parts: &mut Vec<'a, &'a str>) -> Result { + let mut chomped = 0; + let mut i = 1; + + loop { + match chomp_lowercase_part(&buffer[chomped + i..]) { + Ok(name) => { + let value = unsafe { + std::str::from_utf8_unchecked(&buffer[chomped + i..chomped + i + name.len()]) + }; + parts.push(value); + + chomped += name.len(); + i += 1; + } + Err(MadeProgress) => return Err(MadeProgress), + Err(NoProgress) => break, + } + } + + if chomped == 0 { + Err(NoProgress) + } else { + Ok((chomped + i) as u16) + } +} + +/// a `Ok` global tag +fn chomp_global_tag(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> { + debug_assert!(((*buffer.get(0).unwrap()) as char).is_uppercase()); + use encode_unicode::CharExt; + + match chomp_uppercase_part(&buffer[1..]) { + Ok(name) => { + let chomped = 1 + name.len(); + + if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[chomped..]) { + Err(BadIdent::BadPrivateTag(row, col + chomped as u16)) + } else { + Ok(name) + } + } + Err(_) => Err(BadIdent::BadPrivateTag(row, col + 1)), + } +} + fn parse_ident_help_help<'a>( arena: &'a Bump, mut state: State<'a>, -) -> ParseResult<'a, (Ident<'a>, Option), BadIdent> { +) -> ParseResult<'a, Ident<'a>, BadIdent> { let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) let mut noncapitalized_parts: Vec<&'a str> = Vec::new_in(arena); let mut is_capitalized; @@ -424,11 +596,7 @@ fn parse_ident_help_help<'a>( state = advance_state!(state, bytes_parsed)?; - return Ok(( - MadeProgress, - (Ident::AccessorFunction(accessor), None), - state, - )); + return Ok((MadeProgress, Ident::AccessorFunction(accessor), state)); } Err(fail) => return Err((MadeProgress, fail, state)), } @@ -439,7 +607,7 @@ fn parse_ident_help_help<'a>( state = advance_state!(state, bytes_parsed)?; - return Ok((MadeProgress, (Ident::PrivateTag(tagname), None), state)); + return Ok((MadeProgress, Ident::PrivateTag(tagname), state)); } Err(fail) => { state = advance_state!(state, 1)?; @@ -639,5 +807,5 @@ fn parse_ident_help_help<'a>( } }; - Ok((Progress::MadeProgress, (answer, None), state)) + Ok((Progress::MadeProgress, answer, state)) } From 61fc05dace4c0919de1f0ba63b1888d3202fc893 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 21:42:37 +0100 Subject: [PATCH 42/74] working report tests --- compiler/parse/src/ident.rs | 124 +++++++++++-------- compiler/reporting/src/error/canonicalize.rs | 6 +- compiler/reporting/tests/test_reporting.rs | 20 +-- 3 files changed, 84 insertions(+), 66 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index bd2f7c06a2..6a55b8a049 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -444,13 +444,10 @@ fn chomp_identifier_chain<'a>( match chomp_module_chain(&buffer[chomped..]) { Ok(width) => { chomped += width as usize; - unsafe { std::str::from_utf8_unchecked(&buffer[..chomped + width as usize]) } + unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) } } Err(MadeProgress) => todo!(), - Err(NoProgress) => { - chomped += 1; - "" - } + Err(NoProgress) => unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }, } } else { "" @@ -474,9 +471,27 @@ fn chomp_identifier_chain<'a>( Ok((chomped as u16, ident)) } - Err(MadeProgress) => todo!(), - Err(NoProgress) => Err((chomped as u16 + 1, BadIdent::StrayDot(row, col))), + Err(0) if !module_name.is_empty() => Err(( + chomped as u16, + BadIdent::QualifiedTag(row, chomped as u16 + col), + )), + Err(1) if parts.is_empty() => Err(( + chomped as u16 + 1, + BadIdent::WeirdDotQualified(row, chomped as u16 + col + 1), + )), + Err(width) => Err(( + chomped as u16 + width, + BadIdent::WeirdDotAccess(row, chomped as u16 + col + width), + )), } + } else if let Ok(('_', _)) = char::from_utf8_slice_start(&buffer[chomped..]) { + // we don't allow underscores in the middle of an identifier + // but still parse them (and generate a malformed identifier) + // to give good error messages for this case + Err(( + chomped as u16 + 1, + BadIdent::Underscore(row, col + chomped as u16 + 1), + )) } else if first_is_uppercase { // just one segment, starting with an uppercase letter; that's a global tag let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; @@ -494,70 +509,59 @@ fn chomp_identifier_chain<'a>( fn chomp_module_chain<'a>(buffer: &'a [u8]) -> Result { let mut chomped = 0; - // number of dots (equivalently parts) we've seen - let mut i = 1; - loop { - match chomp_uppercase_part(&buffer[chomped + i..]) { - Ok(name) => { - chomped += name.len(); - i += 1; - } - Err(MadeProgress) => return Err(MadeProgress), - Err(NoProgress) => break, + while let Some(b'.') = buffer.get(chomped) { + match &buffer.get(chomped + 1..) { + Some(slice) => match chomp_uppercase_part(slice) { + Ok(name) => { + chomped += name.len() + 1; + } + Err(MadeProgress) => return Err(MadeProgress), + Err(NoProgress) => break, + }, + None => return Err(MadeProgress), } } if chomped == 0 { Err(NoProgress) } else { - Ok((chomped + i) as u16) + Ok(chomped as u16) } } -fn chomp_access_chain<'a>(buffer: &'a [u8], parts: &mut Vec<'a, &'a str>) -> Result { +fn chomp_access_chain<'a>(buffer: &'a [u8], parts: &mut Vec<'a, &'a str>) -> Result { let mut chomped = 0; - let mut i = 1; - loop { - match chomp_lowercase_part(&buffer[chomped + i..]) { - Ok(name) => { - let value = unsafe { - std::str::from_utf8_unchecked(&buffer[chomped + i..chomped + i + name.len()]) - }; - parts.push(value); + while let Some(b'.') = buffer.get(chomped) { + match &buffer.get(chomped + 1..) { + Some(slice) => match chomp_lowercase_part(slice) { + Ok(name) => { + let value = unsafe { + std::str::from_utf8_unchecked( + &buffer[chomped + 1..chomped + 1 + name.len()], + ) + }; + parts.push(value); - chomped += name.len(); - i += 1; - } - Err(MadeProgress) => return Err(MadeProgress), - Err(NoProgress) => break, + chomped += name.len() + 1; + } + Err(_) => return Err(chomped as u16 + 1), + }, + None => return Err(chomped as u16 + 1), } } + dbg!(&parts); + + let value = unsafe { std::str::from_utf8_unchecked(&buffer[chomped..]) }; + + dbg!(value); + if chomped == 0 { - Err(NoProgress) + Err(0) } else { - Ok((chomped + i) as u16) - } -} - -/// a `Ok` global tag -fn chomp_global_tag(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> { - debug_assert!(((*buffer.get(0).unwrap()) as char).is_uppercase()); - use encode_unicode::CharExt; - - match chomp_uppercase_part(&buffer[1..]) { - Ok(name) => { - let chomped = 1 + name.len(); - - if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[chomped..]) { - Err(BadIdent::BadPrivateTag(row, col + chomped as u16)) - } else { - Ok(name) - } - } - Err(_) => Err(BadIdent::BadPrivateTag(row, col + 1)), + Ok(chomped as u16) } } @@ -565,6 +569,20 @@ fn parse_ident_help_help<'a>( arena: &'a Bump, mut state: State<'a>, ) -> ParseResult<'a, Ident<'a>, BadIdent> { + match chomp_identifier_chain(arena, state.bytes, state.line, state.column) { + Ok((width, ident)) => { + state = advance_state!(state, width as usize)?; + return Ok((MadeProgress, ident, state)); + } + Err((0, fail)) => { + return Err((NoProgress, fail, state)); + } + Err((width, fail)) => { + state = advance_state!(state, width as usize)?; + return Err((MadeProgress, fail, state)); + } + } + let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) let mut noncapitalized_parts: Vec<&'a str> = Vec::new_in(arena); let mut is_capitalized; diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index f19637f2c7..4357a63a27 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -358,14 +358,14 @@ fn to_bad_ident_expr_report<'b>( let region = Region::from_row_col(row, col); alloc.stack(vec![ - alloc.reflow(r"I trying to parse a record field accessor here:"), + alloc.reflow(r"I trying to parse a record field access here:"), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ - alloc.reflow("Something like "), + alloc.reflow("So I expect to see a lowercase letter next, like "), alloc.parser_suggestion(".name"), alloc.reflow(" or "), alloc.parser_suggestion(".height"), - alloc.reflow(" that accesses a value from a record."), + alloc.reflow("."), ]), ]) } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 9e8efc54db..3bd5379642 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -4124,13 +4124,13 @@ mod test_reporting { indoc!( r#" ── SYNTAX PROBLEM ────────────────────────────────────────────────────────────── - - I trying to parse a record field accessor here: - + + I trying to parse a record field access here: + 1│ foo.bar. ^ - - Something like .name or .height that accesses a value from a record. + + So I expect to see a lowercase letter next, like .name or .height. "# ), ) @@ -5470,13 +5470,13 @@ mod test_reporting { indoc!( r#" ── SYNTAX PROBLEM ────────────────────────────────────────────────────────────── - - I trying to parse a record field accessor here: - + + I trying to parse a record field access here: + 1│ Num.add . 23 ^ - - Something like .name or .height that accesses a value from a record. + + So I expect to see a lowercase letter next, like .name or .height. "# ), ) From e7bbfe96dbc0f8778b25db8fa984dcb57ece21de Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 21:46:34 +0100 Subject: [PATCH 43/74] fix parse tests --- compiler/parse/src/ident.rs | 9 +++++---- compiler/parse/tests/test_parse.rs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 6a55b8a049..447afa8cc8 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -377,12 +377,13 @@ fn chomp_private_tag(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent match chomp_uppercase_part(&buffer[1..]) { Ok(name) => { - let chomped = 1 + name.len(); + let width = 1 + name.len(); - if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[chomped..]) { - Err(BadIdent::BadPrivateTag(row, col + chomped as u16)) + if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[width..]) { + Err(BadIdent::BadPrivateTag(row, col + width as u16)) } else { - Ok(name) + let value = unsafe { std::str::from_utf8_unchecked(&buffer[..width]) }; + Ok(value) } } Err(_) => Err(BadIdent::BadPrivateTag(row, col + 1)), diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 58dfb65174..412898dfb4 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -1005,7 +1005,7 @@ mod test_parse { use roc_parse::ident::BadIdent; let arena = Bump::new(); - let expected = Expr::MalformedIdent("@One.Two.Whee", BadIdent::QualifiedTag(0, 13)); + let expected = Expr::MalformedIdent("@One.Two.Whee", BadIdent::BadPrivateTag(0, 4)); let actual = parse_expr_with(&arena, "@One.Two.Whee"); assert_eq!(Ok(expected), actual); From 864390a89aafca6db28bb0f96bcdded64aec3663 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 21:56:19 +0100 Subject: [PATCH 44/74] use new ident parser --- compiler/parse/src/ident.rs | 258 +----------------------------------- 1 file changed, 3 insertions(+), 255 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 447afa8cc8..59c3d46f28 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -553,12 +553,6 @@ fn chomp_access_chain<'a>(buffer: &'a [u8], parts: &mut Vec<'a, &'a str>) -> Res } } - dbg!(&parts); - - let value = unsafe { std::str::from_utf8_unchecked(&buffer[chomped..]) }; - - dbg!(value); - if chomped == 0 { Err(0) } else { @@ -573,258 +567,12 @@ fn parse_ident_help_help<'a>( match chomp_identifier_chain(arena, state.bytes, state.line, state.column) { Ok((width, ident)) => { state = advance_state!(state, width as usize)?; - return Ok((MadeProgress, ident, state)); - } - Err((0, fail)) => { - return Err((NoProgress, fail, state)); + Ok((MadeProgress, ident, state)) } + Err((0, fail)) => Err((NoProgress, fail, state)), Err((width, fail)) => { state = advance_state!(state, width as usize)?; - return Err((MadeProgress, fail, state)); + Err((MadeProgress, fail, state)) } } - - let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) - let mut noncapitalized_parts: Vec<&'a str> = Vec::new_in(arena); - let mut is_capitalized; - let is_accessor_fn; - - let bytes = state.bytes; - let mut chomped_capitalized = 0; - let mut cparts = 0; - - let mut chomped_part_buf = 0; - let mut chomped = 0; - - // Identifiers and accessor functions must start with either a letter or a dot. - // If this starts with neither, it must be something else! - match peek_utf8_char(&state) { - Ok((first_ch, bytes_parsed)) => { - if first_ch.is_alphabetic() { - part_buf.push(first_ch); - chomped_part_buf += bytes_parsed; - - is_capitalized = first_ch.is_uppercase(); - is_accessor_fn = false; - - state = advance_state!(state, bytes_parsed)?; - } else if first_ch == '.' { - match chomp_accessor(&state.bytes[1..], state.line, state.column) { - Ok(accessor) => { - let bytes_parsed = 1 + accessor.len(); - - state = advance_state!(state, bytes_parsed)?; - - return Ok((MadeProgress, Ident::AccessorFunction(accessor), state)); - } - Err(fail) => return Err((MadeProgress, fail, state)), - } - } else if first_ch == '@' { - match chomp_private_tag(state.bytes, state.line, state.column) { - Ok(tagname) => { - let bytes_parsed = 1 + tagname.len(); - - state = advance_state!(state, bytes_parsed)?; - - return Ok((MadeProgress, Ident::PrivateTag(tagname), state)); - } - Err(fail) => { - state = advance_state!(state, 1)?; - return Err((MadeProgress, fail, state)); - } - } - } else { - return Err((NoProgress, BadIdent::Start(state.line, state.column), state)); - } - } - Err(_reason) => { - return Err((NoProgress, BadIdent::Start(state.line, state.column), state)); - } - } - - while !state.bytes.is_empty() { - match peek_utf8_char(&state) { - Ok((ch, width)) => { - // After the first character, only these are allowed: - // - // * Unicode alphabetic chars - you might name a variable `鹏` if that's clear to your readers - // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() - // * A dot ('.') - if ch.is_alphabetic() { - if part_buf.is_empty() { - // Capitalization is determined by the first character in the part. - is_capitalized = ch.is_uppercase(); - } - - part_buf.push(ch); - chomped_part_buf += width; - } else if ch.is_ascii_digit() { - // Parts may not start with numbers! - if part_buf.is_empty() { - return Err(( - MadeProgress, - BadIdent::PartStartsWithNumber(state.line, state.column), - state, - )); - } - - part_buf.push(ch); - chomped_part_buf += width; - } else if ch == '.' { - // There are two posssible errors here: - // - // 1. Having two consecutive dots is an error. - // 2. Having capitalized parts after noncapitalized (e.g. `foo.Bar`) is an error. - if part_buf.is_empty() { - return Err(( - MadeProgress, - BadIdent::DoubleDot(state.line, state.column), - state, - )); - } - - if is_capitalized && !noncapitalized_parts.is_empty() { - return Err(( - MadeProgress, - BadIdent::WeirdDotQualified(state.line, state.column), - state, - )); - } - - if is_capitalized { - chomped_capitalized += - chomped_part_buf + (chomped_capitalized != 0) as usize; - cparts += 1; - } else { - let value = unsafe { - std::str::from_utf8_unchecked( - &bytes[chomped..chomped + chomped_part_buf], - ) - }; - noncapitalized_parts.push(value); - } - - // Now that we've recorded the contents of the current buffer, reset it. - part_buf = String::new_in(arena); - chomped += chomped_part_buf + 1; - chomped_part_buf = 0; - } else if ch == '_' { - // we don't allow underscores in the middle of an identifier - // but still parse them (and generate a malformed identifier) - // to give good error messages for this case - state = advance_state!(state, width)?; - return Err(( - MadeProgress, - BadIdent::Underscore(state.line, state.column), - state, - )); - } else { - // This must be the end of the identifier. We're done! - - break; - } - - state = advance_state!(state, width)?; - } - Err(_reason) => { - // - return Err(( - MadeProgress, - BadIdent::Start(state.line, state.column), - state, - )); - } - } - } - - if chomped_part_buf == 0 { - // We probably had a trailing dot, e.g. `Foo.bar.` - this is malformed! - // - // This condition might also occur if we encounter a malformed accessor like `.|` - // - // If we made it this far and don't have a next_char, then necessarily - // we have consumed a '.' char previously. - let fail = if noncapitalized_parts.is_empty() { - if cparts == 0 { - BadIdent::StrayDot(state.line, state.column) - } else { - BadIdent::WeirdDotQualified(state.line, state.column) - } - } else { - BadIdent::WeirdDotAccess(state.line, state.column) - }; - - return Err((MadeProgress, fail, state)); - } - - // Record the final parts. - if is_capitalized { - chomped_capitalized += chomped_part_buf + (chomped_capitalized != 0) as usize; - cparts += 1; - } else { - let value = - unsafe { std::str::from_utf8_unchecked(&bytes[chomped..chomped + chomped_part_buf]) }; - noncapitalized_parts.push(value); - } - - let answer = if is_accessor_fn { - // Handle accessor functions first because they have the strictest requirements. - // Accessor functions may have exactly 1 noncapitalized part, and no capitalzed parts. - if cparts == 0 && noncapitalized_parts.len() == 1 { - // an accessor starts with a `.`, but we drop that from the name - let value = unsafe { - std::str::from_utf8_unchecked(&bytes[1 + chomped..1 + chomped + chomped_part_buf]) - }; - - Ident::AccessorFunction(value) - } else { - return Err(( - MadeProgress, - BadIdent::WeirdAccessor(state.line, state.column), - state, - )); - } - } else if noncapitalized_parts.is_empty() { - // We have capitalized parts only, so this must be a tag. - match cparts { - 0 => { - // We had neither capitalized nor noncapitalized parts, - // yet we made it this far. The only explanation is that this was - // a stray '.' drifting through the cosmos. - return Err(( - MadeProgress, - BadIdent::StrayDot(state.line, state.column), - state, - )); - } - 1 => { - let chomped = chomped_capitalized; - let value = unsafe { std::str::from_utf8_unchecked(&bytes[..chomped]) }; - Ident::GlobalTag(value) - } - _ => { - // This is a qualified tag, which is not allowed! - return Err(( - MadeProgress, - BadIdent::QualifiedTag(state.line, state.column), - state, - )); - } - } - } else { - // We have multiple noncapitalized parts, so this must be field access. - let module_name = if cparts == 0 { - "" - } else { - let chomped = chomped_capitalized; - unsafe { std::str::from_utf8_unchecked(&bytes[..chomped]) } - }; - - Ident::Access { - module_name, - parts: noncapitalized_parts.into_bump_slice(), - } - }; - - Ok((Progress::MadeProgress, answer, state)) } From 6be5f1a6b17e752c007c3a9c45b052abfd788bcc Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 22:08:13 +0100 Subject: [PATCH 45/74] remove context stack --- compiler/parse/src/parser.rs | 49 ------------------------------------ 1 file changed, 49 deletions(-) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 51cb45bc6e..ed141d634f 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -27,8 +27,6 @@ pub struct State<'a> { // the first nonspace char on that line. pub is_indenting: bool, - pub context_stack: &'a ContextStack<'a>, - /// The original length of the string, before any bytes were consumed. /// This is used internally by the State::bytes_consumed() function. /// @@ -50,7 +48,6 @@ impl<'a> State<'a> { column: 0, indent_col: 0, is_indenting: true, - context_stack: arena.alloc(ContextStack::Nil), original_len: bytes.len(), } } @@ -119,7 +116,6 @@ impl<'a> State<'a> { indent_col: 0, is_indenting: true, original_len: self.original_len, - context_stack: arena.alloc(self.context_stack.clone()), }), None => Err(( Progress::NoProgress, @@ -221,7 +217,6 @@ impl<'a> State<'a> { column: column_usize as u16, indent_col, is_indenting, - context_stack: arena.alloc(self.context_stack.clone()), original_len: self.original_len, }) } @@ -269,7 +264,6 @@ impl<'a> fmt::Debug for State<'a> { write!(f, "\n\tindent_col: {}", self.indent_col)?; write!(f, "\n\tis_indenting: {:?}", self.is_indenting)?; write!(f, "\n\toriginal_len: {}", self.original_len)?; - write!(f, "\n\tcontext stack: {:?}", self.context_stack)?; write!(f, "\n}}") } } @@ -2199,39 +2193,6 @@ macro_rules! debug { }; } -#[macro_export] -macro_rules! attempt { - ($attempting:expr, $parser:expr) => { - move |arena: &'a Bump, mut state: $crate::parser::State<'a>| { - let item = $crate::parser::ContextItem { - context: $attempting, - line: state.line, - column: state.column, - }; - - state.context_stack = arena.alloc($crate::parser::ContextStack::Cons( - item, - state.context_stack, - )); - - $parser - .parse(arena, state) - .map(|(progress, answer, mut state)| { - // If the parser suceeded, go back to what we were originally attempting. - // (If it failed, that's exactly where we care what we were attempting!) - match state.context_stack.uncons() { - Some((_item, rest)) => { - state.context_stack = rest; - } - None => unreachable!("context stack contains at least one element"), - } - - (progress, answer, state) - }) - } - }; -} - #[macro_export] macro_rules! either { ($p1:expr, $p2:expr) => { @@ -2315,16 +2276,6 @@ where map_with_arena!(parser, transform) } -/// For some reason, some usages won't compile unless they use this instead of the macro version -#[inline(always)] -pub fn attempt<'a, P, Val, Error>(attempting: Attempting, parser: P) -> impl Parser<'a, Val, Error> -where - P: Parser<'a, Val, Error>, - Error: 'a, -{ - attempt!(attempting, parser) -} - pub fn parse_utf8<'a>(bytes: &[u8]) -> Result<&str, SyntaxError<'a>> { match from_utf8(bytes) { Ok(string) => Ok(string), From 55a92b50ccb395296896d765039ce42459b79c7c Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 23:03:01 +0100 Subject: [PATCH 46/74] remove unused ident errors --- compiler/parse/src/ident.rs | 7 +- compiler/reporting/src/error/canonicalize.rs | 112 ------------------- 2 files changed, 1 insertion(+), 118 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 59c3d46f28..4c5b956363 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -288,18 +288,13 @@ fn malformed_identifier<'a>( pub enum BadIdent { Start(Row, Col), Space(BadInputError, Row, Col), + Underscore(Row, Col), QualifiedTag(Row, Col), - PrivateTagNotUppercase(Row, Col), - PartStartsWithNumber(Row, Col), WeirdAccessor(Row, Col), - PrivateTagFieldAccess(Row, Col), - WeirdDotAccess(Row, Col), WeirdDotQualified(Row, Col), - DoubleDot(Row, Col), StrayDot(Row, Col), - StrayAt(Row, Col), BadPrivateTag(Row, Col), } diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index 4357a63a27..dd380e4866 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -370,22 +370,6 @@ fn to_bad_ident_expr_report<'b>( ]) } - PartStartsWithNumber(row, col) => { - let region = Region::from_row_col(row, col); - - alloc.stack(vec![ - alloc.reflow("I trying to parse a record field access here:"), - alloc.region_with_subregion(surroundings, region), - alloc.concat(vec![ - alloc.reflow("So I expect to see a lowercase letter next, like "), - alloc.parser_suggestion(".name"), - alloc.reflow(" or "), - alloc.parser_suggestion(".height"), - alloc.reflow("."), - ]), - ]) - } - WeirdAccessor(_row, _col) => alloc.stack(vec![ alloc.reflow("I am very confused by this field access"), alloc.region(surroundings), @@ -431,37 +415,6 @@ fn to_bad_ident_expr_report<'b>( ]), ]) } - PrivateTagNotUppercase(row, col) => { - let region = Region::from_row_col(row, col); - - alloc.stack(vec![ - alloc.reflow("I am trying to parse a private tag here:"), - alloc.region_with_subregion(surroundings, region), - alloc.concat(vec![ - alloc.reflow(r"But after the "), - alloc.keyword("@"), - alloc.reflow(r" symbol I found a lowercase letter. "), - alloc.reflow(r"All tag names (global and private)"), - alloc.reflow(r" must start with an uppercase letter, like "), - alloc.parser_suggestion("@UUID"), - alloc.reflow(" or "), - alloc.parser_suggestion("@Secrets"), - alloc.reflow("."), - ]), - ]) - } - - PrivateTagFieldAccess(row, col) => { - let region = - Region::from_rows_cols(surroundings.start_line, surroundings.start_col, row, col); - alloc.stack(vec![ - alloc.reflow("I am very confused by this field access:"), - alloc.region_with_subregion(surroundings, region), - alloc.concat(vec![ - alloc.reflow(r"It looks like a record field access on a private tag.") - ]), - ]) - } Underscore(row, col) => { let region = @@ -475,28 +428,6 @@ fn to_bad_ident_expr_report<'b>( ]) } - DoubleDot(row, col) => { - let region = - Region::from_rows_cols(surroundings.start_line, surroundings.start_col, row, col); - alloc.stack(vec![ - alloc.reflow("I am very confused by these two dots in a row:"), - alloc.region_with_subregion(surroundings, region), - alloc.concat(vec![ - alloc.reflow(r"There always needs to be a name after a dot.") - ]), - ]) - } - - StrayAt(row, col) => { - let region = - Region::from_rows_cols(surroundings.start_line, surroundings.start_col, row, col); - alloc.stack(vec![ - alloc.reflow("I am very confused by this @ symbol"), - alloc.region_with_subregion(surroundings, region), - alloc.concat(vec![alloc.reflow(r"I expected a private tag.")]), - ]) - } - BadPrivateTag(row, col) => { use BadIdentNext::*; match what_is_next(alloc.src_lines, row, col) { @@ -579,22 +510,6 @@ fn to_bad_ident_pattern_report<'b>( ]) } - PartStartsWithNumber(row, col) => { - let region = Region::from_row_col(row, col); - - alloc.stack(vec![ - alloc.reflow("I trying to parse a record field access here:"), - alloc.region_with_subregion(surroundings, region), - alloc.concat(vec![ - alloc.reflow("So I expect to see a lowercase letter next, like "), - alloc.parser_suggestion(".name"), - alloc.reflow(" or "), - alloc.parser_suggestion(".height"), - alloc.reflow("."), - ]), - ]) - } - WeirdAccessor(_row, _col) => alloc.stack(vec![ alloc.reflow("I am very confused by this field access"), alloc.region(surroundings), @@ -640,33 +555,6 @@ fn to_bad_ident_pattern_report<'b>( ]), ]) } - PrivateTagNotUppercase(row, col) => { - let region = Region::from_row_col(row, col); - - alloc.stack(vec![ - alloc.reflow("I am trying to parse a private tag here:"), - alloc.region_with_subregion(surroundings, region), - alloc.concat(vec![ - alloc.reflow(r"But after the "), - alloc.keyword("@"), - alloc.reflow(r" symbol I found a lowercase letter. "), - alloc.reflow(r"All tag names (global and private)"), - alloc.reflow(r" must start with an uppercase letter, like "), - alloc.parser_suggestion("@UUID"), - alloc.reflow(" or "), - alloc.parser_suggestion("@Secrets"), - alloc.reflow("."), - ]), - ]) - } - - PrivateTagFieldAccess(_row, _col) => alloc.stack(vec![ - alloc.reflow("I am very confused by this field access:"), - alloc.region(surroundings), - alloc.concat(vec![ - alloc.reflow(r"It looks like a record field access on a private tag.") - ]), - ]), Underscore(row, col) => { let region = Region::from_row_col(row, col - 1); From 21f58b947aa70185b5c08a0ff890eac8ffcde34e Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 23:04:28 +0100 Subject: [PATCH 47/74] shuffle error message handling --- compiler/parse/src/expr.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 1f69407eca..b99e386218 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -156,9 +156,9 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, &'a str>, EExpr<'a } fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> { - specialize( - |_, r, c| EExpr::Access(r, c), - skip_first!(ascii_char(b'.'), lowercase_ident()), + skip_first!( + word1(b'.', EExpr::Access), + specialize(|_, r, c| EExpr::Access(r, c), lowercase_ident()) ) } From 37462d6ec651a550450813f87d5e0a86320290af Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 23:06:47 +0100 Subject: [PATCH 48/74] use unit error type --- compiler/parse/src/ident.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 4c5b956363..05a7fb4bb9 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -179,12 +179,32 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { /// * A module name /// * A type name /// * A global tag -pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { - global_tag_or_ident(|first_char| first_char.is_uppercase()) +pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { + move |_, mut state: State<'a>| match chomp_uppercase_part(state.bytes) { + Err(progress) => Err((progress, (), state)), + Ok(ident) => { + let width = ident.len(); + state.column += width as u16; + state.bytes = &state.bytes[width..]; + Ok((MadeProgress, ident, state)) + } + } } -pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { - global_tag_or_ident(|first_char| first_char.is_alphabetic()) +pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str, ()> { + move |_, mut state: State<'a>| match chomp_part(|c| c.is_alphabetic(), state.bytes) { + Err(progress) => Err((progress, (), state)), + Ok(ident) => { + if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) { + Err((MadeProgress, (), state)) + } else { + let width = ident.len(); + state.column += width as u16; + state.bytes = &state.bytes[width..]; + Ok((MadeProgress, ident, state)) + } + } + } } pub fn join_module_parts<'a>(arena: &'a Bump, module_parts: &[&str]) -> &'a str { From 30b47b95936df82b3063307692e1e47b596ceb99 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 10 Mar 2021 23:08:12 +0100 Subject: [PATCH 49/74] use unit error type more --- compiler/parse/src/ident.rs | 121 +++++------------------------------- 1 file changed, 15 insertions(+), 106 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 05a7fb4bb9..3413db0cac 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -61,117 +61,26 @@ impl<'a> Ident<'a> { } } -fn chomp_identifier<'a, F>(pred: F, buffer: &[u8]) -> Result<&str, Progress> -where - F: Fn(char) -> bool, -{ - use encode_unicode::CharExt; - - let mut chomped = 0; - - match char::from_utf8_slice_start(&buffer[chomped..]) { - Ok((ch, width)) if pred(ch) => { - chomped += width; - } - _ => { - // no parse - return Err(Progress::NoProgress); - } - } - - while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { - // After the first character, only these are allowed: - // - // * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers - // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() - // * A ':' indicating the end of the field - if ch.is_alphabetic() || ch.is_ascii_digit() { - chomped += width; - } else { - // we're done - break; - } - } - - let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; - - Ok(name) -} - -fn global_tag_or_ident<'a, F>(pred: F) -> impl Parser<'a, &'a str, SyntaxError<'a>> -where - F: Fn(char) -> bool, -{ - move |arena, mut state: State<'a>| { - // pred will determine if this is a tag or ident (based on capitalization) - let (first_letter, bytes_parsed) = match peek_utf8_char(&state) { - Ok((first_letter, bytes_parsed)) => { - if !pred(first_letter) { - return Err(unexpected(0, Attempting::RecordFieldLabel, state)); - } - - (first_letter, bytes_parsed) - } - Err(reason) => return state.fail(arena, NoProgress, reason), - }; - - let mut buf = String::with_capacity_in(1, arena); - - buf.push(first_letter); - - state = state.advance_without_indenting(bytes_parsed)?; - - while !state.bytes.is_empty() { - match peek_utf8_char(&state) { - Ok((ch, bytes_parsed)) => { - // After the first character, only these are allowed: - // - // * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers - // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() - // * A ':' indicating the end of the field - if ch.is_alphabetic() || ch.is_ascii_digit() { - buf.push(ch); - - state = state.advance_without_indenting(bytes_parsed)?; - } else { - // This is the end of the field. We're done! - break; - } - } - Err(reason) => return state.fail(arena, MadeProgress, reason), - }; - } - - Ok((MadeProgress, buf.into_bump_str(), state)) - } -} - /// This could be: /// /// * A record field, e.g. "email" in `.email` or in `email:` /// * A named pattern match, e.g. "foo" in `foo =` or `foo ->` or `\foo ->` -pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { - move |arena, state: State<'a>| { - let (progress, ident, state) = - global_tag_or_ident(|first_char| first_char.is_lowercase()).parse(arena, state)?; - - // to parse a valid ident, progress must be made - debug_assert_eq!(progress, MadeProgress); - - if (ident == keyword::IF) - || (ident == keyword::THEN) - || (ident == keyword::ELSE) - || (ident == keyword::WHEN) - || (ident == keyword::IS) - || (ident == keyword::AS) - { - // TODO Calculate the correct region based on state - let region = Region::zero(); - Err((MadeProgress, SyntaxError::ReservedKeyword(region), state)) - } else { - Ok((MadeProgress, ident, state)) +pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { + debug!( + move |_, mut state: State<'a>| match chomp_lowercase_part(state.bytes) { + Err(progress) => Err((progress, (), state)), + Ok(ident) => { + if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) { + Err((MadeProgress, (), state)) + } else { + let width = ident.len(); + state.column += width as u16; + state.bytes = &state.bytes[width..]; + Ok((MadeProgress, ident, state)) + } + } } - } + ) } /// This could be: From 92cff4c32a869527a8e14962da37f19b19ee080f Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 11 Mar 2021 01:48:05 +0100 Subject: [PATCH 50/74] optimize type parser --- compiler/parse/src/ident.rs | 144 ++++++++++++++------------ compiler/parse/src/type_annotation.rs | 101 +----------------- 2 files changed, 83 insertions(+), 162 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 3413db0cac..dc633c953a 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -1,14 +1,7 @@ -use crate::ast::Attempting; -use crate::keyword; use crate::parser::Progress::{self, *}; -use crate::parser::{ - peek_utf8_char, unexpected, BadInputError, Col, EExpr, ParseResult, Parser, Row, State, - SyntaxError, -}; -use bumpalo::collections::string::String; +use crate::parser::{BadInputError, Col, EExpr, ParseResult, Parser, Row, State}; use bumpalo::collections::vec::Vec; use bumpalo::Bump; -use roc_region::all::Region; /// The parser accepts all of these in any position where any one of them could /// appear. This way, canonicalization can give more helpful error messages like @@ -66,21 +59,20 @@ impl<'a> Ident<'a> { /// * A record field, e.g. "email" in `.email` or in `email:` /// * A named pattern match, e.g. "foo" in `foo =` or `foo ->` or `\foo ->` pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { - debug!( - move |_, mut state: State<'a>| match chomp_lowercase_part(state.bytes) { - Err(progress) => Err((progress, (), state)), - Ok(ident) => { - if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) { - Err((MadeProgress, (), state)) - } else { - let width = ident.len(); - state.column += width as u16; - state.bytes = &state.bytes[width..]; - Ok((MadeProgress, ident, state)) + move |_, state: State<'a>| match chomp_lowercase_part(state.bytes) { + Err(progress) => Err((progress, (), state)), + Ok(ident) => { + if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) { + Err((MadeProgress, (), state)) + } else { + let width = ident.len(); + match state.advance_without_indenting_ee(width, |_, _| ()) { + Ok(state) => Ok((MadeProgress, ident, state)), + Err(bad) => Err(bad), } } } - ) + } } /// This could be: @@ -89,51 +81,35 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { /// * A type name /// * A global tag pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { - move |_, mut state: State<'a>| match chomp_uppercase_part(state.bytes) { + move |_, state: State<'a>| match chomp_uppercase_part(state.bytes) { Err(progress) => Err((progress, (), state)), Ok(ident) => { let width = ident.len(); - state.column += width as u16; - state.bytes = &state.bytes[width..]; - Ok((MadeProgress, ident, state)) + match state.advance_without_indenting_ee(width, |_, _| ()) { + Ok(state) => Ok((MadeProgress, ident, state)), + Err(bad) => Err(bad), + } } } } pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str, ()> { - move |_, mut state: State<'a>| match chomp_part(|c| c.is_alphabetic(), state.bytes) { + move |_, state: State<'a>| match chomp_part(|c| c.is_alphabetic(), state.bytes) { Err(progress) => Err((progress, (), state)), Ok(ident) => { if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) { Err((MadeProgress, (), state)) } else { let width = ident.len(); - state.column += width as u16; - state.bytes = &state.bytes[width..]; - Ok((MadeProgress, ident, state)) + match state.advance_without_indenting_ee(width, |_, _| ()) { + Ok(state) => Ok((MadeProgress, ident, state)), + Err(bad) => Err(bad), + } } } } } -pub fn join_module_parts<'a>(arena: &'a Bump, module_parts: &[&str]) -> &'a str { - let capacity = module_parts.len() * 3; // Module parts tend to be 3+ characters. - let mut buf = String::with_capacity_in(capacity, arena); - let mut any_parts_added = false; - - for part in module_parts { - if any_parts_added { - buf.push('.'); - } else { - any_parts_added = true; - } - - buf.push_str(part); - } - - buf.into_bump_str() -} - macro_rules! advance_state { ($state:expr, $n:expr) => { $state.advance_without_indenting_ee($n, |r, c| { @@ -185,30 +161,26 @@ fn malformed_identifier<'a>( _arena: &'a Bump, mut state: State<'a>, ) -> ParseResult<'a, Ident<'a>, EExpr<'a>> { + use encode_unicode::CharExt; // skip forward to the next non-identifier character - while !state.bytes.is_empty() { - match peek_utf8_char(&state) { - Ok((ch, bytes_parsed)) => { - // We can't use ch.is_alphanumeric() here because that passes for - // things that are "numeric" but not ASCII digits, like `¾` - if ch == '.' || ch == '_' || ch.is_alphabetic() || ch.is_ascii_digit() { - state = state.advance_without_indenting_ee(bytes_parsed, |r, c| { - EExpr::Space(crate::parser::BadInputError::LineTooLong, r, c) - })?; - continue; - } else { - break; - } - } - Err(_reason) => { - break; - } + let mut chomped = 0; + while let Ok((ch, width)) = char::from_utf8_slice_start(&state.bytes[chomped..]) { + // We can't use ch.is_alphanumeric() here because that passes for + // things that are "numeric" but not ASCII digits, like `¾` + if ch == '.' || ch == '_' || ch.is_alphabetic() || ch.is_ascii_digit() { + chomped += width; + continue; + } else { + break; } } - let parsed = &initial_bytes[..(initial_bytes.len() - state.bytes.len())]; + let delta = initial_bytes.len() - state.bytes.len(); + let parsed_str = unsafe { std::str::from_utf8_unchecked(&initial_bytes[..chomped + delta]) }; - let parsed_str = unsafe { std::str::from_utf8_unchecked(parsed) }; + state = state.advance_without_indenting_ee(chomped, |r, c| { + EExpr::Space(crate::parser::BadInputError::LineTooLong, r, c) + })?; Ok((MadeProgress, Ident::Malformed(parsed_str, problem), state)) } @@ -240,7 +212,6 @@ fn chomp_part(leading_is_good: F, buffer: &[u8]) -> Result<&str, Progress> where F: Fn(char) -> bool, { - // assumes the leading `.` has been chomped already use encode_unicode::CharExt; let mut chomped = 0; @@ -455,6 +426,47 @@ fn chomp_module_chain<'a>(buffer: &'a [u8]) -> Result { } } +pub fn concrete_type<'a>() -> impl Parser<'a, (&'a str, &'a str), ()> { + move |_, state: State<'a>| match chomp_concrete_type(state.bytes) { + Err(progress) => Err((progress, (), state)), + Ok((module_name, type_name, width)) => { + match state.advance_without_indenting_ee(width, |_, _| ()) { + Ok(state) => Ok((MadeProgress, (module_name, type_name), state)), + Err(bad) => Err(bad), + } + } + } +} + +// parse a type name like `Result` or `Result.Result` +fn chomp_concrete_type<'a>(buffer: &'a [u8]) -> Result<(&'a str, &'a str, usize), Progress> { + let first = crate::ident::chomp_uppercase_part(buffer)?; + + if let Some(b'.') = buffer.get(first.len()) { + match crate::ident::chomp_module_chain(&buffer[first.len()..]) { + Err(_) => Err(MadeProgress), + Ok(rest) => { + let width = first.len() + rest as usize; + let slice = &buffer[..width]; + + match slice.iter().rev().position(|c| *c == b'.') { + None => Ok(("", first, first.len())), + Some(rev_index) => { + let index = slice.len() - rev_index; + let module_name = + unsafe { std::str::from_utf8_unchecked(&slice[..index - 1]) }; + let type_name = unsafe { std::str::from_utf8_unchecked(&slice[index..]) }; + + Ok((module_name, type_name, width)) + } + } + } + } + } else { + Ok(("", first, first.len())) + } +} + fn chomp_access_chain<'a>(buffer: &'a [u8], parts: &mut Vec<'a, &'a str>) -> Result { let mut chomped = 0; diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index 5f7b6fb5f1..116e345f0e 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -1,6 +1,5 @@ use crate::ast::{AssignedField, Tag, TypeAnnotation}; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; -use crate::ident::join_module_parts; use crate::keyword; use crate::parser::{ allocated, backtrackable, not_e, optional, peek_utf8_char_e, specialize, specialize_ref, word1, @@ -517,102 +516,12 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located fn parse_concrete_type<'a>( arena: &'a Bump, - mut state: State<'a>, + state: State<'a>, ) -> ParseResult<'a, TypeAnnotation<'a>, TApply> { - let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.) - let mut parts: Vec<&'a str> = Vec::new_in(arena); - - // Qualified types must start with a capitalized letter. - match peek_utf8_char_e(&state, TApply::StartNotUppercase, TApply::Space) { - Ok((first_letter, bytes_parsed)) => { - if first_letter.is_alphabetic() && first_letter.is_uppercase() { - part_buf.push(first_letter); - } else { - let problem = TApply::StartNotUppercase(state.line, state.column + 1); - return Err((NoProgress, problem, state)); - } - - state = state.advance_without_indenting_e(bytes_parsed, TApply::Space)?; - } - Err(reason) => return Err((NoProgress, reason, state)), - } - - while !state.bytes.is_empty() { - match peek_utf8_char_e(&state, TApply::End, TApply::Space) { - Ok((ch, bytes_parsed)) => { - // After the first character, only these are allowed: - // - // * Unicode alphabetic chars - you might name a variable `鹏` if that's clear to your readers - // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() - // * A dot ('.') - if ch.is_alphabetic() { - if part_buf.is_empty() && !ch.is_uppercase() { - // Each part must begin with a capital letter. - return Err(( - MadeProgress, - TApply::StartNotUppercase(state.line, state.column), - state, - )); - } - - part_buf.push(ch); - } else if ch.is_ascii_digit() { - // Parts may not start with numbers! - if part_buf.is_empty() { - return Err(( - MadeProgress, - TApply::StartIsNumber(state.line, state.column), - state, - )); - } - - part_buf.push(ch); - } else if ch == '.' { - // Having two consecutive dots is an error. - if part_buf.is_empty() { - return Err(( - MadeProgress, - TApply::DoubleDot(state.line, state.column), - state, - )); - } - - parts.push(part_buf.into_bump_str()); - - // Now that we've recorded the contents of the current buffer, reset it. - part_buf = String::new_in(arena); - } else { - // This must be the end of the type. We're done! - break; - } - - state = state.advance_without_indenting_e(bytes_parsed, TApply::Space)?; - } - Err(reason) => { - return Err((MadeProgress, reason, state)); - } - } - } - - if part_buf.is_empty() { - // We probably had a trailing dot, e.g. `Foo.bar.` - this is malformed! - // - // This condition might also occur if we encounter a malformed accessor like `.|` - // - // If we made it this far and don't have a next_char, then necessarily - // we have consumed a '.' char previously. - return Err(( - MadeProgress, - TApply::TrailingDot(state.line, state.column), - state, - )); - } - - let answer = TypeAnnotation::Apply( - join_module_parts(arena, parts.into_bump_slice()), - part_buf.into_bump_str(), - &[], - ); + let (_, (module_name, type_name), state) = + specialize(|_, r, c| TApply::End(r, c), crate::ident::concrete_type()) + .parse(arena, state)?; + let answer = TypeAnnotation::Apply(module_name, type_name, &[]); Ok((MadeProgress, answer, state)) } From e214674016b4c75c742cda0fa54219e2cd5d017c Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 11 Mar 2021 16:37:42 +0100 Subject: [PATCH 51/74] cleanup blankspace function usage --- compiler/parse/src/blankspace.rs | 16 ++++---- compiler/parse/src/expr.rs | 61 ++++++++++-------------------- compiler/parse/src/module.rs | 19 ++++++++-- compiler/parse/src/test_helpers.rs | 10 ++--- editor/src/lang/expr.rs | 11 ++---- 5 files changed, 51 insertions(+), 66 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 93e6653a3b..0bb8ac2fb0 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -15,7 +15,7 @@ use roc_region::all::{Located, Region}; /// Returns a Located where the location is around the Expr, ignoring the spaces. /// If any newlines or comments were found, the Expr will be wrapped in a SpaceBefore and/or /// SpaceAfter as appropriate. -pub fn space0_around<'a, P, S>( +fn space0_around<'a, P, S>( parser: P, min_indent: u16, ) -> impl Parser<'a, Located, SyntaxError<'a>> @@ -119,7 +119,7 @@ where /// Returns a Located where the location is around the Expr, ignoring the spaces. /// If any newlines or comments were found, the Expr will be wrapped in a SpaceBefore and/or /// SpaceAfter as appropriate. -pub fn space1_around<'a, P, S>( +fn space1_around<'a, P, S>( parser: P, min_indent: u16, ) -> impl Parser<'a, Located, SyntaxError<'a>> @@ -160,7 +160,7 @@ where /// Parses the given expression with 0 or more (spaces/comments/newlines) before it. /// Returns a Located where the location is around the Expr, ignoring the spaces. /// The Expr will be wrapped in a SpaceBefore if there were any newlines or comments found. -pub fn space0_before<'a, P, S>( +fn space0_before<'a, P, S>( parser: P, min_indent: u16, ) -> impl Parser<'a, Located, SyntaxError<'a>> @@ -241,7 +241,7 @@ where /// Parses the given expression with 1 or more (spaces/comments/newlines) before it. /// Returns a Located where the location is around the Expr, ignoring the spaces. /// The Expr will be wrapped in a SpaceBefore if there were any newlines or comments found. -pub fn space1_before<'a, P, S>( +fn space1_before<'a, P, S>( parser: P, min_indent: u16, ) -> impl Parser<'a, Located, SyntaxError<'a>> @@ -268,7 +268,7 @@ where /// Parses the given expression with 0 or more (spaces/comments/newlines) after it. /// Returns a Located where the location is around the Expr, ignoring the spaces. /// The Expr will be wrapped in a SpaceAfter if there were any newlines or comments found. -pub fn space0_after<'a, P, S>( +fn space0_after<'a, P, S>( parser: P, min_indent: u16, ) -> impl Parser<'a, Located, SyntaxError<'a>> @@ -295,7 +295,7 @@ where /// Parses the given expression with 1 or more (spaces/comments/newlines) after it. /// Returns a Located where the location is around the Expr, ignoring the spaces. /// The Expr will be wrapped in a SpaceAfter if there were any newlines or comments found. -pub fn space1_after<'a, P, S>( +fn space1_after<'a, P, S>( parser: P, min_indent: u16, ) -> impl Parser<'a, Located, SyntaxError<'a>> @@ -320,7 +320,7 @@ where } /// Zero or more (spaces/comments/newlines). -pub fn space0<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> { +fn space0<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> { spaces(false, min_indent) } @@ -360,7 +360,7 @@ where } /// One or more (spaces/comments/newlines). -pub fn space1<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> { +fn space1<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> { // TODO try benchmarking a short-circuit for the typical case: see if there is // exactly one space followed by char that isn't [' ', '\n', or '#'], and // if so, return empty slice. The case where there's exactly 1 space should diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index b99e386218..db26b10ebe 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -20,6 +20,24 @@ use roc_region::all::{Located, Region}; use crate::parser::Progress::{self, *}; +pub fn test_parse_expr<'a>( + min_indent: u16, + arena: &'a bumpalo::Bump, + state: State<'a>, +) -> Result<(Located>, State<'a>), EExpr<'a>> { + let parser = space0_before_e( + loc!(|a, s| parse_expr_help(min_indent, a, s)), + min_indent, + EExpr::Space, + EExpr::IndentStart, + ); + + match parser.parse(arena, state) { + Ok((_, expression, state)) => Ok((expression, state)), + Err((_, fail, _)) => Err(fail), + } +} + // public for testing purposes pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> { // Recursive parsers must not directly invoke functions which return (impl Parser), @@ -718,47 +736,6 @@ fn assigned_expr_field_to_pattern_help<'a>( }) } -/// A def beginning with a parenthetical pattern, for example: -/// -/// (UserId userId) = ... -/// -/// Note: Parenthetical patterns are a shorthand convenience, and may not have type annotations. -/// It would be too weird to parse; imagine `(UserId userId) : ...` above `(UserId userId) = ...` -/// !!!! THIS IS NOT USED !!!! -// fn loc_parenthetical_def<'a>(min_indent: u16) -> impl Parser<'a, Located>> { -// move |arena, state| { -// let (loc_tuple, state) = loc!(and!( -// space0_after( -// between!( -// ascii_char(b'('), -// space0_around(loc_pattern(min_indent), min_indent), -// ascii_char(b')') -// ), -// min_indent, -// ), -// equals_with_indent() -// )) -// .parse(arena, state)?; - -// let region = loc_tuple.region; -// let (loc_first_pattern, equals_sign_indent) = loc_tuple.value; - -// // Continue parsing the expression as a Def. -// let (spaces_after_equals, state) = space0(min_indent).parse(arena, state)?; -// let (value, state) = parse_def_expr( -// region.start_col, -// min_indent, -// equals_sign_indent, -// arena, -// state, -// loc_first_pattern, -// spaces_after_equals, -// )?; - -// Ok((Located { value, region }, state)) -// } -// } - fn parse_defs_help<'a>( min_indent: u16, ) -> impl Parser<'a, Vec<'a, &'a Located>>, EExpr<'a>> { @@ -795,7 +772,7 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>, SyntaxError<'a>> { specialize(|e, _, _| SyntaxError::Expr(e), def_help(min_indent)) } -fn def_help<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>, EExpr<'a>> { +pub fn def_help<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>, EExpr<'a>> { let indented_more = min_indent + 1; enum DefKind { diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 32d2fb2919..0068ae6933 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -1,6 +1,5 @@ use crate::ast::{CommentOrNewline, Def, Module}; -use crate::blankspace::{space0_around, space0_before_e, space0_e}; -use crate::expr::def; +use crate::blankspace::{space0_before_e, space0_e}; use crate::header::{ package_entry, package_name, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, To, TypedIdent, @@ -252,8 +251,22 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { #[inline(always)] pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located>>, SyntaxError<'a>> { + use crate::parser::EExpr; // force that we pare until the end of the input - skip_second!(zero_or_more!(space0_around(loc!(def(0)), 0)), end_of_file()) + let min_indent = 0; + skip_second!( + specialize( + |e, _, _| SyntaxError::Expr(e), + zero_or_more!(crate::blankspace::space0_around_ee( + loc!(crate::expr::def_help(min_indent)), + min_indent, + EExpr::Space, + EExpr::IndentStart, + EExpr::IndentEnd, + )) + ), + end_of_file() + ) } #[derive(Debug)] struct ProvidesTo<'a> { diff --git a/compiler/parse/src/test_helpers.rs b/compiler/parse/src/test_helpers.rs index 7fce100612..b5c6e226d8 100644 --- a/compiler/parse/src/test_helpers.rs +++ b/compiler/parse/src/test_helpers.rs @@ -1,5 +1,4 @@ use crate::ast; -use crate::blankspace::space0_before; use crate::expr::expr; use crate::module::module_defs; use crate::parser::{loc, Parser, State, SyntaxError}; @@ -32,10 +31,9 @@ pub fn parse_loc_with<'a>( input: &'a str, ) -> Result>, SyntaxError<'a>> { let state = State::new_in(arena, input.trim().as_bytes()); - let parser = space0_before(loc(expr(0)), 0); - let answer = parser.parse(&arena, state); - answer - .map(|(_, loc_expr, _)| loc_expr) - .map_err(|(_, fail, _)| fail) + match crate::expr::test_parse_expr(0, arena, state) { + Ok((loc_expr, _state)) => Ok(loc_expr), + Err(fail) => Err(SyntaxError::Expr(fail)), + } } diff --git a/editor/src/lang/expr.rs b/editor/src/lang/expr.rs index 410009bc2e..58243df0ce 100644 --- a/editor/src/lang/expr.rs +++ b/editor/src/lang/expr.rs @@ -18,7 +18,6 @@ use roc_module::operator::CalledVia; use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol}; use roc_parse::ast; use roc_parse::ast::StrLiteral; -use roc_parse::blankspace::space0_before; use roc_parse::expr::expr; use roc_parse::parser::{loc, Parser, State, SyntaxError}; use roc_problem::can::{Problem, RuntimeError}; @@ -236,13 +235,11 @@ pub fn str_to_expr2<'a>( region: Region, ) -> Result<(Expr2, self::Output), SyntaxError<'a>> { let state = State::new_in(arena, input.trim().as_bytes()); - let parser = space0_before(loc(expr(0)), 0); - let parse_res = parser.parse(&arena, state); - parse_res - .map(|(_, loc_expr, _)| arena.alloc(loc_expr.value)) - .map(|loc_expr_val_ref| to_expr2(env, scope, loc_expr_val_ref, region)) - .map_err(|(_, fail, _)| fail) + match roc_parse::expr::test_parse_expr(0, arena, state) { + Ok((loc_expr, _state)) => Ok(to_expr2(env, scope, arena.alloc(loc_expr.value), region)), + Err(fail) => Err(SyntaxError::Expr(fail)), + } } pub fn to_expr2<'a>( From b4b77add087992143141cacd50c0120ff8b51a68 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 11 Mar 2021 16:40:01 +0100 Subject: [PATCH 52/74] cleanup --- compiler/parse/src/blankspace.rs | 234 +------------------------------ 1 file changed, 3 insertions(+), 231 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 0bb8ac2fb0..ce9f6d45eb 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -1,64 +1,15 @@ use crate::ast::CommentOrNewline::{self, *}; use crate::ast::{Attempting, Spaceable}; use crate::parser::{ - self, and, ascii_char, ascii_string, backtrackable, bad_input_to_syntax_error, optional, - parse_utf8, peek_utf8_char, then, unexpected, unexpected_eof, BadInputError, Col, Parser, + self, and, ascii_char, ascii_string, optional, parse_utf8, peek_utf8_char, then, unexpected, + unexpected_eof, BadInputError, Col, Parser, Progress::{self, *}, Row, State, SyntaxError, }; use bumpalo::collections::string::String; use bumpalo::collections::vec::Vec; use bumpalo::Bump; -use roc_region::all::{Located, Region}; - -/// Parses the given expression with 0 or more (spaces/comments/newlines) before and/or after it. -/// Returns a Located where the location is around the Expr, ignoring the spaces. -/// If any newlines or comments were found, the Expr will be wrapped in a SpaceBefore and/or -/// SpaceAfter as appropriate. -fn space0_around<'a, P, S>( - parser: P, - min_indent: u16, -) -> impl Parser<'a, Located, SyntaxError<'a>> -where - S: Spaceable<'a>, - S: 'a, - S: Sized, - P: Parser<'a, Located, SyntaxError<'a>>, - P: 'a, -{ - parser::map_with_arena( - and(space0(min_indent), and(parser, space0(min_indent))), - move |arena: &'a Bump, - tuples: ( - &'a [CommentOrNewline<'a>], - (Located, &'a [CommentOrNewline<'a>]), - )| { - let (spaces_before, (loc_val, spaces_after)) = tuples; - - if spaces_before.is_empty() { - if spaces_after.is_empty() { - loc_val - } else { - arena - .alloc(loc_val.value) - .with_spaces_after(spaces_after, loc_val.region) - } - } else if spaces_after.is_empty() { - arena - .alloc(loc_val.value) - .with_spaces_before(spaces_before, loc_val.region) - } else { - let wrapped_expr = arena - .alloc(loc_val.value) - .with_spaces_after(spaces_after, loc_val.region); - - arena - .alloc(wrapped_expr.value) - .with_spaces_before(spaces_before, wrapped_expr.region) - } - }, - ) -} +use roc_region::all::Located; pub fn space0_around_ee<'a, P, S, E>( parser: P, @@ -114,76 +65,6 @@ where ) } -/// Parses the given expression with 1 or more (spaces/comments/newlines) before it, -/// and also 1 or more spaces after it. -/// Returns a Located where the location is around the Expr, ignoring the spaces. -/// If any newlines or comments were found, the Expr will be wrapped in a SpaceBefore and/or -/// SpaceAfter as appropriate. -fn space1_around<'a, P, S>( - parser: P, - min_indent: u16, -) -> impl Parser<'a, Located, SyntaxError<'a>> -where - S: Spaceable<'a>, - S: 'a, - P: Parser<'a, Located, SyntaxError<'a>>, - P: 'a, -{ - parser::map_with_arena( - and(space1(min_indent), and(parser, space1(min_indent))), - |arena, (spaces_before, (loc_expr, spaces_after))| { - if spaces_before.is_empty() { - if spaces_after.is_empty() { - loc_expr - } else { - arena - .alloc(loc_expr.value) - .with_spaces_after(spaces_after, loc_expr.region) - } - } else if spaces_after.is_empty() { - arena - .alloc(loc_expr.value) - .with_spaces_before(spaces_before, loc_expr.region) - } else { - let loc_wrapped_expr = arena - .alloc(loc_expr.value) - .with_spaces_after(spaces_after, loc_expr.region); - - arena - .alloc(loc_wrapped_expr.value) - .with_spaces_before(spaces_before, loc_wrapped_expr.region) - } - }, - ) -} - -/// Parses the given expression with 0 or more (spaces/comments/newlines) before it. -/// Returns a Located where the location is around the Expr, ignoring the spaces. -/// The Expr will be wrapped in a SpaceBefore if there were any newlines or comments found. -fn space0_before<'a, P, S>( - parser: P, - min_indent: u16, -) -> impl Parser<'a, Located, SyntaxError<'a>> -where - S: Spaceable<'a>, - S: 'a, - P: Parser<'a, Located, SyntaxError<'a>>, - P: 'a, -{ - parser::map_with_arena( - and!(space0(min_indent), parser), - |arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Located)| { - if space_list.is_empty() { - loc_expr - } else { - arena - .alloc(loc_expr.value) - .with_spaces_before(space_list, loc_expr.region) - } - }, - ) -} - pub fn space0_before_e<'a, P, S, E>( parser: P, min_indent: u16, @@ -238,92 +119,6 @@ where ) } -/// Parses the given expression with 1 or more (spaces/comments/newlines) before it. -/// Returns a Located where the location is around the Expr, ignoring the spaces. -/// The Expr will be wrapped in a SpaceBefore if there were any newlines or comments found. -fn space1_before<'a, P, S>( - parser: P, - min_indent: u16, -) -> impl Parser<'a, Located, SyntaxError<'a>> -where - S: Spaceable<'a>, - S: 'a, - P: Parser<'a, Located, SyntaxError<'a>>, - P: 'a, -{ - parser::map_with_arena( - and!(backtrackable(space1(min_indent)), parser), - |arena, (space_list, loc_expr)| { - if space_list.is_empty() { - loc_expr - } else { - arena - .alloc(loc_expr.value) - .with_spaces_before(space_list, loc_expr.region) - } - }, - ) -} - -/// Parses the given expression with 0 or more (spaces/comments/newlines) after it. -/// Returns a Located where the location is around the Expr, ignoring the spaces. -/// The Expr will be wrapped in a SpaceAfter if there were any newlines or comments found. -fn space0_after<'a, P, S>( - parser: P, - min_indent: u16, -) -> impl Parser<'a, Located, SyntaxError<'a>> -where - S: Spaceable<'a>, - S: 'a, - P: Parser<'a, Located, SyntaxError<'a>>, - P: 'a, -{ - parser::map_with_arena( - and!(parser, space0(min_indent)), - |arena, (loc_expr, space_list)| { - if space_list.is_empty() { - loc_expr - } else { - arena - .alloc(loc_expr.value) - .with_spaces_after(space_list, loc_expr.region) - } - }, - ) -} - -/// Parses the given expression with 1 or more (spaces/comments/newlines) after it. -/// Returns a Located where the location is around the Expr, ignoring the spaces. -/// The Expr will be wrapped in a SpaceAfter if there were any newlines or comments found. -fn space1_after<'a, P, S>( - parser: P, - min_indent: u16, -) -> impl Parser<'a, Located, SyntaxError<'a>> -where - S: Spaceable<'a>, - S: 'a, - P: Parser<'a, Located, SyntaxError<'a>>, - P: 'a, -{ - parser::map_with_arena( - and!(parser, space1(min_indent)), - |arena, (loc_expr, space_list)| { - if space_list.is_empty() { - loc_expr - } else { - arena - .alloc(loc_expr.value) - .with_spaces_after(space_list, loc_expr.region) - } - }, - ) -} - -/// Zero or more (spaces/comments/newlines). -fn space0<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> { - spaces(false, min_indent) -} - pub fn space0_e<'a, E>( min_indent: u16, space_problem: fn(BadInputError, Row, Col) -> E, @@ -359,15 +154,6 @@ where } } -/// One or more (spaces/comments/newlines). -fn space1<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> { - // TODO try benchmarking a short-circuit for the typical case: see if there is - // exactly one space followed by char that isn't [' ', '\n', or '#'], and - // if so, return empty slice. The case where there's exactly 1 space should - // be by far the most common. - spaces(true, min_indent) -} - #[derive(Debug, Clone, Copy)] enum LineState { Normal, @@ -512,20 +298,6 @@ pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser: } } -#[inline(always)] -fn spaces<'a>( - require_at_least_one: bool, - min_indent: u16, -) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> { - spaces_help( - require_at_least_one, - min_indent, - bad_input_to_syntax_error, - |_, _| SyntaxError::OutdentedTooFar, - |_, _| SyntaxError::Eof(Region::zero()), - ) -} - #[inline(always)] fn spaces_help<'a, E>( require_at_least_one: bool, From 3e554cb21f64eac2913e942e76ec5a096cbe905e Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 11 Mar 2021 21:21:00 +0100 Subject: [PATCH 53/74] cleanup --- compiler/fmt/tests/test_fmt.rs | 12 +-- compiler/parse/src/blankspace.rs | 81 +++++++++++---- compiler/parse/src/expr.rs | 40 +++++--- compiler/parse/src/ident.rs | 34 ++++--- compiler/parse/src/parser.rs | 1 + compiler/parse/src/test_helpers.rs | 3 +- compiler/parse/src/type_annotation.rs | 32 +++++- compiler/reporting/src/error/parse.rs | 27 +++++ compiler/reporting/tests/helpers/mod.rs | 12 +-- compiler/reporting/tests/test_reporting.rs | 112 ++++++++++++--------- 10 files changed, 237 insertions(+), 117 deletions(-) diff --git a/compiler/fmt/tests/test_fmt.rs b/compiler/fmt/tests/test_fmt.rs index d2faf53847..13ade561a9 100644 --- a/compiler/fmt/tests/test_fmt.rs +++ b/compiler/fmt/tests/test_fmt.rs @@ -4,8 +4,6 @@ extern crate pretty_assertions; extern crate indoc; extern crate bumpalo; extern crate roc_fmt; -#[macro_use] -extern crate roc_parse; #[cfg(test)] mod test_fmt { @@ -15,18 +13,16 @@ mod test_fmt { use roc_fmt::def::fmt_def; use roc_fmt::module::fmt_module; use roc_parse::ast::Expr; - use roc_parse::blankspace::space0_before; use roc_parse::module::{self, module_defs}; use roc_parse::parser::{Parser, State, SyntaxError}; fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result, SyntaxError<'a>> { let state = State::new_in(arena, input.trim().as_bytes()); - let parser = space0_before(loc!(roc_parse::expr::expr(0)), 0); - let answer = parser.parse(&arena, state); - answer - .map(|(_, loc_expr, _)| loc_expr.value) - .map_err(|(_, fail, _)| fail) + match roc_parse::expr::test_parse_expr(0, arena, state) { + Ok((loc_expr, _state)) => Ok(loc_expr.value), + Err(fail) => Err(SyntaxError::Expr(fail)), + } } fn expr_formats_to(input: &str, expected: &str) { diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index ce9f6d45eb..1cf3ee992f 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -1,8 +1,7 @@ use crate::ast::CommentOrNewline::{self, *}; use crate::ast::{Attempting, Spaceable}; use crate::parser::{ - self, and, ascii_char, ascii_string, optional, parse_utf8, peek_utf8_char, then, unexpected, - unexpected_eof, BadInputError, Col, Parser, + self, and, peek_utf8_char, unexpected, unexpected_eof, BadInputError, Col, Parser, Progress::{self, *}, Row, State, SyntaxError, }; @@ -161,31 +160,69 @@ enum LineState { DocComment, } -pub fn line_comment<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { - then( - and!(ascii_char(b'#'), optional(ascii_string("# "))), - |arena: &'a Bump, state: State<'a>, _, (_, opt_doc)| { - if opt_doc != None { - return Err(unexpected(3, Attempting::LineComment, state)); - } - let mut length = 0; +// then( +// and!(ascii_char(b'#'), optional(ascii_string("# "))), +// |arena: &'a Bump, state: State<'a>, _, (_, opt_doc)| { +// if opt_doc != None { +// return Err(unexpected(3, Attempting::LineComment, state)); +// } +// let mut length = 0; +// +// for &byte in state.bytes.iter() { +// if byte != b'\n' { +// length += 1; +// } else { +// break; +// } +// } +// +// let comment = &state.bytes[..length]; +// let state = state.advance_without_indenting(length + 1)?; +// match parse_utf8(comment) { +// Ok(comment_str) => Ok((MadeProgress, comment_str, state)), +// Err(reason) => state.fail(arena, MadeProgress, reason), +// } +// }, +// ) - for &byte in state.bytes.iter() { - if byte != b'\n' { - length += 1; - } else { +pub fn line_comment<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { + |_, state: State<'a>| match chomp_line_comment(state.bytes) { + Ok(comment) => { + let width = 1 + comment.len(); + let state = state.advance_without_indenting(width + 1)?; + + Ok((MadeProgress, comment, state)) + } + Err(progress) => Err((progress, SyntaxError::ConditionFailed, state)), + } +} + +fn chomp_line_comment<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { + if let Some(b'#') = buffer.get(0) { + if (&buffer[1..]).starts_with(b"# ") { + // this is a doc comment, not a line comment + Err(NoProgress) + } else { + use encode_unicode::CharExt; + + let mut chomped = 1; + + while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + if ch == '\n' { break; + } else { + chomped += width; } } - let comment = &state.bytes[..length]; - let state = state.advance_without_indenting(length + 1)?; - match parse_utf8(comment) { - Ok(comment_str) => Ok((MadeProgress, comment_str, state)), - Err(reason) => state.fail(arena, MadeProgress, reason), - } - }, - ) + let comment_bytes = &buffer[1..chomped]; + let comment = unsafe { std::str::from_utf8_unchecked(comment_bytes) }; + + Ok(comment) + } + } else { + Err(NoProgress) + } } #[inline(always)] diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index db26b10ebe..b22458e4ed 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -1171,7 +1171,7 @@ fn parse_def_signature_help<'a>( // Indented more beyond the original indent. let indented_more = original_indent + 1; - and!( + let parser1 = { // Parse the first annotation. It doesn't need any spaces // around it parsed, because both the subsquent defs and the // final body will have space1_before on them. @@ -1183,19 +1183,24 @@ fn parse_def_signature_help<'a>( specialize(EExpr::Type, type_annotation::located_help(indented_more)), min_indent, EExpr::Space, - EExpr::IndentAnnotation + EExpr::IndentAnnotation, ), // The first annotation may be immediately (spaces_then_comment_or_newline()) // followed by a body at the exact same indent_level // leading to an AnnotatedBody in this case - |_progress, type_ann, indent_level| map( - optional(and!( - backtrackable(spaces_then_comment_or_newline_help()), - body_at_indent_help(indent_level) - )), - move |opt_body| (type_ann.clone(), opt_body) - ) - ), + |_progress, type_ann, indent_level| { + map( + optional(and!( + backtrackable(spaces_then_comment_or_newline_help()), + body_at_indent_help(indent_level) + )), + move |opt_body| (type_ann.clone(), opt_body), + ) + }, + ) + }; + + let parser2 = { and!( // Optionally parse additional defs. zero_or_more!(backtrackable(allocated(space0_before_e( @@ -1207,15 +1212,22 @@ fn parse_def_signature_help<'a>( // Parse the final expression that will be returned. // It should be indented the same amount as the original. space0_before_e( - loc!(|arena, state| parse_expr_help(original_indent, arena, state)), + loc!(one_of![ + |arena, state| parse_expr_help(original_indent, arena, state), + |_, state: State<'a>| Err(( + MadeProgress, + EExpr::DefMissingFinalExpr(state.line, state.column), + state + )), + ]), original_indent, EExpr::Space, EExpr::IndentEnd, ) ) - ) - .parse(arena, state) - .map( + }; + + and!(parser1, parser2).parse(arena, state).map( move |(progress, ((loc_first_annotation, opt_body), (mut defs, loc_ret)), state)| { let loc_first_def: Located> = match opt_body { None => { diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index dc633c953a..402d20386f 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -150,7 +150,7 @@ pub fn parse_ident_help<'a>( Err((MadeProgress, fail, state)) => match fail { BadIdent::Start(r, c) => Err((NoProgress, EExpr::Start(r, c), state)), BadIdent::Space(e, r, c) => Err((NoProgress, EExpr::Space(e, r, c), state)), - _ => malformed_identifier(initial.bytes, fail, arena, state), + _ => malformed_identifier(initial.bytes, fail, state), }, } } @@ -158,13 +158,24 @@ pub fn parse_ident_help<'a>( fn malformed_identifier<'a>( initial_bytes: &'a [u8], problem: BadIdent, - _arena: &'a Bump, mut state: State<'a>, ) -> ParseResult<'a, Ident<'a>, EExpr<'a>> { + let chomped = chomp_malformed(state.bytes); + let delta = initial_bytes.len() - state.bytes.len(); + let parsed_str = unsafe { std::str::from_utf8_unchecked(&initial_bytes[..chomped + delta]) }; + + state = state.advance_without_indenting_ee(chomped, |r, c| { + EExpr::Space(crate::parser::BadInputError::LineTooLong, r, c) + })?; + + Ok((MadeProgress, Ident::Malformed(parsed_str, problem), state)) +} + +/// skip forward to the next non-identifier character +pub fn chomp_malformed<'a>(bytes: &'a [u8]) -> usize { use encode_unicode::CharExt; - // skip forward to the next non-identifier character let mut chomped = 0; - while let Ok((ch, width)) = char::from_utf8_slice_start(&state.bytes[chomped..]) { + while let Ok((ch, width)) = char::from_utf8_slice_start(&bytes[chomped..]) { // We can't use ch.is_alphanumeric() here because that passes for // things that are "numeric" but not ASCII digits, like `¾` if ch == '.' || ch == '_' || ch.is_alphabetic() || ch.is_ascii_digit() { @@ -175,14 +186,7 @@ fn malformed_identifier<'a>( } } - let delta = initial_bytes.len() - state.bytes.len(); - let parsed_str = unsafe { std::str::from_utf8_unchecked(&initial_bytes[..chomped + delta]) }; - - state = state.advance_without_indenting_ee(chomped, |r, c| { - EExpr::Space(crate::parser::BadInputError::LineTooLong, r, c) - })?; - - Ok((MadeProgress, Ident::Malformed(parsed_str, problem), state)) + chomped } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -447,6 +451,12 @@ fn chomp_concrete_type<'a>(buffer: &'a [u8]) -> Result<(&'a str, &'a str, usize) Err(_) => Err(MadeProgress), Ok(rest) => { let width = first.len() + rest as usize; + + // we must explicitly check here for a trailing `.` + if let Some(b'.') = buffer.get(width) { + return Err(MadeProgress); + } + let slice = &buffer[..width]; match slice.iter().rev().position(|c| *c == b'.') { diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index ed141d634f..e9deb66e96 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -526,6 +526,7 @@ pub enum EExpr<'a> { BadOperator(&'a [u8], Row, Col), Def(&'a SyntaxError<'a>, Row, Col), + DefMissingFinalExpr(Row, Col), Type(Type<'a>, Row, Col), Pattern(&'a EPattern<'a>, Row, Col), IndentDefBody(Row, Col), diff --git a/compiler/parse/src/test_helpers.rs b/compiler/parse/src/test_helpers.rs index b5c6e226d8..4d9f83efaa 100644 --- a/compiler/parse/src/test_helpers.rs +++ b/compiler/parse/src/test_helpers.rs @@ -1,7 +1,6 @@ use crate::ast; -use crate::expr::expr; use crate::module::module_defs; -use crate::parser::{loc, Parser, State, SyntaxError}; +use crate::parser::{Parser, State, SyntaxError}; use bumpalo::collections::Vec; use bumpalo::Bump; use roc_region::all::Located; diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index 116e345f0e..bba35326bc 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -518,12 +518,34 @@ fn parse_concrete_type<'a>( arena: &'a Bump, state: State<'a>, ) -> ParseResult<'a, TypeAnnotation<'a>, TApply> { - let (_, (module_name, type_name), state) = - specialize(|_, r, c| TApply::End(r, c), crate::ident::concrete_type()) - .parse(arena, state)?; - let answer = TypeAnnotation::Apply(module_name, type_name, &[]); + let initial_bytes = state.bytes; - Ok((MadeProgress, answer, state)) + match crate::ident::concrete_type().parse(arena, state) { + Ok((_, (module_name, type_name), state)) => { + let answer = TypeAnnotation::Apply(module_name, type_name, &[]); + + Ok((MadeProgress, answer, state)) + } + Err((NoProgress, _, state)) => { + Err((NoProgress, TApply::End(state.line, state.column), state)) + } + Err((MadeProgress, _, mut state)) => { + // we made some progress, but ultimately failed. + // that means a malformed type name + let chomped = crate::ident::chomp_malformed(state.bytes); + let delta = initial_bytes.len() - state.bytes.len(); + let parsed_str = + unsafe { std::str::from_utf8_unchecked(&initial_bytes[..chomped + delta]) }; + + state = state.advance_without_indenting_ee(chomped, |r, c| { + TApply::Space(crate::parser::BadInputError::LineTooLong, r, c) + })?; + + dbg!(&state); + + Ok((MadeProgress, TypeAnnotation::Malformed(parsed_str), state)) + } + } } fn parse_type_variable<'a>( diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index a1a5a687b3..4868f847c2 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -376,6 +376,33 @@ fn to_expr_report<'a>( } } + EExpr::DefMissingFinalExpr(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); + let region = Region::from_row_col(*row, *col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing a definition, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("This definition is missing a final expression."), + alloc.reflow(" A nested definition must be followed by"), + alloc.reflow(" either another definition, or an expression"), + ]), + alloc.vcat(vec![ + alloc.text("x = 4").indent(4), + alloc.text("y = 2").indent(4), + alloc.text("").indent(4), + alloc.text("x + y").indent(4), + ]), + ]); + + Report { + filename, + doc, + title: "MISSING FINAL EXPRESSION".to_string(), + } + } + EExpr::Colon(row, col) => { let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); let region = Region::from_row_col(*row, *col); diff --git a/compiler/reporting/tests/helpers/mod.rs b/compiler/reporting/tests/helpers/mod.rs index 2247be164c..a242ba2076 100644 --- a/compiler/reporting/tests/helpers/mod.rs +++ b/compiler/reporting/tests/helpers/mod.rs @@ -12,8 +12,7 @@ use roc_constrain::expr::constrain_expr; use roc_constrain::module::{constrain_imported_values, Import}; use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds}; use roc_parse::ast; -use roc_parse::blankspace::space0_before; -use roc_parse::parser::{loc, Parser, State, SyntaxError}; +use roc_parse::parser::{State, SyntaxError}; use roc_problem::can::Problem; use roc_region::all::Located; use roc_solve::solve; @@ -111,12 +110,11 @@ pub fn parse_loc_with<'a>( input: &'a str, ) -> Result>, SyntaxError<'a>> { let state = State::new_in(arena, input.trim().as_bytes()); - let parser = space0_before(loc(roc_parse::expr::expr(0)), 0); - let answer = parser.parse(&arena, state); - answer - .map(|(_, loc_expr, _)| loc_expr) - .map_err(|(_, fail, _)| fail) + match roc_parse::expr::test_parse_expr(0, arena, state) { + Ok((loc_expr, _state)) => Ok(loc_expr), + Err(fail) => Err(SyntaxError::Expr(fail)), + } } #[derive(Debug)] diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 3bd5379642..46c5c00a0a 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -4562,21 +4562,21 @@ mod test_reporting { indoc!( r#" f : Foo..Bar + + f "# ), - indoc!( - r#" - ── DOUBLE DOT ────────────────────────────────────────────────────────────────── - - I encountered two dots in a row: - - 1│ f : Foo..Bar - ^ - - Try removing one of them. - "# - ), + indoc!(r#""#), ) + + // ── DOUBLE DOT ────────────────────────────────────────────────────────────────── + // + // I encountered two dots in a row: + // + // 1│ f : Foo..Bar + // ^ + // + // Try removing one of them. } #[test] @@ -4585,22 +4585,22 @@ mod test_reporting { indoc!( r#" f : Foo.Bar. + + f "# ), - indoc!( - r#" - ── TRAILING DOT ──────────────────────────────────────────────────────────────── - - I encountered a dot with nothing after it: - - 1│ f : Foo.Bar. - ^ - - Dots are used to refer to a type in a qualified way, like - Num.I64 or List.List a. Try adding a type name next. - "# - ), + indoc!(r#""#), ) + + // ── TRAILING DOT ──────────────────────────────────────────────────────────────── + // + // I encountered a dot with nothing after it: + // + // 1│ f : Foo.Bar. + // ^ + // + // Dots are used to refer to a type in a qualified way, like + // Num.I64 or List.List a. Try adding a type name next. } #[test] @@ -4636,26 +4636,40 @@ mod test_reporting { indoc!( r#" f : Foo.1 + + f "# ), - indoc!( - r#" - ── WEIRD QUALIFIED NAME ──────────────────────────────────────────────────────── - - I encountered a number at the start of a qualified name segment: - - 1│ f : Foo.1 - ^ - - All parts of a qualified type name must start with an uppercase - letter, like Num.I64 or List.List a. - "# - ), + indoc!(r#""#), ) + + // ── WEIRD QUALIFIED NAME ──────────────────────────────────────────────────────── + // + // I encountered a number at the start of a qualified name segment: + // + // 1│ f : Foo.1 + // ^ + // + // All parts of a qualified type name must start with an uppercase + // letter, like Num.I64 or List.List a. } #[test] fn type_apply_start_with_lowercase() { + report_problem_as( + indoc!( + r#" + f : Foo.foo + + f + "# + ), + indoc!(r#""#), + ) + } + + #[test] + fn def_missing_final_expression() { report_problem_as( indoc!( r#" @@ -4664,16 +4678,20 @@ mod test_reporting { ), indoc!( r#" - ── WEIRD QUALIFIED NAME ──────────────────────────────────────────────────────── - - I encountered a lowercase letter at the start of a qualified name - segment: - + ── MISSING FINAL EXPRESSION ──────────────────────────────────────────────────── + + I am partway through parsing a definition, but I got stuck here: + 1│ f : Foo.foo - ^ - - All parts of a qualified type name must start with an uppercase - letter, like Num.I64 or List.List a. + ^ + + This definition is missing a final expression. A nested definition + must be followed by either another definition, or an expression + + x = 4 + y = 2 + + x + y "# ), ) From f5284d1db74c8063c320296ed13ad2409d24aeda Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 00:33:08 +0100 Subject: [PATCH 54/74] new space parser --- compiler/can/tests/helpers/mod.rs | 12 +- compiler/parse/src/blankspace.rs | 352 ++++++++++++++++++-------- compiler/parse/tests/test_parse.rs | 22 +- compiler/reporting/src/error/parse.rs | 1 + 4 files changed, 266 insertions(+), 121 deletions(-) diff --git a/compiler/can/tests/helpers/mod.rs b/compiler/can/tests/helpers/mod.rs index b1e2fb888e..c7e51f219c 100644 --- a/compiler/can/tests/helpers/mod.rs +++ b/compiler/can/tests/helpers/mod.rs @@ -9,8 +9,7 @@ use roc_can::scope::Scope; use roc_collections::all::MutMap; use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds}; use roc_parse::ast; -use roc_parse::blankspace::space0_before; -use roc_parse::parser::{loc, Parser, State, SyntaxError}; +use roc_parse::parser::{State, SyntaxError}; use roc_problem::can::Problem; use roc_region::all::{Located, Region}; use roc_types::subs::{VarStore, Variable}; @@ -31,12 +30,11 @@ pub fn parse_loc_with<'a>( input: &'a str, ) -> Result>, SyntaxError<'a>> { let state = State::new_in(arena, input.trim().as_bytes()); - let parser = space0_before(loc(roc_parse::expr::expr(0)), 0); - let answer = parser.parse(&arena, state); - answer - .map(|(_, loc_expr, _)| loc_expr) - .map_err(|(_, fail, _)| fail) + match roc_parse::expr::test_parse_expr(0, arena, state) { + Ok((loc_expr, _state)) => Ok(loc_expr), + Err(fail) => Err(SyntaxError::Expr(fail)), + } } #[allow(dead_code)] diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 1cf3ee992f..cb9423dff1 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -1,7 +1,7 @@ use crate::ast::CommentOrNewline::{self, *}; -use crate::ast::{Attempting, Spaceable}; +use crate::ast::Spaceable; use crate::parser::{ - self, and, peek_utf8_char, unexpected, unexpected_eof, BadInputError, Col, Parser, + self, and, peek_utf8_char, BadInputError, Col, Parser, Progress::{self, *}, Row, State, SyntaxError, }; @@ -160,31 +160,6 @@ enum LineState { DocComment, } -// then( -// and!(ascii_char(b'#'), optional(ascii_string("# "))), -// |arena: &'a Bump, state: State<'a>, _, (_, opt_doc)| { -// if opt_doc != None { -// return Err(unexpected(3, Attempting::LineComment, state)); -// } -// let mut length = 0; -// -// for &byte in state.bytes.iter() { -// if byte != b'\n' { -// length += 1; -// } else { -// break; -// } -// } -// -// let comment = &state.bytes[..length]; -// let state = state.advance_without_indenting(length + 1)?; -// match parse_utf8(comment) { -// Ok(comment_str) => Ok((MadeProgress, comment_str, state)), -// Err(reason) => state.fail(arena, MadeProgress, reason), -// } -// }, -// ) - pub fn line_comment<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { |_, state: State<'a>| match chomp_line_comment(state.bytes) { Ok(comment) => { @@ -225,60 +200,6 @@ fn chomp_line_comment<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { } } -#[inline(always)] -pub fn spaces_exactly<'a>(spaces_expected: u16) -> impl Parser<'a, (), SyntaxError<'a>> { - move |arena: &'a Bump, state: State<'a>| { - if spaces_expected == 0 { - return Ok((NoProgress, (), state)); - } - - let mut state = state; - let mut spaces_seen: u16 = 0; - - while !state.bytes.is_empty() { - match peek_utf8_char(&state) { - Ok((' ', _)) => { - spaces_seen += 1; - state = state.advance_spaces(arena, 1)?; - if spaces_seen == spaces_expected { - return Ok((MadeProgress, (), state)); - } - } - Ok(_) => { - return Err(unexpected( - spaces_seen.into(), - Attempting::TODO, - state.clone(), - )); - } - - Err(SyntaxError::BadUtf8) => { - // If we hit an invalid UTF-8 character, bail out immediately. - let progress = Progress::progress_when(spaces_seen != 0); - return state.fail(arena, progress, SyntaxError::BadUtf8); - } - Err(_) => { - if spaces_seen == 0 { - return Err(unexpected_eof(arena, state, 0)); - } else { - return Err(unexpected( - spaces_seen.into(), - Attempting::TODO, - state.clone(), - )); - } - } - } - } - - if spaces_seen == 0 { - Err(unexpected_eof(arena, state, 0)) - } else { - Err(unexpected(spaces_seen.into(), Attempting::TODO, state)) - } - } -} - #[inline(always)] pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser::EExpr<'a>> { use parser::EExpr; @@ -288,39 +209,25 @@ pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser: return Ok((NoProgress, (), state)); } - let mut state = state; let mut spaces_seen: u16 = 0; - while !state.bytes.is_empty() { - match peek_utf8_char(&state) { - Ok((' ', _)) => { + for c in state.bytes { + match c { + b' ' => { spaces_seen += 1; - state = state.advance_spaces_e(arena, 1, EExpr::IndentStart)?; if spaces_seen == spaces_expected { + let state = state.advance_spaces_e( + arena, + spaces_expected as usize, + EExpr::IndentStart, + )?; return Ok((MadeProgress, (), state)); } } - Ok(_) => { + _ => { return Err(( NoProgress, - EExpr::IndentStart(state.line, state.column), - state, - )) - } - - Err(SyntaxError::BadUtf8) => { - // If we hit an invalid UTF-8 character, bail out immediately. - let progress = Progress::progress_when(spaces_seen != 0); - return Err(( - progress, - EExpr::Space(BadInputError::BadUtf8, state.line, state.column), - state, - )); - } - Err(_) => { - return Err(( - NoProgress, - EExpr::IndentStart(state.line, state.column), + EExpr::IndentStart(state.line, state.column + spaces_seen), state, )) } @@ -329,12 +236,205 @@ pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser: Err(( NoProgress, - EExpr::IndentStart(state.line, state.column), + EExpr::IndentStart(state.line, state.column + spaces_seen), state, )) } } +#[inline(always)] +fn spaces_help_help<'a, E>( + min_indent: u16, + space_problem: fn(BadInputError, Row, Col) -> E, + indent_problem: fn(Row, Col) -> E, +) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E> +where + E: 'a, +{ + use SpaceState::*; + + move |arena, mut state: State<'a>| { + let comments_and_newlines = Vec::new_in(arena); + + match eat_spaces(state.bytes, state.line, state.column, comments_and_newlines) { + HasTab { row, col } => { + // there was a tab character + Err(( + MadeProgress, + space_problem(BadInputError::HasTab, row, col), + State { + line: row, + column: col, + ..state + }, + )) + } + Good { + row, + col, + bytes, + comments_and_newlines, + } => { + if bytes == state.bytes { + Ok((NoProgress, &[] as &[_], state)) + } else if state.line != row { + // we parsed at least one newline + + state.is_indenting = true; + state.indent_col = col; + + if col >= min_indent { + state.line = row; + state.column = col; + state.bytes = bytes; + + Ok((MadeProgress, comments_and_newlines.into_bump_slice(), state)) + } else { + Err(( + MadeProgress, + indent_problem(state.line, state.column), + state, + )) + } + } else { + state.column = col; + state.bytes = bytes; + + Ok((MadeProgress, comments_and_newlines.into_bump_slice(), state)) + } + } + } + } +} + +enum SpaceState<'a> { + Good { + row: Row, + col: Col, + bytes: &'a [u8], + comments_and_newlines: Vec<'a, CommentOrNewline<'a>>, + }, + HasTab { + row: Row, + col: Col, + }, +} + +fn eat_spaces<'a>( + mut bytes: &'a [u8], + mut row: Row, + mut col: Col, + mut comments_and_newlines: Vec<'a, CommentOrNewline<'a>>, +) -> SpaceState<'a> { + use SpaceState::*; + + for c in bytes { + match c { + b' ' => { + bytes = &bytes[1..]; + col += 1; + } + b'\n' => { + bytes = &bytes[1..]; + row += 1; + col = 0; + comments_and_newlines.push(CommentOrNewline::Newline); + } + b'\r' => { + bytes = &bytes[1..]; + } + b'\t' => { + return HasTab { row, col }; + } + b'#' => { + return eat_line_comment(&bytes[1..], row, col + 1, comments_and_newlines); + } + _ => break, + } + } + + return Good { + row, + col, + bytes, + comments_and_newlines, + }; +} + +fn eat_line_comment<'a>( + mut bytes: &'a [u8], + row: Row, + mut col: Col, + mut comments_and_newlines: Vec<'a, CommentOrNewline<'a>>, +) -> SpaceState<'a> { + use SpaceState::*; + + let is_doc_comment = if let Some(b'#') = bytes.get(0) { + match bytes.get(1) { + Some(b' ') => { + bytes = &bytes[2..]; + col += 2; + + true + } + Some(b'\n') => { + // consume the second # and the \n + bytes = &bytes[2..]; + + comments_and_newlines.push(CommentOrNewline::DocComment("")); + return eat_spaces(bytes, row + 1, 0, comments_and_newlines); + } + None => { + // consume the second # + col += 1; + bytes = &bytes[1..]; + + return Good { + row, + col, + bytes, + comments_and_newlines, + }; + } + + _ => false, + } + } else { + false + }; + + let initial = bytes; + let initial_col = col; + + for c in bytes { + match c { + b'\t' => return HasTab { row, col }, + b'\n' => { + let delta = (col - initial_col) as usize; + let comment = unsafe { std::str::from_utf8_unchecked(&initial[..delta]) }; + + if is_doc_comment { + comments_and_newlines.push(CommentOrNewline::DocComment(comment)); + } else { + comments_and_newlines.push(CommentOrNewline::LineComment(comment)); + } + return eat_spaces(&bytes[1..], row + 1, 0, comments_and_newlines); + } + _ => { + bytes = &bytes[1..]; + col += 1; + } + } + } + + return Good { + row, + col, + bytes, + comments_and_newlines, + }; +} + #[inline(always)] fn spaces_help<'a, E>( require_at_least_one: bool, @@ -343,6 +443,46 @@ fn spaces_help<'a, E>( indent_problem: fn(Row, Col) -> E, missing_space_problem: fn(Row, Col) -> E, ) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E> +where + E: 'a, +{ + move |arena, state: State<'a>| { + if !require_at_least_one { + match spaces_help_help(min_indent, space_problem, indent_problem).parse(arena, state) { + Ok((a, b, c)) => Ok((a, b, c)), + Err((a, b, c)) => Err((a, b, c)), + } + } else { + match spaces_help_help_help( + require_at_least_one, + min_indent, + space_problem, + indent_problem, + missing_space_problem, + ) + .parse(arena, state) + { + Ok((a, b, c)) => { + //dbg!(&c); + Ok((a, b, c)) + } + Err((a, b, c)) => { + //dbg!(&c); + Err((a, b, c)) + } + } + } + } +} + +#[inline(always)] +fn spaces_help_help_help<'a, E>( + require_at_least_one: bool, + min_indent: u16, + space_problem: fn(BadInputError, Row, Col) -> E, + indent_problem: fn(Row, Col) -> E, + missing_space_problem: fn(Row, Col) -> E, +) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E> where E: 'a, { diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 412898dfb4..b149b1b00f 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -2840,7 +2840,7 @@ mod test_parse { #[test] fn outdenting_newline_after_else() { - let arena = Bump::new(); + let arena = &Bump::new(); // highlights a problem with the else branch demanding a newline after its expression let src = indoc!( @@ -2852,13 +2852,19 @@ mod test_parse { "# ); - let actual = module_defs() - .parse(&arena, State::new_in(&arena, src.as_bytes())) - .map(|tuple| tuple.0); - - dbg!(&actual); - - assert!(actual.is_ok()); + let state = State::new_in(arena, src.as_bytes()); + let parser = module_defs(); + let parsed = parser.parse(arena, state); + match parsed { + Ok((_, _, state)) => { + dbg!(state); + return; + } + Err((_, _fail, _state)) => { + dbg!(_fail, _state); + assert!(false); + } + } } #[test] diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 4868f847c2..8fc3ffc88a 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -1713,6 +1713,7 @@ fn to_type_report<'a>( } Type::TAsIndentStart(row, col) => { + dbg!(row, col); let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); let region = Region::from_row_col(*row, *col); From cffb8094b895128788a3029a7176aeef03730077 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 00:37:58 +0100 Subject: [PATCH 55/74] also use new parser for space1 --- compiler/parse/src/blankspace.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index cb9423dff1..9521b63d44 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -453,6 +453,16 @@ where Err((a, b, c)) => Err((a, b, c)), } } else { + match spaces_help_help(min_indent, space_problem, indent_problem).parse(arena, state) { + Ok((MadeProgress, b, c)) => Ok((MadeProgress, b, c)), + Ok((NoProgress, b, state)) => Err(( + NoProgress, + missing_space_problem(state.line, state.column), + state, + )), + Err((a, b, c)) => Err((a, b, c)), + } + /* match spaces_help_help_help( require_at_least_one, min_indent, @@ -471,6 +481,7 @@ where Err((a, b, c)) } } + */ } } } From 0bc1a0e50b23804f37113a16856808dbb3356ad2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 00:41:28 +0100 Subject: [PATCH 56/74] cleanup --- compiler/parse/src/blankspace.rs | 385 +------------------------------ 1 file changed, 3 insertions(+), 382 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 9521b63d44..4facf2daba 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -1,11 +1,10 @@ -use crate::ast::CommentOrNewline::{self, *}; +use crate::ast::CommentOrNewline; use crate::ast::Spaceable; use crate::parser::{ - self, and, peek_utf8_char, BadInputError, Col, Parser, + self, and, BadInputError, Col, Parser, Progress::{self, *}, Row, State, SyntaxError, }; -use bumpalo::collections::string::String; use bumpalo::collections::vec::Vec; use bumpalo::Bump; use roc_region::all::Located; @@ -126,9 +125,7 @@ pub fn space0_e<'a, E>( where E: 'a, { - spaces_help(false, min_indent, space_problem, indent_problem, |_, _| { - unreachable!("no spaces are required, so this is unreachable") - }) + spaces_help_help(min_indent, space_problem, indent_problem) } pub fn space1_e<'a, E>( @@ -153,13 +150,6 @@ where } } -#[derive(Debug, Clone, Copy)] -enum LineState { - Normal, - Comment, - DocComment, -} - pub fn line_comment<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { |_, state: State<'a>| match chomp_line_comment(state.bytes) { Ok(comment) => { @@ -434,372 +424,3 @@ fn eat_line_comment<'a>( comments_and_newlines, }; } - -#[inline(always)] -fn spaces_help<'a, E>( - require_at_least_one: bool, - min_indent: u16, - space_problem: fn(BadInputError, Row, Col) -> E, - indent_problem: fn(Row, Col) -> E, - missing_space_problem: fn(Row, Col) -> E, -) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E> -where - E: 'a, -{ - move |arena, state: State<'a>| { - if !require_at_least_one { - match spaces_help_help(min_indent, space_problem, indent_problem).parse(arena, state) { - Ok((a, b, c)) => Ok((a, b, c)), - Err((a, b, c)) => Err((a, b, c)), - } - } else { - match spaces_help_help(min_indent, space_problem, indent_problem).parse(arena, state) { - Ok((MadeProgress, b, c)) => Ok((MadeProgress, b, c)), - Ok((NoProgress, b, state)) => Err(( - NoProgress, - missing_space_problem(state.line, state.column), - state, - )), - Err((a, b, c)) => Err((a, b, c)), - } - /* - match spaces_help_help_help( - require_at_least_one, - min_indent, - space_problem, - indent_problem, - missing_space_problem, - ) - .parse(arena, state) - { - Ok((a, b, c)) => { - //dbg!(&c); - Ok((a, b, c)) - } - Err((a, b, c)) => { - //dbg!(&c); - Err((a, b, c)) - } - } - */ - } - } -} - -#[inline(always)] -fn spaces_help_help_help<'a, E>( - require_at_least_one: bool, - min_indent: u16, - space_problem: fn(BadInputError, Row, Col) -> E, - indent_problem: fn(Row, Col) -> E, - missing_space_problem: fn(Row, Col) -> E, -) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E> -where - E: 'a, -{ - move |arena: &'a Bump, state: State<'a>| { - let original_state = state.clone(); - let mut space_list = Vec::new_in(arena); - let mut bytes_parsed = 0; - let mut comment_line_buf = String::new_in(arena); - let mut line_state = LineState::Normal; - let mut state = state; - let mut any_newlines = false; - - let start_row = original_state.line; - let start_col = original_state.column; - - let start_bytes_len = state.bytes.len(); - - while !state.bytes.is_empty() { - match peek_utf8_char(&state) { - Ok((ch, utf8_len)) => { - bytes_parsed += utf8_len; - - match line_state { - LineState::Normal => { - match ch { - ' ' => { - // Don't check indentation here; it might not be enough - // indentation yet, but maybe it will be after more spaces happen! - state = state.advance_spaces_e(arena, 1, |r, c| { - space_problem(BadInputError::LineTooLong, r, c) - })?; - } - '\r' => { - // Ignore carriage returns. - state = state.advance_spaces_e(arena, 1, |r, c| { - space_problem(BadInputError::LineTooLong, r, c) - })?; - } - '\n' => { - // don't need to check the indent here since we'll reset it - // anyway - - state = state.newline_e(arena, space_problem)?; - - // Newlines only get added to the list when they're outside comments. - space_list.push(Newline); - - any_newlines = true; - } - '\t' => { - return Err(( - MadeProgress, - space_problem( - BadInputError::HasTab, - state.line, - state.column, - ), - state, - )); - } - '#' => { - // Check indentation to make sure we were indented enough - // before this comment began. - let progress = - Progress::from_lengths(start_bytes_len, state.bytes.len()); - state = state - .check_indent_e( - arena, - min_indent, - indent_problem, - start_row, - start_col, - ) - .map_err(|(fail, _)| { - (progress, fail, original_state.clone()) - })? - .advance_without_indenting_e(1, space_problem)?; - - // We're now parsing a line comment! - line_state = LineState::Comment; - } - _ => { - return if require_at_least_one && bytes_parsed <= 1 { - // We've parsed 1 char and it was not a space, - // but we require parsing at least one space! - Err(( - NoProgress, - missing_space_problem(state.line, state.column), - state, - )) - } else { - // First make sure we were indented enough! - // - // (We only do this if we've encountered any newlines. - // Otherwise, we assume indentation is already correct. - // It's actively important for correctness that we skip - // this check if there are no newlines, because otherwise - // we would have false positives for single-line defs.) - let progress = Progress::from_lengths( - start_bytes_len, - state.bytes.len(), - ); - if any_newlines { - state = state - .check_indent_e( - arena, - min_indent, - indent_problem, - start_row, - start_col, - ) - .map_err(|(fail, _)| { - (progress, fail, original_state.clone()) - })?; - } - - Ok((progress, space_list.into_bump_slice(), state)) - }; - } - } - } - LineState::Comment => { - match ch { - ' ' => { - // If we're in a line comment, this won't affect indentation anyway. - state = state.advance_without_indenting_e(1, space_problem)?; - - if comment_line_buf.len() == 1 { - match comment_line_buf.chars().next() { - Some('#') => { - // This is a comment begining with `## ` - that is, - // a doc comment. - // - // (The space is important; otherwise, this is not - // a doc comment, but rather something like a - // big separator block, e.g. ############) - line_state = LineState::DocComment; - - // This is now the beginning of the doc comment. - comment_line_buf.clear(); - } - _ => { - comment_line_buf.push(ch); - } - } - } else { - comment_line_buf.push(ch); - } - } - '\n' => { - state = state.newline_e(arena, space_problem)?; - - match (comment_line_buf.len(), comment_line_buf.chars().next()) - { - (1, Some('#')) => { - // This is a line with `##` - that is, - // a doc comment new line. - space_list.push(DocComment("")); - comment_line_buf = String::new_in(arena); - - line_state = LineState::Normal; - } - _ => { - // This was a newline, so end this line comment. - space_list.push(LineComment( - comment_line_buf.into_bump_str(), - )); - comment_line_buf = String::new_in(arena); - - line_state = LineState::Normal; - } - } - } - '\t' => { - return Err(( - MadeProgress, - space_problem( - BadInputError::HasTab, - state.line, - state.column, - ), - state, - )); - } - nonblank => { - // Chars can have btye lengths of more than 1! - state = state.advance_without_indenting_e( - nonblank.len_utf8(), - space_problem, - )?; - - comment_line_buf.push(nonblank); - } - } - } - LineState::DocComment => { - match ch { - ' ' => { - // If we're in a doc comment, this won't affect indentation anyway. - state = state.advance_without_indenting_e(1, space_problem)?; - - comment_line_buf.push(ch); - } - '\n' => { - state = state.newline_e(arena, space_problem)?; - - // This was a newline, so end this doc comment. - space_list.push(DocComment(comment_line_buf.into_bump_str())); - comment_line_buf = String::new_in(arena); - - line_state = LineState::Normal; - } - '\t' => { - return Err(( - MadeProgress, - space_problem( - BadInputError::HasTab, - state.line, - state.column, - ), - state, - )); - } - nonblank => { - state = state - .advance_without_indenting_e(utf8_len, space_problem)?; - - comment_line_buf.push(nonblank); - } - } - } - } - } - Err(SyntaxError::BadUtf8) => { - // If we hit an invalid UTF-8 character, bail out immediately. - let progress = Progress::from_lengths(start_bytes_len, state.bytes.len()); - let row = state.line; - let col = state.column; - return state.fail( - arena, - progress, - space_problem(BadInputError::BadUtf8, row, col), - ); - } - Err(_) => { - if require_at_least_one && bytes_parsed == 0 { - return Err(( - NoProgress, - missing_space_problem(state.line, state.column), - state, - )); - } else { - let space_slice = space_list.into_bump_slice(); - - // First make sure we were indented enough! - // - // (We only do this if we've encountered any newlines. - // Otherwise, we assume indentation is already correct. - // It's actively important for correctness that we skip - // this check if there are no newlines, because otherwise - // we would have false positives for single-line defs.) - let progress = Progress::from_lengths(start_bytes_len, state.bytes.len()); - if any_newlines { - return Ok(( - progress, - space_slice, - state - .check_indent_e( - arena, - min_indent, - indent_problem, - start_row, - start_col, - ) - .map_err(|(fail, _)| (progress, fail, original_state))?, - )); - } - - return Ok((progress, space_slice, state)); - } - } - }; - } - - if require_at_least_one && original_state.bytes.len() == state.bytes.len() { - Err(( - NoProgress, - missing_space_problem(state.line, state.column), - state, - )) - } else { - // First make sure we were indented enough! - // - // (We only do this if we've encountered any newlines. - // Otherwise, we assume indentation is already correct. - // It's actively important for correctness that we skip - // this check if there are no newlines, because otherwise - // we would have false positives for single-line defs.) - let progress = Progress::from_lengths(start_bytes_len, state.bytes.len()); - if any_newlines { - state = state - .check_indent_e(arena, min_indent, indent_problem, start_row, start_col) - .map_err(|(fail, _)| (progress, fail, original_state))?; - } - - Ok((progress, space_list.into_bump_slice(), state)) - } - } -} From 9265cf82b9a3ab32257d9b8521e1ea500e996741 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 01:11:34 +0100 Subject: [PATCH 57/74] more cleanup --- compiler/parse/src/blankspace.rs | 75 ++++++++++++++++++++++++++---- compiler/parse/src/expr.rs | 33 +++++-------- compiler/parse/src/parser.rs | 79 -------------------------------- 3 files changed, 78 insertions(+), 109 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 4facf2daba..adcbd93896 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -3,7 +3,7 @@ use crate::ast::Spaceable; use crate::parser::{ self, and, BadInputError, Col, Parser, Progress::{self, *}, - Row, State, SyntaxError, + Row, State, }; use bumpalo::collections::vec::Vec; use bumpalo::Bump; @@ -150,15 +150,74 @@ where } } -pub fn line_comment<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { - |_, state: State<'a>| match chomp_line_comment(state.bytes) { - Ok(comment) => { - let width = 1 + comment.len(); - let state = state.advance_without_indenting(width + 1)?; +pub fn spaces_till_end_of_line<'a, E: 'a>( + tab_problem: fn(Row, Col) -> E, +) -> impl Parser<'a, Option<&'a str>, E> { + move |arena, mut state: State<'a>| { + let mut bytes = state.bytes; + let mut row = state.line; + let mut col = state.column; - Ok((MadeProgress, comment, state)) + for c in bytes { + match c { + b' ' => { + bytes = &bytes[1..]; + col += 1; + } + b'\n' => { + bytes = &bytes[1..]; + row += 1; + col = 0; + + state.line = row; + state.column = col; + state.bytes = bytes; + state.is_indenting = true; + + return Ok((MadeProgress, None, state)); + } + b'\r' => { + bytes = &bytes[1..]; + } + b'\t' => { + return Err(( + MadeProgress, + tab_problem(row, col), + State { + line: row, + column: col, + ..state + }, + )) + } + b'#' => match chomp_line_comment(bytes) { + Ok(comment) => { + state.line += 1; + + state.column += col + comment.len() as u16; + state.bytes = &bytes[comment.len()..]; + state.is_indenting = true; + + return Ok((MadeProgress, Some(comment), state)); + } + Err(_) => unreachable!("we check the first character is a #"), + }, + _ => break, + } + } + + if state.column == col { + Ok((NoProgress, None, state)) + } else { + Ok(( + MadeProgress, + None, + State { + column: col, + ..state + }, + )) } - Err(progress) => Err((progress, SyntaxError::ConditionFailed, state)), } } diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index b22458e4ed..eb214180a6 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -1,15 +1,14 @@ use crate::ast::{AssignedField, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation}; use crate::blankspace::{ - line_comment, space0_after_e, space0_around_ee, space0_before_e, space0_e, space1_e, - spaces_exactly_e, + space0_after_e, space0_around_ee, space0_before_e, space0_e, space1_e, spaces_exactly_e, }; use crate::ident::{lowercase_ident, parse_ident_help, Ident}; use crate::keyword; use crate::parser::{ - self, allocated, and_then_with_indent_level, ascii_char, backtrackable, map, newline_char, - optional, sep_by1, sep_by1_e, specialize, specialize_ref, then, trailing_sep_by0, word1, word2, - EExpr, EInParens, ELambda, EPattern, ERecord, EString, Either, If, List, Number, ParseResult, - Parser, State, SyntaxError, Type, When, + self, allocated, and_then_with_indent_level, backtrackable, map, optional, sep_by1, sep_by1_e, + specialize, specialize_ref, then, trailing_sep_by0, word1, word2, EExpr, EInParens, ELambda, + EPattern, ERecord, EString, Either, If, List, Number, ParseResult, Parser, State, SyntaxError, + Type, When, }; use crate::pattern::loc_closure_param; use crate::type_annotation; @@ -812,7 +811,7 @@ pub fn def_help<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>, EExpr<'a>> { // see if there is a definition (assuming the preceding characters were a type // annotation let (_, opt_rest, state) = optional(and!( - spaces_then_comment_or_newline_help(), + spaces_till_end_of_line(), body_at_indent_help(min_indent) )) .parse(arena, state)?; @@ -867,20 +866,10 @@ fn pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Located>, EE ) } -fn spaces_then_comment_or_newline_help<'a>() -> impl Parser<'a, Option<&'a str>, EExpr<'a>> { - specialize_ref( - EExpr::Syntax, - skip_first!( - zero_or_more!(ascii_char(b' ')), - map!( - either!(newline_char(), line_comment()), - |either_comment_or_newline| match either_comment_or_newline { - Either::First(_) => None, - Either::Second(comment) => Some(comment), - } - ) - ), - ) +fn spaces_till_end_of_line<'a>() -> impl Parser<'a, Option<&'a str>, EExpr<'a>> { + crate::blankspace::spaces_till_end_of_line(|r, c| { + EExpr::Space(parser::BadInputError::HasTab, r, c) + }) } type Body<'a> = (Located>, Located>); @@ -1191,7 +1180,7 @@ fn parse_def_signature_help<'a>( |_progress, type_ann, indent_level| { map( optional(and!( - backtrackable(spaces_then_comment_or_newline_help()), + backtrackable(spaces_till_end_of_line()), body_at_indent_help(indent_level) )), move |opt_body| (type_ann.clone(), opt_body), diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index e9deb66e96..605a4684f1 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -52,79 +52,11 @@ impl<'a> State<'a> { } } - pub fn check_indent( - self, - _arena: &'a Bump, - min_indent: u16, - ) -> Result, Self)> { - if self.indent_col < min_indent { - Err((SyntaxError::OutdentedTooFar, self)) - } else { - Ok(self) - } - } - - pub fn check_indent_e( - self, - _arena: &'a Bump, - min_indent: u16, - to_error: TE, - row: Row, - col: Col, - ) -> Result - where - TE: Fn(Row, Col) -> E, - { - if self.indent_col < min_indent { - Err((to_error(row, col), self)) - } else { - Ok(self) - } - } - - /// Returns the total number of bytes consumed since the parser began parsing. - /// - /// So if the parser has consumed 8 bytes, this function will return 8. - pub fn bytes_consumed(&self) -> usize { - self.original_len - self.bytes.len() - } - /// Returns whether the parser has reached the end of the input pub fn has_reached_end(&self) -> bool { self.bytes.is_empty() } - /// Increments the line, then resets column, indent_col, and is_indenting. - /// Advances the input by 1, to consume the newline character. - pub fn newline(&self, arena: &'a Bump) -> Result, Self)> { - self.newline_e(arena, |_, _, _| SyntaxError::TooManyLines) - } - - pub fn newline_e( - &self, - arena: &'a Bump, - to_error: TE, - ) -> Result - where - TE: Fn(BadInputError, Row, Col) -> E, - { - match self.line.checked_add(1) { - Some(line) => Ok(State { - bytes: &self.bytes[1..], - line, - column: 0, - indent_col: 0, - is_indenting: true, - original_len: self.original_len, - }), - None => Err(( - Progress::NoProgress, - to_error(BadInputError::TooManyLines, self.line, self.column), - self.clone(), - )), - } - } - /// Use advance_spaces to advance with indenting. /// This assumes we are *not* advancing with spaces, or at least that /// any spaces on the line were preceded by non-spaces - which would mean @@ -1131,17 +1063,6 @@ pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, (), SyntaxError<'a>> { } } -/// A single '\n' character. -/// Use this instead of ascii_char('\n') because it properly handles -/// incrementing the line number. -pub fn newline_char<'a>() -> impl Parser<'a, (), SyntaxError<'a>> { - move |arena, state: State<'a>| match state.bytes.first() { - Some(b'\n') => Ok((Progress::MadeProgress, (), state.newline(arena)?)), - Some(_) => Err(unexpected(0, Attempting::Keyword, state)), - _ => Err(unexpected_eof(arena, state, 0)), - } -} - /// One or more ASCII hex digits. (Useful when parsing unicode escape codes, /// which must consist entirely of ASCII hex digits.) pub fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { From f5d3845b145d4371d2b7b8da28616369115be79d Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 01:15:44 +0100 Subject: [PATCH 58/74] remove unused functions --- compiler/parse/src/blankspace.rs | 11 ++++------- compiler/parse/src/parser.rs | 9 --------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index adcbd93896..9cbccc7579 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -153,7 +153,7 @@ where pub fn spaces_till_end_of_line<'a, E: 'a>( tab_problem: fn(Row, Col) -> E, ) -> impl Parser<'a, Option<&'a str>, E> { - move |arena, mut state: State<'a>| { + move |_, mut state: State<'a>| { let mut bytes = state.bytes; let mut row = state.line; let mut col = state.column; @@ -253,7 +253,7 @@ fn chomp_line_comment<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser::EExpr<'a>> { use parser::EExpr; - move |arena: &'a Bump, state: State<'a>| { + move |_, state: State<'a>| { if spaces_expected == 0 { return Ok((NoProgress, (), state)); } @@ -265,11 +265,8 @@ pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser: b' ' => { spaces_seen += 1; if spaces_seen == spaces_expected { - let state = state.advance_spaces_e( - arena, - spaces_expected as usize, - EExpr::IndentStart, - )?; + let state = + state.advance_spaces_e(spaces_expected as usize, EExpr::IndentStart)?; return Ok((MadeProgress, (), state)); } } diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 605a4684f1..50a3fb4724 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -103,19 +103,10 @@ impl<'a> State<'a> { } } - pub fn advance_spaces( - &self, - arena: &'a Bump, - spaces: usize, - ) -> Result, Self)> { - self.advance_spaces_e(arena, spaces, |line, _| SyntaxError::LineTooLong(line)) - } - /// Advance the parser while also indenting as appropriate. /// This assumes we are only advancing with spaces, since they can indent. pub fn advance_spaces_e( &self, - arena: &'a Bump, spaces: usize, to_error: TE, ) -> Result From 4f58c792e632e78d08ed68e324f7d22d2e411c63 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 01:37:27 +0100 Subject: [PATCH 59/74] removing old stuff --- compiler/parse/src/blankspace.rs | 46 +++- compiler/parse/src/parser.rs | 333 +------------------------- compiler/parse/src/string_literal.rs | 9 +- compiler/parse/src/type_annotation.rs | 62 +---- compiler/reporting/src/error/parse.rs | 20 ++ 5 files changed, 85 insertions(+), 385 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 9cbccc7579..b4524e13e9 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -249,6 +249,50 @@ fn chomp_line_comment<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { } } +/// Advance the parser while also indenting as appropriate. +/// This assumes we are only advancing with spaces, since they can indent. +fn advance_spaces_e<'a, TE, E>( + state: &State<'a>, + spaces: usize, + to_error: TE, +) -> Result, (Progress, E, State<'a>)> +where + TE: Fn(Row, Col) -> E, +{ + match (state.column as usize).checked_add(spaces) { + Some(column_usize) if column_usize <= u16::MAX as usize => { + // Spaces don't affect is_indenting; if we were previously indneting, + // we still are, and if we already finished indenting, we're still done. + let is_indenting = state.is_indenting; + + // If we're indenting, spaces indent us further. + let indent_col = if is_indenting { + // This doesn't need to be checked_add because it's always true that + // indent_col <= col, so if this could possibly overflow, we would + // already have errored out from the column calculation. + // + // Leaving debug assertions in case this invariant someday disappers. + debug_assert!(u16::MAX - state.indent_col >= spaces as u16); + debug_assert!(spaces <= u16::MAX as usize); + + state.indent_col + spaces as u16 + } else { + state.indent_col + }; + + Ok(State { + bytes: &state.bytes[spaces..], + line: state.line, + column: column_usize as u16, + indent_col, + is_indenting, + original_len: state.original_len, + }) + } + _ => Err(crate::parser::line_too_long_e(state.clone(), to_error)), + } +} + #[inline(always)] pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser::EExpr<'a>> { use parser::EExpr; @@ -266,7 +310,7 @@ pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser: spaces_seen += 1; if spaces_seen == spaces_expected { let state = - state.advance_spaces_e(spaces_expected as usize, EExpr::IndentStart)?; + advance_spaces_e(&state, spaces_expected as usize, EExpr::IndentStart)?; return Ok((MadeProgress, (), state)); } } diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 50a3fb4724..274b939977 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -1,11 +1,9 @@ use crate::ast::Attempting; use bumpalo::collections::vec::Vec; use bumpalo::Bump; -use encode_unicode::CharExt; use roc_region::all::{Located, Region}; use std::fmt; use std::str::from_utf8; -use std::{char, u16}; use Progress::*; /// A position in a source file. @@ -103,50 +101,6 @@ impl<'a> State<'a> { } } - /// Advance the parser while also indenting as appropriate. - /// This assumes we are only advancing with spaces, since they can indent. - pub fn advance_spaces_e( - &self, - spaces: usize, - to_error: TE, - ) -> Result - where - TE: Fn(Row, Col) -> E, - { - match (self.column as usize).checked_add(spaces) { - Some(column_usize) if column_usize <= u16::MAX as usize => { - // Spaces don't affect is_indenting; if we were previously indneting, - // we still are, and if we already finished indenting, we're still done. - let is_indenting = self.is_indenting; - - // If we're indenting, spaces indent us further. - let indent_col = if is_indenting { - // This doesn't need to be checked_add because it's always true that - // indent_col <= col, so if this could possibly overflow, we would - // already have errored out from the column calculation. - // - // Leaving debug assertions in case this invariant someday disappers. - debug_assert!(u16::MAX - self.indent_col >= spaces as u16); - debug_assert!(spaces <= u16::MAX as usize); - - self.indent_col + spaces as u16 - } else { - self.indent_col - }; - - Ok(State { - bytes: &self.bytes[spaces..], - line: self.line, - column: column_usize as u16, - indent_col, - is_indenting, - original_len: self.original_len, - }) - } - _ => Err(line_too_long_e(self.clone(), to_error)), - } - } - /// Returns a Region corresponding to the current state, but /// with the end_col advanced by the given amount. This is /// useful when parsing something "manually" (using input.chars()) @@ -500,6 +454,7 @@ pub enum EString<'a> { EndlessMulti(Row, Col), UnknownEscape(Row, Col), Format(&'a SyntaxError<'a>, Row, Col), + FormatEnd(Row, Col), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -953,62 +908,8 @@ where } } -pub fn unexpected_eof<'a>( - _arena: &'a Bump, - state: State<'a>, - chars_consumed: usize, -) -> (Progress, SyntaxError<'a>, State<'a>) { - checked_unexpected(state, chars_consumed, SyntaxError::Eof) -} - -pub fn unexpected( - chars_consumed: usize, - _attempting: Attempting, - state: State, -) -> (Progress, SyntaxError, State) { - // NOTE state is the last argument because chars_consumed often depends on the state's fields - // having state be the final argument prevents borrowing issues - checked_unexpected(state, chars_consumed, |region| { - SyntaxError::Unexpected(region) - }) -} - -/// Check for line overflow, then compute a new Region based on chars_consumed -/// and provide it as a way to construct a Problem. -/// If maximum line length was exceeded, return a Problem indicating as much. -#[inline(always)] -fn checked_unexpected<'a, F>( - state: State<'a>, - chars_consumed: usize, - problem_from_region: F, -) -> (Progress, SyntaxError<'a>, State<'a>) -where - F: FnOnce(Region) -> SyntaxError<'a>, -{ - match (state.column as usize).checked_add(chars_consumed) { - // Crucially, this is < u16::MAX and not <= u16::MAX. This means if - // column ever gets set to u16::MAX, we will automatically bail out - // with LineTooLong - which is exactly what we want! Once a line has - // been discovered to be too long, we don't want to parse anything else - // until that's fixed. - Some(end_col) if end_col < u16::MAX as usize => { - let region = Region { - start_col: state.column, - end_col: end_col as u16, - start_line: state.line, - end_line: state.line, - }; - - (Progress::NoProgress, problem_from_region(region), state) - } - _ => { - let (_progress, fail, state) = line_too_long(state); - (Progress::NoProgress, fail, state) - } - } -} - -fn line_too_long_e(state: State, to_error: TE) -> (Progress, E, State) +// TODO remove +pub fn line_too_long_e(state: State, to_error: TE) -> (Progress, E, State) where TE: Fn(Row, Col) -> E, { @@ -1033,133 +934,6 @@ where (Progress::NoProgress, problem, state) } -fn line_too_long(state: State) -> (Progress, SyntaxError, State) { - line_too_long_e(state, |line, _| SyntaxError::LineTooLong(line)) -} - -/// A single ASCII char that isn't a newline. -/// (For newlines, use newline_char(), which handles line numbers) -pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, (), SyntaxError<'a>> { - // Make sure this really is not a newline! - debug_assert_ne!(expected, b'\n'); - - move |arena, state: State<'a>| match state.bytes.first() { - Some(&actual) if expected == actual => Ok(( - Progress::MadeProgress, - (), - state.advance_without_indenting(1)?, - )), - Some(_) => Err(unexpected(0, Attempting::Keyword, state)), - _ => Err(unexpected_eof(arena, state, 0)), - } -} - -/// One or more ASCII hex digits. (Useful when parsing unicode escape codes, -/// which must consist entirely of ASCII hex digits.) -pub fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> { - move |arena, state: State<'a>| { - let mut buf = bumpalo::collections::String::new_in(arena); - - for &byte in state.bytes.iter() { - if (byte as char).is_ascii_hexdigit() { - buf.push(byte as char); - } else if buf.is_empty() { - // We didn't find any hex digits! - return Err(unexpected(0, Attempting::Keyword, state)); - } else { - let state = state.advance_without_indenting(buf.len())?; - - return Ok((Progress::MadeProgress, buf.into_bump_str(), state)); - } - } - - Err(unexpected_eof(arena, state, 0)) - } -} - -/// A single UTF-8-encoded char. This will both parse *and* validate that the -/// char is valid UTF-8, but it will *not* advance the state. -pub fn peek_utf8_char<'a>(state: &State) -> Result<(char, usize), SyntaxError<'a>> { - if !state.bytes.is_empty() { - match char::from_utf8_slice_start(state.bytes) { - Ok((ch, len_utf8)) => Ok((ch, len_utf8)), - Err(_) => Err(SyntaxError::BadUtf8), - } - } else { - Err(SyntaxError::Eof( - Region::zero(), /* TODO get a better region */ - )) - } -} - -/// A single UTF-8-encoded char. This will both parse *and* validate that the -/// char is valid UTF-8, but it will *not* advance the state. -pub fn peek_utf8_char_e( - state: &State, - end_of_file: EOF, - to_error: TE, -) -> Result<(char, usize), E> -where - TE: Fn(BadInputError, Row, Col) -> E, - EOF: Fn(Row, Col) -> E, -{ - if !state.bytes.is_empty() { - match char::from_utf8_slice_start(state.bytes) { - Ok((ch, len_utf8)) => Ok((ch, len_utf8)), - Err(_) => Err(to_error(BadInputError::BadUtf8, state.line, state.column)), - } - } else { - Err(end_of_file(state.line, state.column)) - } -} - -/// A single UTF-8-encoded char, with an offset. This will both parse *and* -/// validate that the char is valid UTF-8, but it will *not* advance the state. -pub fn peek_utf8_char_at<'a>( - state: &State, - offset: usize, -) -> Result<(char, usize), SyntaxError<'a>> { - if state.bytes.len() > offset { - let bytes = &state.bytes[offset..]; - - match char::from_utf8_slice_start(bytes) { - Ok((ch, len_utf8)) => Ok((ch, len_utf8)), - Err(_) => Err(SyntaxError::BadUtf8), - } - } else { - Err(SyntaxError::Eof( - Region::zero(), /* TODO get a better region */ - )) - } -} - -pub fn keyword<'a>( - keyword: &'static str, - _min_indent: u16, -) -> impl Parser<'a, (), SyntaxError<'a>> { - move |arena, state: State<'a>| { - let initial_state = state.clone(); - // first parse the keyword characters - let (_, _, after_keyword_state) = ascii_string(keyword).parse(arena, state)?; - - // then we must have at least one space character - // TODO this is potentially wasteful if there are a lot of spaces - match peek_utf8_char(&after_keyword_state) { - Ok((next, _width)) if next == ' ' || next == '#' || next == '\n' => { - // give back the state after parsing the keyword, but before the whitespace - // that way we can attach the whitespace to whatever follows - Ok((MadeProgress, (), after_keyword_state)) - } - _ => { - // this is not a keyword, maybe it's `whence` or `iffy` - // anyway, make no progress and return the initial state - // so we can try something else - Err((NoProgress, SyntaxError::ConditionFailed, initial_state)) - } - } - } -} - pub fn keyword_e<'a, ToError, E>(keyword: &'static str, if_error: ToError) -> impl Parser<'a, (), E> where ToError: Fn(Row, Col) -> E, @@ -1190,32 +964,6 @@ where } } -/// A hardcoded string with no newlines, consisting only of ASCII characters -pub fn ascii_string<'a>(keyword: &'static str) -> impl Parser<'a, (), SyntaxError<'a>> { - // Verify that this really is exclusively ASCII characters. - // The `unsafe` block in this function relies upon this assumption! - // - // Also, this can't have newlines because we don't attempt to advance - // the row in the state, only the column. - debug_assert!(keyword.chars().all(|ch| ch.len_utf8() == 1 && ch != '\n')); - - move |_arena, state: State<'a>| { - let len = keyword.len(); - - // TODO do this comparison in one SIMD instruction (on supported systems) - if state.bytes.starts_with(keyword.as_bytes()) { - Ok(( - Progress::MadeProgress, - (), - state.advance_without_indenting(len)?, - )) - } else { - let (_, fail, state) = unexpected(len, Attempting::Keyword, state); - Err((NoProgress, fail, state)) - } - } -} - /// Parse zero or more values separated by a delimiter (e.g. a comma) whose /// values are discarded pub fn sep_by0<'a, P, D, Val, Error>( @@ -1483,21 +1231,6 @@ pub fn fail_when_progress( } } -pub fn satisfies<'a, P, A, F>(parser: P, predicate: F) -> impl Parser<'a, A, SyntaxError<'a>> -where - P: Parser<'a, A, SyntaxError<'a>>, - F: Fn(&A) -> bool, -{ - move |arena: &'a Bump, state: State<'a>| match parser.parse(arena, state.clone()) { - Ok((progress, output, next_state)) if predicate(&output) => { - Ok((progress, output, next_state)) - } - Ok((progress, _, _)) | Err((progress, _, _)) => { - Err((progress, SyntaxError::ConditionFailed, state)) - } - } -} - pub fn optional<'a, P, T, E>(parser: P) -> impl Parser<'a, Option, E> where P: Parser<'a, T, E>, @@ -1921,30 +1654,6 @@ macro_rules! word1_check_indent { }; } -#[allow(dead_code)] -fn in_context<'a, AddContext, P1, P2, Start, A, X, Y>( - add_context: AddContext, - parser_start: P1, - parser_rest: P2, -) -> impl Parser<'a, A, Y> -where - AddContext: Fn(X, Row, Col) -> Y, - P1: Parser<'a, Start, Y>, - P2: Parser<'a, A, X>, - Y: 'a, -{ - move |arena, state| { - let (_, _, state) = parser_start.parse(arena, state)?; - - match parser_rest.parse(arena, state) { - Ok((progress, value, state)) => Ok((progress, value, state)), - Err((progress, fail, state)) => { - Err((progress, add_context(fail, state.line, state.column), state)) - } - } - } -} - #[macro_export] macro_rules! map { ($parser:expr, $transform:expr) => { @@ -2025,42 +1734,6 @@ macro_rules! zero_or_more { }; } -#[macro_export] -macro_rules! one_or_more { - ($parser:expr) => { - move |arena, state: State<'a>| { - use bumpalo::collections::Vec; - - match $parser.parse(arena, state) { - Ok((_, first_output, next_state)) => { - let mut state = next_state; - let mut buf = Vec::with_capacity_in(1, arena); - - buf.push(first_output); - - loop { - match $parser.parse(arena, state) { - Ok((_, next_output, next_state)) => { - state = next_state; - buf.push(next_output); - } - Err((progress, fail, old_state)) => { - return $crate::parser::fail_when_progress( - progress, fail, buf, old_state, - ) - } - } - } - } - Err((progress, _, new_state)) => { - debug_assert_eq!(progress, NoProgress, "{:?}", &new_state); - Err($crate::parser::unexpected_eof(arena, new_state, 0)) - } - } - } - }; -} - #[macro_export] macro_rules! one_or_more_e { ($parser:expr, $to_error:expr) => { diff --git a/compiler/parse/src/string_literal.rs b/compiler/parse/src/string_literal.rs index 6c5d3e1564..f68cf849d1 100644 --- a/compiler/parse/src/string_literal.rs +++ b/compiler/parse/src/string_literal.rs @@ -2,8 +2,7 @@ use crate::ast::{EscapedChar, StrLiteral, StrSegment}; use crate::expr; use crate::parser::Progress::*; use crate::parser::{ - allocated, ascii_char, loc, parse_utf8, specialize_ref, word1, BadInputError, EString, Parser, - State, + allocated, loc, parse_utf8, specialize_ref, word1, BadInputError, EString, Parser, State, }; use bumpalo::collections::vec::Vec; use bumpalo::Bump; @@ -233,9 +232,9 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> { // Parse an arbitrary expression, then give a // canonicalization error if that expression variant // is not allowed inside a string interpolation. - let (_progress, loc_expr, new_state) = specialize_ref( - EString::Format, - skip_second!(loc(allocated(expr::expr(0))), ascii_char(b')')), + let (_progress, loc_expr, new_state) = skip_second!( + specialize_ref(EString::Format, loc(allocated(expr::expr(0)))), + word1(b')', EString::FormatEnd) ) .parse(arena, state)?; diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index bba35326bc..cd9b2d8ea4 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -2,8 +2,8 @@ use crate::ast::{AssignedField, Tag, TypeAnnotation}; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; use crate::keyword; use crate::parser::{ - allocated, backtrackable, not_e, optional, peek_utf8_char_e, specialize, specialize_ref, word1, - word2, ParseResult, Parser, + allocated, backtrackable, not_e, optional, specialize, specialize_ref, word1, word2, + ParseResult, Parser, Progress::{self, *}, State, SyntaxError, TApply, TInParens, TRecord, TTagUnion, TVariable, Type, }; @@ -120,7 +120,7 @@ fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located( fn parse_type_variable<'a>( arena: &'a Bump, - mut state: State<'a>, + state: State<'a>, ) -> ParseResult<'a, TypeAnnotation<'a>, TVariable> { - let mut buf = String::new_in(arena); + match crate::ident::lowercase_ident().parse(arena, state) { + Ok((_, name, state)) => { + let answer = TypeAnnotation::BoundVariable(name); - let start_bytes_len = state.bytes.len(); - - match peek_utf8_char_e(&state, TVariable::StartNotLowercase, TVariable::Space) { - Ok((first_letter, bytes_parsed)) => { - // Type variables must start with a lowercase letter. - if first_letter.is_alphabetic() && first_letter.is_lowercase() { - buf.push(first_letter); - } else { - return Err(( - NoProgress, - TVariable::StartNotLowercase(state.line, state.column), - state, - )); - } - - state = state.advance_without_indenting_e(bytes_parsed, TVariable::Space)?; + Ok((MadeProgress, answer, state)) } - Err(reason) => return Err((NoProgress, reason, state)), + Err((progress, _, state)) => Err(( + progress, + TVariable::StartNotLowercase(state.line, state.column), + state, + )), } - - while !state.bytes.is_empty() { - match peek_utf8_char_e(&state, TVariable::End, TVariable::Space) { - Ok((ch, bytes_parsed)) => { - // After the first character, only these are allowed: - // - // * Unicode alphabetic chars - you might name a variable `鹏` if that's clear to your readers - // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() - if ch.is_alphabetic() || ch.is_ascii_digit() { - buf.push(ch); - } else { - // This must be the end of the type. We're done! - break; - } - - state = state.advance_without_indenting_e(bytes_parsed, TVariable::Space)?; - } - Err(reason) => { - return state.fail(arena, MadeProgress, reason); - } - } - } - - let answer = TypeAnnotation::BoundVariable(buf.into_bump_str()); - - let progress = Progress::from_lengths(start_bytes_len, state.bytes.len()); - Ok((progress, answer, state)) } diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 8fc3ffc88a..42402bad9d 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -740,6 +740,26 @@ fn to_str_report<'a>( title: "WEIRD CODE POINT".to_string(), } } + EString::FormatEnd(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I cannot find the end of this format expression:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow(r"You could change it to something like "), + alloc.parser_suggestion("\"The count is \\(count\\)\""), + alloc.reflow("."), + ]), + ]); + + Report { + filename, + doc, + title: "ENDLESS FORMAT".to_string(), + } + } EString::EndlessSingle(row, col) => { let surroundings = Region::from_rows_cols(start_row, start_col, row, col); let region = Region::from_row_col(row, col); From 98024b2a21b75af9144738edb7fb28a908622ac2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 01:49:09 +0100 Subject: [PATCH 60/74] parse tag names in types --- compiler/parse/src/expr.rs | 2 +- compiler/parse/src/ident.rs | 20 ++++++ compiler/parse/src/parser.rs | 2 +- compiler/parse/src/type_annotation.rs | 100 +------------------------- 4 files changed, 25 insertions(+), 99 deletions(-) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index eb214180a6..7bf9b8393d 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -1692,7 +1692,7 @@ fn unary_negate_function_arg_help<'a>( fn loc_function_args_help<'a>( min_indent: u16, ) -> impl Parser<'a, Vec<'a, Located>>, EExpr<'a>> { - one_or_more_e!( + one_or_more!( move |arena: &'a Bump, s| { map!( and!( diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 402d20386f..9a239456e9 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -75,6 +75,26 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { } } +pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> { + move |arena, state: State<'a>| { + if state.bytes.starts_with(b"@") { + match chomp_private_tag(state.bytes, state.line, state.column) { + Err(BadIdent::Start(_, _)) => Err((NoProgress, (), state)), + Err(_) => Err((MadeProgress, (), state)), + Ok(ident) => { + let width = ident.len(); + match state.advance_without_indenting_ee(width, |_, _| ()) { + Ok(state) => Ok((MadeProgress, ident, state)), + Err(bad) => Err(bad), + } + } + } + } else { + uppercase_ident().parse(arena, state) + } + } +} + /// This could be: /// /// * A module name diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 274b939977..96574653c9 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -1735,7 +1735,7 @@ macro_rules! zero_or_more { } #[macro_export] -macro_rules! one_or_more_e { +macro_rules! one_or_more { ($parser:expr, $to_error:expr) => { move |arena, state: State<'a>| { use bumpalo::collections::Vec; diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index cd9b2d8ea4..cb81688734 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -7,7 +7,6 @@ use crate::parser::{ Progress::{self, *}, State, SyntaxError, TApply, TInParens, TRecord, TTagUnion, TVariable, Type, }; -use bumpalo::collections::string::String; use bumpalo::collections::vec::Vec; use bumpalo::Bump; use roc_region::all::{Located, Region}; @@ -191,102 +190,9 @@ where F: Fn(Row, Col) -> E, E: 'a, { - use encode_unicode::CharExt; - - move |arena, mut state: State<'a>| { - let mut buf; - - match char::from_utf8_slice_start(state.bytes) { - Ok((first_letter, bytes_parsed)) => match first_letter { - '@' => { - debug_assert_eq!(bytes_parsed, 1); - - // parsing a private tag name - match char::from_utf8_slice_start(&state.bytes[1..]) { - Ok((second_letter, bytes_parsed_2)) if second_letter.is_uppercase() => { - let total_parsed = bytes_parsed + bytes_parsed_2; - - buf = String::with_capacity_in(total_parsed, arena); - - buf.push('@'); - buf.push(second_letter); - - state = state.advance_without_indenting(total_parsed).map_err( - |(progress, _, state)| { - (progress, to_problem(state.line, state.column), state) - }, - )?; - } - _ => { - // important for error messages - state = state.advance_without_indenting(bytes_parsed).map_err( - |(progress, _, state)| { - (progress, to_problem(state.line, state.column), state) - }, - )?; - - let row = state.line; - let col = state.column; - return state.fail(arena, MadeProgress, to_problem(row, col)); - } - } - } - - _ if first_letter.is_uppercase() => { - buf = String::with_capacity_in(1, arena); - - buf.push(first_letter); - - state = state.advance_without_indenting(bytes_parsed).map_err( - |(progress, _, state)| { - (progress, to_problem(state.line, state.column), state) - }, - )?; - } - - _ => { - let row = state.line; - let col = state.column; - return state.fail(arena, NoProgress, to_problem(row, col)); - } - }, - Err(_) => { - let row = state.line; - let col = state.column; - return state.fail(arena, NoProgress, to_problem(row, col)); - } - }; - - while !state.bytes.is_empty() { - match char::from_utf8_slice_start(state.bytes) { - Ok((ch, bytes_parsed)) => { - // After the first character, only these are allowed: - // - // * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers - // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() - // * A ':' indicating the end of the field - if ch.is_alphabetic() || ch.is_ascii_digit() { - buf.push(ch); - - state = state.advance_without_indenting(bytes_parsed).map_err( - |(progress, _, state)| { - (progress, to_problem(state.line, state.column), state) - }, - )?; - } else { - // This is the end of the field. We're done! - break; - } - } - Err(_) => { - let row = state.line; - let col = state.column; - return state.fail(arena, MadeProgress, to_problem(row, col)); - } - }; - } - - Ok((MadeProgress, buf.into_bump_str(), state)) + move |arena, state: State<'a>| match crate::ident::tag_name().parse(arena, state) { + Ok(good) => Ok(good), + Err((progress, _, state)) => Err((progress, to_problem(state.line, state.column), state)), } } From 5e4db62c464f6016710fc6b557b25a00bac79ae9 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 02:09:48 +0100 Subject: [PATCH 61/74] remove Attempting and more --- compiler/parse/src/ast.rs | 27 ---------- compiler/parse/src/blankspace.rs | 3 +- compiler/parse/src/module.rs | 14 +++++- compiler/parse/src/number_literal.rs | 74 +++++++++++----------------- compiler/parse/src/parser.rs | 58 +--------------------- compiler/parse/src/string_literal.rs | 6 +-- 6 files changed, 46 insertions(+), 136 deletions(-) diff --git a/compiler/parse/src/ast.rs b/compiler/parse/src/ast.rs index 86b35524bf..bc0e0d1259 100644 --- a/compiler/parse/src/ast.rs +++ b/compiler/parse/src/ast.rs @@ -589,33 +589,6 @@ impl<'a> Spaceable<'a> for Def<'a> { } } -/// What we're currently attempting to parse, e.g. -/// "currently attempting to parse a list." This helps error messages! -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Attempting { - LineComment, - List, - Keyword, - StrLiteral, - RecordLiteral, - RecordFieldLabel, - InterpolatedString, - NumberLiteral, - UnicodeEscape, - ClosureParams, - ClosureBody, - Def, - Module, - Record, - Identifier, - HexDigit, - ConcreteType, - TypeVariable, - WhenCondition, - WhenBranch, - TODO, -} - impl<'a> Expr<'a> { pub fn loc_ref(&'a self, region: Region) -> Loc<&'a Self> { Loc { diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index b4524e13e9..7e05ef6d9d 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -275,7 +275,8 @@ where debug_assert!(u16::MAX - state.indent_col >= spaces as u16); debug_assert!(spaces <= u16::MAX as usize); - state.indent_col + spaces as u16 + // state.indent_col + spaces as u16 + state.indent_col } else { state.indent_col }; diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 0068ae6933..39058ff686 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -7,8 +7,8 @@ use crate::header::{ use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ - backtrackable, end_of_file, specialize, word1, Col, EEffects, EExposes, EHeader, EImports, - EPackages, EProvides, ERequires, ETypedIdent, Parser, Row, State, SyntaxError, + backtrackable, specialize, word1, Col, EEffects, EExposes, EHeader, EImports, EPackages, + EProvides, ERequires, ETypedIdent, Parser, Row, State, SyntaxError, }; use crate::string_literal; use crate::type_annotation; @@ -249,6 +249,16 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { } } +fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> { + |_arena, state: State<'a>| { + if state.has_reached_end() { + Ok((NoProgress, (), state)) + } else { + Err((NoProgress, SyntaxError::ConditionFailed, state)) + } + } +} + #[inline(always)] pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located>>, SyntaxError<'a>> { use crate::parser::EExpr; diff --git a/compiler/parse/src/number_literal.rs b/compiler/parse/src/number_literal.rs index e9f3ed8a9c..1f6ee8609e 100644 --- a/compiler/parse/src/number_literal.rs +++ b/compiler/parse/src/number_literal.rs @@ -1,7 +1,5 @@ use crate::ast::Base; -use crate::parser::{parse_utf8, Number, ParseResult, Parser, Progress, State, SyntaxError}; -use std::char; -use std::str::from_utf8_unchecked; +use crate::parser::{Number, ParseResult, Parser, Progress, State}; pub enum NumLiteral<'a> { Float(&'a str), @@ -52,29 +50,21 @@ fn chomp_number_base<'a>( ) -> ParseResult<'a, NumLiteral<'a>, Number> { let (_is_float, chomped) = chomp_number(bytes); - match parse_utf8(&bytes[0..chomped]) { - Ok(string) => match state.advance_without_indenting(chomped + 2 + is_negative as usize) { - Ok(new) => { - // all is well - Ok(( - Progress::MadeProgress, - NumLiteral::NonBase10Int { - is_negative, - string, - base, - }, - new, - )) - } - Err((_, SyntaxError::LineTooLong(_), new)) => { - // the only error we care about in this context - Err((Progress::MadeProgress, Number::LineTooLong, new)) - } - Err(_) => unreachable!("we know advancing will succeed if there is space on the line"), - }, + let string = unsafe { std::str::from_utf8_unchecked(&bytes[..chomped]) }; - Err(_) => unreachable!("no invalid utf8 could have been chomped"), - } + let new = state.advance_without_indenting_ee(chomped + 2 + is_negative as usize, |_, _| { + Number::LineTooLong + })?; + + Ok(( + Progress::MadeProgress, + NumLiteral::NonBase10Int { + is_negative, + string, + base, + }, + new, + )) } fn chomp_number_dec<'a>( @@ -94,27 +84,21 @@ fn chomp_number_dec<'a>( return Err((Progress::NoProgress, Number::End, state)); } - let string = unsafe { from_utf8_unchecked(&state.bytes[0..chomped + is_negative as usize]) }; + let string = + unsafe { std::str::from_utf8_unchecked(&state.bytes[0..chomped + is_negative as usize]) }; - match state.advance_without_indenting(chomped + is_negative as usize) { - Ok(new) => { - // all is well - Ok(( - Progress::MadeProgress, - if is_float { - NumLiteral::Float(string) - } else { - NumLiteral::Num(string) - }, - new, - )) - } - Err((_, SyntaxError::LineTooLong(_), new)) => { - // the only error we care about in this context - Err((Progress::MadeProgress, Number::LineTooLong, new)) - } - Err(_) => unreachable!("we know advancing will succeed if there is space on the line"), - } + let new = state + .advance_without_indenting_ee(chomped + is_negative as usize, |_, _| Number::LineTooLong)?; + + Ok(( + Progress::MadeProgress, + if is_float { + NumLiteral::Float(string) + } else { + NumLiteral::Num(string) + }, + new, + )) } fn chomp_number(mut bytes: &[u8]) -> (bool, usize) { diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 96574653c9..96a1d6c16c 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -1,9 +1,7 @@ -use crate::ast::Attempting; use bumpalo::collections::vec::Vec; use bumpalo::Bump; use roc_region::all::{Located, Region}; use std::fmt; -use std::str::from_utf8; use Progress::*; /// A position in a source file. @@ -59,13 +57,6 @@ impl<'a> State<'a> { /// This assumes we are *not* advancing with spaces, or at least that /// any spaces on the line were preceded by non-spaces - which would mean /// they weren't eligible to indent anyway. - pub fn advance_without_indenting( - self, - quantity: usize, - ) -> Result, Self)> { - self.advance_without_indenting_ee(quantity, |line, _| SyntaxError::LineTooLong(line)) - } - pub fn advance_without_indenting_e( self, quantity: usize, @@ -132,7 +123,7 @@ impl<'a> fmt::Debug for State<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "State {{")?; - match from_utf8(self.bytes) { + match std::str::from_utf8(self.bytes) { Ok(string) => write!(f, "\n\tbytes: [utf8] {:?}", string)?, Err(_) => write!(f, "\n\tbytes: [invalid utf8] {:?}", self.bytes)?, } @@ -704,36 +695,6 @@ pub enum TVariable { Space(BadInputError, Row, Col), } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ContextStack<'a> { - Cons(ContextItem, &'a ContextStack<'a>), - Nil, -} - -impl<'a> ContextStack<'a> { - pub fn uncons(&'a self) -> Option<(ContextItem, &'a Self)> { - match self { - ContextStack::Cons(item, rest) => Some((*item, rest)), - ContextStack::Nil => None, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ContextItem { - pub line: u32, - pub column: u16, - pub context: Attempting, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DeadEnd<'a, T> { - pub line: u32, - pub column: u16, - pub problem: T, - pub context_stack: ContextStack<'a>, -} - /// use std vec to escape the arena's lifetime bound /// since this is only used when there is in fact an error /// I think this is fine @@ -1862,23 +1823,6 @@ where map_with_arena!(parser, transform) } -pub fn parse_utf8<'a>(bytes: &[u8]) -> Result<&str, SyntaxError<'a>> { - match from_utf8(bytes) { - Ok(string) => Ok(string), - Err(_) => Err(SyntaxError::BadUtf8), - } -} - -pub fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> { - |_arena: &'a Bump, state: State<'a>| { - if state.has_reached_end() { - Ok((NoProgress, (), state)) - } else { - Err((NoProgress, SyntaxError::ConditionFailed, state)) - } - } -} - pub fn backtrackable<'a, P, Val, Error>(parser: P) -> impl Parser<'a, Val, Error> where P: Parser<'a, Val, Error>, diff --git a/compiler/parse/src/string_literal.rs b/compiler/parse/src/string_literal.rs index f68cf849d1..4dd531ff33 100644 --- a/compiler/parse/src/string_literal.rs +++ b/compiler/parse/src/string_literal.rs @@ -1,9 +1,7 @@ use crate::ast::{EscapedChar, StrLiteral, StrSegment}; use crate::expr; use crate::parser::Progress::*; -use crate::parser::{ - allocated, loc, parse_utf8, specialize_ref, word1, BadInputError, EString, Parser, State, -}; +use crate::parser::{allocated, loc, specialize_ref, word1, BadInputError, EString, Parser, State}; use bumpalo::collections::vec::Vec; use bumpalo::Bump; @@ -101,7 +99,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> { // to exclude that char we just parsed. let string_bytes = &state.bytes[0..(segment_parsed_bytes - 1)]; - match parse_utf8(string_bytes) { + match std::str::from_utf8(string_bytes) { Ok(string) => { state = advance_state!(state, string.len())?; From c938a93dea5652fd0d70789933962d9288096df7 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 02:24:00 +0100 Subject: [PATCH 62/74] fix comment newline chomping issue --- compiler/parse/src/blankspace.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 7e05ef6d9d..d560fe969a 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -193,11 +193,18 @@ pub fn spaces_till_end_of_line<'a, E: 'a>( b'#' => match chomp_line_comment(bytes) { Ok(comment) => { state.line += 1; + state.column = 0; - state.column += col + comment.len() as u16; - state.bytes = &bytes[comment.len()..]; + let width = 1 + comment.len(); + if let Some(b'\n') = bytes.get(width) { + state.bytes = &bytes[width + 1..]; + } else { + state.bytes = &bytes[width..]; + } state.is_indenting = true; + dbg!(comment, &state); + return Ok((MadeProgress, Some(comment), state)); } Err(_) => unreachable!("we check the first character is a #"), @@ -214,6 +221,7 @@ pub fn spaces_till_end_of_line<'a, E: 'a>( None, State { column: col, + bytes, ..state }, )) @@ -275,8 +283,8 @@ where debug_assert!(u16::MAX - state.indent_col >= spaces as u16); debug_assert!(spaces <= u16::MAX as usize); - // state.indent_col + spaces as u16 - state.indent_col + state.indent_col + spaces as u16 + // state.indent_col } else { state.indent_col }; From 222638e9bb41178f074f8dbb03887ac140adf405 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 02:28:57 +0100 Subject: [PATCH 63/74] confirm is_indenting is not needed --- compiler/parse/src/blankspace.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index d560fe969a..43d7e90cf2 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -283,8 +283,8 @@ where debug_assert!(u16::MAX - state.indent_col >= spaces as u16); debug_assert!(spaces <= u16::MAX as usize); - state.indent_col + spaces as u16 - // state.indent_col + // state.indent_col + spaces as u16 + state.indent_col } else { state.indent_col }; From f7744b4caa01051aaf7979ca0a71d6353e225f50 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 02:38:46 +0100 Subject: [PATCH 64/74] remove is_indenting --- compiler/parse/src/blankspace.rs | 44 ++++++-------------------------- compiler/parse/src/parser.rs | 9 +------ 2 files changed, 9 insertions(+), 44 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 43d7e90cf2..02c2bd2da8 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -172,7 +172,6 @@ pub fn spaces_till_end_of_line<'a, E: 'a>( state.line = row; state.column = col; state.bytes = bytes; - state.is_indenting = true; return Ok((MadeProgress, None, state)); } @@ -201,7 +200,6 @@ pub fn spaces_till_end_of_line<'a, E: 'a>( } else { state.bytes = &bytes[width..]; } - state.is_indenting = true; dbg!(comment, &state); @@ -260,7 +258,7 @@ fn chomp_line_comment<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { /// Advance the parser while also indenting as appropriate. /// This assumes we are only advancing with spaces, since they can indent. fn advance_spaces_e<'a, TE, E>( - state: &State<'a>, + state: State<'a>, spaces: usize, to_error: TE, ) -> Result, (Progress, E, State<'a>)> @@ -268,37 +266,12 @@ where TE: Fn(Row, Col) -> E, { match (state.column as usize).checked_add(spaces) { - Some(column_usize) if column_usize <= u16::MAX as usize => { - // Spaces don't affect is_indenting; if we were previously indneting, - // we still are, and if we already finished indenting, we're still done. - let is_indenting = state.is_indenting; - - // If we're indenting, spaces indent us further. - let indent_col = if is_indenting { - // This doesn't need to be checked_add because it's always true that - // indent_col <= col, so if this could possibly overflow, we would - // already have errored out from the column calculation. - // - // Leaving debug assertions in case this invariant someday disappers. - debug_assert!(u16::MAX - state.indent_col >= spaces as u16); - debug_assert!(spaces <= u16::MAX as usize); - - // state.indent_col + spaces as u16 - state.indent_col - } else { - state.indent_col - }; - - Ok(State { - bytes: &state.bytes[spaces..], - line: state.line, - column: column_usize as u16, - indent_col, - is_indenting, - original_len: state.original_len, - }) - } - _ => Err(crate::parser::line_too_long_e(state.clone(), to_error)), + Some(column_usize) => Ok(State { + bytes: &state.bytes[spaces..], + column: column_usize as u16, + ..state + }), + _ => Err((NoProgress, to_error(state.line, state.column), state)), } } @@ -319,7 +292,7 @@ pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser: spaces_seen += 1; if spaces_seen == spaces_expected { let state = - advance_spaces_e(&state, spaces_expected as usize, EExpr::IndentStart)?; + advance_spaces_e(state, spaces_expected as usize, EExpr::IndentStart)?; return Ok((MadeProgress, (), state)); } } @@ -379,7 +352,6 @@ where } else if state.line != row { // we parsed at least one newline - state.is_indenting = true; state.indent_col = col; if col >= min_indent { diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 96a1d6c16c..bd2589d7bb 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -5,7 +5,7 @@ use std::fmt; use Progress::*; /// A position in a source file. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct State<'a> { /// The raw input bytes from the file. pub bytes: &'a [u8], @@ -19,10 +19,6 @@ pub struct State<'a> { /// (so no indent is col 1 - this saves an arithmetic operation.) pub indent_col: u16, - // true at the beginning of each line, then false after encountering - // the first nonspace char on that line. - pub is_indenting: bool, - /// The original length of the string, before any bytes were consumed. /// This is used internally by the State::bytes_consumed() function. /// @@ -43,7 +39,6 @@ impl<'a> State<'a> { line: 0, column: 0, indent_col: 0, - is_indenting: true, original_len: bytes.len(), } } @@ -84,7 +79,6 @@ impl<'a> State<'a> { bytes: &self.bytes[quantity..], column: column_usize as u16, // Once we hit a nonspace character, we are no longer indenting. - is_indenting: false, ..self }) } @@ -130,7 +124,6 @@ impl<'a> fmt::Debug for State<'a> { write!(f, "\n\t(line, col): ({}, {}),", self.line, self.column)?; write!(f, "\n\tindent_col: {}", self.indent_col)?; - write!(f, "\n\tis_indenting: {:?}", self.is_indenting)?; write!(f, "\n\toriginal_len: {}", self.original_len)?; write!(f, "\n}}") } From 362459b647123dbc728594875ac1afcf24a4d5a1 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 02:39:47 +0100 Subject: [PATCH 65/74] remove size from parser state --- compiler/parse/src/parser.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index bd2589d7bb..e4485e9906 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -18,12 +18,6 @@ pub struct State<'a> { /// Current indentation level, in columns /// (so no indent is col 1 - this saves an arithmetic operation.) pub indent_col: u16, - - /// The original length of the string, before any bytes were consumed. - /// This is used internally by the State::bytes_consumed() function. - /// - /// TODO make this private, in a way that doesn't break macros! - pub original_len: usize, } #[derive(Debug, PartialEq, Eq)] @@ -39,7 +33,6 @@ impl<'a> State<'a> { line: 0, column: 0, indent_col: 0, - original_len: bytes.len(), } } @@ -124,7 +117,6 @@ impl<'a> fmt::Debug for State<'a> { write!(f, "\n\t(line, col): ({}, {}),", self.line, self.column)?; write!(f, "\n\tindent_col: {}", self.indent_col)?; - write!(f, "\n\toriginal_len: {}", self.original_len)?; write!(f, "\n}}") } } From 4e4854ceaa1422bbbe4ebafee5ba192015af4f44 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 02:48:07 +0100 Subject: [PATCH 66/74] remove unneeded argument --- compiler/can/tests/helpers/mod.rs | 2 +- compiler/fmt/tests/test_fmt.rs | 4 ++-- compiler/load/src/file.rs | 4 ++-- compiler/parse/src/parser.rs | 2 +- compiler/parse/src/test_helpers.rs | 4 ++-- compiler/parse/tests/test_parse.rs | 24 +++++++++++----------- compiler/reporting/tests/helpers/mod.rs | 2 +- compiler/reporting/tests/test_reporting.rs | 2 +- editor/src/lang/expr.rs | 2 +- editor/src/lang/roc_file.rs | 2 +- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/compiler/can/tests/helpers/mod.rs b/compiler/can/tests/helpers/mod.rs index c7e51f219c..662ebd65b5 100644 --- a/compiler/can/tests/helpers/mod.rs +++ b/compiler/can/tests/helpers/mod.rs @@ -29,7 +29,7 @@ pub fn parse_loc_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes()); + let state = State::new(input.trim().as_bytes()); match roc_parse::expr::test_parse_expr(0, arena, state) { Ok((loc_expr, _state)) => Ok(loc_expr), diff --git a/compiler/fmt/tests/test_fmt.rs b/compiler/fmt/tests/test_fmt.rs index 13ade561a9..bda88c82f9 100644 --- a/compiler/fmt/tests/test_fmt.rs +++ b/compiler/fmt/tests/test_fmt.rs @@ -17,7 +17,7 @@ mod test_fmt { use roc_parse::parser::{Parser, State, SyntaxError}; fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes()); + let state = State::new(input.trim().as_bytes()); match roc_parse::expr::test_parse_expr(0, arena, state) { Ok((loc_expr, _state)) => Ok(loc_expr.value), @@ -51,7 +51,7 @@ mod test_fmt { let src = src.trim_end(); let expected = expected.trim_end(); - match module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) { + match module::parse_header(&arena, State::new(src.as_bytes())) { Ok((actual, state)) => { let mut buf = String::new_in(&arena); diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index a92d075677..9082abf113 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2304,7 +2304,7 @@ fn load_pkg_config<'a>( Ok(bytes_vec) => { let parse_start = SystemTime::now(); let bytes = arena.alloc(bytes_vec); - let parse_state = parser::State::new_in(arena, bytes); + let parse_state = parser::State::new(bytes); let parsed = roc_parse::module::parse_header(&arena, parse_state); let parse_header_duration = parse_start.elapsed().unwrap(); @@ -2474,7 +2474,7 @@ fn parse_header<'a>( start_time: SystemTime, ) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> { let parse_start = SystemTime::now(); - let parse_state = parser::State::new_in(arena, src_bytes); + let parse_state = parser::State::new(src_bytes); let parsed = roc_parse::module::parse_header(&arena, parse_state); let parse_header_duration = parse_start.elapsed().unwrap(); diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index e4485e9906..001bfa299a 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -27,7 +27,7 @@ pub enum Either { } impl<'a> State<'a> { - pub fn new_in(arena: &'a Bump, bytes: &'a [u8]) -> State<'a> { + pub fn new(bytes: &'a [u8]) -> State<'a> { State { bytes, line: 0, diff --git a/compiler/parse/src/test_helpers.rs b/compiler/parse/src/test_helpers.rs index 4d9f83efaa..e70782358f 100644 --- a/compiler/parse/src/test_helpers.rs +++ b/compiler/parse/src/test_helpers.rs @@ -17,7 +17,7 @@ pub fn parse_defs_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>>, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes()); + let state = State::new(input.trim().as_bytes()); let answer = module_defs().parse(arena, state); answer .map(|(_, loc_expr, _)| loc_expr) @@ -29,7 +29,7 @@ pub fn parse_loc_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes()); + let state = State::new(input.trim().as_bytes()); match crate::expr::test_parse_expr(0, arena, state) { Ok((loc_expr, _state)) => Ok(loc_expr), diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index b149b1b00f..a152464ce0 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -2433,7 +2433,7 @@ mod test_parse { app "test-app" packages {} imports [] provides [] to blah "# ); - let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); @@ -2473,7 +2473,7 @@ mod test_parse { "# ); - let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); @@ -2528,7 +2528,7 @@ mod test_parse { "# ); - let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); @@ -2573,7 +2573,7 @@ mod test_parse { let expected = roc_parse::ast::Module::Platform { header }; let src = "platform rtfeldman/blah requires {} exposes [] packages {} imports [] provides [] effects fx.Blah {}"; - let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); @@ -2642,7 +2642,7 @@ mod test_parse { effects fx.Effect {} "# ); - let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); @@ -2673,7 +2673,7 @@ mod test_parse { interface Foo exposes [] imports [] "# ); - let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); @@ -2704,7 +2704,7 @@ mod test_parse { interface Foo.Bar.Baz exposes [] imports [] "# ); - let actual = roc_parse::module::parse_header(&arena, State::new_in(&arena, src.as_bytes())) + let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.0); assert_eq!(Ok(expected), actual); @@ -2731,7 +2731,7 @@ mod test_parse { "# ); let actual = module_defs() - .parse(&arena, State::new_in(&arena, src.as_bytes())) + .parse(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.1); // It should occur twice in the debug output - once for the pattern, @@ -2790,7 +2790,7 @@ mod test_parse { ); let actual = module_defs() - .parse(&arena, State::new_in(&arena, src.as_bytes())) + .parse(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.1); assert_eq!(Ok(expected), actual); @@ -2810,7 +2810,7 @@ mod test_parse { ); let actual = module_defs() - .parse(&arena, State::new_in(&arena, src.as_bytes())) + .parse(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.0); assert!(actual.is_ok()); @@ -2832,7 +2832,7 @@ mod test_parse { ); let actual = module_defs() - .parse(&arena, State::new_in(&arena, src.as_bytes())) + .parse(&arena, State::new(src.as_bytes())) .map(|tuple| tuple.0); assert!(actual.is_ok()); @@ -2852,7 +2852,7 @@ mod test_parse { "# ); - let state = State::new_in(arena, src.as_bytes()); + let state = State::new(src.as_bytes()); let parser = module_defs(); let parsed = parser.parse(arena, state); match parsed { diff --git a/compiler/reporting/tests/helpers/mod.rs b/compiler/reporting/tests/helpers/mod.rs index a242ba2076..f5f8d128cb 100644 --- a/compiler/reporting/tests/helpers/mod.rs +++ b/compiler/reporting/tests/helpers/mod.rs @@ -109,7 +109,7 @@ pub fn parse_loc_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>, SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes()); + let state = State::new(input.trim().as_bytes()); match roc_parse::expr::test_parse_expr(0, arena, state) { Ok((loc_expr, _state)) => Ok(loc_expr), diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 46c5c00a0a..2a0f18dd5e 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -177,7 +177,7 @@ mod test_reporting { use roc_parse::parser::State; - let state = State::new_in(arena, src.as_bytes()); + let state = State::new(src.as_bytes()); let filename = filename_from_string(r"\code\proj\Main.roc"); let src_lines: Vec<&str> = src.split('\n').collect(); diff --git a/editor/src/lang/expr.rs b/editor/src/lang/expr.rs index 58243df0ce..1ec15169ed 100644 --- a/editor/src/lang/expr.rs +++ b/editor/src/lang/expr.rs @@ -234,7 +234,7 @@ pub fn str_to_expr2<'a>( scope: &mut Scope, region: Region, ) -> Result<(Expr2, self::Output), SyntaxError<'a>> { - let state = State::new_in(arena, input.trim().as_bytes()); + let state = State::new(input.trim().as_bytes()); match roc_parse::expr::test_parse_expr(0, arena, state) { Ok((loc_expr, _state)) => Ok(to_expr2(env, scope, arena.alloc(loc_expr.value), region)), diff --git a/editor/src/lang/roc_file.rs b/editor/src/lang/roc_file.rs index f551d987e2..98f1a25210 100644 --- a/editor/src/lang/roc_file.rs +++ b/editor/src/lang/roc_file.rs @@ -36,7 +36,7 @@ impl<'a> File<'a> { let allocation = arena.alloc(bytes); - let module_parse_state = parser::State::new_in(arena, allocation); + let module_parse_state = parser::State::new(allocation); let parsed_module = roc_parse::module::parse_header(&arena, module_parse_state); match parsed_module { From 9f146bf702a43a0e9e230bd27a52199a42737b7e Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 02:52:36 +0100 Subject: [PATCH 67/74] remove syntax error --- compiler/parse/src/expr.rs | 2 +- compiler/parse/src/parser.rs | 3 --- compiler/reporting/src/error/parse.rs | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 7bf9b8393d..01f7f09b70 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -1193,7 +1193,7 @@ fn parse_def_signature_help<'a>( and!( // Optionally parse additional defs. zero_or_more!(backtrackable(allocated(space0_before_e( - loc!(specialize_ref(EExpr::Syntax, def(original_indent))), + loc!(def_help(original_indent)), original_indent, EExpr::Space, EExpr::IndentStart, diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 001bfa299a..ded7e62c4c 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -378,7 +378,6 @@ pub enum EExpr<'a> { UnaryNegate(Row, Col), BadOperator(&'a [u8], Row, Col), - Def(&'a SyntaxError<'a>, Row, Col), DefMissingFinalExpr(Row, Col), Type(Type<'a>, Row, Col), Pattern(&'a EPattern<'a>, Row, Col), @@ -395,8 +394,6 @@ pub enum EExpr<'a> { BackpassComma(Row, Col), BackpassArrow(Row, Col), - Syntax(&'a SyntaxError<'a>, Row, Col), - When(When<'a>, Row, Col), If(If<'a>, Row, Col), diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 42402bad9d..8c8c89ffc3 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -198,8 +198,6 @@ fn to_expr_report<'a>( to_expr_in_parens_report(alloc, filename, context, &expr, *row, *col) } EExpr::Type(tipe, row, col) => to_type_report(alloc, filename, &tipe, *row, *col), - EExpr::Def(syntax, row, col) => to_syntax_report(alloc, filename, syntax, *row, *col), - EExpr::ElmStyleFunction(region, row, col) => { let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); let region = *region; From b688fd11a99c4f1f6f825d5d902886d1b0750cd2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 02:54:25 +0100 Subject: [PATCH 68/74] remove syntax error --- compiler/parse/src/expr.rs | 6 +++++- compiler/parse/src/parser.rs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 01f7f09b70..909eaffbef 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -48,6 +48,10 @@ pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> { ) } +fn expr_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { + move |arena, state: State<'a>| parse_expr_help(min_indent, arena, state) +} + fn loc_expr_in_parens_help<'a>( min_indent: u16, ) -> impl Parser<'a, Located>, EInParens<'a>> { @@ -2106,7 +2110,7 @@ fn record_field_help<'a>( word1(b'?', ERecord::QuestionMark) ), space0_before_e( - specialize_ref(ERecord::Syntax, loc!(expr(min_indent))), + specialize_ref(ERecord::Expr, loc!(expr_help(min_indent))), min_indent, ERecord::Space, ERecord::IndentEnd, diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index ded7e62c4c..b64acc62a7 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -443,7 +443,7 @@ pub enum ERecord<'a> { Ampersand(Row, Col), // TODO remove - Syntax(&'a SyntaxError<'a>, Row, Col), + Expr(&'a EExpr<'a>, Row, Col), Space(BadInputError, Row, Col), From e815e57dcfa90798f74f4f30b26f4f60b6055a93 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 03:04:47 +0100 Subject: [PATCH 69/74] remove more syntax errors --- compiler/parse/src/expr.rs | 4 ++-- compiler/parse/src/parser.rs | 15 ++++-------- compiler/parse/src/pattern.rs | 13 +++-------- compiler/parse/src/string_literal.rs | 2 +- compiler/reporting/src/error/parse.rs | 33 ++++++++++++++++++++++----- 5 files changed, 37 insertions(+), 30 deletions(-) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 909eaffbef..5aa810b7b8 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -48,7 +48,7 @@ pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> { ) } -fn expr_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { +pub fn expr_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { move |arena, state: State<'a>| parse_expr_help(min_indent, arena, state) } @@ -2062,7 +2062,7 @@ fn list_literal_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, List<'a>> move |arena, state| { let (_, (parsed_elems, final_comments), state) = collection_trailing_sep_e!( word1(b'[', List::Open), - specialize_ref(List::Syntax, loc!(expr(min_indent))), + specialize_ref(List::Expr, loc!(expr_help(min_indent))), word1(b',', List::End), word1(b']', List::End), min_indent, diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index b64acc62a7..f6d98d1145 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -426,7 +426,7 @@ pub enum EString<'a> { EndlessSingle(Row, Col), EndlessMulti(Row, Col), UnknownEscape(Row, Col), - Format(&'a SyntaxError<'a>, Row, Col), + Format(&'a EExpr<'a>, Row, Col), FormatEnd(Row, Col), } @@ -489,7 +489,6 @@ pub enum List<'a> { End(Row, Col), Space(BadInputError, Row, Col), - Syntax(&'a SyntaxError<'a>, Row, Col), Expr(&'a EExpr<'a>, Row, Col), IndentOpen(Row, Col), @@ -510,7 +509,6 @@ pub enum When<'a> { Condition(&'a EExpr<'a>, Row, Col), Branch(&'a EExpr<'a>, Row, Col), - Syntax(&'a SyntaxError<'a>, Row, Col), IndentIs(Row, Col), IndentCondition(Row, Col), @@ -531,7 +529,6 @@ pub enum If<'a> { Condition(&'a EExpr<'a>, Row, Col), ThenBranch(&'a EExpr<'a>, Row, Col), ElseBranch(&'a EExpr<'a>, Row, Col), - Syntax(&'a SyntaxError<'a>, Row, Col), IndentCondition(Row, Col), IndentIf(Row, Col), @@ -566,9 +563,9 @@ pub enum PRecord<'a> { Field(Row, Col), Colon(Row, Col), Optional(Row, Col), + Pattern(&'a EPattern<'a>, Row, Col), - // TODO remove - Syntax(&'a SyntaxError<'a>, Row, Col), + Expr(&'a EExpr<'a>, Row, Col), Space(BadInputError, Row, Col), @@ -582,13 +579,9 @@ pub enum PRecord<'a> { pub enum PInParens<'a> { End(Row, Col), Open(Row, Col), - /// - // TODO remove - Syntax(&'a SyntaxError<'a>, Row, Col), + Pattern(&'a EPattern<'a>, Row, Col), - /// Space(BadInputError, Row, Col), - /// IndentOpen(Row, Col), IndentEnd(Row, Col), } diff --git a/compiler/parse/src/pattern.rs b/compiler/parse/src/pattern.rs index 146940d790..781fc4afc3 100644 --- a/compiler/parse/src/pattern.rs +++ b/compiler/parse/src/pattern.rs @@ -51,13 +51,6 @@ fn parse_closure_param<'a>( .parse(arena, state) } -pub fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located>, SyntaxError<'a>> { - specialize( - |e, _, _| SyntaxError::Pattern(e), - loc_pattern_help(min_indent), - ) -} - pub fn loc_pattern_help<'a>( min_indent: u16, ) -> impl Parser<'a, Located>, EPattern<'a>> { @@ -130,7 +123,7 @@ fn loc_pattern_in_parens_help<'a>( between!( word1(b'(', PInParens::Open), space0_around_ee( - move |arena, state| specialize_ref(PInParens::Syntax, loc_pattern(min_indent)) + move |arena, state| specialize_ref(PInParens::Pattern, loc_pattern_help(min_indent)) .parse(arena, state), min_indent, PInParens::Space, @@ -386,7 +379,7 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Located { - let val_parser = specialize_ref(PRecord::Syntax, loc_pattern(min_indent)); + let val_parser = specialize_ref(PRecord::Pattern, loc_pattern_help(min_indent)); let (_, loc_val, state) = space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon) .parse(arena, state)?; @@ -414,7 +407,7 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Located { let val_parser = - specialize_ref(PRecord::Syntax, loc!(crate::expr::expr(min_indent))); + specialize_ref(PRecord::Expr, loc!(crate::expr::expr_help(min_indent))); let (_, loc_val, state) = space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon) diff --git a/compiler/parse/src/string_literal.rs b/compiler/parse/src/string_literal.rs index 4dd531ff33..740ef52f29 100644 --- a/compiler/parse/src/string_literal.rs +++ b/compiler/parse/src/string_literal.rs @@ -231,7 +231,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> { // canonicalization error if that expression variant // is not allowed inside a string interpolation. let (_progress, loc_expr, new_state) = skip_second!( - specialize_ref(EString::Format, loc(allocated(expr::expr(0)))), + specialize_ref(EString::Format, loc(allocated(expr::expr_help(0)))), word1(b')', EString::FormatEnd) ) .parse(arena, state)?; diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 8c8c89ffc3..f0bdeafe68 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -172,6 +172,8 @@ enum Node { IfElseBranch, ListElement, InsideParens, + RecordConditionalDefault, + StringFormat, } fn to_expr_report<'a>( @@ -343,6 +345,8 @@ fn to_expr_report<'a>( ]), ), Node::ListElement => (r, c, alloc.text("a list")), + Node::RecordConditionalDefault => (r, c, alloc.text("record field default")), + Node::StringFormat => (r, c, alloc.text("a string format")), Node::InsideParens => (r, c, alloc.text("some parentheses")), }, Context::InDef(r, c) => (r, c, alloc.text("a definition")), @@ -662,7 +666,7 @@ fn to_unfinished_lambda_report<'a>( fn to_str_report<'a>( alloc: &'a RocDocAllocator<'a>, filename: PathBuf, - _context: Context, + context: Context, parse_problem: &roc_parse::parser::EString<'a>, start_row: Row, start_col: Col, @@ -671,7 +675,14 @@ fn to_str_report<'a>( match *parse_problem { EString::Open(_row, _col) => unreachable!("another branch would be taken"), - EString::Format(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col), + EString::Format(expr, row, col) => to_expr_report( + alloc, + filename, + Context::InNode(Node::StringFormat, start_row, start_col, Box::new(context)), + expr, + row, + col, + ), EString::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), EString::UnknownEscape(row, col) => { let surroundings = Region::from_rows_cols(start_row, start_col, row, col); @@ -885,7 +896,6 @@ fn to_list_report<'a>( use roc_parse::parser::List; match *parse_problem { - List::Syntax(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col), List::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), List::Expr(expr, row, col) => to_expr_report( @@ -994,7 +1004,6 @@ fn to_if_report<'a>( use roc_parse::parser::If; match *parse_problem { - If::Syntax(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col), If::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), If::Condition(expr, row, col) => to_expr_report( @@ -1165,7 +1174,6 @@ fn to_when_report<'a>( } } - When::Syntax(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col), When::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), When::Branch(expr, row, col) => to_expr_report( @@ -1558,7 +1566,20 @@ fn to_precord_report<'a>( PRecord::Pattern(pattern, row, col) => { to_pattern_report(alloc, filename, pattern, row, col) } - PRecord::Syntax(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col), + + PRecord::Expr(expr, row, col) => to_expr_report( + alloc, + filename, + Context::InNode( + Node::RecordConditionalDefault, + start_row, + start_col, + Box::new(Context::InDef(row, col)), + ), + expr, + row, + col, + ), PRecord::IndentOpen(row, col) => { let surroundings = Region::from_rows_cols(start_row, start_col, row, col); From 16a2dfd05844e166930f06a729f96eb14f79fa9e Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 03:10:57 +0100 Subject: [PATCH 70/74] remove unused functions --- compiler/parse/src/parser.rs | 92 +----------------------------------- 1 file changed, 1 insertion(+), 91 deletions(-) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index f6d98d1145..16921afd31 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -75,7 +75,7 @@ impl<'a> State<'a> { ..self }) } - _ => Err(line_too_long_e(self.clone(), to_error)), + _ => Err((NoProgress, to_error(self.line, self.column), self)), } } @@ -670,9 +670,6 @@ pub enum TVariable { Space(BadInputError, Row, Col), } -/// use std vec to escape the arena's lifetime bound -/// since this is only used when there is in fact an error -/// I think this is fine #[derive(Debug)] pub struct ParseProblem<'a, T> { pub line: u32, @@ -682,10 +679,6 @@ pub struct ParseProblem<'a, T> { pub bytes: &'a [u8], } -pub fn fail<'a, T>() -> impl Parser<'a, T, SyntaxError<'a>> { - move |_arena, state: State<'a>| Err((NoProgress, SyntaxError::ConditionFailed, state)) -} - pub trait Parser<'a, Output, Error> { fn parse(&self, _: &'a Bump, _: State<'a>) -> ParseResult<'a, Output, Error>; } @@ -713,46 +706,6 @@ where } } -pub fn not_followed_by<'a, P, ByParser, By, Val>( - parser: P, - by: ByParser, -) -> impl Parser<'a, Val, SyntaxError<'a>> -where - ByParser: Parser<'a, By, SyntaxError<'a>>, - P: Parser<'a, Val, SyntaxError<'a>>, -{ - move |arena, state: State<'a>| { - let original_state = state.clone(); - - parser - .parse(arena, state) - .and_then(|(progress, answer, state)| { - let after_parse = state.clone(); - - match by.parse(arena, state) { - Ok((_, _, _state)) => { - Err((NoProgress, SyntaxError::ConditionFailed, original_state)) - } - Err(_) => Ok((progress, answer, after_parse)), - } - }) - } -} - -pub fn not<'a, P, Val>(parser: P) -> impl Parser<'a, (), SyntaxError<'a>> -where - P: Parser<'a, Val, SyntaxError<'a>>, -{ - move |arena, state: State<'a>| { - let original_state = state.clone(); - - match parser.parse(arena, state) { - Ok((_, _, _)) => Err((NoProgress, SyntaxError::ConditionFailed, original_state)), - Err((_, _, _)) => Ok((NoProgress, (), original_state)), - } - } -} - pub fn not_e<'a, P, TE, E, X, Val>(parser: P, to_error: TE) -> impl Parser<'a, (), E> where TE: Fn(Row, Col) -> E, @@ -773,23 +726,6 @@ where } } -pub fn lookahead<'a, Peek, P, PeekVal, Val, Error>( - peek: Peek, - parser: P, -) -> impl Parser<'a, Val, Error> -where - Error: 'a, - Peek: Parser<'a, PeekVal, Error>, - P: Parser<'a, Val, Error>, -{ - move |arena, state: State<'a>| { - let original_state = state.clone(); - - peek.parse(arena, state) - .and_then(|_| parser.parse(arena, original_state)) - } -} - pub fn and_then<'a, P1, P2, F, Before, After, Error>( parser: P1, transform: F, @@ -844,32 +780,6 @@ where } } -// TODO remove -pub fn line_too_long_e(state: State, to_error: TE) -> (Progress, E, State) -where - TE: Fn(Row, Col) -> E, -{ - let problem = to_error(state.line, state.column); - // Set column to MAX and advance the parser to end of input. - // This way, all future parsers will fail on EOF, and then - // unexpected_eof will take them back here - thus propagating - // the initial LineTooLong error all the way to the end, even if - // (for example) the LineTooLong initially occurs in the middle of - // a one_of chain, which would otherwise prevent it from propagating. - let column = u16::MAX; - let bytes = state.bytes.get(0..state.bytes.len()).unwrap(); - let state = State { - bytes, - line: state.line, - column, - ..state - }; - - // TODO do we make progress in this case? - // isn't this error fatal? - (Progress::NoProgress, problem, state) -} - pub fn keyword_e<'a, ToError, E>(keyword: &'static str, if_error: ToError) -> impl Parser<'a, (), E> where ToError: Fn(Row, Col) -> E, From b349ae7ab556641c276d531ae56e935167c3b964 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 03:18:31 +0100 Subject: [PATCH 71/74] type variable errors --- compiler/parse/src/ident.rs | 2 +- compiler/parse/src/parser.rs | 30 +---------------------- compiler/parse/src/type_annotation.rs | 34 ++++++++++----------------- compiler/reporting/src/error/parse.rs | 1 - 4 files changed, 15 insertions(+), 52 deletions(-) diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index 9a239456e9..a22c2348e2 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -63,7 +63,7 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { Err(progress) => Err((progress, (), state)), Ok(ident) => { if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) { - Err((MadeProgress, (), state)) + Err((NoProgress, (), state)) } else { let width = ident.len(); match state.advance_without_indenting_ee(width, |_, _| ()) { diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 16921afd31..793c6bfdb5 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -592,7 +592,7 @@ pub enum Type<'a> { TTagUnion(TTagUnion<'a>, Row, Col), TInParens(TInParens<'a>, Row, Col), TApply(TApply, Row, Col), - TVariable(TVariable, Row, Col), + TBadTypeVariable(Row, Col), TWildcard(Row, Col), /// TStart(Row, Col), @@ -662,14 +662,6 @@ pub enum TApply { StartIsNumber(Row, Col), } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TVariable { - /// - StartNotLowercase(Row, Col), - End(Row, Col), - Space(BadInputError, Row, Col), -} - #[derive(Debug)] pub struct ParseProblem<'a, T> { pub line: u32, @@ -706,26 +698,6 @@ where } } -pub fn not_e<'a, P, TE, E, X, Val>(parser: P, to_error: TE) -> impl Parser<'a, (), E> -where - TE: Fn(Row, Col) -> E, - P: Parser<'a, Val, X>, - E: 'a, -{ - move |arena, state: State<'a>| { - let original_state = state.clone(); - - match parser.parse(arena, state) { - Ok((_, _, _)) => Err(( - NoProgress, - to_error(original_state.line, original_state.column), - original_state, - )), - Err((_, _, _)) => Ok((NoProgress, (), original_state)), - } - } -} - pub fn and_then<'a, P1, P2, F, Before, After, Error>( parser: P1, transform: F, diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index cb81688734..9cc07f713a 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -2,10 +2,10 @@ use crate::ast::{AssignedField, Tag, TypeAnnotation}; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; use crate::keyword; use crate::parser::{ - allocated, backtrackable, not_e, optional, specialize, specialize_ref, word1, word2, - ParseResult, Parser, + allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, ParseResult, + Parser, Progress::{self, *}, - State, SyntaxError, TApply, TInParens, TRecord, TTagUnion, TVariable, Type, + State, SyntaxError, TApply, TInParens, TRecord, TTagUnion, Type, }; use bumpalo::collections::vec::Vec; use bumpalo::Bump; @@ -60,7 +60,7 @@ fn term<'a>(min_indent: u16) -> impl Parser<'a, Located>, Typ loc!(specialize(Type::TRecord, record_type(min_indent))), loc!(specialize(Type::TTagUnion, tag_union_type(min_indent))), loc!(applied_type(min_indent)), - loc!(specialize(Type::TVariable, parse_type_variable)) + loc!(parse_type_variable) ), // Inline alias notation, e.g. [ Nil, Cons a (List a) ] as List a one_of![ @@ -115,21 +115,13 @@ fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located>)| { @@ -457,7 +449,7 @@ fn parse_concrete_type<'a>( fn parse_type_variable<'a>( arena: &'a Bump, state: State<'a>, -) -> ParseResult<'a, TypeAnnotation<'a>, TVariable> { +) -> ParseResult<'a, TypeAnnotation<'a>, Type<'a>> { match crate::ident::lowercase_ident().parse(arena, state) { Ok((_, name, state)) => { let answer = TypeAnnotation::BoundVariable(name); @@ -466,7 +458,7 @@ fn parse_type_variable<'a>( } Err((progress, _, state)) => Err(( progress, - TVariable::StartNotLowercase(state.line, state.column), + Type::TBadTypeVariable(state.line, state.column), state, )), } diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index f0bdeafe68..1a46b03b87 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -1752,7 +1752,6 @@ fn to_type_report<'a>( } Type::TAsIndentStart(row, col) => { - dbg!(row, col); let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); let region = Region::from_row_col(*row, *col); From cba55734cb7b449ba198a9347e64dcbe187e0f16 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 03:41:01 +0100 Subject: [PATCH 72/74] clean up helpers --- compiler/can/tests/helpers/mod.rs | 22 +--------------------- compiler/fmt/tests/test_fmt.rs | 14 ++------------ compiler/parse/src/expr.rs | 4 ++-- compiler/parse/src/pattern.rs | 13 +------------ compiler/parse/src/test_helpers.rs | 2 +- compiler/parse/src/type_annotation.rs | 8 +------- compiler/reporting/tests/helpers/mod.rs | 24 ++---------------------- editor/src/lang/expr.rs | 8 +++----- 8 files changed, 13 insertions(+), 82 deletions(-) diff --git a/compiler/can/tests/helpers/mod.rs b/compiler/can/tests/helpers/mod.rs index 662ebd65b5..8540427d98 100644 --- a/compiler/can/tests/helpers/mod.rs +++ b/compiler/can/tests/helpers/mod.rs @@ -8,8 +8,6 @@ use roc_can::operator; use roc_can::scope::Scope; use roc_collections::all::MutMap; use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds}; -use roc_parse::ast; -use roc_parse::parser::{State, SyntaxError}; use roc_problem::can::Problem; use roc_region::all::{Located, Region}; use roc_types::subs::{VarStore, Variable}; @@ -19,24 +17,6 @@ pub fn test_home() -> ModuleId { ModuleIds::default().get_or_insert(&"Test".into()) } -#[allow(dead_code)] -pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result, SyntaxError<'a>> { - parse_loc_with(arena, input).map(|loc_expr| loc_expr.value) -} - -#[allow(dead_code)] -pub fn parse_loc_with<'a>( - arena: &'a Bump, - input: &'a str, -) -> Result>, SyntaxError<'a>> { - let state = State::new(input.trim().as_bytes()); - - match roc_parse::expr::test_parse_expr(0, arena, state) { - Ok((loc_expr, _state)) => Ok(loc_expr), - Err(fail) => Err(SyntaxError::Expr(fail)), - } -} - #[allow(dead_code)] pub fn can_expr(expr_str: &str) -> CanExprOut { can_expr_with(&Bump::new(), test_home(), expr_str) @@ -54,7 +34,7 @@ pub struct CanExprOut { #[allow(dead_code)] pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut { - let loc_expr = parse_loc_with(&arena, expr_str).unwrap_or_else(|e| { + let loc_expr = roc_parse::test_helpers::parse_loc_with(&arena, expr_str).unwrap_or_else(|e| { panic!( "can_expr_with() got a parse error when attempting to canonicalize:\n\n{:?} {:?}", expr_str, e diff --git a/compiler/fmt/tests/test_fmt.rs b/compiler/fmt/tests/test_fmt.rs index bda88c82f9..151dd27635 100644 --- a/compiler/fmt/tests/test_fmt.rs +++ b/compiler/fmt/tests/test_fmt.rs @@ -12,25 +12,15 @@ mod test_fmt { use roc_fmt::annotation::{Formattable, Newlines, Parens}; use roc_fmt::def::fmt_def; use roc_fmt::module::fmt_module; - use roc_parse::ast::Expr; use roc_parse::module::{self, module_defs}; - use roc_parse::parser::{Parser, State, SyntaxError}; - - fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result, SyntaxError<'a>> { - let state = State::new(input.trim().as_bytes()); - - match roc_parse::expr::test_parse_expr(0, arena, state) { - Ok((loc_expr, _state)) => Ok(loc_expr.value), - Err(fail) => Err(SyntaxError::Expr(fail)), - } - } + use roc_parse::parser::{Parser, State}; fn expr_formats_to(input: &str, expected: &str) { let arena = Bump::new(); let input = input.trim_end(); let expected = expected.trim_end(); - match parse_with(&arena, input) { + match roc_parse::test_helpers::parse_expr_with(&arena, input.trim()) { Ok(actual) => { let mut buf = String::new_in(&arena); diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 5aa810b7b8..8c7da493b5 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -23,7 +23,7 @@ pub fn test_parse_expr<'a>( min_indent: u16, arena: &'a bumpalo::Bump, state: State<'a>, -) -> Result<(Located>, State<'a>), EExpr<'a>> { +) -> Result>, EExpr<'a>> { let parser = space0_before_e( loc!(|a, s| parse_expr_help(min_indent, a, s)), min_indent, @@ -32,7 +32,7 @@ pub fn test_parse_expr<'a>( ); match parser.parse(arena, state) { - Ok((_, expression, state)) => Ok((expression, state)), + Ok((_, expression, _)) => Ok(expression), Err((_, fail, _)) => Err(fail), } } diff --git a/compiler/parse/src/pattern.rs b/compiler/parse/src/pattern.rs index 781fc4afc3..b27875f9ad 100644 --- a/compiler/parse/src/pattern.rs +++ b/compiler/parse/src/pattern.rs @@ -4,7 +4,7 @@ use crate::ident::{lowercase_ident, parse_ident_help, Ident}; use crate::parser::Progress::{self, *}; use crate::parser::{ backtrackable, optional, specialize, specialize_ref, word1, EPattern, PInParens, PRecord, - ParseResult, Parser, State, SyntaxError, + ParseResult, Parser, State, }; use bumpalo::collections::string::String; use bumpalo::collections::Vec; @@ -290,10 +290,6 @@ fn loc_ident_pattern_help<'a>( } } -pub fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> { - specialize(|e, _, _| SyntaxError::Pattern(e), underscore_pattern_help()) -} - fn underscore_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> { move |arena: &'a Bump, state: State<'a>| { let (_, _, next_state) = word1(b'_', EPattern::Underscore).parse(arena, state)?; @@ -318,13 +314,6 @@ fn lowercase_ident_pattern<'a>( specialize(move |_, _, _| EPattern::End(row, col), lowercase_ident()).parse(arena, state) } -pub fn record_pattern<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> { - specialize( - |e, r, c| SyntaxError::Pattern(EPattern::Record(e, r, c)), - record_pattern_help(min_indent), - ) -} - #[inline(always)] fn record_pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRecord<'a>> { move |arena, state| { diff --git a/compiler/parse/src/test_helpers.rs b/compiler/parse/src/test_helpers.rs index e70782358f..a1b34105b8 100644 --- a/compiler/parse/src/test_helpers.rs +++ b/compiler/parse/src/test_helpers.rs @@ -32,7 +32,7 @@ pub fn parse_loc_with<'a>( let state = State::new(input.trim().as_bytes()); match crate::expr::test_parse_expr(0, arena, state) { - Ok((loc_expr, _state)) => Ok(loc_expr), + Ok(loc_expr) => Ok(loc_expr), Err(fail) => Err(SyntaxError::Expr(fail)), } } diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index 9cc07f713a..b540775c0e 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -5,18 +5,12 @@ use crate::parser::{ allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, ParseResult, Parser, Progress::{self, *}, - State, SyntaxError, TApply, TInParens, TRecord, TTagUnion, Type, + State, TApply, TInParens, TRecord, TTagUnion, Type, }; use bumpalo::collections::vec::Vec; use bumpalo::Bump; use roc_region::all::{Located, Region}; -pub fn located<'a>( - min_indent: u16, -) -> impl Parser<'a, Located>, SyntaxError<'a>> { - specialize(|x, _, _| SyntaxError::Type(x), expression(min_indent)) -} - pub fn located_help<'a>(min_indent: u16) -> impl Parser<'a, Located>, Type<'a>> { expression(min_indent) } diff --git a/compiler/reporting/tests/helpers/mod.rs b/compiler/reporting/tests/helpers/mod.rs index f5f8d128cb..3cade3ded1 100644 --- a/compiler/reporting/tests/helpers/mod.rs +++ b/compiler/reporting/tests/helpers/mod.rs @@ -11,8 +11,6 @@ use roc_collections::all::{ImMap, MutMap, SendSet}; use roc_constrain::expr::constrain_expr; use roc_constrain::module::{constrain_imported_values, Import}; use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds}; -use roc_parse::ast; -use roc_parse::parser::{State, SyntaxError}; use roc_problem::can::Problem; use roc_region::all::Located; use roc_solve::solve; @@ -99,27 +97,9 @@ pub struct CanExprOut { pub constraint: Constraint, } -#[allow(dead_code)] -pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result, SyntaxError<'a>> { - parse_loc_with(arena, input).map(|loc_expr| loc_expr.value) -} - -#[allow(dead_code)] -pub fn parse_loc_with<'a>( - arena: &'a Bump, - input: &'a str, -) -> Result>, SyntaxError<'a>> { - let state = State::new(input.trim().as_bytes()); - - match roc_parse::expr::test_parse_expr(0, arena, state) { - Ok((loc_expr, _state)) => Ok(loc_expr), - Err(fail) => Err(SyntaxError::Expr(fail)), - } -} - #[derive(Debug)] pub struct ParseErrOut<'a> { - pub fail: SyntaxError<'a>, + pub fail: roc_parse::parser::SyntaxError<'a>, pub home: ModuleId, pub interns: Interns, } @@ -130,7 +110,7 @@ pub fn can_expr_with<'a>( home: ModuleId, expr_str: &'a str, ) -> Result> { - let loc_expr = match parse_loc_with(&arena, expr_str) { + let loc_expr = match roc_parse::test_helpers::parse_loc_with(&arena, expr_str) { Ok(e) => e, Err(fail) => { let interns = Interns::default(); diff --git a/editor/src/lang/expr.rs b/editor/src/lang/expr.rs index 1ec15169ed..5f7d60062d 100644 --- a/editor/src/lang/expr.rs +++ b/editor/src/lang/expr.rs @@ -234,11 +234,9 @@ pub fn str_to_expr2<'a>( scope: &mut Scope, region: Region, ) -> Result<(Expr2, self::Output), SyntaxError<'a>> { - let state = State::new(input.trim().as_bytes()); - - match roc_parse::expr::test_parse_expr(0, arena, state) { - Ok((loc_expr, _state)) => Ok(to_expr2(env, scope, arena.alloc(loc_expr.value), region)), - Err(fail) => Err(SyntaxError::Expr(fail)), + match roc_parse::test_helpers::parse_loc_with(arena, input.trim()) { + Ok(loc_expr) => Ok(to_expr2(env, scope, arena.alloc(loc_expr.value), region)), + Err(fail) => Err(fail), } } From f5f98b2400a22824bebdce8720a02626bcf094e8 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 03:43:39 +0100 Subject: [PATCH 73/74] clippy --- compiler/parse/src/blankspace.rs | 14 ++++++-------- compiler/parse/src/parser.rs | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 02c2bd2da8..03ae66d3e5 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -201,8 +201,6 @@ pub fn spaces_till_end_of_line<'a, E: 'a>( state.bytes = &bytes[width..]; } - dbg!(comment, &state); - return Ok((MadeProgress, Some(comment), state)); } Err(_) => unreachable!("we check the first character is a #"), @@ -227,7 +225,7 @@ pub fn spaces_till_end_of_line<'a, E: 'a>( } } -fn chomp_line_comment<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { +fn chomp_line_comment(buffer: &[u8]) -> Result<&str, Progress> { if let Some(b'#') = buffer.get(0) { if (&buffer[1..]).starts_with(b"# ") { // this is a doc comment, not a line comment @@ -257,11 +255,11 @@ fn chomp_line_comment<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> { /// Advance the parser while also indenting as appropriate. /// This assumes we are only advancing with spaces, since they can indent. -fn advance_spaces_e<'a, TE, E>( - state: State<'a>, +fn advance_spaces_e( + state: State, spaces: usize, to_error: TE, -) -> Result, (Progress, E, State<'a>)> +) -> Result where TE: Fn(Row, Col) -> E, { @@ -424,7 +422,7 @@ fn eat_spaces<'a>( } } - return Good { + Good { row, col, bytes, @@ -498,7 +496,7 @@ fn eat_line_comment<'a>( } } - return Good { + Good { row, col, bytes, diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 793c6bfdb5..b806305b07 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -1057,7 +1057,7 @@ where move |arena: &'a Bump, state: State<'a>| { // We have to clone this because if the optional parser fails, // we need to revert back to the original state. - let original_state = state.clone(); + let original_state = state; match parser.parse(arena, state) { Ok((progress, out1, state)) => Ok((progress, Some(out1), state)), @@ -1686,7 +1686,7 @@ where Error: 'a, { move |arena: &'a Bump, state: State<'a>| { - let old_state = state.clone(); + let old_state = state; match parser.parse(arena, state) { Ok((_, a, s1)) => Ok((NoProgress, a, s1)), From fd5ab353db0fad22addf92712d41775977716490 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 12 Mar 2021 03:44:03 +0100 Subject: [PATCH 74/74] clippy --- compiler/parse/src/blankspace.rs | 4 ++-- compiler/parse/src/expr.rs | 2 +- compiler/parse/src/ident.rs | 8 ++++---- compiler/parse/src/pattern.rs | 2 +- compiler/reporting/src/error/canonicalize.rs | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 03ae66d3e5..8fd5dac813 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -427,7 +427,7 @@ fn eat_spaces<'a>( col, bytes, comments_and_newlines, - }; + } } fn eat_line_comment<'a>( @@ -501,5 +501,5 @@ fn eat_line_comment<'a>( col, bytes, comments_and_newlines, - }; + } } diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 8c7da493b5..88475f39e3 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -509,7 +509,7 @@ fn parse_expr_help<'a>( ] .parse(arena, state)?; - let initial = state.clone(); + let initial = state; match space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state) { Err((_, _, state)) => Ok((MadeProgress, loc_expr1.value, state)), diff --git a/compiler/parse/src/ident.rs b/compiler/parse/src/ident.rs index a22c2348e2..21c55123f1 100644 --- a/compiler/parse/src/ident.rs +++ b/compiler/parse/src/ident.rs @@ -142,7 +142,7 @@ pub fn parse_ident_help<'a>( arena: &'a Bump, state: State<'a>, ) -> ParseResult<'a, Ident<'a>, EExpr<'a>> { - let initial = state.clone(); + let initial = state; match parse_ident_help_help(arena, state) { Ok((progress, ident, state)) => { @@ -192,7 +192,7 @@ fn malformed_identifier<'a>( } /// skip forward to the next non-identifier character -pub fn chomp_malformed<'a>(bytes: &'a [u8]) -> usize { +pub fn chomp_malformed(bytes: &[u8]) -> usize { use encode_unicode::CharExt; let mut chomped = 0; while let Ok((ch, width)) = char::from_utf8_slice_start(&bytes[chomped..]) { @@ -427,7 +427,7 @@ fn chomp_identifier_chain<'a>( } } -fn chomp_module_chain<'a>(buffer: &'a [u8]) -> Result { +fn chomp_module_chain(buffer: &[u8]) -> Result { let mut chomped = 0; while let Some(b'.') = buffer.get(chomped) { @@ -463,7 +463,7 @@ pub fn concrete_type<'a>() -> impl Parser<'a, (&'a str, &'a str), ()> { } // parse a type name like `Result` or `Result.Result` -fn chomp_concrete_type<'a>(buffer: &'a [u8]) -> Result<(&'a str, &'a str, usize), Progress> { +fn chomp_concrete_type(buffer: &[u8]) -> Result<(&str, &str, usize), Progress> { let first = crate::ident::chomp_uppercase_part(buffer)?; if let Some(b'.') = buffer.get(first.len()) { diff --git a/compiler/parse/src/pattern.rs b/compiler/parse/src/pattern.rs index b27875f9ad..50883f5352 100644 --- a/compiler/parse/src/pattern.rs +++ b/compiler/parse/src/pattern.rs @@ -169,7 +169,7 @@ fn loc_ident_pattern_help<'a>( can_have_arguments: bool, ) -> impl Parser<'a, Located>, EPattern<'a>> { move |arena: &'a Bump, state: State<'a>| { - let original_state = state.clone(); + let original_state = state; let (_, loc_ident, state) = specialize(|_, r, c| EPattern::Start(r, c), loc!(parse_ident_help)) diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index dd380e4866..9ccd46c4c0 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -617,13 +617,13 @@ fn what_is_next<'a>(source_lines: &'a [&'a str], row: Row, col: Col) -> BadIdent } } -fn till_whitespace(mut it: I) -> usize +fn till_whitespace(it: I) -> usize where I: Iterator, { let mut chomped = 0; - while let Some(c) = it.next() { + for c in it { if c.is_ascii_whitespace() || c == '#' { break; } else {