--- a/Cargo.lock +++ /dev/null @@ -1,984 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - -[[package]] -name = "annotate-snippets" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36" -dependencies = [ - "unicode-width", - "yansi-term", -] - -[[package]] -name = "anstream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is-terminal", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" - -[[package]] -name = "anstyle-parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] -name = "anyhow" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" - -[[package]] -name = "async-trait" -version = "0.1.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base64" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "4.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eab9e8ceb9afdade1ab3f0fd8dbce5b1b2f468ad653baf10e771781b2b67b73" -dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", -] - -[[package]] -name = "clap_builder" -version = "4.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2763db829349bf00cfc06251268865ed4363b93a943174f638daf3ecdba2cd" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_complete" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce" -dependencies = [ - "clap", -] - -[[package]] -name = "clap_derive" -version = "4.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "clap_lex" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "jrsonnet" -version = "0.5.0-pre95" -dependencies = [ - "clap", - "clap_complete", - "jrsonnet-cli", - "jrsonnet-evaluator", - "jrsonnet-gcmodule", - "jrsonnet-parser", - "mimallocator", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "jrsonnet-cli" -version = "0.5.0-pre95" -dependencies = [ - "clap", - "jrsonnet-evaluator", - "jrsonnet-gcmodule", - "jrsonnet-parser", - "jrsonnet-stdlib", -] - -[[package]] -name = "jrsonnet-evaluator" -version = "0.5.0-pre95" -dependencies = [ - "annotate-snippets", - "anyhow", - "async-trait", - "bincode", - "derivative", - "hashbrown 0.13.2", - "jrsonnet-gcmodule", - "jrsonnet-interner", - "jrsonnet-macros", - "jrsonnet-parser", - "jrsonnet-types", - "num-bigint", - "pathdiff", - "rustc-hash", - "serde", - "static_assertions", - "strsim", - "thiserror", -] - -[[package]] -name = "jrsonnet-gcmodule" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11fb98940a7f8b419619e98ccbf2e094671a5fdd0e277f05acd373071186d57" -dependencies = [ - "jrsonnet-gcmodule-derive", - "parking_lot", -] - -[[package]] -name = "jrsonnet-gcmodule-derive" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bee774b7ba86fc86ee84482cd6732aa860ae3559f9827c65efd75c51e66ac76" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "jrsonnet-interner" -version = "0.5.0-pre95" -dependencies = [ - "hashbrown 0.13.2", - "jrsonnet-gcmodule", - "rustc-hash", - "serde", - "structdump", -] - -[[package]] -name = "jrsonnet-macros" -version = "0.5.0-pre95" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "jrsonnet-parser" -version = "0.5.0-pre95" -dependencies = [ - "jrsonnet-gcmodule", - "jrsonnet-interner", - "peg", - "serde", - "static_assertions", - "structdump", -] - -[[package]] -name = "jrsonnet-stdlib" -version = "0.5.0-pre95" -dependencies = [ - "base64", - "bincode", - "jrsonnet-evaluator", - "jrsonnet-gcmodule", - "jrsonnet-macros", - "jrsonnet-parser", - "md5", - "num-bigint", - "serde", - "serde_json", - "serde_yaml_with_quirks", - "sha1", - "sha2", - "sha3", - "structdump", -] - -[[package]] -name = "jrsonnet-types" -version = "0.5.0-pre95" -dependencies = [ - "jrsonnet-gcmodule", - "peg", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libjsonnet" -version = "0.5.0-pre95" -dependencies = [ - "jrsonnet-evaluator", - "jrsonnet-gcmodule", - "jrsonnet-parser", - "jrsonnet-stdlib", -] - -[[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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "mimalloc-sys" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa3cefb626f6ae3d0b2f71c5378c89d2b1d4d7bc246b0ca9a7ee61a4daad291" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "mimallocator" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d44fe4ebf6b538fcf39d9975c2b90bb3232d1ba8e8bffeacd004f27b20c577a" -dependencies = [ - "mimalloc-sys", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "peg" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07f2cafdc3babeebc087e499118343442b742cc7c31b4d054682cc598508554" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a90084dc05cf0428428e3d12399f39faad19b0909f64fb9170c9fdd6d9cd49b" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739" - -[[package]] -name = "proc-macro2" -version = "1.0.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92de25114670a878b1261c79c9f8f729fb97e95bac93f6312f583c60dd6a1dfe" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustix" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" -dependencies = [ - "bitflags 2.3.3", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "serde" -version = "1.0.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "serde_json" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml_with_quirks" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c5983eba86eae2d0058c35fb1065ccffb23af7f8965871069269088098321a" -dependencies = [ - "indexmap", - "ryu", - "serde", - "yaml-rust", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "structdump" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0570327507bf281d8a6e6b0d4c082b12cb6bcee27efce755aa5efacd44076c1" -dependencies = [ - "proc-macro2", - "quote", - "structdump-derive", -] - -[[package]] -name = "structdump-derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cc0b59cfa11f1bceda09a9a7e37e6a6c3138575fd24ade8aa9af6d09aedf28" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tests" -version = "0.1.0" -dependencies = [ - "jrsonnet-evaluator", - "jrsonnet-gcmodule", - "jrsonnet-stdlib", - "serde", -] - -[[package]] -name = "thiserror" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[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.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "yansi-term" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" -dependencies = [ - "winapi", -] --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] package.version = "0.5.0-pre95" package.repository = "https://github.com/CertainLach/jrsonnet" -members = ["crates/*", "bindings/jsonnet", "cmds/jrsonnet", "tests"] +members = ["crates/*", "bindings/jsonnet", "cmds/*", "tests"] default-members = ["cmds/jrsonnet"] resolver = "2" --- /dev/null +++ b/crates/jrsonnet-rowan-parser/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "jrsonnet-rowan-parser" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.51" +backtrace = "0.3.63" +drop_bomb = "0.1.5" +indoc = "1.0.3" +logos = "0.12.0" +miette = { version = "4.2.1", features = ["fancy"] } +rowan = "0.15.0" +text-size = "1.1.0" +thiserror = "1.0.30" + +[dev-dependencies] +backtrace = "0.3.63" +indoc = "1.0.3" +insta = "1.10.0" --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/binary.rs @@ -0,0 +1,47 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BinaryOperator { + Mul, + Div, + Mod, + Plus, + Minus, + ShiftLeft, + ShiftRight, + LessThan, + GreaterThan, + LessThanOrEqual, + GreaterThanOrEqual, + Equal, + NotEqual, + BitAnd, + BitXor, + BitOr, + And, + Or, + In, + ObjectApply, + Invalid, +} + +impl BinaryOperator { + pub fn binding_power(&self) -> (u8, u8) { + match self { + Self::ObjectApply => (22, 23), + Self::Mul | Self::Div | Self::Mod => (20, 21), + Self::Plus | Self::Minus => (18, 19), + Self::ShiftLeft | Self::ShiftRight => (16, 17), + Self::LessThan + | Self::GreaterThan + | Self::LessThanOrEqual + | Self::GreaterThanOrEqual + | Self::In => (14, 15), + Self::Equal | Self::NotEqual => (12, 13), + Self::BitAnd => (10, 11), + Self::BitXor => (8, 9), + Self::BitOr => (6, 7), + Self::And => (4, 5), + Self::Or => (2, 3), + Self::Invalid => (0, 1), + } + } +} --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/event.rs @@ -0,0 +1,107 @@ +use std::mem; + +use rowan::{GreenNode, GreenNodeBuilder, Language}; + +use crate::{ + lex::{Lang, Lexeme, SyntaxKind}, + parser::{Parse, SyntaxError}, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Event { + Start { + kind: SyntaxKind, + forward_parent: Option, + }, + Token, + Finish, + Placeholder, + Error(SyntaxError), +} + +pub(super) struct Sink<'i> { + pub builder: GreenNodeBuilder<'static>, + lexemes: &'i [Lexeme<'i>], + offset: usize, + events: Vec, + pub errors: Vec, +} + +impl<'i> Sink<'i> { + pub(super) fn new(events: Vec, lexemes: &'i [Lexeme<'i>]) -> Self { + Self { + builder: GreenNodeBuilder::new(), + lexemes, + offset: 0, + events, + errors: vec![], + } + } + + pub(super) fn finish(mut self) -> Parse { + for idx in 0..self.events.len() { + match mem::replace(&mut self.events[idx], Event::Placeholder) { + Event::Start { + kind, + forward_parent, + } => { + let mut kinds = vec![kind]; + + let mut idx = idx; + let mut forward_parent = forward_parent; + + // Walk through the forward parent of the forward parent, and the forward parent + // of that, and of that, etc. until we reach a StartNode event without a forward + // parent. + while let Some(fp) = forward_parent { + idx += fp; + + forward_parent = if let Event::Start { + kind, + forward_parent, + } = mem::replace(&mut self.events[idx], Event::Placeholder) + { + kinds.push(kind); + forward_parent + } else { + unreachable!() + }; + } + + for kind in kinds.into_iter().rev() { + self.builder.start_node(Lang::kind_to_raw(kind)); + } + } + Event::Token => self.token(), + Event::Finish => { + self.builder.finish_node(); + } + Event::Placeholder => {} + Event::Error(e) => { + self.errors.push(e); + } + } + self.skip_whitespace(); + } + + Parse { + green_node: self.builder.finish(), + errors: self.errors, + } + } + fn token(&mut self) { + let lexeme = self.lexemes[self.offset]; + self.builder + .token(Lang::kind_to_raw(lexeme.kind), lexeme.text); + self.offset += 1; + } + fn skip_whitespace(&mut self) { + while let Some(lexeme) = self.lexemes.get(self.offset) { + if !lexeme.kind.is_trivia() { + break; + } + + self.token(); + } + } +} --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/lex.rs @@ -0,0 +1,315 @@ +use crate::string_block::lex_str_block_test; +use core::ops::Range; +use logos::Logos; +use rowan::{Checkpoint, TextRange, TextSize}; +use std::{convert::TryFrom, iter::Peekable}; + +#[derive(Logos, Debug, PartialEq, Hash, Eq, PartialOrd, Ord, Clone, Copy)] +#[repr(u16)] +pub enum SyntaxKind { + #[token("assert")] + KeywordAssert = 0, + + #[token("else")] + KeywordElse, + + #[token("error")] + KeywordError, + + #[token("false")] + KeywordFalse, + + #[token("for")] + KeywordFor, + + #[token("function")] + KeywordFunction, + + #[token("if")] + KeywordIf, + + #[token("import")] + KeywordImport, + + #[token("importstr")] + KeywordImportStr, + + #[token("local")] + KeywordLocal, + + #[token("null")] + KeywordNull, + + #[token("tailstrict")] + KeywordTailStrict, + + #[token("then")] + KeywordThen, + + #[token("self")] + KeywordSelf, + + #[token("super")] + KeywordSuper, + + #[token("true")] + KeywordTrue, + + #[regex(r"[_a-zA-Z][_a-zA-Z0-9]*")] + Ident, + + #[regex(r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?")] + Number, + + #[regex(r"(?:0|[1-9][0-9]*)\.[^0-9]")] + ErrorNumJunkAfterDecimalPoint, + + #[regex(r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?[eE][^+\-0-9]")] + ErrorNumJunkAfterExponent, + + #[regex(r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?[eE][+-][^0-9]")] + ErrorNumJunkAfterExponentSign, + + #[token("{")] + SymbolLeftBrace, + + #[token("}")] + SymbolRightBrace, + + #[token("[")] + SymbolLeftBracket, + + #[token("]")] + SymbolRightBracket, + + #[token(",")] + SymbolComma, + + #[token(".")] + SymbolDot, + + #[token("(")] + LParen, + + #[token(")")] + RParen, + + #[token(";")] + SymbolSemi, + #[token(":")] + SymbolColon, + + #[token("$")] + SymbolDollar, + + #[token("*")] + OpMul, + #[token("/")] + OpDiv, + #[token("%")] + OpMod, + #[token("+")] + OpPlus, + #[token("-")] + OpMinus, + #[token("<<")] + OpShiftLeft, + #[token(">>")] + OpShiftRight, + #[token("<")] + OpLessThan, + #[token(">")] + OpGreaterThan, + #[token("<=")] + OpLessThanOrEqual, + #[token(">=")] + OpGreaterThanOrEqual, + #[token("==")] + OpEqual, + #[token("!=")] + OpNotEqual, + #[token("&")] + OpBitAnd, + #[token("^")] + OpBitXor, + #[token("|")] + OpBitOr, + #[token("&&")] + OpAnd, + #[token("||")] + OpOr, + #[token("in")] + OpIn, + #[token("!")] + OpNot, + #[token("~")] + OpBitNegate, + #[token("=")] + SymbolAssign, + + #[regex("\"(?s:[^\"\\\\]|\\\\.)*\"")] + StringDoubleQuoted, + + #[regex("'(?s:[^'\\\\]|\\\\.)*'")] + StringSingleQuoted, + + #[regex("@\"(?:[^\"]|\"\")*\"")] + StringDoubleVerbatim, + + #[regex("@'(?:[^']|'')*'")] + StringSingleVerbatim, + + #[regex(r"\|\|\|", lex_str_block_test)] + StringBlock, //(StringBlockToken), + + #[regex("\"(?s:[^\"\\\\]|\\\\.)*")] + ErrorStringDoubleQuotedUnterminated, + + #[regex("'(?s:[^'\\\\]|\\\\.)*")] + ErrorStringSingleQuotedUnterminated, + + #[regex("@\"(?:[^\"]|\"\")*")] + ErrorStringDoubleVerbatimUnterminated, + + #[regex("@'(?:[^']|'')*")] + ErrorStringSingleVerbatimUnterminated, + + #[regex("@[^\"'\\s]\\S+")] + ErrorStringMissingQuotes, + + #[token("/*/")] + ErrorCommentTooShort, + + #[regex(r"/\*([^*]|\*[^/])+")] + ErrorCommentUnterminated, + + #[regex(r"[ \t\n\r]+")] + Whitespace, + + #[regex(r"//[^\r\n]*(\r\n|\n)?")] + SingelLineSlashComment, + + #[regex(r"#[^\r\n]*(\r\n|\n)?")] + SingleLineHashComment, + + #[regex(r"/\*([^*]|\*[^/])*\*/")] + MultiLineComment, + + #[error] + Error, + + ErrorPositionalAfterNamed, + + Literal, + Expr, + Array, + ArrayElem, + Object, + Field, + + CompspecFor, + CompspecIf, + + Slice, + FieldAccess, + ObjectApply, + FunctionCall, + FunctionDef, + BodyDef, + + BinOp, + UnaryOp, + Local, + ExprError, + ExprAssert, + ExprImport, + + DefParam, + DefParams, + + DefArgs, + DefNamedArg, + DefPositionalArg, + + Parened, + + Root, +} + +impl SyntaxKind { + pub fn is_trivia(self) -> bool { + matches!( + self, + Self::Whitespace + | Self::MultiLineComment + | Self::SingelLineSlashComment + | Self::SingleLineHashComment + ) + } +} + +pub struct Lexer<'a> { + inner: logos::Lexer<'a, SyntaxKind>, +} + +impl<'a> Lexer<'a> { + pub fn new(input: &'a str) -> Self { + Self { + inner: SyntaxKind::lexer(input), + } + } +} + +impl<'a> Iterator for Lexer<'a> { + type Item = Lexeme<'a>; + + fn next(&mut self) -> Option { + let kind = self.inner.next()?; + let text = self.inner.slice(); + + Some(Self::Item { + kind, + text, + range: { + let Range { start, end } = self.inner.span(); + + TextRange::new( + TextSize::try_from(start).unwrap(), + TextSize::try_from(end).unwrap(), + ) + }, + }) + } +} + +#[derive(Clone, Copy)] +pub struct Lexeme<'i> { + pub kind: SyntaxKind, + pub text: &'i str, + pub range: TextRange, +} + +pub fn lex(input: &str) -> Vec> { + Lexer::new(input).collect() +} + +impl From for rowan::SyntaxKind { + fn from(kind: SyntaxKind) -> Self { + Self(kind as u16) + } +} + +use SyntaxKind::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Lang {} +impl rowan::Language for Lang { + type Kind = SyntaxKind; + fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { + assert!(raw.0 <= Root as u16); + unsafe { std::mem::transmute::(raw.0) } + } + fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { + kind.into() + } +} --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/lib.rs @@ -0,0 +1,139 @@ +#![deny(unused_must_use)] + +mod binary; +mod event; +mod lex; +mod marker; +mod parser; +mod string_block; +mod token_set; +mod unary; + +#[cfg(test)] +mod tests { + use miette::{Diagnostic, GraphicalReportHandler, LabeledSpan}; + use thiserror::Error; + + use crate::parser::parse; + + #[derive(Debug, Error)] + #[error("syntax error")] + struct MyDiagnostic { + code: String, + spans: Vec, + } + impl Diagnostic for MyDiagnostic { + fn code<'a>(&'a self) -> Option> { + None + } + + fn severity(&self) -> Option { + None + } + + fn help<'a>(&'a self) -> Option> { + None + } + + fn url<'a>(&'a self) -> Option> { + None + } + + fn source_code(&self) -> Option<&dyn miette::SourceCode> { + Some(&self.code) + } + + fn labels(&self) -> Option + '_>> { + Some(Box::new(self.spans.clone().into_iter())) + } + + fn related<'a>(&'a self) -> Option + 'a>> { + None + } + } + + fn process(text: &str) -> String { + use std::fmt::Write; + let mut out = String::new(); + let node = parse(text); + write!(out, "{:#?}", node.syntax()).unwrap(); + if !node.errors.is_empty() && !text.is_empty() { + writeln!(out, "===").unwrap(); + for err in &node.errors { + writeln!(out, "{:?}", err).unwrap(); + } + let diag = MyDiagnostic { + code: text.to_string(), + spans: node.errors.into_iter().map(|e| e.into()).collect(), + }; + + let handler = GraphicalReportHandler::new(); + + write!(out, "===").unwrap(); + handler.render_report(&mut out, &diag).unwrap(); + } + out + } + macro_rules! mk_test { + ($($name:ident => $test:expr)+) => {$( + #[test] + fn $name() { + let src = indoc::indoc!($test); + let result = process(&src); + insta::assert_snapshot!(stringify!($name), result, src); + + } + )+}; + } + mk_test!( + empty => r#" "# + function => r#" + function(a, b = 1) a + b + "# + function_error_no_value => r#" + function(a, b = ) a + b + "# + function_error_rparen => r#" + function(a, b + "# + function_error_body => r#" + function(a, b) + "# + local_novalue => r#" + local a = + "# + local_no_value_recovery => r#" + local a = + local b = 3; + 1 + "# + + array_comp => r#" + [a for a in [1, 2, 3]] + "# + array_comp_incompatible_with_multiple_elems => r#" + [a for a in [1, 2, 3], b] + "# + + no_rhs => r#" + a + + "# + no_lhs => r#" + + 2 + "# + no_operator => " + 2 2 + " + + named_before_positional => " + a(1, 2, b=4, 3, 5, k = 12, 6) + " + + wrong_field_end => " + { + a: 1; + b: 2; + } + " + ); +} --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/marker.rs @@ -0,0 +1,114 @@ +use drop_bomb::DropBomb; +use rowan::TextRange; + +use crate::{event::Event, lex::SyntaxKind, parser::Parser}; + +pub struct Ranger { + pub pos: usize, +} +impl Ranger { + pub fn finish(mut self, p: &Parser) -> FinishedRanger { + FinishedRanger { + start_token: self.pos, + end_token: self.pos.max(p.offset.saturating_sub(1)), + } + } +} + +pub struct FinishedRanger { + pub start_token: usize, + pub end_token: usize, +} +impl FinishedRanger { + pub fn had_error_since(&self, p: &Parser) -> bool { + p.last_error_token >= self.start_token + } +} + +#[must_use] +pub struct Marker { + pub start_event_idx: usize, + pub token: usize, + bomb: DropBomb, +} +impl Marker { + pub fn new(pos: usize, token: usize) -> Self { + Self { + start_event_idx: pos, + token, + bomb: DropBomb::new("marked dropped while not completed"), + } + } + pub fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker { + self.bomb.defuse(); + let event_at_pos = &mut p.events[self.start_event_idx]; + assert_eq!(*event_at_pos, Event::Placeholder); + + *event_at_pos = Event::Start { + kind, + forward_parent: None, + }; + + p.events.push(Event::Finish); + p.entered -= 1; + p.clear_outdated_hints(); + CompletedMarker { + start_event_idx: self.start_event_idx, + start_token: self.token, + end_token: self.token.max(p.offset.saturating_sub(1)), + } + } +} +pub struct CompletedMarker { + start_event_idx: usize, + pub start_token: usize, + pub end_token: usize, +} +impl CompletedMarker { + pub(super) fn precede(self, p: &mut Parser) -> Marker { + let mut new_m = p.start(); + new_m.token = self.start_token; + + if let Event::Start { + ref mut forward_parent, + .. + } = p.events[self.start_event_idx] + { + *forward_parent = Some(new_m.start_event_idx - self.start_event_idx); + } else { + unreachable!(); + } + + new_m + } +} + +pub trait AsRange { + fn as_range(&self, p: &Parser) -> TextRange; + fn end_token(&self) -> usize; +} + +impl AsRange for CompletedMarker { + fn as_range(&self, p: &Parser) -> TextRange { + TextRange::new( + p.start_of_token(self.start_token), + p.end_of_token(self.end_token), + ) + } + fn end_token(&self) -> usize { + self.end_token + } +} + +impl AsRange for FinishedRanger { + fn as_range(&self, p: &Parser) -> TextRange { + TextRange::new( + p.start_of_token(self.start_token), + p.end_of_token(self.end_token), + ) + } + + fn end_token(&self) -> usize { + self.end_token + } +} --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/parser.rs @@ -0,0 +1,809 @@ +use std::cell::Cell; +use std::fmt::Display; +use std::rc::Rc; + +use miette::Diagnostic; +use miette::LabeledSpan; +use miette::SourceOffset; +use miette::SourceSpan; +use rowan::GreenNode; + +use rowan::TextRange; +use rowan::TextSize; +use thiserror::Error; + +use crate::binary::BinaryOperator; +use crate::event::Event; +use crate::event::Sink; +use crate::lex::lex; +use crate::lex::Lang; +use crate::lex::Lexeme; +use crate::lex::SyntaxKind; +use crate::lex::SyntaxKind::*; +use crate::marker::AsRange; +use crate::marker::CompletedMarker; +use crate::marker::FinishedRanger; +use crate::marker::Marker; +use crate::marker::Ranger; +use crate::token_set::TokenSet; +use crate::unary::UnaryOperator; + +pub struct Parse { + pub green_node: GreenNode, + pub errors: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum ExpectedSyntax { + Named(&'static str), + Unnamed(SyntaxKind), +} +impl Display for ExpectedSyntax { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ExpectedSyntax::Named(n) => write!(f, "{}", n), + ExpectedSyntax::Unnamed(u) => write!(f, "{:?}", u), + } + } +} + +pub struct Parser<'i> { + lexemes: &'i [Lexeme<'i>], + pub offset: usize, + pub events: Vec, + pub entered: u32, + pub hints: Vec<(u32, TextRange, String)>, + pub last_error_token: usize, + expected_syntax: Option, + expected_syntax_tracking_state: Rc>, +} + +const DEFAULT_RECOVERY_SET: TokenSet = TokenSet::new(&[ + SymbolSemi, + RParen, + SymbolRightBracket, + SymbolRightBrace, + KeywordLocal, +]); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SyntaxError { + Unexpected { + expected: ExpectedSyntax, + found: SyntaxKind, + range: TextRange, + }, + Missing { + expected: ExpectedSyntax, + offset: TextSize, + }, + Custom { + error: String, + range: TextRange, + }, + Hint { + error: String, + range: TextRange, + }, +} + +impl Into for SyntaxError { + fn into(self) -> LabeledSpan { + match self { + SyntaxError::Unexpected { + expected, + found, + range, + } => LabeledSpan::new_with_span( + Some(format!("expected {}, found {:?}", expected, found)), + SourceSpan::new( + SourceOffset::from(usize::from(range.start())), + SourceOffset::from(usize::from(range.end() - range.start())), + ), + ), + SyntaxError::Missing { expected, offset } => LabeledSpan::new_with_span( + Some(format!("missing {}", expected)), + SourceSpan::new( + SourceOffset::from(usize::from(offset)), + SourceOffset::from(0), + ), + ), + SyntaxError::Custom { error, range } | SyntaxError::Hint { error, range } => { + LabeledSpan::new_with_span( + Some(format!("{}", error)), + SourceSpan::new( + SourceOffset::from(usize::from(range.start())), + SourceOffset::from(usize::from(range.end() - range.start())), + ), + ) + } + } + } +} + +impl<'i> Parser<'i> { + fn new(lexemes: &'i [Lexeme<'i>]) -> Self { + Self { + lexemes, + offset: 0, + events: vec![], + entered: 0, + last_error_token: 0, + hints: vec![], + expected_syntax: None, + expected_syntax_tracking_state: Rc::new(Cell::new( + ExpectedSyntaxTrackingState::Unnamed, + )), + } + } + pub fn clear_outdated_hints(&mut self) { + let amount = self + .hints + .iter() + .rev() + .take_while(|h| h.0 > self.entered) + .count(); + self.hints.truncate(self.hints.len() - amount) + } + fn clear_expected_syntaxes(&mut self) { + self.expected_syntax = None; + self.expected_syntax_tracking_state + .set(ExpectedSyntaxTrackingState::Unnamed); + } + pub fn start(&mut self) -> Marker { + let start_event_idx = self.events.len(); + self.events.push(Event::Placeholder); + self.entered += 1; + Marker::new(start_event_idx, self.offset) + } + pub fn start_ranger(&mut self) -> Ranger { + let pos = self.offset; + Ranger { pos } + } + fn parse(mut self) -> Vec { + let m = self.start(); + expr(&mut self); + if !self.at_end() { + let ranger = self.start_ranger(); + + while self.peek().is_some() { + self.bump() + } + let end = ranger.finish(&self); + self.custom_error(end, "unexpected input after expression"); + } + m.complete(&mut self, Root); + + self.events + } + + pub(crate) fn expect(&mut self, kind: SyntaxKind) { + self.expect_with_recovery_set(kind, TokenSet::default()) + } + + pub(crate) fn expect_with_recovery_set(&mut self, kind: SyntaxKind, recovery_set: TokenSet) { + if self.at(kind) { + self.bump(); + } else { + self.error_with_recovery_set(recovery_set); + } + } + + pub(crate) fn expect_with_no_skip(&mut self, kind: SyntaxKind) { + if self.at(kind) { + self.bump(); + } else { + self.error_with_no_skip(); + } + } + pub(crate) fn last_token_range(&self) -> Option { + self.lexemes.last().map(|Lexeme { range, .. }| *range) + } + fn current_token(&self) -> Lexeme<'i> { + self.lexemes[self.offset] + } + fn previous_token(&mut self) -> Option> { + if self.offset == 0 { + return None; + } + let mut previous_token_idx = self.offset - 1; + while self + .lexemes + .get(previous_token_idx) + .map_or(false, |l| l.kind.is_trivia()) + && previous_token_idx != 0 + { + previous_token_idx -= 1; + } + + Some(self.lexemes[previous_token_idx]) + } + pub fn start_of_token(&self, mut idx: usize) -> TextSize { + while self.lexemes[idx].kind.is_trivia() { + idx += 1; + } + self.lexemes[idx].range.start() + } + pub fn end_of_token(&self, mut idx: usize) -> TextSize { + while self.lexemes[idx].kind.is_trivia() { + idx -= 1; + } + self.lexemes[idx].range.end() + } + pub(crate) fn custom_error(&mut self, marker: impl AsRange, error: impl AsRef) { + self.last_error_token = marker.end_token(); + self.events.push(Event::Error(SyntaxError::Custom { + error: error.as_ref().to_string(), + range: marker.as_range(self), + })); + } + pub(crate) fn error_with_recovery_set( + &mut self, + recovery_set: TokenSet, + ) -> Option { + self.error_with_recovery_set_no_default(recovery_set.union(DEFAULT_RECOVERY_SET)) + } + pub fn error_with_no_skip(&mut self) -> Option { + self.error_with_recovery_set_no_default(TokenSet::ALL) + } + + pub fn error_with_recovery_set_no_default( + &mut self, + recovery_set: TokenSet, + ) -> Option { + let expected_syntax = self.expected_syntax.take().unwrap(); + self.expected_syntax_tracking_state + .set(ExpectedSyntaxTrackingState::Unnamed); + + if self.at_end() || self.at_set(recovery_set) { + let range = self + .previous_token() + .map(|t| t.range) + .unwrap_or(TextRange::at(TextSize::from(0), TextSize::from(0))); + + self.events.push(Event::Error(SyntaxError::Missing { + expected: expected_syntax, + offset: range.end(), + })); + return None; + } + + let current_token = self.current_token(); + + self.events.push(Event::Error(SyntaxError::Unexpected { + expected: expected_syntax.clone(), + found: current_token.kind, + range: current_token.range, + })); + self.clear_expected_syntaxes(); + self.last_error_token = self.offset; + + let m = self.start(); + self.bump(); + Some(m.complete(self, SyntaxKind::Error)) + } + + fn bump(&mut self) { + self.skip_trivia(); + assert_ne!(self.offset, self.lexemes.len(), "already at end"); + self.events.push(Event::Token); + self.offset += 1; + self.clear_expected_syntaxes(); + } + fn peek(&mut self) -> Option { + self.skip_trivia(); + self.peek_raw() + } + pub fn peek_token(&mut self) -> Option<&Lexeme<'i>> { + self.skip_trivia(); + self.peek_token_raw() + } + fn skip_trivia(&mut self) { + while self.peek_raw().map(|c| c.is_trivia()).unwrap_or(false) { + self.offset += 1; + } + } + fn peek_raw(&mut self) -> Option { + self.lexemes.get(self.offset).map(|l| l.kind) + } + fn peek_token_raw(&mut self) -> Option<&Lexeme<'i>> { + self.lexemes.get(self.offset) + } + #[must_use] + pub(crate) fn expected_syntax_name(&mut self, name: &'static str) -> ExpectedSyntaxGuard { + self.expected_syntax_tracking_state + .set(ExpectedSyntaxTrackingState::Named); + self.expected_syntax = Some(ExpectedSyntax::Named(name)); + + ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state)) + } + pub fn at(&mut self, kind: SyntaxKind) -> bool { + if let ExpectedSyntaxTrackingState::Unnamed = self.expected_syntax_tracking_state.get() { + self.expected_syntax = Some(ExpectedSyntax::Unnamed(kind)); + } + self.peek() == Some(kind) + } + pub fn at_set(&mut self, set: TokenSet) -> bool { + self.peek().map_or(false, |k| set.contains(k)) + } + pub fn at_end(&mut self) -> bool { + self.peek().is_none() + } +} +pub(crate) struct ExpectedSyntaxGuard { + expected_syntax_tracking_state: Rc>, +} + +impl ExpectedSyntaxGuard { + fn new(expected_syntax_tracking_state: Rc>) -> Self { + Self { + expected_syntax_tracking_state, + } + } +} + +impl Drop for ExpectedSyntaxGuard { + fn drop(&mut self) { + self.expected_syntax_tracking_state + .set(ExpectedSyntaxTrackingState::Unnamed); + } +} + +#[derive(Debug, Clone, Copy)] +enum ExpectedSyntaxTrackingState { + Named, + Unnamed, +} +macro_rules! at_match { + ($p:ident { + $($r:ident => $e:expr,)* + _ => $else:expr $(,)? + }) => {{ + $( + if $p.at($r) {$e} else + )* { + $else + } + }} +} + +fn expr(p: &mut Parser) { + expr_binding_power(p, 0); +} +fn expr_binding_power(p: &mut Parser, minimum_binding_power: u8) -> Option { + let mut lhs = lhs(p)?; + + loop { + let op = at_match!(p { + OpMul => BinaryOperator::Mul, + OpDiv => BinaryOperator::Div, + OpMod => BinaryOperator::Mod, + OpPlus => BinaryOperator::Plus, + OpMinus => BinaryOperator::Minus, + OpShiftLeft => BinaryOperator::ShiftLeft, + OpShiftRight => BinaryOperator::ShiftRight, + OpLessThan => BinaryOperator::LessThan, + OpGreaterThan => BinaryOperator::GreaterThan, + OpLessThanOrEqual => BinaryOperator::LessThanOrEqual, + OpGreaterThanOrEqual => BinaryOperator::GreaterThanOrEqual, + OpEqual => BinaryOperator::Equal, + OpNotEqual => BinaryOperator::NotEqual, + OpBitAnd => BinaryOperator::BitAnd, + OpBitXor => BinaryOperator::BitXor, + OpBitOr => BinaryOperator::BitOr, + OpAnd => BinaryOperator::And, + OpOr => BinaryOperator::Or, + OpIn => BinaryOperator::In, + SymbolLeftBrace => BinaryOperator::ObjectApply, + _ => break, + }); + let (left_binding_power, right_binding_power) = op.binding_power(); + if left_binding_power < minimum_binding_power { + break; + } + + // Object apply is not a real operator, we dont have something to bump + if op != BinaryOperator::ObjectApply { + p.bump(); + } + + let m = lhs.precede(p); + let parsed_rhs = expr_binding_power(p, right_binding_power).is_some(); + lhs = m.complete( + p, + if op == BinaryOperator::ObjectApply { + ObjectApply + } else { + BinOp + }, + ); + + if !parsed_rhs { + break; + } + } + Some(lhs) +} +fn compspec(p: &mut Parser) { + assert!(p.at(KeywordFor) || p.at(KeywordIf)); + if p.at(KeywordFor) { + let m = p.start(); + p.bump(); + p.expect(Ident); + p.expect(OpIn); + expr(p); + m.complete(p, CompspecFor); + } else if p.at(KeywordIf) { + let m = p.start(); + p.bump(); + expr(p); + m.complete(p, CompspecIf); + } else { + unreachable!() + } +} +fn comma(p: &mut Parser) -> bool { + if p.at(SymbolComma) { + p.bump(); + true + } else { + false + } +} +fn comma_with_alternatives(p: &mut Parser, set: TokenSet) -> bool { + if p.at(SymbolComma) { + p.bump(); + true + } else if p.at_set(set) { + p.expect_with_no_skip(SymbolComma); + p.bump(); + true + } else { + false + } +} +fn field_name(p: &mut Parser) { + let _e = p.expected_syntax_name("field name"); + if p.at(SymbolLeftBracket) { + p.bump(); + expr(p); + p.expect(SymbolRightBracket); + } else if p.at(Ident) { + p.bump() + } else { + p.error_with_recovery_set(TokenSet::new(&[SymbolSemi])); + } +} +fn object(p: &mut Parser) -> CompletedMarker { + assert!(p.at(SymbolLeftBrace)); + let m = p.start(); + p.bump(); + + loop { + if p.at(SymbolRightBrace) { + p.bump(); + break; + } + let m = p.start(); + field_name(p); + p.expect(SymbolColon); + expr(p); + while p.at(KeywordFor) || p.at(KeywordIf) { + compspec(p) + } + m.complete(p, Field); + if comma_with_alternatives(p, TokenSet::new(&[SymbolAssign])) { + continue; + } + p.expect(SymbolRightBrace); + break; + } + + m.complete(p, Object) +} + +fn params(p: &mut Parser) -> CompletedMarker { + assert!(p.at(LParen)); + let m = p.start(); + p.bump(); + + loop { + if p.at(RParen) { + p.bump(); + break; + } + let m = p.start(); + p.expect(Ident); + if p.at(SymbolAssign) { + p.bump(); + expr(p); + } + m.complete(p, DefParam); + if comma(p) { + continue; + } + p.expect(RParen); + break; + } + + m.complete(p, DefParams) +} +fn args(p: &mut Parser) { + assert!(p.at(LParen)); + p.bump(); + + let mut error_positional_start = None::; + let mut started_named = Cell::new(false); + let mut on_positional = |p: &mut Parser, m: Marker| { + let c = m.complete(p, DefPositionalArg); + if started_named.get() && error_positional_start.is_none() { + error_positional_start = Some(c.precede(p)); + } + }; + loop { + if p.at(RParen) { + break; + } + + let m = p.start(); + if p.at(Ident) { + p.bump(); + if p.at(SymbolAssign) { + p.bump(); + expr(p); + m.complete(p, DefNamedArg); + started_named.set(true); + } else { + on_positional(p, m); + } + } else { + expr(p); + on_positional(p, m); + } + if comma(p) { + continue; + } + break; + } + if let Some(error_positional_start) = error_positional_start { + let c = error_positional_start.complete(p, ErrorPositionalAfterNamed); + p.custom_error(c, "positional arguments can't be placed after named") + } + p.expect(RParen); +} + +fn array(p: &mut Parser) -> CompletedMarker { + assert!(p.at(SymbolLeftBracket)); + // Start the list node + let m = p.start(); + p.bump(); // '[' + + // This vec will have at most one element in case of correct input + let mut compspecs = Vec::with_capacity(1); + let mut elems = 0; + + loop { + if p.at(SymbolRightBracket) { + p.bump(); + break; + } + elems += 1; + let m = p.start(); + { + let m = p.start(); + expr(p); + m.complete(p, BodyDef); + } + let c = p.start_ranger(); + let mut had_spec = false; + while p.at(KeywordFor) || p.at(KeywordIf) { + had_spec = true; + compspec(p) + } + if had_spec { + compspecs.push(c.finish(p)); + } + m.complete(p, ArrayElem); + if comma(p) { + continue; + } + p.expect(SymbolRightBracket); + break; + } + + if elems > 1 && !compspecs.is_empty() { + for spec in compspecs { + p.custom_error( + spec, + "compspec may only be used if there is only one array element", + ) + } + } + + m.complete(p, Array) +} + +fn lhs(p: &mut Parser) -> Option { + let mut lhs = lhs_basic(p)?; + + loop { + if p.at(SymbolDot) { + let m = lhs.precede(p); + p.bump(); + p.expect(Ident); + lhs = m.complete(p, FieldAccess); + } else if p.at(SymbolLeftBracket) { + let m = lhs.precede(p); + p.bump(); + // Start + if !p.at(SymbolColon) { + expr(p); + } + if p.at(SymbolColon) { + p.bump(); + // End + if !p.at(SymbolRightBracket) && !p.at(SymbolColon) { + expr(p); + } + if p.at(SymbolColon) { + p.bump(); + // Step + if !p.at(SymbolRightBracket) { + expr(p); + } + } + } + p.expect(SymbolRightBracket); + lhs = m.complete(p, Slice); + } else if p.at(LParen) { + let m = lhs.precede(p); + args(p); + lhs = m.complete(p, FunctionCall); + } else { + break; + } + } + + Some(lhs) +} + +fn lhs_basic(p: &mut Parser) -> Option { + let _e = p.expected_syntax_name("value"); + Some( + if p.at(Number) + || p.at(StringSingleQuoted) + || p.at(StringDoubleQuoted) + || p.at(StringSingleVerbatim) + || p.at(StringDoubleVerbatim) + || p.at(StringBlock) + || p.at(KeywordNull) + || p.at(SymbolDollar) + || p.at(KeywordSuper) + || p.at(KeywordSelf) + { + let m = p.start(); + p.bump(); + m.complete(p, Literal) + } else if p.at(Ident) { + let m = p.start(); + p.bump(); + m.complete(p, Ident) + } else if p.at(SymbolLeftBracket) { + array(p) + } else if p.at(SymbolLeftBrace) { + object(p) + } else if p.at(KeywordLocal) { + let m = p.start(); + p.bump(); + let mut sus_local = None; + loop { + p.expect_with_recovery_set( + Ident, + TokenSet::new(&[SymbolAssign, SymbolSemi, KeywordLocal]), + ); + if p.at(LParen) { + params(p); + } + + let sus_local_candidate = p.start_ranger(); + p.expect_with_recovery_set( + SymbolAssign, + TokenSet::new(&[SymbolSemi, KeywordLocal]), + ); + + sus_local = p.at(KeywordLocal).then(|| sus_local_candidate.finish(p)); + expr(p); + + if !comma(p) { + break; + } + } + p.expect(SymbolSemi); + if let Some(sus_local) = sus_local { + if sus_local.had_error_since(p) { + p.custom_error(sus_local, "unusal local placement, missing ';' ?") + } + } + { + let m = p.start(); + expr(p); + m.complete(p, BodyDef); + } + m.complete(p, Local) + } else if p.at(KeywordFunction) { + let m = p.start(); + p.bump(); + args(p); + { + let m = p.start(); + expr(p); + m.complete(p, BodyDef); + } + m.complete(p, FunctionDef) + } else if p.at(KeywordError) { + let m = p.start(); + p.bump(); + expr(p); + m.complete(p, ExprError) + } else if p.at(KeywordAssert) { + let m = p.start(); + p.bump(); + expr(p); + if p.at(SymbolColon) { + p.bump(); + expr(p); + } + m.complete(p, ExprAssert) + } else if p.at(KeywordImport) || p.at(KeywordImportStr) { + let m = p.start(); + p.bump(); + expr(p); + m.complete(p, ExprImport) + } else if p.at(OpMinus) || p.at(OpNot) || p.at(OpBitNegate) { + let op = match p.peek().unwrap() { + OpMinus => UnaryOperator::Minus, + OpNot => UnaryOperator::Not, + OpBitNegate => UnaryOperator::BitNegate, + _ => unreachable!(), + }; + let ((), right_binding_power) = op.binding_power(); + + let m = p.start(); + p.bump(); + expr_binding_power(p, right_binding_power); + m.complete(p, UnaryOp) + } else if p.at(LParen) { + let m = p.start(); + p.bump(); + expr(p); + assert!(p.at(RParen)); + p.bump(); + m.complete(p, Parened) + } else { + p.error_with_no_skip(); + return None; + }, + ) +} + +type SyntaxNode = rowan::SyntaxNode; +#[allow(unused)] +type SyntaxToken = rowan::SyntaxToken; +#[allow(unused)] +type SyntaxElement = rowan::NodeOrToken; + +impl Parse { + pub fn syntax(&self) -> SyntaxNode { + SyntaxNode::new_root(self.green_node.clone()) + } +} + +pub fn parse(input: &str) -> Parse { + let lexemes = lex(input); + let parser = Parser::new(&lexemes); + let events = parser.parse(); + dbg!(&events); + let sink = Sink::new(events, &lexemes); + + sink.finish() +} --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp.snap @@ -0,0 +1,43 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "[a for a in [1, 2, 3]]\n" + +--- +Root@0..23 + Array@0..23 + SymbolLeftBracket@0..1 "[" + ArrayElem@1..21 + BodyDef@1..3 + Ident@1..3 + Ident@1..2 "a" + Whitespace@2..3 " " + CompspecFor@3..21 + KeywordFor@3..6 "for" + Whitespace@6..7 " " + Ident@7..8 "a" + Whitespace@8..9 " " + OpIn@9..11 "in" + Whitespace@11..12 " " + Array@12..21 + SymbolLeftBracket@12..13 "[" + ArrayElem@13..14 + BodyDef@13..14 + Literal@13..14 + Number@13..14 "1" + SymbolComma@14..15 "," + Whitespace@15..16 " " + ArrayElem@16..17 + BodyDef@16..17 + Literal@16..17 + Number@16..17 "2" + SymbolComma@17..18 "," + Whitespace@18..19 " " + ArrayElem@19..20 + BodyDef@19..20 + Literal@19..20 + Number@19..20 "3" + SymbolRightBracket@20..21 "]" + SymbolRightBracket@21..22 "]" + Whitespace@22..23 "\n" + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp_incompatible_with_multiple_elems.snap @@ -0,0 +1,58 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "[a for a in [1, 2, 3], b]\n" + +--- +Root@0..26 + Array@0..26 + SymbolLeftBracket@0..1 "[" + ArrayElem@1..21 + BodyDef@1..3 + Ident@1..3 + Ident@1..2 "a" + Whitespace@2..3 " " + CompspecFor@3..21 + KeywordFor@3..6 "for" + Whitespace@6..7 " " + Ident@7..8 "a" + Whitespace@8..9 " " + OpIn@9..11 "in" + Whitespace@11..12 " " + Array@12..21 + SymbolLeftBracket@12..13 "[" + ArrayElem@13..14 + BodyDef@13..14 + Literal@13..14 + Number@13..14 "1" + SymbolComma@14..15 "," + Whitespace@15..16 " " + ArrayElem@16..17 + BodyDef@16..17 + Literal@16..17 + Number@16..17 "2" + SymbolComma@17..18 "," + Whitespace@18..19 " " + ArrayElem@19..20 + BodyDef@19..20 + Literal@19..20 + Number@19..20 "3" + SymbolRightBracket@20..21 "]" + SymbolComma@21..22 "," + Whitespace@22..23 " " + ArrayElem@23..24 + BodyDef@23..24 + Ident@23..24 + Ident@23..24 "b" + SymbolRightBracket@24..25 "]" + Whitespace@25..26 "\n" +=== +Custom { error: "compspec may only be used if there is only one array element", range: 3..21 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ [a for a in [1, 2, 3], b] + ยท  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + ยท โ•ฐโ”€โ”€ compspec may only be used if there is only one array element + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__empty.snap @@ -0,0 +1,18 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: " " + +--- +Root@0..1 + Whitespace@0..1 " " +=== +Missing { expected: Named("value"), offset: 1 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ + ยท  โ–ฒ + ยท โ•ฐโ”€โ”€ missing value + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function.snap @@ -0,0 +1,34 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "function(a, b = 1) a + b\n" + +--- +Root@0..25 + FunctionDef@0..25 + KeywordFunction@0..8 "function" + LParen@8..9 "(" + DefPositionalArg@9..10 + Ident@9..10 "a" + SymbolComma@10..11 "," + Whitespace@11..12 " " + DefNamedArg@12..17 + Ident@12..13 "b" + Whitespace@13..14 " " + SymbolAssign@14..15 "=" + Whitespace@15..16 " " + Literal@16..17 + Number@16..17 "1" + RParen@17..18 ")" + Whitespace@18..19 " " + BodyDef@19..25 + BinOp@19..25 + Ident@19..21 + Ident@19..20 "a" + Whitespace@20..21 " " + OpPlus@21..22 "+" + Whitespace@22..23 " " + Ident@23..25 + Ident@23..24 "b" + Whitespace@24..25 "\n" + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_body.snap @@ -0,0 +1,29 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "function(a, b)\n" + +--- +Root@0..15 + FunctionDef@0..15 + KeywordFunction@0..8 "function" + LParen@8..9 "(" + DefPositionalArg@9..10 + Ident@9..10 "a" + SymbolComma@10..11 "," + Whitespace@11..12 " " + DefPositionalArg@12..13 + Ident@12..13 "b" + RParen@13..14 ")" + Whitespace@14..15 "\n" + BodyDef@15..15 +=== +Missing { expected: Named("value"), offset: 14 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ function(a, b) + ยท  โ–ฒ + ยท โ•ฐโ”€โ”€ missing value + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_no_value.snap @@ -0,0 +1,41 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "function(a, b = ) a + b\n" + +--- +Root@0..24 + FunctionDef@0..24 + KeywordFunction@0..8 "function" + LParen@8..9 "(" + DefPositionalArg@9..10 + Ident@9..10 "a" + SymbolComma@10..11 "," + Whitespace@11..12 " " + DefNamedArg@12..16 + Ident@12..13 "b" + Whitespace@13..14 " " + SymbolAssign@14..15 "=" + Whitespace@15..16 " " + RParen@16..17 ")" + Whitespace@17..18 " " + BodyDef@18..24 + BinOp@18..24 + Ident@18..20 + Ident@18..19 "a" + Whitespace@19..20 " " + OpPlus@20..21 "+" + Whitespace@21..22 " " + Ident@22..24 + Ident@22..23 "b" + Whitespace@23..24 "\n" +=== +Missing { expected: Named("value"), offset: 15 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ function(a, b = ) a + b + ยท  โ–ฒ + ยท โ•ฐโ”€โ”€ missing value + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_rparen.snap @@ -0,0 +1,30 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "function(a, b\n" + +--- +Root@0..14 + FunctionDef@0..14 + KeywordFunction@0..8 "function" + LParen@8..9 "(" + DefPositionalArg@9..10 + Ident@9..10 "a" + SymbolComma@10..11 "," + Whitespace@11..12 " " + DefPositionalArg@12..14 + Ident@12..13 "b" + Whitespace@13..14 "\n" + BodyDef@14..14 +=== +Missing { expected: Unnamed(RParen), offset: 13 } +Missing { expected: Named("value"), offset: 13 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ function(a, b + ยท  โ–ฒ + ยท โ”‚โ•ฐโ”€โ”€ missing value + ยท โ•ฐโ”€โ”€ missing RParen + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_no_value_recovery.snap @@ -0,0 +1,47 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "local a =\nlocal b = 3;\n1\n" + +--- +Root@0..25 + Local@0..25 + KeywordLocal@0..5 "local" + Whitespace@5..6 " " + Ident@6..7 "a" + Whitespace@7..8 " " + SymbolAssign@8..9 "=" + Whitespace@9..10 "\n" + Local@10..25 + KeywordLocal@10..15 "local" + Whitespace@15..16 " " + Ident@16..17 "b" + Whitespace@17..18 " " + SymbolAssign@18..19 "=" + Whitespace@19..20 " " + Literal@20..21 + Number@20..21 "3" + SymbolSemi@21..22 ";" + Whitespace@22..23 "\n" + BodyDef@23..25 + Literal@23..25 + Number@23..24 "1" + Whitespace@24..25 "\n" + BodyDef@25..25 +=== +Missing { expected: Unnamed(SymbolSemi), offset: 24 } +Custom { error: "unusal local placement, missing ';' ?", range: 8..9 } +Missing { expected: Named("value"), offset: 24 } +=== + ร— syntax error + โ•ญโ”€[1:1] + 1 โ”‚ local a = + ยท  โ”ฌ + ยท โ•ฐโ”€โ”€ unusal local placement, missing ';' ? + 2 โ”‚ local b = 3; + 3 โ”‚ 1 + ยท  โ–ฒ + ยท โ”‚โ•ฐโ”€โ”€ missing value + ยท โ•ฐโ”€โ”€ missing SymbolSemi + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_no_value_recovery.snap.new @@ -0,0 +1,43 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "local a =\nlocal b = 3;\n1\n" + +--- +Root@0..25 + Local@0..25 + KeywordLocal@0..5 "local" + Whitespace@5..6 " " + Ident@6..7 "a" + Whitespace@7..8 " " + SymbolAssign@8..9 "=" + Whitespace@9..10 "\n" + Local@10..25 + KeywordLocal@10..15 "local" + Whitespace@15..16 " " + Ident@16..17 "b" + Whitespace@17..18 " " + SymbolAssign@18..19 "=" + Whitespace@19..20 " " + Literal@20..21 + Number@20..21 "3" + SymbolSemi@21..22 ";" + Whitespace@22..23 "\n" + BodyDef@23..25 + Literal@23..25 + Number@23..24 "1" + Whitespace@24..25 "\n" + BodyDef@25..25 +=== +Missing { expected: Unnamed(SymbolSemi), offset: 24 } +Missing { expected: Named("value"), offset: 24 } +=== + ร— syntax error + โ•ญโ”€[2:1] + 2 โ”‚ local b = 3; + 3 โ”‚ 1 + ยท  โ–ฒ + ยท โ”‚โ•ฐโ”€โ”€ missing value + ยท โ•ฐโ”€โ”€ missing SymbolSemi + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_novalue.snap @@ -0,0 +1,29 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "local a =\n" + +--- +Root@0..10 + Local@0..10 + KeywordLocal@0..5 "local" + Whitespace@5..6 " " + Ident@6..7 "a" + Whitespace@7..8 " " + SymbolAssign@8..9 "=" + Whitespace@9..10 "\n" + BodyDef@10..10 +=== +Missing { expected: Named("value"), offset: 9 } +Missing { expected: Unnamed(SymbolSemi), offset: 9 } +Missing { expected: Named("value"), offset: 9 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ local a = + ยท  โ–ฒ + ยท โ•ฐโ”€โ”€ missing value + ยท โ”‚โ•ฐโ”€โ”€ missing SymbolSemi + ยท โ•ฐโ”€โ”€ missing value + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__named_before_positional.snap @@ -0,0 +1,63 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "a(1, 2, b=4, 3, 5, k = 12, 6)\n" + +--- +Root@0..30 + FunctionCall@0..30 + Ident@0..1 + Ident@0..1 "a" + LParen@1..2 "(" + DefPositionalArg@2..3 + Literal@2..3 + Number@2..3 "1" + SymbolComma@3..4 "," + Whitespace@4..5 " " + DefPositionalArg@5..6 + Literal@5..6 + Number@5..6 "2" + SymbolComma@6..7 "," + Whitespace@7..8 " " + DefNamedArg@8..11 + Ident@8..9 "b" + SymbolAssign@9..10 "=" + Literal@10..11 + Number@10..11 "4" + SymbolComma@11..12 "," + Whitespace@12..13 " " + ErrorPositionalAfterNamed@13..28 + DefPositionalArg@13..14 + Literal@13..14 + Number@13..14 "3" + SymbolComma@14..15 "," + Whitespace@15..16 " " + DefPositionalArg@16..17 + Literal@16..17 + Number@16..17 "5" + SymbolComma@17..18 "," + Whitespace@18..19 " " + DefNamedArg@19..25 + Ident@19..20 "k" + Whitespace@20..21 " " + SymbolAssign@21..22 "=" + Whitespace@22..23 " " + Literal@23..25 + Number@23..25 "12" + SymbolComma@25..26 "," + Whitespace@26..27 " " + DefPositionalArg@27..28 + Literal@27..28 + Number@27..28 "6" + RParen@28..29 ")" + Whitespace@29..30 "\n" +=== +Custom { error: "positional arguments can't be placed after named", range: 13..28 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ a(1, 2, b=4, 3, 5, k = 12, 6) + ยท  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€ + ยท โ•ฐโ”€โ”€ positional arguments can't be placed after named + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_lhs.snap @@ -0,0 +1,23 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "+ 2\n" + +--- +Root@0..4 + OpPlus@0..1 "+" + Whitespace@1..2 " " + Number@2..3 "2" + Whitespace@3..4 "\n" +=== +Missing { expected: Named("value"), offset: 0 } +Custom { error: "unexpected input after expression", range: 0..3 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ + 2 + ยท โ–ฒโ”€โ”ฌโ”€ + ยท โ”‚โ•ฐโ”€โ”€ unexpected input after expression + ยท โ•ฐโ”€โ”€ missing value + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_operator.snap @@ -0,0 +1,22 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "2 2\n" + +--- +Root@0..4 + Literal@0..2 + Number@0..1 "2" + Whitespace@1..2 " " + Number@2..3 "2" + Whitespace@3..4 "\n" +=== +Custom { error: "unexpected input after expression", range: 2..3 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ 2 2 + ยท  โ”ฌ + ยท โ•ฐโ”€โ”€ unexpected input after expression + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_rhs.snap @@ -0,0 +1,23 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "a +\n" + +--- +Root@0..4 + BinOp@0..4 + Ident@0..2 + Ident@0..1 "a" + Whitespace@1..2 " " + OpPlus@2..3 "+" + Whitespace@3..4 "\n" +=== +Missing { expected: Named("value"), offset: 3 } +=== + ร— syntax error + โ•ญโ”€โ”€โ”€โ”€ + 1 โ”‚ a + + ยท  โ–ฒ + ยท โ•ฐโ”€โ”€ missing value + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__wrong_field_end.snap.new @@ -0,0 +1,41 @@ +--- +source: crates/jrsonnet-rowan-parser/src/lib.rs +assertion_line: 88 +expression: "{\n\ta: 1;\n\tb: 2;\n}\n" + +--- +Root@0..18 + Object@0..7 + SymbolLeftBrace@0..1 "{" + Whitespace@1..3 "\n\t" + Field@3..7 + Ident@3..4 "a" + SymbolColon@4..5 ":" + Whitespace@5..6 " " + Literal@6..7 + Number@6..7 "1" + SymbolSemi@7..8 ";" + Whitespace@8..10 "\n\t" + Ident@10..11 "b" + SymbolColon@11..12 ":" + Whitespace@12..13 " " + Number@13..14 "2" + SymbolSemi@14..15 ";" + Whitespace@15..16 "\n" + SymbolRightBrace@16..17 "}" + Whitespace@17..18 "\n" +=== +Missing { expected: Unnamed(SymbolRightBrace), offset: 7 } +Custom { error: "unexpected input after expression", range: 7..17 } +=== + ร— syntax error + โ•ญโ”€[1:1] + 1 โ”‚ { + 2 โ”‚ โ•ญโ”€โ–ถ a: 1; + ยท โ”‚โ”‚ โ–ฒ + ยท โ”‚โ”‚ โ•ฐโ”€โ”€ missing SymbolRightBrace + 3 โ”‚ โ”‚ b: 2; + 4 โ”‚ โ”œโ”€โ–ถ } + ยท โ•ฐโ”€โ”€โ”€โ”€ unexpected input after expression + โ•ฐโ”€โ”€โ”€โ”€ + --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/string_block.rs @@ -0,0 +1,221 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StringBlockToken { + Valid, + UnexpectedEndOfString, + MissingTextBlockNewLine, + MissingTextBlockTermination, + MissingTextBlockIndent, +} + +use std::ops::Range; + +use StringBlockToken::*; + +use crate::lex::SyntaxKind; + +pub fn lex_str_block_test<'a>(lex: &mut logos::Lexer<'a, SyntaxKind>) { + lex_str_block(lex); +} + +fn lex_str_block<'a>(lex: &mut logos::Lexer<'a, SyntaxKind>) -> StringBlockToken { + struct Context<'a> { + source: &'a str, + index: usize, + offset: usize, + } + + impl<'a> Context<'a> { + fn rest(&self) -> &'a str { + &self.source[self.index..] + } + + fn next(&mut self) -> Option { + if self.index == self.source.len() { + return None; + } + + match self.rest().chars().next() { + None => None, + Some(c) => { + self.index += c.len_utf8(); + Some(c) + } + } + } + + fn peek(&self) -> Option { + if self.index == self.source.len() { + return None; + } + + self.rest().chars().next() + } + + fn eat_while(&mut self, f: impl Fn(char) -> bool) -> usize { + if self.index == self.source.len() { + return 0; + } + + let next_char = self.rest().char_indices().find(|(_, c)| !f(*c)); + + match next_char { + None => { + let diff = self.source.len() - self.index; + self.index = self.source.len(); + diff + } + Some((idx, _)) => { + self.index += idx; + idx + } + } + } + + fn skip(&mut self, len: usize) { + self.index = match self.index + len { + n if n > self.source.len() => self.source.len(), + n => n, + }; + } + + fn pos(&self) -> Range { + if self.index == self.source.len() { + self.offset + self.index..self.offset + self.index + } else { + // TODO: char size + self.offset + self.index..self.offset + self.index + 1 + } + } + } + + // Check that b has at least the same whitespace prefix as a and returns the + // amount of this whitespace, otherwise returns 0. If a has no whitespace + // prefix than return 0. + fn check_whitespace(a: &str, b: &str) -> usize { + let a = a.as_bytes(); + let b = b.as_bytes(); + + for i in 0..a.len() { + if a[i] != b' ' && a[i] != b'\t' { + // a has run out of whitespace and b matched up to this point. Return result. + return i; + } + + if i >= b.len() { + // We ran off the edge of b while a still has whitespace. Return 0 as failure. + return 0; + } + + if a[i] != b[i] { + // a has whitespace but b does not. Return 0 as failure. + return 0; + } + } + + // We ran off the end of a and b kept up + a.len() + } + + fn guess_token_end_and_bump<'a>(lex: &mut logos::Lexer<'a, SyntaxKind>, ctx: &Context<'a>) { + let end_index = ctx + .rest() + .find("|||") + .map(|v| v + 3) + .unwrap_or_else(|| ctx.rest().len()); + lex.bump(ctx.index + end_index); + } + + debug_assert_eq!(lex.slice(), "|||"); + let mut ctx = Context { + source: lex.remainder(), + index: 0, + offset: lex.span().end, + }; + + // Skip whitespaces + ctx.eat_while(|r| r == ' ' || r == '\t' || r == '\r'); + + // Skip \n + match ctx.next() { + Some('\n') => (), + None => { + guess_token_end_and_bump(lex, &ctx); + return UnexpectedEndOfString; + } + // Text block requires new line after |||. + Some(_) => { + guess_token_end_and_bump(lex, &ctx); + return MissingTextBlockNewLine; + } + } + + // Process leading blank lines before calculating string block indent + while let Some('\n') = ctx.peek() { + ctx.next(); + } + + let mut num_whitespace = check_whitespace(ctx.rest(), ctx.rest()); + let str_block_indent = &ctx.rest()[..num_whitespace]; + + if num_whitespace == 0 { + // Text block's first line must start with whitespace + guess_token_end_and_bump(lex, &ctx); + return MissingTextBlockIndent; + } + + loop { + debug_assert_ne!(num_whitespace, 0, "Unexpected value for num_whitespace"); + ctx.skip(num_whitespace); + + loop { + match ctx.next() { + None => { + guess_token_end_and_bump(lex, &ctx); + return UnexpectedEndOfString; + } + Some('\n') => break, + Some(_) => (), + } + } + + // Skip any blank lines + while let Some('\n') = ctx.peek() { + ctx.next(); + } + + // Look at the next line + num_whitespace = check_whitespace(str_block_indent, ctx.rest()); + if num_whitespace == 0 { + // End of the text block + let mut term_indent = String::with_capacity(num_whitespace); + loop { + match ctx.peek() { + Some(' ') | Some('\t') => { + term_indent.push(ctx.next().unwrap()); + } + _ => break, + } + } + + if !ctx.rest().starts_with("|||") { + // Text block not terminated with ||| + let pos = ctx.pos(); + if pos.len() == 0 { + // eof + lex.bump(ctx.index); + return UnexpectedEndOfString; + } + + guess_token_end_and_bump(lex, &ctx); + return MissingTextBlockTermination; + } + + // Skip '|||' + ctx.skip(3); + break; + } + } + + lex.bump(ctx.index); + Valid +} --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/token_set.rs @@ -0,0 +1,31 @@ +use crate::lex::SyntaxKind; + +#[derive(Clone, Copy, Default)] +pub struct TokenSet(u64); + +impl TokenSet { + pub const EMPTY: Self = Self(0); + pub const ALL: Self = Self(u64::MAX); + + pub const fn new(kinds: &[SyntaxKind]) -> TokenSet { + let mut res = 0u64; + let mut i = 0; + while i < kinds.len() { + res |= mask(kinds[i]); + i += 1 + } + TokenSet(res) + } + + pub const fn union(self, other: TokenSet) -> TokenSet { + TokenSet(self.0 | other.0) + } + + pub const fn contains(&self, kind: SyntaxKind) -> bool { + self.0 & mask(kind) != 0 + } +} + +const fn mask(kind: SyntaxKind) -> u64 { + 1u64 << (kind as usize) +} --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/unary.rs @@ -0,0 +1,15 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UnaryOperator { + Minus, + Not, + BitNegate, +} +impl UnaryOperator { + pub fn binding_power(&self) -> ((), u8) { + match self { + UnaryOperator::Minus => ((), 20), + UnaryOperator::Not => ((), 20), + UnaryOperator::BitNegate => ((), 20), + } + } +} --- /dev/null +++ b/jrsonnet-lsp/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "jrsonnet-lsp" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.48" +jrsonnet-evaluator = { path = "../jrsonnet-evaluator" } +jrsonnet-parser = { path = "../jrsonnet-parser" } +lsp-server = "0.5.2" +lsp-types = "0.92.0" +serde = "1.0.130" +serde_json = "1.0.71" --- /dev/null +++ b/jrsonnet-lsp/src/main.rs @@ -0,0 +1,211 @@ +use std::{ + collections::HashMap, + fs::File, + path::{Path, PathBuf}, + str::FromStr, +}; + +use jrsonnet_evaluator::{EvaluationState, FileImportResolver, Val}; +use jrsonnet_parser::{ExprLocation, ParserSettings}; +use lsp_server::{Connection, ErrorCode, Message, Request, RequestId, Response}; +use lsp_types::{ + notification::{DidChangeTextDocument, DidOpenTextDocument, Notification}, + request::{DocumentLinkRequest, HoverRequest}, + CompletionOptions, DidChangeTextDocumentParams, DidOpenTextDocumentParams, DocumentLink, + DocumentLinkOptions, Hover, HoverContents, MarkupContent, MarkupKind, ServerCapabilities, + TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, Url, + WorkDoneProgressOptions, +}; + +use std::io::Write; + +fn main() { + let mut log = File::create("test").unwrap(); + writeln!(log, "start").unwrap(); + let (connection, io_threads) = Connection::stdio(); + let capabilities = serde_json::to_value(&ServerCapabilities { + completion_provider: Some(CompletionOptions::default()), + definition_provider: Some(lsp_types::OneOf::Left(true)), + document_link_provider: Some(DocumentLinkOptions { + resolve_provider: Some(false), + work_done_progress_options: WorkDoneProgressOptions::default(), + }), + hover_provider: Some(lsp_types::HoverProviderCapability::Simple(true)), + text_document_sync: Some(TextDocumentSyncCapability::Options( + TextDocumentSyncOptions { + change: Some(TextDocumentSyncKind::FULL), + open_close: Some(true), + ..TextDocumentSyncOptions::default() + }, + )), + ..ServerCapabilities::default() + }) + .expect("failed to convert capabilities to json"); + + connection + .initialize(capabilities) + .expect("failed to initialize connection"); + + writeln!(log, "initialized").unwrap(); + + main_loop(&mut log, &connection).expect("main loop failed"); + + io_threads.join().expect("failed to join io_threads"); +} +fn main_loop(log: &mut File, connection: &Connection) -> anyhow::Result<()> { + let mut es = EvaluationState::default(); + es.set_import_resolver(Box::new(FileImportResolver::default())); + + let reply = |response: Response| { + connection + .sender + .send(Message::Response(response)) + .expect("failed to respond"); + }; + + for msg in &connection.receiver { + match msg { + Message::Response(_) => (), + Message::Request(req) => { + if connection.handle_shutdown(&req)? { + return Ok(()); + } + if let Some((id, params)) = cast::(&req) { + reply(Response::new_ok(id, >::new())); + } else if let Some((id, params)) = cast::(&req) { + let pos = params + .text_document_position_params + .text_document + .uri + .path(); + let buf = PathBuf::from_str(pos).unwrap(); + let pos = es + .map_from_source_location( + &buf, + params.text_document_position_params.position.line as usize + 1, + params.text_document_position_params.position.character as usize + 1, + ) + .unwrap(); + let el = ExprLocation(buf.clone().into(), pos as usize, pos as usize); + let es2 = es.clone(); + // reply(Response::new_ok( + // id, + // Some(Hover { + // range: None, + // contents: HoverContents::Markup(MarkupContent { + // kind: MarkupKind::Markdown, + // value: es + // .run_in_state_with_breakpoint(el, move || { + // es2.reset_evaluation_state(&buf); + // es2.import_file(&PathBuf::new(), &buf)? + // .to_string() + // .map(|_| ()) + // }) + // .unwrap() + // .unwrap_or_else(|| Val::Null) + // .value_type() + // .to_string(), + // }), + // }), + // )); + } else + /* + if let Some((id, params)) = cast::(&req) { + let links = handle_links(&files, params).unwrap_or_default(); + reply(Response::new_ok(id, links)); + } else if let Some((id, params)) = cast::(&req) { + if let Some(loc) = handle_goto(&files, params) { + reply(Response::new_ok(id, loc)) + } else { + reply(Response::new_ok(id, ())) + } + } else if let Some((id, params)) = cast::(&req) { + match handle_hover(&files, params) { + Some((range, markdown)) => { + reply(Response::new_ok( + id, + Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: markdown, + }), + range, + }, + )); + } + None => { + reply(Response::new_ok(id, ())); + } + } + } else if let Some((id, params)) = cast::(&req) { + let completions = handle_completion(&files, params.text_document_position) + .unwrap_or_default(); + reply(Response::new_ok(id, completions)); + } else + */ + { + reply(Response::new_err( + req.id, + ErrorCode::MethodNotFound as i32, + format!("unrecognized request {}", req.method), + )) + } + } + Message::Notification(req) => { + let mut handle = |text: String, uri: Url| { + writeln!(log, "updated file: {:?}", uri).unwrap(); + let path = match PathBuf::from_str(uri.path()) { + Ok(x) => x, + Err(_) => return, + }; + let parsed = match jrsonnet_parser::parse( + &text, + &ParserSettings { + file_name: path.clone().into(), + }, + ) { + Ok(v) => v, + Err(e) => { + writeln!(log, "fuck D: {:?}", e).unwrap(); + return; + // connection.sender.send(Message::Notification(Notification::new_err(req.id, ErrorCode::ParseError as i32, format!("Fuck D: {:?}", e)))) + } + }; + es.add_parsed_file(path.into(), text.into(), parsed) + .unwrap(); + writeln!(log, "parsed: {:?}", uri).unwrap(); + }; + + match &*req.method { + DidOpenTextDocument::METHOD => { + let params: DidOpenTextDocumentParams = + match serde_json::from_value(req.params) { + Ok(x) => x, + Err(_) => continue, + }; + handle(params.text_document.text, params.text_document.uri); + } + DidChangeTextDocument::METHOD => { + let params: DidChangeTextDocumentParams = + match serde_json::from_value(req.params) { + Ok(x) => x, + Err(_) => continue, + }; + for change in params.content_changes.into_iter() { + handle(change.text, params.text_document.uri.clone()); + } + } + _ => continue, + } + } + } + } + Ok(()) +} +fn cast(req: &Request) -> Option<(RequestId, R::Params)> +where + R: lsp_types::request::Request, + R::Params: serde::de::DeserializeOwned, +{ + req.clone().extract(R::METHOD).ok() +}