difftreelog
feat lsp prototype
in: master
29 files changed
Cargo.lockdiffbeforeafterboth--- 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",
-]
Cargo.tomldiffbeforeafterboth--- 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"
crates/jrsonnet-rowan-parser/Cargo.tomldiffbeforeafterboth--- /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"
crates/jrsonnet-rowan-parser/src/binary.rsdiffbeforeafterboth--- /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),
+ }
+ }
+}
crates/jrsonnet-rowan-parser/src/event.rsdiffbeforeafterboth--- /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<usize>,
+ },
+ Token,
+ Finish,
+ Placeholder,
+ Error(SyntaxError),
+}
+
+pub(super) struct Sink<'i> {
+ pub builder: GreenNodeBuilder<'static>,
+ lexemes: &'i [Lexeme<'i>],
+ offset: usize,
+ events: Vec<Event>,
+ pub errors: Vec<SyntaxError>,
+}
+
+impl<'i> Sink<'i> {
+ pub(super) fn new(events: Vec<Event>, 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();
+ }
+ }
+}
crates/jrsonnet-rowan-parser/src/lex.rsdiffbeforeafterboth--- /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<Self::Item> {
+ 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<Lexeme<'_>> {
+ Lexer::new(input).collect()
+}
+
+impl From<SyntaxKind> 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::<u16, SyntaxKind>(raw.0) }
+ }
+ fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
+ kind.into()
+ }
+}
crates/jrsonnet-rowan-parser/src/lib.rsdiffbeforeafterboth--- /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<LabeledSpan>,
+ }
+ impl Diagnostic for MyDiagnostic {
+ fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
+ None
+ }
+
+ fn severity(&self) -> Option<miette::Severity> {
+ None
+ }
+
+ fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
+ None
+ }
+
+ fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
+ None
+ }
+
+ fn source_code(&self) -> Option<&dyn miette::SourceCode> {
+ Some(&self.code)
+ }
+
+ fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
+ Some(Box::new(self.spans.clone().into_iter()))
+ }
+
+ fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + '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;
+ }
+ "
+ );
+}
crates/jrsonnet-rowan-parser/src/marker.rsdiffbeforeafterboth--- /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
+ }
+}
crates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth--- /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<SyntaxError>,
+}
+
+#[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<Event>,
+ pub entered: u32,
+ pub hints: Vec<(u32, TextRange, String)>,
+ pub last_error_token: usize,
+ expected_syntax: Option<ExpectedSyntax>,
+ expected_syntax_tracking_state: Rc<Cell<ExpectedSyntaxTrackingState>>,
+}
+
+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<LabeledSpan> 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<Event> {
+ 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<TextRange> {
+ self.lexemes.last().map(|Lexeme { range, .. }| *range)
+ }
+ fn current_token(&self) -> Lexeme<'i> {
+ self.lexemes[self.offset]
+ }
+ fn previous_token(&mut self) -> Option<Lexeme<'i>> {
+ 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<str>) {
+ 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<CompletedMarker> {
+ self.error_with_recovery_set_no_default(recovery_set.union(DEFAULT_RECOVERY_SET))
+ }
+ pub fn error_with_no_skip(&mut self) -> Option<CompletedMarker> {
+ self.error_with_recovery_set_no_default(TokenSet::ALL)
+ }
+
+ pub fn error_with_recovery_set_no_default(
+ &mut self,
+ recovery_set: TokenSet,
+ ) -> Option<CompletedMarker> {
+ 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<SyntaxKind> {
+ 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<SyntaxKind> {
+ 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<Cell<ExpectedSyntaxTrackingState>>,
+}
+
+impl ExpectedSyntaxGuard {
+ fn new(expected_syntax_tracking_state: Rc<Cell<ExpectedSyntaxTrackingState>>) -> 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<CompletedMarker> {
+ 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::<Marker>;
+ 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<CompletedMarker> {
+ 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<CompletedMarker> {
+ 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<Lang>;
+#[allow(unused)]
+type SyntaxToken = rowan::SyntaxToken<Lang>;
+#[allow(unused)]
+type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
+
+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()
+}
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp.snapdiffbeforeafterboth--- /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"
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp_incompatible_with_multiple_elems.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β [a for a in [1, 2, 3], b]
+ Β· [38;2;246;87;248m ββββββββββ¬ββββββββ[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mcompspec may only be used if there is only one array element[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__empty.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β
+ Β· [38;2;246;87;248m β²[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mmissing value[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function.snapdiffbeforeafterbothno changes
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_body.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β function(a, b)
+ Β· [38;2;246;87;248m β²[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mmissing value[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_no_value.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β function(a, b = ) a + b
+ Β· [38;2;246;87;248m β²[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mmissing value[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_rparen.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β function(a, b
+ Β· [38;2;246;87;248m β²[0m
+ Β· [38;2;246;87;248mβ[0m[38;2;30;201;212mβ°ββ [38;2;30;201;212mmissing value[0m[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mmissing RParen[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_no_value_recovery.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ ββ[1:1]
+ [2m1[0m β local a =
+ Β· [38;2;246;87;248m β¬[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248munusal local placement, missing ';' ?[0m[0m
+ [2m2[0m β local b = 3;
+ [2m3[0m β 1
+ Β· [38;2;30;201;212m β²[0m
+ Β· [38;2;30;201;212mβ[0m[38;2;145;246;111mβ°ββ [38;2;145;246;111mmissing value[0m[0m
+ Β· [38;2;30;201;212mβ°ββ [38;2;30;201;212mmissing SymbolSemi[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_no_value_recovery.snap.newdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ ββ[2:1]
+ [2m2[0m β local b = 3;
+ [2m3[0m β 1
+ Β· [38;2;246;87;248m β²[0m
+ Β· [38;2;246;87;248mβ[0m[38;2;30;201;212mβ°ββ [38;2;30;201;212mmissing value[0m[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mmissing SymbolSemi[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_novalue.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β local a =
+ Β· [38;2;246;87;248m β²[0m
+ Β· [38;2;145;246;111mβ°ββ [38;2;145;246;111mmissing value[0m[0m
+ Β· [38;2;246;87;248mβ[0m[38;2;30;201;212mβ°ββ [38;2;30;201;212mmissing SymbolSemi[0m[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mmissing value[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__named_before_positional.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β a(1, 2, b=4, 3, 5, k = 12, 6)
+ Β· [38;2;246;87;248m ββββββββ¬βββββββ[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mpositional arguments can't be placed after named[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_lhs.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β + 2
+ Β· [38;2;246;87;248mβ²[0m[38;2;30;201;212mββ¬β[0m
+ Β· [38;2;246;87;248mβ[0m[38;2;30;201;212mβ°ββ [38;2;30;201;212munexpected input after expression[0m[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mmissing value[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_operator.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β 2 2
+ Β· [38;2;246;87;248m β¬[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248munexpected input after expression[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_rhs.snapdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ βββββ
+ [2m1[0m β a +
+ Β· [38;2;246;87;248m β²[0m
+ Β· [38;2;246;87;248mβ°ββ [38;2;246;87;248mmissing value[0m[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__wrong_field_end.snap.newdiffbeforeafterboth--- /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 }
+===
+ [38;2;255;30;30mΓ[0m syntax error
+ ββ[1:1]
+ [2m1[0m β {
+ [2m2[0m β [38;2;246;87;248mβ[0m[38;2;246;87;248mβ[0m[38;2;246;87;248mβΆ[0m a: 1;
+ Β· [38;2;246;87;248mβ[0m[38;2;30;201;212mβ[0m[38;2;246;87;248m β²[0m
+ Β· [38;2;246;87;248mβ[0m[38;2;30;201;212mβ[0m [38;2;246;87;248mβ°ββ [38;2;246;87;248mmissing SymbolRightBrace[0m[0m
+ [2m3[0m β [38;2;30;201;212mβ[0m b: 2;
+ [2m4[0m β [38;2;30;201;212mβ[0m[38;2;30;201;212mβ[0m[38;2;30;201;212mβΆ[0m }
+ Β· [38;2;30;201;212mβ°[0m[38;2;30;201;212mβββ[0m[38;2;30;201;212mβ[0m [38;2;30;201;212munexpected input after expression[0m
+ β°ββββ
+
crates/jrsonnet-rowan-parser/src/string_block.rsdiffbeforeafterboth--- /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<char> {
+ 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<char> {
+ 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<usize> {
+ 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
+}
crates/jrsonnet-rowan-parser/src/token_set.rsdiffbeforeafterboth--- /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)
+}
crates/jrsonnet-rowan-parser/src/unary.rsdiffbeforeafterboth--- /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),
+ }
+ }
+}
jrsonnet-lsp/Cargo.tomldiffbeforeafterboth--- /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"
jrsonnet-lsp/src/main.rsdiffbeforeafterboth--- /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::<DocumentLinkRequest>(&req) {
+ reply(Response::new_ok(id, <Vec<DocumentLink>>::new()));
+ } else if let Some((id, params)) = cast::<HoverRequest>(&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::<DocumentLinkRequest>(&req) {
+ let links = handle_links(&files, params).unwrap_or_default();
+ reply(Response::new_ok(id, links));
+ } else if let Some((id, params)) = cast::<GotoDefinition>(&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::<HoverRequest>(&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::<Completion>(&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<R>(req: &Request) -> Option<(RequestId, R::Params)>
+where
+ R: lsp_types::request::Request,
+ R::Params: serde::de::DeserializeOwned,
+{
+ req.clone().extract(R::METHOD).ok()
+}