From 0fce67c7d73938c300faeac985fe42b3702c5619 Mon Sep 17 00:00:00 2001 From: Borodinov Ilya Date: Tue, 3 Dec 2024 23:14:54 +0300 Subject: [PATCH] errors are now pretty --- Cargo.lock | 807 +++++++++++++++++++++++ Cargo.toml | 35 + crates/codegen/Cargo.toml | 2 + crates/codegen/src/lib.rs | 15 +- crates/parser/Cargo.toml | 3 +- crates/parser/src/error.rs | 179 +++++ crates/parser/src/lib.rs | 109 ++- src/diagnostics.rs | 33 + src/lib.rs | 40 ++ src/main.rs | 10 +- src/tests.rs | 15 + crates/parser/src/main.rs => tests/1.cog | 7 +- 12 files changed, 1178 insertions(+), 77 deletions(-) create mode 100644 crates/parser/src/error.rs create mode 100644 src/diagnostics.rs create mode 100644 src/lib.rs create mode 100644 src/tests.rs rename crates/parser/src/main.rs => tests/1.cog (64%) diff --git a/Cargo.lock b/Cargo.lock index 65d2e31..5fa3763 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,120 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "ariadne" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31beedec3ce83ae6da3a79592b3d8d7afd146a5b15bb9bb940279aced60faa89" +dependencies = [ + "unicode-width", + "yansi", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cogs" +version = "0.1.0" +dependencies = [ + "ariadne", + "cogs_ast", + "cogs_codegen", + "cogs_parser", + "color-eyre", + "eyre", + "insta", + "proptest", + "tracing", + "tracing-error", + "tracing-subscriber", +] + [[package]] name = "cogs_ast" version = "0.1.0" @@ -13,6 +127,10 @@ version = "0.1.0" [[package]] name = "cogs_codegen" version = "0.1.0" +dependencies = [ + "cogs_ast", + "eyre", +] [[package]] name = "cogs_parser" @@ -20,6 +138,164 @@ version = "0.1.0" dependencies = [ "cogs_ast", "nom", + "tracing", +] + +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "insta" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "similar", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.167" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", ] [[package]] @@ -34,6 +310,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + [[package]] name = "nom" version = "7.1.3" @@ -43,3 +328,525 @@ dependencies = [ "memchr", "minimal-lexical", ] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.8.5", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 39fb07a..a676895 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,38 @@ members = [ "integrations/axum" ] resolver = "2" + +[workspace.dependencies] +eyre = "0.6" +tracing = "0.1" + +# crates/ +cogs_ast = { path = "crates/ast" } +cogs_codegen = { path = "crates/codegen" } +cogs_parser = { path = "crates/parser" } + +[package] +name = "cogs" +version = "0.1.0" +edition = "2021" +description = "Rust code blocks in HTML" + +[dependencies] +# errors yay +color-eyre = "0.6" +eyre.workspace = true +tracing.workspace = true +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing-error = "0.2" + +cogs_ast.workspace = true +cogs_codegen.workspace = true +cogs_parser.workspace = true +proptest = "1.5.0" +insta = "1.41.1" +ariadne = "0.5.0" + +# Test optimization +[profile.dev.package] +insta.opt-level = 3 +similar.opt-level = 3 diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml index e4f8fab..647fcaf 100644 --- a/crates/codegen/Cargo.toml +++ b/crates/codegen/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] +cogs_ast = { path = "../ast" } +eyre.workspace = true diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index b93cf3f..141eb8d 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -1,14 +1,5 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +extern crate cogs_ast as ast; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +pub fn generate(ast: &ast::Component) -> eyre::Result { + Ok(format!("not yet implemented")) } diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 78a4b88..a69bd71 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -5,4 +5,5 @@ edition = "2021" [dependencies] nom = "7.1.3" -cogs_ast.path = "../ast" \ No newline at end of file +cogs_ast.path = "../ast" +tracing.workspace = true diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs new file mode 100644 index 0000000..c5c9139 --- /dev/null +++ b/crates/parser/src/error.rs @@ -0,0 +1,179 @@ +use std::{borrow::Cow, fmt, ops::Range}; + +use nom::{ + error::{ContextError, FromExternalError, ParseError}, + Err, +}; + +#[derive(Debug)] +pub struct Error { + pub errors: Vec<(I, ErrorKind)>, + pub message: Option, + pub notes: Vec, + pub help: Option, +} + +impl Default for Error { + fn default() -> Self { + Self { + errors: Vec::new(), + message: None, + notes: Vec::new(), + help: None, + } + } +} + +impl Error { + pub fn single(input: I, error: ErrorKind) -> Self { + Self { + errors: vec![(input, error)], + ..Default::default() + } + } + + pub fn eof(input: I) -> Err { + Err::Error(Self::single( + input, + ErrorKind::Nom(nom::error::ErrorKind::Eof), + )) + } + + pub fn make_custom(input: I, message: impl Into>) -> Self { + let cow: Cow<'static, str> = message.into(); + Self::single(input, ErrorKind::Custom(cow.clone())).with_message(cow.to_string()) + } + + pub fn with_message(mut self, message: impl ToString) -> Self { + self.message = Some(message.to_string()); + self + } + + pub fn with_note(mut self, note: impl ToString) -> Self { + self.notes.push(note.to_string()); + self + } + + pub fn with_help(mut self, help: impl ToString) -> Self { + self.help = Some(help.to_string()); + self + } + + pub fn clear_message(mut self) -> Self { + self.message = None; + self + } + + pub fn custom(input: I, message: impl Into>) -> Err { + Err::Error(Self::make_custom(input, message)) + } + + pub fn custom_failure(input: I, message: impl Into>) -> Err { + Err::Failure(Self::make_custom(input, message)) + } +} + +#[derive(Debug)] +pub struct ReportInfo { + pub message: Option, + pub notes: Vec, + pub help: Option, +} + +impl<'a> Error<&'a str> { + pub fn resolve_spans( + self, + main: &'a str, + ) -> ( + impl Iterator, ErrorKind)> + use<'a>, + ReportInfo, + ) { + // what in the hellspawn is use<'a> + ( + self.errors.into_iter().map(|(input, kind)| { + let main_ptr = main.as_ptr() as usize; + let input_ptr = input.as_ptr() as usize; + let start = input_ptr.abs_diff(main_ptr); + let end = start + input.len(); + (start..end, kind) + }), + ReportInfo { + message: self.message, + notes: self.notes, + help: self.help, + }, + ) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (input, error) in &self.errors { + match error { + ErrorKind::Nom(e) => writeln!(f, "{e:?} at:\n{input}")?, + ErrorKind::Char(c) => writeln!(f, "expected '{c}' at:\n{input}")?, + ErrorKind::Context(s) => writeln!(f, "in {s}, at:\n{input}")?, + ErrorKind::Custom(s) => writeln!(f, "{s} at:\n{input}")?, + } + } + Ok(()) + } +} + +impl std::error::Error for Error {} + +impl ContextError for Error { + fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self { + other.errors.push((input, ErrorKind::Context(ctx))); + other + } +} + +impl FromExternalError for Error { + fn from_external_error(input: I, _kind: nom::error::ErrorKind, e: E) -> Self { + Self::single(input, ErrorKind::Custom(Cow::Owned(e.to_string()))) + } +} + +impl ParseError for Error { + fn from_error_kind(input: I, kind: nom::error::ErrorKind) -> Self { + Self::single(input, ErrorKind::Nom(kind)) + } + + fn or(mut self, other: Self) -> Self { + self.errors.extend(other.errors); + self.notes.extend(other.notes); + if let Some(message) = other.message { + if self.message.is_none() { + self.message = Some(message); + } + } + + if let Some(help) = other.help { + if self.help.is_none() { + self.help = Some(help); + } + } + + self + } + + fn append(input: I, kind: nom::error::ErrorKind, mut other: Self) -> Self { + other.errors.push((input, ErrorKind::Nom(kind))); + other + } +} + +#[derive(Debug)] +pub enum ErrorKind { + Nom(nom::error::ErrorKind), + Char(char), + Context(&'static str), + Custom(Cow<'static, str>), +} + +impl ErrorKind { + pub fn custom(message: impl Into>) -> Self { + Self::Custom(message.into()) + } +} diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index b4f99e3..a83e84f 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -1,8 +1,24 @@ use nom::{ - branch::alt, bytes::complete::{is_not, tag, take_while1}, character::complete::{char, multispace0, one_of, space0, space1}, combinator::{opt, peek}, error::{Error, ErrorKind}, multi::{many0, many1, separated_list0}, sequence::{delimited, pair, preceded, terminated, tuple}, Err, IResult, InputLength + branch::alt, + bytes::complete::{is_not, tag, take_while1}, + character::complete::{char, multispace0, one_of, space0, space1}, + combinator::{opt, peek}, + error::context, + multi::{many0, many1, separated_list0}, + sequence::{delimited, pair, preceded, terminated, tuple}, + InputLength, }; +use tracing::debug; + +type IResult = nom::IResult>; +use error::Error; use cogs_ast::{Attribute, CodeBlock, Component, Element, HtmlTag}; +// reexport for cogs crate +#[doc(hidden)] +pub use nom; + +pub mod error; pub fn parse_cog(input: &str) -> IResult<&str, Component> { let (input, elements) = parse_consecutive_proper_elements(input)?; @@ -18,7 +34,7 @@ pub fn parse_consecutive_proper_elements(input: &str) -> IResult<&str, Vec IResult<&str, Element> { let (input, _) = multispace0(input)?; - alt((parse_html, parse_code_block))(input) + alt((context("html", parse_html), context("code block", parse_code_block)))(input) } fn parse_proper_element(input: &str) -> IResult<&str, Element> { @@ -28,7 +44,6 @@ fn parse_proper_element(input: &str) -> IResult<&str, Element> { res } - fn is_valid_tag_name_char(c: char) -> bool { c.is_alphanumeric() || c == '-' } @@ -53,7 +68,7 @@ fn parse_attribute(input: &str) -> IResult<&str, Attribute> { let (input, key) = take_while1(is_valid_attr_char)(input)?; let (input, value) = opt(preceded( tuple((tag("="), space0)), - delimited(tag("\""), is_not("\""), tag("\"")) + delimited(tag("\""), is_not("\""), tag("\"")), ))(input)?; let mut resulting_value = None; @@ -71,38 +86,32 @@ fn parse_attribute(input: &str) -> IResult<&str, Attribute> { )) } - fn parse_attributes(input: &str) -> IResult<&str, Vec> { - let (input, attrs) = separated_list0( - pair(alt((char(','), char(' '))), space0), - parse_attribute, - )(input)?; + let (input, attrs) = + separated_list0(pair(alt((char(','), char(' '))), space0), parse_attribute)(input)?; let (input, _) = space0(input)?; // dump any trailing spaces // dbg!(&attrs); - Ok(( - input, - attrs - )) + Ok((input, attrs)) } fn parse_inside_html_opening_tag(input: &str) -> IResult<&str, HtmlTag> { let (input, tag) = parse_tag_name(input)?; // dbg!(&tag); let (input, _) = space0(input)?; - let (input, attributes) = parse_attributes(input)?; + let (input, attributes) = context("html attributes", parse_attributes)(input)?; // dbg!(&attributes); Ok(( input, - HtmlTag{ + HtmlTag { tag: tag.to_string(), attributes, - content: Vec::new() - } - )) + content: Vec::new(), + }, + )) } fn parse_html_opening_tag(input: &str) -> IResult<&str, HtmlTag> { @@ -110,39 +119,36 @@ fn parse_html_opening_tag(input: &str) -> IResult<&str, HtmlTag> { // dbg!(&tag); - Ok(( - input, - tag - )) + Ok((input, tag)) } fn parse_html_closing_tag(input: &str) -> IResult<&str, &str> { - let (input, tag) = delimited(tag("'))(input)?; + let (input, tag) = delimited( + tag("'), + )(input)?; - Ok(( - input, - tag - )) + Ok((input, tag)) } - fn parse_text(input: &str) -> IResult<&str, Element> { // dbg!(&input); let mut index = 0; while index < input.len() { let current_slice = &input[index..]; - if peek(parse_element)(current_slice).is_ok() { // dbg!(¤t_slice); break; } - - if peek(parse_html_opening_tag)(current_slice).is_ok() || - peek(parse_html_closing_tag)(current_slice).is_ok() || - peek::<_, _, Error<&str>, _>(char('}'))(current_slice).is_ok(){ - // dbg!(¤t_slice); - break; // Stop if any of these parsers match + + if peek(parse_html_opening_tag)(current_slice).is_ok() + || peek(parse_html_closing_tag)(current_slice).is_ok() + || peek::<_, _, Error<&str>, _>(char('}'))(current_slice).is_ok() + { + // dbg!(¤t_slice); + break; // Stop if any of these parsers match } index += 1; // Increment to check the next character @@ -150,7 +156,7 @@ fn parse_text(input: &str) -> IResult<&str, Element> { if input[0..index].is_empty() { // dbg!(&input[0..index]); - return Err(Err::Error(Error::new(input, ErrorKind::Eof))); + return Err(Error::eof(input)); } // dbg!(&input[0..index]); @@ -163,10 +169,7 @@ fn parse_html_contents(input: &str) -> IResult<&str, Vec> { // dbg!(&out); - Ok(( - input, - out - )) + Ok((input, out)) } fn parse_html(input: &str) -> IResult<&str, Element> { @@ -177,14 +180,13 @@ fn parse_html(input: &str) -> IResult<&str, Element> { let (input, close_name) = parse_html_closing_tag(input)?; if htag.tag != close_name { - return Err(Err::Failure(Error::new(input, ErrorKind::Fail))); // Is there a way to give a custom error message? + return Err(Error::custom_failure( + input, + format!("expected closing tag ``, got ``", htag.tag, close_name), + )); } - - Ok(( - input, - Element::Html(htag) - )) + Ok((input, Element::Html(htag))) } /* @@ -199,21 +201,16 @@ fn parse_code_until_interrupted(input: &str) -> IResult<&str, Element> { */ fn parse_inside_code_block(input: &str) -> IResult<&str, Vec> { - println!("Attempting inside code block {input}"); + debug!("Attempting inside code block {input}"); let (input, elems) = parse_consecutive_proper_elements(input)?; dbg!(&elems); - Ok(( - input, - elems - )) - + Ok((input, elems)) } - fn parse_code_block(input: &str) -> IResult<&str, Element> { if input.chars().nth(0) == Some('{') { - println!("Attempting code block on {input}"); + debug!("Attempting code block on {input}"); } let (input, content) = delimited( char('{'), @@ -225,7 +222,7 @@ fn parse_code_block(input: &str) -> IResult<&str, Element> { input, Element::Block(CodeBlock { // is_async: is_async.unwrap_or(false), - content: content + content, }), )) -} \ No newline at end of file +} diff --git a/src/diagnostics.rs b/src/diagnostics.rs new file mode 100644 index 0000000..1e44f30 --- /dev/null +++ b/src/diagnostics.rs @@ -0,0 +1,33 @@ +use ariadne::{Label, Source}; +use cogs_parser::error::{Error, ErrorKind}; +use std::{ops::Range, sync::Arc}; + +type Span = Range; +type SpanWithFile = (Arc, Span); + +fn add_error(builder: &mut ariadne::ReportBuilder<'static, SpanWithFile>, span: Span, kind: ErrorKind, file: Arc) { + let label = Label::new((file, span)); + builder.add_label(match kind { + ErrorKind::Nom(e) => label.with_message(e.description()), + ErrorKind::Char(c) => label.with_message(format!("expected '{c}'")), + ErrorKind::Context(s) => label.with_message(format!("while parsing {s}")), + ErrorKind::Custom(s) => label.with_message(s), + }); +} + +pub fn nom_diagnostic<'a>(main: &'a str, error: Error<&'a str>, file: &str) { + let file = Arc::::from(file); + let (mut iter, info) = error.resolve_spans(main); + let Some((span, kind)) = iter.next() else { + return; + }; + let mut report = ariadne::Report::build(ariadne::ReportKind::Error, (file.clone(), span.clone())); + if let Some(message) = info.message { + report.set_message(message); + } + add_error(&mut report, span, kind, file.clone()); + for (span, kind) in iter { + add_error(&mut report, span, kind, file.clone()); + } + let _ = report.finish().print((file, Source::from(main))); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7607a8f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,40 @@ +mod diagnostics; +#[cfg(test)] +mod tests; + +use tracing::level_filters::LevelFilter; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +#[doc(hidden)] +pub fn parse_cog(input: String, file: &str) -> eyre::Result { + use cogs_parser::nom::Finish; + + match cogs_parser::parse_cog(&input).finish() { + Ok((leftover, ast)) => { + if leftover.is_empty() { + Ok(ast) + } else { + Err(eyre::eyre!("Not all input parsed, leftover: {leftover}")) + } + } + Err(error) => { + diagnostics::nom_diagnostic(&input, error, file); + Err(eyre::Report::msg("parsing failed")) + } + } +} + +#[doc(hidden)] +pub fn init_tracing() -> eyre::Result<()> { + color_eyre::install()?; + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(), + ) + .with(tracing_error::ErrorLayer::default()) + .with(tracing_subscriber::fmt::layer()) + .try_init()?; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..e8af338 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,9 @@ -fn main() { - println!("Hello, world!"); +use cogs::*; + +fn main() -> eyre::Result<()> { + init_tracing()?; + let ast = parse_cog(include_str!("../tests/1.cog").to_owned(), "tests/1.cog")?; + println!("{ast:#?}"); + + Ok(()) } diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..8a11f2c --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,15 @@ +use proptest::prelude::*; + +proptest! { + // Update when new tests are added + #[test] + fn test_cogs(test_index in 1..=1) { + crate::init_tracing().unwrap(); + let file = std::fs::read_to_string(format!("tests/{}.cog", test_index)).unwrap(); + let ast = crate::parse_cog(file).unwrap(); + insta::with_settings!({ snapshot_suffix => format!("{test_index}") }, { + insta::assert_debug_snapshot!(ast); + insta::assert_snapshot!(cogs_codegen::generate(&ast).unwrap()); + }); + } +} diff --git a/crates/parser/src/main.rs b/tests/1.cog similarity index 64% rename from crates/parser/src/main.rs rename to tests/1.cog index b514911..73ef184 100644 --- a/crates/parser/src/main.rs +++ b/tests/1.cog @@ -1,7 +1,3 @@ -use cogs_parser::parse_cog; - -fn main() { - println!("{:#?}", parse_cog("

Yo.

Click this @@ -9,5 +5,4 @@ fn main() { println!(\"test\");

More Html

} -")); -} \ No newline at end of file +