difftreelog
Merge pull request #132 from CertainLach/lsp
in: master
Introduce experimental rowan parser and pretty-printer
74 files changed
.cargo/config.tomldiffbeforeafterboth--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,2 @@
+[alias]
+xtask = "run --package xtask --bin xtask --"
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,21 @@
version = 3
[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -25,24 +40,23 @@
[[package]]
name = "anstream"
-version = "0.3.2"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
- "is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
+checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
[[package]]
name = "anstyle-parse"
@@ -59,34 +73,57 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
name = "anstyle-wincon"
-version = "1.0.1"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
dependencies = [
"anstyle",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
name = "anyhow"
-version = "1.0.72"
+version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "ass-stroke"
+version = "0.1.0"
+dependencies = [
+ "num-traits",
+ "rand 0.8.5",
+ "random_color",
+ "range-map",
+ "smallvec",
+]
[[package]]
+name = "ass-stroke"
+version = "0.1.0"
+source = "git+https://github.com/CertainLach/ass-stroke#c98c0213b9c5f775c0bddaa7b233a38c60859008"
+dependencies = [
+ "num-traits",
+ "rand 0.8.5",
+ "random_color",
+ "range-map",
+ "smallvec",
+]
+
+[[package]]
name = "async-trait"
-version = "0.1.71"
+version = "0.1.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
+checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.31",
]
[[package]]
@@ -96,10 +133,40 @@
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "backtrace-ext"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
name = "base64"
-version = "0.21.2"
+version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
+checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
+
+[[package]]
+name = "beef"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "bincode"
@@ -118,9 +185,9 @@
[[package]]
name = "bitflags"
-version = "2.3.3"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "block-buffer"
@@ -132,10 +199,19 @@
]
[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
+[[package]]
name = "cc"
-version = "1.0.79"
+version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
[[package]]
name = "cfg-if"
@@ -145,20 +221,19 @@
[[package]]
name = "clap"
-version = "4.3.12"
+version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3eab9e8ceb9afdade1ab3f0fd8dbce5b1b2f468ad653baf10e771781b2b67b73"
+checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6"
dependencies = [
"clap_builder",
"clap_derive",
- "once_cell",
]
[[package]]
name = "clap_builder"
-version = "4.3.12"
+version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f2763db829349bf00cfc06251268865ed4363b93a943174f638daf3ecdba2cd"
+checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08"
dependencies = [
"anstream",
"anstyle",
@@ -168,30 +243,30 @@
[[package]]
name = "clap_complete"
-version = "4.3.2"
+version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce"
+checksum = "586a385f7ef2f8b4d86bddaa0c094794e7ccbfe5ffef1f434fe928143fc783a5"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
-version = "4.3.12"
+version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
+checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.31",
]
[[package]]
name = "clap_lex"
-version = "0.5.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
name = "colorchoice"
@@ -200,6 +275,24 @@
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
+name = "console"
+version = "0.15.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
+dependencies = [
+ "encode_unicode",
+ "lazy_static",
+ "libc",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "countme"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
+
+[[package]]
name = "cpufeatures"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -209,6 +302,25 @@
]
[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -240,14 +352,46 @@
]
[[package]]
+name = "dprint-core"
+version = "0.63.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77fb4fc41e8a0217e1c0031c26640126e3ff3aba40a98db8b1db7b4e13bfce29"
+dependencies = [
+ "anyhow",
+ "bumpalo",
+ "indexmap",
+ "rustc-hash",
+ "serde",
+ "unicode-width",
+]
+
+[[package]]
+name = "drop_bomb"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "encode_unicode"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+
+[[package]]
name = "errno"
-version = "0.3.1"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
dependencies = [
"errno-dragonfly",
"libc",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -261,6 +405,27 @@
]
[[package]]
+name = "fastrand"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -271,6 +436,34 @@
]
[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -298,6 +491,16 @@
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
+name = "idna"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -305,9 +508,29 @@
dependencies = [
"autocfg",
"hashbrown 0.12.3",
+ "serde",
]
[[package]]
+name = "indoc"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
+
+[[package]]
+name = "insta"
+version = "1.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0770b0a3d4c70567f0d58331f3088b0e4c4f56c9b8d764efe654b4a5d46de3a"
+dependencies = [
+ "console",
+ "lazy_static",
+ "linked-hash-map",
+ "similar",
+ "yaml-rust",
+]
+
+[[package]]
name = "is-terminal"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -315,7 +538,22 @@
dependencies = [
"hermit-abi",
"rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "is_ci"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
]
[[package]]
@@ -328,6 +566,7 @@
name = "jrsonnet"
version = "0.5.0-pre95"
dependencies = [
+ "ass-stroke 0.1.0 (git+https://github.com/CertainLach/ass-stroke)",
"clap",
"clap_complete",
"jrsonnet-cli",
@@ -376,6 +615,20 @@
]
[[package]]
+name = "jrsonnet-fmt"
+version = "0.5.0-pre95"
+dependencies = [
+ "ass-stroke 0.1.0",
+ "clap",
+ "dprint-core",
+ "indoc",
+ "insta",
+ "jrsonnet-rowan-parser",
+ "tempfile",
+ "thiserror",
+]
+
+[[package]]
name = "jrsonnet-gcmodule"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -408,6 +661,19 @@
]
[[package]]
+name = "jrsonnet-lsp"
+version = "0.5.0-pre95"
+dependencies = [
+ "anyhow",
+ "jrsonnet-evaluator",
+ "jrsonnet-rowan-parser",
+ "lsp-server",
+ "lsp-types",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
name = "jrsonnet-macros"
version = "0.5.0-pre95"
dependencies = [
@@ -429,6 +695,22 @@
]
[[package]]
+name = "jrsonnet-rowan-parser"
+version = "0.5.0-pre95"
+dependencies = [
+ "anyhow",
+ "backtrace",
+ "drop_bomb",
+ "indoc",
+ "insta",
+ "logos",
+ "miette",
+ "rowan",
+ "text-size",
+ "thiserror",
+]
+
+[[package]]
name = "jrsonnet-stdlib"
version = "0.5.0-pre95"
dependencies = [
@@ -467,6 +749,12 @@
]
[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -490,9 +778,9 @@
[[package]]
name = "linux-raw-sys"
-version = "0.4.3"
+version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[package]]
name = "lock_api"
@@ -505,12 +793,113 @@
]
[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "logos"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf8b031682c67a8e3d5446840f9573eb7fe26efe7ec8d195c9ac4c0647c502f1"
+dependencies = [
+ "logos-derive",
+]
+
+[[package]]
+name = "logos-derive"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d849148dbaf9661a6151d1ca82b13bb4c4c128146a88d05253b38d4e2f496c"
+dependencies = [
+ "beef",
+ "fnv",
+ "proc-macro2",
+ "quote",
+ "regex-syntax",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "lsp-server"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f70570c1c29cf6654029b8fe201a5507c153f0d85be6f234d471d756bc36775a"
+dependencies = [
+ "crossbeam-channel",
+ "log",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "lsp-types"
+version = "0.93.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9be6e9c7e2d18f651974370d7aff703f9513e0df6e464fd795660edc77e6ca51"
+dependencies = [
+ "bitflags 1.3.2",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "url",
+]
+
+[[package]]
name = "md5"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
+name = "memchr"
+version = "2.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
+
+[[package]]
+name = "memoffset"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "miette"
+version = "5.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
+dependencies = [
+ "backtrace",
+ "backtrace-ext",
+ "is-terminal",
+ "miette-derive",
+ "once_cell",
+ "owo-colors",
+ "supports-color",
+ "supports-hyperlinks",
+ "supports-unicode",
+ "terminal_size",
+ "textwrap",
+ "thiserror",
+ "unicode-width",
+]
+
+[[package]]
+name = "miette-derive"
+version = "5.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.31",
+]
+
+[[package]]
name = "mimalloc-sys"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -530,10 +919,19 @@
]
[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
name = "num-bigint"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
@@ -553,20 +951,35 @@
[[package]]
name = "num-traits"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
[[package]]
+name = "object"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+
+[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -586,7 +999,7 @@
"libc",
"redox_syscall",
"smallvec",
- "windows-targets",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -623,24 +1036,135 @@
checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739"
[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
name = "proc-macro2"
-version = "1.0.65"
+version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92de25114670a878b1261c79c9f8f729fb97e95bac93f6312f583c60dd6a1dfe"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.30"
+version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc",
+ "rand_pcg",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.10",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "random_color"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5f34bd6526786b2ce5141fd37a4084b5da1ebae74595b5b0d05482a7cef7181"
+dependencies = [
+ "rand 0.7.3",
+]
+
+[[package]]
+name = "range-map"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12a5a2d6c7039059af621472a4389be1215a816df61aa4d531cfe85264aee95f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -650,6 +1174,31 @@
]
[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "rowan"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64449cfef9483a475ed56ae30e2da5ee96448789fb2aa240a04beb6a055078bf"
+dependencies = [
+ "countme",
+ "hashbrown 0.12.3",
+ "memoffset",
+ "rustc-hash",
+ "text-size",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -657,15 +1206,15 @@
[[package]]
name = "rustix"
-version = "0.38.4"
+version = "0.38.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
+checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"errno",
"libc",
"linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -676,9 +1225,9 @@
[[package]]
name = "scopeguard"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
@@ -697,14 +1246,14 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.31",
]
[[package]]
name = "serde_json"
-version = "1.0.104"
+version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
+checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
@@ -712,6 +1261,17 @@
]
[[package]]
+name = "serde_repr"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.31",
+]
+
+[[package]]
name = "serde_yaml_with_quirks"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -756,12 +1316,24 @@
]
[[package]]
+name = "similar"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
+
+[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
+name = "smawk"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
+
+[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -796,6 +1368,34 @@
]
[[package]]
+name = "supports-color"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354"
+dependencies = [
+ "is-terminal",
+ "is_ci",
+]
+
+[[package]]
+name = "supports-hyperlinks"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
+dependencies = [
+ "is-terminal",
+]
+
+[[package]]
+name = "supports-unicode"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7"
+dependencies = [
+ "is-terminal",
+]
+
+[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -808,9 +1408,9 @@
[[package]]
name = "syn"
-version = "2.0.28"
+version = "2.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
+checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
dependencies = [
"proc-macro2",
"quote",
@@ -818,6 +1418,29 @@
]
[[package]]
+name = "tempfile"
+version = "3.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "terminal_size"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
name = "tests"
version = "0.1.0"
dependencies = [
@@ -828,44 +1451,115 @@
]
[[package]]
+name = "text-size"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
+
+[[package]]
+name = "textwrap"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d"
+dependencies = [
+ "smawk",
+ "unicode-linebreak",
+ "unicode-width",
+]
+
+[[package]]
name = "thiserror"
-version = "1.0.43"
+version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42"
+checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.43"
+version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
+checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.31",
]
[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
+name = "ungrammar"
+version = "1.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+
+[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
+name = "unicode-linebreak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
+name = "url"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -878,6 +1572,18 @@
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -901,69 +1607,163 @@
[[package]]
name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
]
[[package]]
name = "windows-targets"
-version = "0.48.1"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
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",
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
]
[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
name = "windows_aarch64_msvc"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.48.0"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "xshell"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad"
+dependencies = [
+ "xshell-macros",
+]
+
+[[package]]
+name = "xshell-macros"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e"
+
+[[package]]
+name = "xtask"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "ungrammar",
+ "xshell",
+]
[[package]]
name = "yaml-rust"
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", "xtask"]
default-members = ["cmds/jrsonnet"]
resolver = "2"
@@ -9,6 +9,7 @@
jrsonnet-evaluator = { path = "./crates/jrsonnet-evaluator", version = "0.5.0-pre95" }
jrsonnet-macros = { path = "./crates/jrsonnet-macros", version = "0.5.0-pre95" }
jrsonnet-parser = { path = "./crates/jrsonnet-parser", version = "0.5.0-pre95" }
+jrsonnet-rowan-parser = { path = "./crates/jrsonnet-rowan-parser", version = "0.5.0-pre95" }
jrsonnet-interner = { path = "./crates/jrsonnet-interner", version = "0.5.0-pre95" }
jrsonnet-stdlib = { path = "./crates/jrsonnet-stdlib", version = "0.5.0-pre95" }
jrsonnet-cli = { path = "./crates/jrsonnet-cli", version = "0.5.0-pre95" }
cmds/jrsonnet-fmt/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "jrsonnet-fmt"
+version.workspace = true
+edition = "2021"
+
+[dependencies]
+dprint-core = "0.63.2"
+jrsonnet-rowan-parser.workspace = true
+insta = "1.15"
+indoc = "1.0"
+ass-stroke = { git = "https://github.com/CertainLach/ass-stroke.git", version = "0.1.0" }
+clap = { version = "4.4.2", features = ["derive"] }
+tempfile = "3.8.0"
+thiserror = "1.0.48"
cmds/jrsonnet-fmt/src/children.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/children.rs
@@ -0,0 +1,290 @@
+// TODO: Return errors as trivia
+
+use std::{fmt::Debug, mem};
+
+use jrsonnet_rowan_parser::{
+ nodes::{CustomError, Trivia, TriviaKind},
+ AstNode, AstToken, SyntaxElement, SyntaxNode, TS,
+};
+
+pub type ChildTrivia = Vec<Result<Trivia, String>>;
+
+/// Node should have no non-trivia tokens before element
+pub fn trivia_before(node: SyntaxNode, end: Option<&SyntaxElement>) -> ChildTrivia {
+ let mut out = Vec::new();
+ for item in node.children_with_tokens() {
+ if Some(&item) == end {
+ break;
+ }
+
+ if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {
+ out.push(Ok(trivia));
+ } else if CustomError::can_cast(item.kind()) {
+ out.push(Err(item.to_string()));
+ } else if end.is_none() {
+ break;
+ } else {
+ assert!(
+ TS![, ;].contains(item.kind()),
+ "silently eaten token: {:?}",
+ item.kind()
+ )
+ }
+ }
+ out
+}
+/// Node should have no non-trivia tokens after element
+pub fn trivia_after(node: SyntaxNode, start: Option<&SyntaxElement>) -> ChildTrivia {
+ if start.is_none() {
+ return Vec::new();
+ }
+ let mut iter = node.children_with_tokens().peekable();
+ while iter.peek() != start {
+ iter.next();
+ }
+ iter.next();
+ let mut out = Vec::new();
+ for item in iter {
+ if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {
+ out.push(Ok(trivia));
+ } else if CustomError::can_cast(item.kind()) {
+ out.push(Err(item.to_string()))
+ } else {
+ assert!(
+ TS![, ;].contains(item.kind()),
+ "silently eaten token: {:?}",
+ item.kind()
+ )
+ }
+ }
+ out
+}
+
+pub fn trivia_between(
+ node: SyntaxNode,
+ start: Option<&SyntaxElement>,
+ end: Option<&SyntaxElement>,
+) -> EndingComments {
+ let mut iter = node.children_with_tokens().peekable();
+ while iter.peek() != start {
+ iter.next();
+ }
+ iter.next();
+
+ let loose = start.is_none() || end.is_none();
+
+ let mut out = Vec::new();
+ for item in iter.take_while(|i| Some(i) != end) {
+ if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {
+ out.push(Ok(trivia));
+ } else if CustomError::can_cast(item.kind()) {
+ out.push(Err(item.to_string()))
+ } else if loose {
+ break;
+ } else {
+ assert!(
+ TS![, ;].contains(item.kind()),
+ "silently eaten token: {:?}",
+ item.kind()
+ )
+ }
+ }
+ EndingComments {
+ should_start_with_newline: should_start_with_newline(None, &out),
+ trivia: out,
+ }
+}
+
+pub fn children_between<T: AstNode + Debug>(
+ node: SyntaxNode,
+ start: Option<&SyntaxElement>,
+ end: Option<&SyntaxElement>,
+ trailing: Option<ChildTrivia>,
+) -> (Vec<Child<T>>, EndingComments) {
+ let mut iter = node.children_with_tokens().peekable();
+ if start.is_some() {
+ while iter.peek() != start {
+ iter.next();
+ }
+ iter.next();
+ }
+ children(
+ iter.take_while(|i| Some(i) != end),
+ start.is_none() && end.is_none(),
+ trailing,
+ )
+}
+
+pub fn should_start_with_newline(prev_inline: Option<&ChildTrivia>, tt: &ChildTrivia) -> bool {
+ count_newlines_before(tt)
+ + prev_inline
+ .map(count_newlines_after)
+ .unwrap_or_default()
+
+ // First for previous item end, second for current item
+ >= 2
+}
+
+fn count_newlines_before(tt: &ChildTrivia) -> usize {
+ let mut nl_count = 0;
+ for t in tt {
+ match t {
+ Ok(t) => match t.kind() {
+ TriviaKind::Whitespace => {
+ nl_count += t.text().bytes().filter(|b| *b == b'\n').count();
+ }
+ _ => break,
+ },
+ Err(_) => {
+ nl_count += 1;
+ }
+ }
+ }
+ nl_count
+}
+fn count_newlines_after(tt: &ChildTrivia) -> usize {
+ let mut nl_count = 0;
+ for t in tt.iter().rev() {
+ match t {
+ Ok(t) => match t.kind() {
+ TriviaKind::Whitespace => {
+ nl_count += t.text().bytes().filter(|b| *b == b'\n').count();
+ }
+ TriviaKind::SingleLineHashComment => {
+ nl_count += 1;
+ break;
+ }
+ TriviaKind::SingleLineSlashComment => {
+ nl_count += 1;
+ break;
+ }
+ _ => {}
+ },
+ Err(_) => nl_count += 1,
+ }
+ }
+ nl_count
+}
+
+pub fn children<T: AstNode + Debug>(
+ items: impl Iterator<Item = SyntaxElement>,
+ loose: bool,
+ mut trailing: Option<ChildTrivia>,
+) -> (Vec<Child<T>>, EndingComments) {
+ let mut out = Vec::new();
+ let mut current_child = None::<Child<T>>;
+ let mut next = ChildTrivia::new();
+ // Previous element ended, do not add more inline comments
+ let mut started_next = false;
+ let mut had_some = false;
+
+ for item in items {
+ if let Some(value) = item.as_node().cloned().and_then(T::cast) {
+ let before_trivia = if let Some(trailing) = trailing.take() {
+ assert!(next.is_empty());
+ trailing
+ } else {
+ mem::take(&mut next)
+ };
+ let last_child = current_child.replace(Child {
+ // First item should not start with newline
+ should_start_with_newline: had_some
+ && should_start_with_newline(
+ current_child.as_ref().map(|c| &c.inline_trivia),
+ &before_trivia,
+ ),
+ before_trivia,
+ value,
+ inline_trivia: Vec::new(),
+ });
+ if let Some(last_child) = last_child {
+ out.push(last_child)
+ }
+ had_some = true;
+ started_next = false;
+ } else if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {
+ let is_single_line_comment = trivia.kind() == TriviaKind::SingleLineHashComment
+ || trivia.kind() == TriviaKind::SingleLineSlashComment;
+ if trailing.is_some() {
+ // Someone have already parsed trivia for us
+ continue;
+ } else if started_next
+ || current_child.is_none()
+ || trivia.text().contains('\n') && !is_single_line_comment
+ {
+ next.push(Ok(trivia.clone()));
+ started_next = true;
+ } else {
+ let cur = current_child.as_mut().expect("checked not none");
+ cur.inline_trivia.push(Ok(trivia));
+ if is_single_line_comment {
+ started_next = true;
+ }
+ }
+ had_some = true;
+ } else if CustomError::can_cast(item.kind()) {
+ next.push(Err(item.to_string()))
+ } else if loose {
+ if had_some {
+ break;
+ }
+ started_next = true;
+ } else {
+ assert!(
+ TS![, ;].contains(item.kind()),
+ "silently eaten token: {:?}",
+ item.kind()
+ )
+ }
+ }
+
+ let ending_comments = EndingComments {
+ should_start_with_newline: should_start_with_newline(
+ current_child.as_ref().map(|c| &c.inline_trivia),
+ &next,
+ ),
+ trivia: next,
+ };
+
+ if let Some(current_child) = current_child {
+ out.push(current_child);
+ }
+
+ (out, ending_comments)
+}
+
+#[derive(Debug)]
+pub struct Child<T> {
+ /// If this child has two newlines above in source code, so it needs to have it in the output
+ pub should_start_with_newline: bool,
+ /// Comment before item, i.e
+ ///
+ /// ```ignore
+ /// // Comment
+ /// item
+ /// ```
+ pub before_trivia: ChildTrivia,
+ pub value: T,
+ /// Comment after line, but located at same line
+ ///
+ /// ```ignore
+ /// item1, // Inline comment
+ /// // Not inline comment
+ /// item2,
+ /// ```
+ pub inline_trivia: ChildTrivia,
+}
+
+pub struct EndingComments {
+ /// If this child has two newlines above in source code, so it needs to have it in the output
+ pub should_start_with_newline: bool,
+ pub trivia: ChildTrivia,
+}
+impl EndingComments {
+ pub fn is_empty(&self) -> bool {
+ !self.should_start_with_newline && self.trivia.is_empty()
+ }
+ pub fn extract_trailing(&mut self) -> ChildTrivia {
+ mem::take(&mut self.trivia)
+ }
+}
cmds/jrsonnet-fmt/src/comments.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/comments.rs
@@ -0,0 +1,183 @@
+use dprint_core::formatting::PrintItems;
+use jrsonnet_rowan_parser::{nodes::TriviaKind, AstToken};
+
+use crate::{children::ChildTrivia, p, pi};
+
+pub enum CommentLocation {
+ /// Above local, field, other things
+ AboveItem,
+ /// After item
+ ItemInline,
+ /// After all items in object
+ EndOfItems,
+}
+
+#[must_use]
+pub fn format_comments(comments: &ChildTrivia, loc: CommentLocation) -> PrintItems {
+ let mut pi = p!(new:);
+
+ for c in comments {
+ let Ok(c) = c else {
+ let mut text = c.as_ref().unwrap_err() as &str;
+ while !text.is_empty() {
+ let pos = text.find(|c| c == '\n' || c == '\t').unwrap_or(text.len());
+ let sliced = &text[..pos];
+ p!(pi: string(sliced.to_string()));
+ text = &text[pos..];
+ if !text.is_empty() {
+ match text.as_bytes()[0] {
+ b'\n' => p!(pi: nl),
+ b'\t' => p!(pi: tab),
+ _ => unreachable!(),
+ }
+ text = &text[1..];
+ }
+ }
+ continue;
+ };
+ match c.kind() {
+ TriviaKind::Whitespace => {}
+ TriviaKind::MultiLineComment => {
+ let mut text = c
+ .text()
+ .strip_prefix("/*")
+ .expect("ml comment starts with /*")
+ .strip_suffix("*/")
+ .expect("ml comment ends with */");
+ // doc-style comment, /**
+ let doc = if text.starts_with('*') {
+ text = &text[1..];
+ true
+ } else {
+ false
+ };
+ // Is comment starts with text immediatly, i.e /*text
+ let mut immediate_start = true;
+ let mut lines = text
+ .split('\n')
+ .map(|l| l.trim_end().to_string())
+ .skip_while(|l| {
+ if l.is_empty() {
+ immediate_start = false;
+ true
+ } else {
+ false
+ }
+ })
+ .collect::<Vec<_>>();
+ while lines.last().map(|l| l.is_empty()).unwrap_or(false) {
+ lines.pop();
+ }
+ if lines.len() == 1 && !doc {
+ if matches!(loc, CommentLocation::ItemInline) {
+ p!(pi: str(" "));
+ }
+ p!(pi: str("/* ") string(lines[0].trim().to_string()) str(" */"))
+ } else if !lines.is_empty() {
+ fn common_ws_prefix<'a>(a: &'a str, b: &str) -> &'a str {
+ let offset = a
+ .bytes()
+ .zip(b.bytes())
+ .take_while(|(a, b)| a == b && (a.is_ascii_whitespace() || *a == b'*'))
+ .count();
+ &a[..offset]
+ }
+ // First line is not empty, extract ws prefix of it
+ let mut common_ws_padding = (if immediate_start && lines.len() > 1 {
+ common_ws_prefix(&lines[1], &lines[1])
+ } else {
+ common_ws_prefix(&lines[0], &lines[0])
+ })
+ .to_string();
+ for line in lines
+ .iter()
+ .skip(if immediate_start { 2 } else { 1 })
+ .filter(|l| !l.is_empty())
+ {
+ common_ws_padding = common_ws_prefix(&common_ws_padding, line).to_string();
+ }
+ for line in lines
+ .iter_mut()
+ .skip(if immediate_start { 1 } else { 0 })
+ .filter(|l| !l.is_empty())
+ {
+ *line = line
+ .strip_prefix(&common_ws_padding)
+ .expect("all non-empty lines start with this padding")
+ .to_string();
+ }
+
+ p!(pi: str("/*"));
+ if doc {
+ p!(pi: str("*"));
+ }
+ p!(pi: nl);
+ for mut line in lines {
+ if doc {
+ p!(pi: str(" *"));
+ }
+ if line.is_empty() {
+ p!(pi: nl);
+ } else {
+ if doc {
+ p!(pi: str(" "));
+ }
+ while let Some(new_line) = line.strip_prefix('\t') {
+ if doc {
+ p!(pi: str(" "));
+ } else {
+ p!(pi: tab);
+ }
+ line = new_line.to_string();
+ }
+ p!(pi: string(line.to_string()) nl)
+ }
+ }
+ if doc {
+ p!(pi: str(" "));
+ }
+ p!(pi: str("*/") nl)
+ }
+ }
+ // TODO: Keep common padding for multiple continous lines of single-line comments
+ // I.e
+ // ```
+ // # Line1
+ // # Line2
+ // ```
+ // Should be reformatted as
+ // ```
+ // # Line1
+ // # Line2
+ // ```
+ // But currently comment formatter is not aware of continous comment lines, and reformats it as
+ // ```
+ // # Line1
+ // # Line2
+ // ```
+ TriviaKind::SingleLineHashComment => {
+ if matches!(loc, CommentLocation::ItemInline) {
+ p!(pi: str(" "))
+ }
+ p!(pi: str("# ") string(c.text().strip_prefix('#').expect("hash comment starts with #").trim().to_string()));
+ if !matches!(loc, CommentLocation::ItemInline) {
+ p!(pi: nl)
+ }
+ }
+ TriviaKind::SingleLineSlashComment => {
+ if matches!(loc, CommentLocation::ItemInline) {
+ p!(pi: str(" "))
+ }
+ p!(pi: str("// ") string(c.text().strip_prefix("//").expect("comment starts with //").trim().to_string()));
+ if !matches!(loc, CommentLocation::ItemInline) {
+ p!(pi: nl)
+ }
+ }
+ // Garbage in - garbage out
+ TriviaKind::ErrorCommentTooShort => p!(pi: str("/*/")),
+ TriviaKind::ErrorCommentUnterminated => p!(pi: string(c.text().to_string())),
+ }
+ }
+
+ pi
+}
cmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/main.rs
@@ -0,0 +1,804 @@
+use std::{
+ any::type_name,
+ fs,
+ io::{self, Write},
+ path::PathBuf,
+ process,
+};
+
+use children::{children_between, trivia_before};
+use clap::Parser;
+use dprint_core::formatting::{PrintItems, PrintOptions};
+use jrsonnet_rowan_parser::{
+ nodes::{
+ ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,
+ DestructRest, Expr, ExprBase, FieldName, ForSpec, IfSpec, ImportKind, Literal, Member,
+ Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Stmt, Suffix, Text,
+ UnaryOperator, Visibility,
+ },
+ AstNode, AstToken, SyntaxToken,
+};
+
+use crate::{
+ children::{trivia_after, trivia_between},
+ comments::{format_comments, CommentLocation},
+};
+
+mod children;
+mod comments;
+#[cfg(test)]
+mod tests;
+
+pub trait Printable {
+ fn print(&self) -> PrintItems;
+}
+
+macro_rules! pi {
+ (@i; $($t:tt)*) => {{
+ #[allow(unused_mut)]
+ let mut o = dprint_core::formatting::PrintItems::new();
+ pi!(@s; o: $($t)*);
+ o
+ }};
+ (@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{
+ $o.push_str($e);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: string($e:expr $(,)?) $($t:tt)*) => {{
+ $o.push_string($e);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: nl $($t:tt)*) => {{
+ $o.push_signal(dprint_core::formatting::Signal::NewLine);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: tab $($t:tt)*) => {{
+ $o.push_signal(dprint_core::formatting::Signal::Tab);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: >i $($t:tt)*) => {{
+ $o.push_signal(dprint_core::formatting::Signal::StartIndent);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: <i $($t:tt)*) => {{
+ $o.push_signal(dprint_core::formatting::Signal::FinishIndent);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: {$expr:expr} $($t:tt)*) => {{
+ $o.extend($expr.print());
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: items($expr:expr) $($t:tt)*) => {{
+ $o.extend($expr);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{
+ if $e {
+ pi!(@s; $o: $($then)*);
+ }
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{
+ if $e {
+ pi!(@s; $o: $($then)*);
+ } else {
+ pi!(@s; $o: $($else)*);
+ }
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $i:ident:) => {}
+}
+macro_rules! p {
+ (new: $($t:tt)*) => {
+ pi!(@i; $($t)*)
+ };
+ ($o:ident: $($t:tt)*) => {
+ pi!(@s; $o: $($t)*)
+ };
+}
+pub(crate) use p;
+pub(crate) use pi;
+
+impl<P> Printable for Option<P>
+where
+ P: Printable,
+{
+ fn print(&self) -> PrintItems {
+ if let Some(v) = self {
+ v.print()
+ } else {
+ p!(new: string(
+ format!(
+ "/*missing {}*/",
+ type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")
+ ),
+ ))
+ }
+ }
+}
+
+impl Printable for SyntaxToken {
+ fn print(&self) -> PrintItems {
+ p!(new: string(self.to_string()))
+ }
+}
+
+impl Printable for Text {
+ fn print(&self) -> PrintItems {
+ p!(new: string(format!("{}", self)))
+ }
+}
+impl Printable for Number {
+ fn print(&self) -> PrintItems {
+ p!(new: string(format!("{}", self)))
+ }
+}
+
+impl Printable for Name {
+ fn print(&self) -> PrintItems {
+ p!(new: {self.ident_lit()})
+ }
+}
+
+impl Printable for DestructRest {
+ fn print(&self) -> PrintItems {
+ let mut pi = p!(new: str("..."));
+ if let Some(name) = self.into() {
+ p!(pi: {name});
+ }
+ pi
+ }
+}
+
+impl Printable for Destruct {
+ fn print(&self) -> PrintItems {
+ let mut pi = p!(new:);
+ match self {
+ Destruct::DestructFull(f) => {
+ p!(pi: {f.name()})
+ }
+ Destruct::DestructSkip(_) => p!(pi: str("?")),
+ Destruct::DestructArray(a) => {
+ p!(pi: str("[") >i nl);
+ for el in a.destruct_array_parts() {
+ match el {
+ DestructArrayPart::DestructArrayElement(e) => {
+ p!(pi: {e.destruct()} str(",") nl)
+ }
+ DestructArrayPart::DestructRest(d) => {
+ p!(pi: {d} str(",") nl)
+ }
+ }
+ }
+ p!(pi: <i str("]"));
+ }
+ Destruct::DestructObject(o) => {
+ p!(pi: str("{") >i nl);
+ for item in o.destruct_object_fields() {
+ p!(pi: {item.field()});
+ if let Some(des) = item.destruct() {
+ p!(pi: str(": ") {des})
+ }
+ if let Some(def) = item.expr() {
+ p!(pi: str(" = ") {def});
+ }
+ p!(pi: str(",") nl);
+ }
+ if let Some(rest) = o.destruct_rest() {
+ p!(pi: {rest} nl)
+ }
+ p!(pi: <i str("}"));
+ }
+ }
+ pi
+ }
+}
+
+impl Printable for FieldName {
+ fn print(&self) -> PrintItems {
+ match self {
+ FieldName::FieldNameFixed(f) => {
+ if let Some(id) = f.id() {
+ p!(new: {id})
+ } else if let Some(str) = f.text() {
+ p!(new: {str})
+ } else {
+ p!(new: str("/*missing FieldName*/"))
+ }
+ }
+ FieldName::FieldNameDynamic(d) => {
+ p!(new: str("[") {d.expr()} str("]"))
+ }
+ }
+ }
+}
+
+impl Printable for Visibility {
+ fn print(&self) -> PrintItems {
+ p!(new: string(self.to_string()))
+ }
+}
+
+impl Printable for ObjLocal {
+ fn print(&self) -> PrintItems {
+ p!(new: str("local ") {self.bind()})
+ }
+}
+
+impl Printable for Assertion {
+ fn print(&self) -> PrintItems {
+ let mut pi = p!(new: str("assert ") {self.condition()});
+ if self.colon_token().is_some() || self.message().is_some() {
+ p!(pi: str(": ") {self.message()})
+ }
+ pi
+ }
+}
+
+impl Printable for ParamsDesc {
+ fn print(&self) -> PrintItems {
+ let mut pi = p!(new: str("(") >i nl);
+ for param in self.params() {
+ p!(pi: {param.destruct()});
+ if param.assign_token().is_some() || param.expr().is_some() {
+ p!(pi: str(" = ") {param.expr()})
+ }
+ p!(pi: str(",") nl)
+ }
+ p!(pi: <i str(")"));
+ pi
+ }
+}
+impl Printable for ArgsDesc {
+ fn print(&self) -> PrintItems {
+ let mut pi = p!(new: str("(") >i nl);
+ for arg in self.args() {
+ if arg.name().is_some() || arg.assign_token().is_some() {
+ p!(pi: {arg.name()} str(" = "));
+ }
+ p!(pi: {arg.expr()} str(",") nl)
+ }
+ p!(pi: <i str(")"));
+ pi
+ }
+}
+impl Printable for SliceDesc {
+ fn print(&self) -> PrintItems {
+ let mut pi = p!(new: str("["));
+ if self.from().is_some() {
+ p!(pi: {self.from()});
+ }
+ p!(pi: str(":"));
+ if self.end().is_some() {
+ p!(pi: {self.end().map(|e|e.expr())})
+ }
+ // Keep only one : in case if we don't need step
+ if self.step().is_some() {
+ p!(pi: str(":") {self.step().map(|e|e.expr())});
+ }
+ p!(pi: str("]"));
+ pi
+ }
+}
+
+impl Printable for Member {
+ fn print(&self) -> PrintItems {
+ match self {
+ Member::MemberBindStmt(b) => {
+ p!(new: {b.obj_local()})
+ }
+ Member::MemberAssertStmt(ass) => {
+ p!(new: {ass.assertion()})
+ }
+ Member::MemberFieldNormal(n) => {
+ p!(new: {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()})
+ }
+ Member::MemberFieldMethod(m) => {
+ p!(new: {m.field_name()} {m.params_desc()} {m.visibility()} str(" ") {m.expr()})
+ }
+ }
+ }
+}
+
+impl Printable for ObjBody {
+ fn print(&self) -> PrintItems {
+ match self {
+ ObjBody::ObjBodyComp(l) => {
+ let (children, mut end_comments) = children_between::<Member>(
+ l.syntax().clone(),
+ l.l_brace_token().map(Into::into).as_ref(),
+ Some(
+ &(l.comp_specs()
+ .next()
+ .expect("at least one spec is defined")
+ .syntax()
+ .clone())
+ .into(),
+ ),
+ None,
+ );
+ let trailing_for_comp = end_comments.extract_trailing();
+ let mut pi = p!(new: str("{") >i nl);
+ for mem in children.into_iter() {
+ if mem.should_start_with_newline {
+ p!(pi: nl);
+ }
+ p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));
+ p!(pi: {mem.value} str(","));
+ p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));
+ p!(pi: nl)
+ }
+
+ if end_comments.should_start_with_newline {
+ p!(pi: nl);
+ }
+ p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));
+
+ let (compspecs, end_comments) = children_between::<CompSpec>(
+ l.syntax().clone(),
+ l.member_comps()
+ .last()
+ .map(|m| m.syntax().clone())
+ .map(Into::into)
+ .or_else(|| l.l_brace_token().map(Into::into))
+ .as_ref(),
+ l.r_brace_token().map(Into::into).as_ref(),
+ Some(trailing_for_comp),
+ );
+ for mem in compspecs.into_iter() {
+ if mem.should_start_with_newline {
+ p!(pi: nl);
+ }
+ p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));
+ p!(pi: {mem.value});
+ p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));
+ }
+ if end_comments.should_start_with_newline {
+ p!(pi: nl);
+ }
+ p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));
+
+ p!(pi: nl <i str("}"));
+ pi
+ }
+ ObjBody::ObjBodyMemberList(l) => {
+ let (children, end_comments) = children_between::<Member>(
+ l.syntax().clone(),
+ l.l_brace_token().map(Into::into).as_ref(),
+ l.r_brace_token().map(Into::into).as_ref(),
+ None,
+ );
+ if children.is_empty() && end_comments.is_empty() {
+ return p!(new: str("{ }"));
+ }
+ let mut pi = p!(new: str("{") >i nl);
+ for mem in children.into_iter() {
+ if mem.should_start_with_newline {
+ p!(pi: nl);
+ }
+ p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));
+ p!(pi: {mem.value} str(","));
+ p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));
+ p!(pi: nl)
+ }
+
+ if end_comments.should_start_with_newline {
+ p!(pi: nl);
+ }
+ p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));
+ p!(pi: <i str("}"));
+ pi
+ }
+ }
+ }
+}
+impl Printable for UnaryOperator {
+ fn print(&self) -> PrintItems {
+ p!(new: string(self.text().to_string()))
+ }
+}
+impl Printable for BinaryOperator {
+ fn print(&self) -> PrintItems {
+ p!(new: string(self.text().to_string()))
+ }
+}
+impl Printable for Bind {
+ fn print(&self) -> PrintItems {
+ match self {
+ Bind::BindDestruct(d) => {
+ p!(new: {d.into()} str(" = ") {d.value()})
+ }
+ Bind::BindFunction(f) => {
+ p!(new: {f.name()} {f.params()} str(" = ") {f.value()})
+ }
+ }
+ }
+}
+impl Printable for Literal {
+ fn print(&self) -> PrintItems {
+ p!(new: string(self.syntax().to_string()))
+ }
+}
+impl Printable for ImportKind {
+ fn print(&self) -> PrintItems {
+ p!(new: string(self.syntax().to_string()))
+ }
+}
+impl Printable for ForSpec {
+ fn print(&self) -> PrintItems {
+ p!(new: str("for ") {self.bind()} str(" in ") {self.expr()})
+ }
+}
+impl Printable for IfSpec {
+ fn print(&self) -> PrintItems {
+ p!(new: str("if ") {self.expr()})
+ }
+}
+impl Printable for CompSpec {
+ fn print(&self) -> PrintItems {
+ match self {
+ CompSpec::ForSpec(f) => f.print(),
+ CompSpec::IfSpec(i) => i.print(),
+ }
+ }
+}
+impl Printable for Expr {
+ fn print(&self) -> PrintItems {
+ let mut o = p!(new:);
+ let (stmts, ending) = children_between::<Stmt>(
+ self.syntax().clone(),
+ None,
+ self.expr_base()
+ .as_ref()
+ .map(ExprBase::syntax)
+ .cloned()
+ .map(Into::into)
+ .as_ref(),
+ None,
+ );
+ for stmt in stmts {
+ p!(o: {stmt.value});
+ }
+ p!(o: {self.expr_base()});
+ let (suffixes, ending) = children_between::<Suffix>(
+ self.syntax().clone(),
+ self.expr_base()
+ .as_ref()
+ .map(ExprBase::syntax)
+ .cloned()
+ .map(Into::into)
+ .as_ref(),
+ None,
+ None,
+ );
+ for suffix in suffixes {
+ p!(o: {suffix.value});
+ }
+ o
+ }
+}
+impl Printable for Suffix {
+ fn print(&self) -> PrintItems {
+ let mut o = p!(new:);
+ match self {
+ Suffix::SuffixIndex(i) => {
+ if i.question_mark_token().is_some() {
+ p!(o: str("?"));
+ }
+ p!(o: str(".") {i.index()});
+ }
+ Suffix::SuffixIndexExpr(e) => {
+ if e.question_mark_token().is_some() {
+ p!(o: str(".?"));
+ }
+ p!(o: str("[") {e.index()} str("]"))
+ }
+ Suffix::SuffixSlice(d) => {
+ p!(o: {d.slice_desc()})
+ }
+ Suffix::SuffixApply(a) => {
+ p!(o: {a.args_desc()})
+ }
+ }
+ o
+ }
+}
+impl Printable for Stmt {
+ fn print(&self) -> PrintItems {
+ match self {
+ Stmt::StmtLocal(l) => {
+ let mut pi = p!(new:);
+ let (binds, end_comments) = children_between::<Bind>(
+ l.syntax().clone(),
+ l.local_kw_token().map(Into::into).as_ref(),
+ l.semi_token().map(Into::into).as_ref(),
+ None,
+ );
+ if binds.len() == 1 {
+ let bind = &binds[0];
+ p!(pi: items(format_comments(&bind.before_trivia, CommentLocation::AboveItem)));
+ p!(pi: str("local ") {bind.value});
+ // TODO: keep end_comments, child.inline_trivia somehow, force multiple locals formatting in case of presence?
+ } else {
+ p!(pi: str("local") >i nl);
+ for bind in binds {
+ if bind.should_start_with_newline {
+ p!(pi: nl);
+ }
+ p!(pi: items(format_comments(&bind.before_trivia, CommentLocation::AboveItem)));
+ p!(pi: {bind.value} str(","));
+ p!(pi: items(format_comments(&bind.inline_trivia, CommentLocation::ItemInline)) nl);
+ }
+ if end_comments.should_start_with_newline {
+ p!(pi: nl)
+ }
+ p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));
+ p!(pi: <i);
+ }
+ p!(pi: str(";") nl);
+ pi
+ }
+ Stmt::StmtAssert(a) => {
+ p!(new: {a.assertion()} str(";") nl)
+ }
+ }
+ }
+}
+impl Printable for ExprBase {
+ fn print(&self) -> PrintItems {
+ match self {
+ Self::ExprBinary(b) => {
+ p!(new: {b.lhs_work()} str(" ") {b.binary_operator()} str(" ") {b.rhs_work()})
+ }
+ Self::ExprUnary(u) => p!(new: {u.unary_operator()} {u.rhs()}),
+ // Self::ExprSlice(s) => {
+ // p!(new: {s.expr()} {s.slice_desc()})
+ // }
+ // Self::ExprIndex(i) => {
+ // p!(new: {i.expr()} str(".") {i.index()})
+ // }
+ // Self::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),
+ // Self::ExprApply(a) => {
+ // let mut pi = p!(new: {a.expr()} {a.args_desc()});
+ // if a.tailstrict_kw_token().is_some() {
+ // p!(pi: str(" tailstrict"));
+ // }
+ // pi
+ // }
+ Self::ExprObjExtend(ex) => {
+ p!(new: {ex.lhs_work()} str(" ") {ex.rhs_work()})
+ }
+ Self::ExprParened(p) => {
+ p!(new: str("(") {p.expr()} str(")"))
+ }
+ Self::ExprString(s) => p!(new: {s.text()}),
+ Self::ExprNumber(n) => p!(new: {n.number()}),
+ Self::ExprArray(a) => {
+ let mut pi = p!(new: str("[") >i nl);
+ for el in a.exprs() {
+ p!(pi: {el} str(",") nl);
+ }
+ p!(pi: <i str("]"));
+ pi
+ }
+ Self::ExprObject(obj) => {
+ p!(new: {obj.obj_body()})
+ }
+ Self::ExprArrayComp(arr) => {
+ let mut pi = p!(new: str("[") {arr.expr()});
+ for spec in arr.comp_specs() {
+ p!(pi: str(" ") {spec});
+ }
+ p!(pi: str("]"));
+ pi
+ }
+ Self::ExprImport(v) => {
+ p!(new: {v.import_kind()} str(" ") {v.text()})
+ }
+ Self::ExprVar(n) => p!(new: {n.name()}),
+ // Self::ExprLocal(l) => {
+ // }
+ Self::ExprIfThenElse(ite) => {
+ let mut pi =
+ p!(new: str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});
+ if ite.else_kw_token().is_some() || ite.else_().is_some() {
+ p!(pi: str(" else ") {ite.else_().map(|t| t.expr())})
+ }
+ pi
+ }
+ Self::ExprFunction(f) => p!(new: str("function") {f.params_desc()} nl {f.expr()}),
+ // Self::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),
+ Self::ExprError(e) => p!(new: str("error ") {e.expr()}),
+ Self::ExprLiteral(l) => {
+ p!(new: {l.literal()})
+ }
+ }
+ }
+}
+
+impl Printable for SourceFile {
+ fn print(&self) -> PrintItems {
+ let mut pi = p!(new:);
+ let before = trivia_before(
+ self.syntax().clone(),
+ self.expr()
+ .map(|e| e.syntax().clone())
+ .map(Into::into)
+ .as_ref(),
+ );
+ let after = trivia_after(
+ self.syntax().clone(),
+ self.expr()
+ .map(|e| e.syntax().clone())
+ .map(Into::into)
+ .as_ref(),
+ );
+ p!(pi: items(format_comments(&before, CommentLocation::AboveItem)));
+ p!(pi: {self.expr()} nl);
+ p!(pi: items(format_comments(&after, CommentLocation::EndOfItems)));
+ pi
+ }
+}
+
+struct FormatOptions {
+ // 0 for hard tabs
+ indent: u8,
+}
+fn format(input: &str, opts: &FormatOptions) -> Option<String> {
+ let (parsed, errors) = jrsonnet_rowan_parser::parse(input);
+ if !errors.is_empty() {
+ let mut builder = ass_stroke::SnippetBuilder::new(input);
+ for error in errors {
+ builder
+ .error(ass_stroke::Text::single(
+ format!("{:?}", error.error).chars(),
+ Default::default(),
+ ))
+ .range(
+ error.range.start().into()
+ ..=(usize::from(error.range.end()) - 1).max(error.range.start().into()),
+ )
+ .build();
+ }
+ let snippet = builder.build();
+ let ansi = ass_stroke::source_to_ansi(&snippet);
+ eprintln!("{ansi}");
+ // It is possible to recover from this failure, but the output may be broken, as formatter is free to skip
+ // ERROR rowan nodes.
+ // Recovery needs to be enabled for LSP, though.
+ //
+ // TODO: Verify how formatter interacts in cases of missing positional values, i.e `if cond then /*missing Expr*/ else residual`.
+ return None;
+ }
+ Some(dprint_core::formatting::format(
+ || parsed.print(),
+ PrintOptions {
+ indent_width: if opts.indent == 0 {
+ // Reasonable max length for both 2 and 4 space sized tabs.
+ 3
+ } else {
+ opts.indent
+ },
+ max_width: 100,
+ use_tabs: opts.indent == 0,
+ new_line_text: "\n",
+ },
+ ))
+}
+
+#[derive(Parser)]
+struct Opts {
+ /// Treat input as code, reformat it instead of reading file.
+ #[clap(long, short = 'e')]
+ exec: bool,
+ /// Path to be reformatted if `--exec` if unset, otherwise code itself.
+ input: String,
+ /// Replace code with formatted in-place, instead of printing it to stdout.
+ /// Only applicable if `--exec` is unset.
+ #[clap(long, short = 'i')]
+ in_place: bool,
+
+ /// Exit with error if formatted does not match input
+ #[arg(long)]
+ test: bool,
+ /// Number of spaces to indent with
+ ///
+ /// 0 for guess from input (default), and use hard tabs if unable to guess.
+ #[arg(long, default_value = "0")]
+ indent: u8,
+ /// Force hard tab for indentation
+ #[arg(long)]
+ hard_tabs: bool,
+
+ /// Debug option: how many times to call reformatting in case of unstable dprint output resolution.
+ ///
+ /// 0 for not retrying to reformat.
+ #[arg(long, default_value = "0")]
+ conv_limit: usize,
+}
+
+#[derive(thiserror::Error, Debug)]
+enum Error {
+ #[error("--in-place is incompatible with --exec")]
+ InPlaceExec,
+ #[error("io: {0}")]
+ Io(#[from] io::Error),
+ #[error("persist: {0}")]
+ Persist(#[from] tempfile::PersistError),
+ #[error("parsing failed, refusing to reformat corrupted input")]
+ ParseError,
+}
+
+fn main_result() -> Result<(), Error> {
+ eprintln!("jrsonnet-fmt is a prototype of a jsonnet code formatter, do not expect it to produce meaningful results right now.");
+ eprintln!("It is not expected for its output to match other implementations, it will be completly separate implementation with maybe different name.");
+ let mut opts = Opts::parse();
+ let input = if opts.exec {
+ if opts.in_place {
+ return Err(Error::InPlaceExec);
+ }
+ opts.input.clone()
+ } else {
+ fs::read_to_string(&opts.input)?
+ };
+
+ if opts.indent == 0 {
+ // Sane default.
+ // TODO: Implement actual guessing.
+ opts.hard_tabs = true;
+ }
+
+ let mut iteration = 0;
+ let mut formatted = input.clone();
+ let mut tmp;
+ // https://github.com/dprint/dprint/pull/423
+ loop {
+ let Some(reformatted) = format(
+ &formatted,
+ &FormatOptions {
+ indent: if opts.indent == 0 || opts.hard_tabs {
+ 0
+ } else {
+ opts.indent
+ },
+ },
+ ) else {
+ return Err(Error::ParseError);
+ };
+ tmp = reformatted.trim().to_owned();
+ if formatted == tmp {
+ break;
+ }
+ formatted = tmp;
+ if opts.conv_limit == 0 {
+ break;
+ }
+ iteration += 1;
+ if iteration > opts.conv_limit {
+ panic!("formatting not converged");
+ }
+ }
+ formatted.push('\n');
+ if opts.test && formatted != input {
+ process::exit(1);
+ }
+ if opts.in_place {
+ let path = PathBuf::from(opts.input);
+ let mut temp = tempfile::NamedTempFile::new_in(path.parent().expect(
+ "not failed during read, this path is not a directory, and there is a parent",
+ ))?;
+ temp.write_all(formatted.as_bytes())?;
+ temp.flush()?;
+ temp.persist(&path)?;
+ } else {
+ print!("{formatted}")
+ }
+ Ok(())
+}
+
+fn main() {
+ if let Err(e) = main_result() {
+ eprintln!("{e}");
+ process::exit(1);
+ }
+}
cmds/jrsonnet-fmt/src/snapshots/jrsonnet_fmt__tests__complex_comments_snapshot.snapdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/snapshots/jrsonnet_fmt__tests__complex_comments_snapshot.snap
@@ -0,0 +1,53 @@
+---
+source: cmds/jrsonnet-fmt/src/tests.rs
+expression: "reformat(indoc!(\"{\n\t\t comments: {\n\t\t\t_: '',\n\t\t\t// Plain comment\n\t\t\ta: '',\n\n\t\t\t# Plain comment with empty line before\n\t\t\tb: '',\n\t\t\t/*Single-line multiline comment\n\n\t\t\t*/\n\t\t\tc: '',\n\n\t\t\t/**Single-line multiline doc comment\n\n\t\t\t*/\n\t\t\tc: '',\n\n\t\t\t/**Multiline doc\n\t\t\tComment\n\t\t\t*/\n\t\t\tc: '',\n\n\t\t\t/*\n\n\tMulti-line\n\n\tcomment\n\t\t\t*/\n\t\t\td: '',\n\n\t\t\te: '', // Inline comment\n\n\t\t\tk: '',\n\n\t\t\t// Text after everything\n\t\t },\n\t\t comments2: {\n\t\t\tk: '',\n\t\t\t// Text after everything, but no newline above\n\t\t },\n spacing: {\n a: '',\n\n b: '',\n },\n noSpacing: {\n a: '',\n b: '',\n },\n }\"))"
+---
+{
+ comments: {
+ _: '',
+ // Plain comment
+ a: '',
+
+ # Plain comment with empty line before
+ b: '',
+ /* Single-line multiline comment */
+ c: '',
+
+ /**
+ * Single-line multiline doc comment
+ */
+ c: '',
+
+ /**
+ * Multiline doc
+ * Comment
+ */
+ c: '',
+
+ /*
+ Multi-line
+
+ comment
+ */
+ d: '',
+
+ e: '', // Inline comment
+
+ k: '',
+
+ // Text after everything
+ },
+ comments2: {
+ k: '',
+ // Text after everything, but no newline above
+ },
+ spacing: {
+ a: '',
+
+ b: '',
+ },
+ noSpacing: {
+ a: '',
+ b: '',
+ },
+}
cmds/jrsonnet-fmt/src/tests.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/tests.rs
@@ -0,0 +1,124 @@
+use dprint_core::formatting::PrintOptions;
+use indoc::indoc;
+
+use crate::Printable;
+
+fn reformat(input: &str) -> String {
+ let (source, _) = jrsonnet_rowan_parser::parse(input);
+
+ dprint_core::formatting::format(
+ || source.print(),
+ PrintOptions {
+ indent_width: 2,
+ max_width: 100,
+ use_tabs: false,
+ new_line_text: "\n",
+ },
+ )
+}
+
+macro_rules! assert_formatted {
+ ($input:literal, $output:literal) => {
+ let formatted = reformat(indoc!($input));
+ let mut expected = indoc!($output).to_owned();
+ expected.push('\n');
+ if formatted != expected {
+ panic!(
+ "bad formatting, expected\n```\n{formatted}\n```\nto be equal to\n```\n{expected}\n```",
+ )
+ }
+ };
+}
+
+#[test]
+fn padding_stripped_for_multiline_comment() {
+ assert_formatted!(
+ "{
+ /*
+ Hello
+ World
+ */
+ _: null,
+ }",
+ "{
+ /*
+ Hello
+ World
+ */
+ _: null,
+ }"
+ );
+}
+
+#[test]
+fn last_comment_respects_spacing_with_inline_comment_above() {
+ assert_formatted!(
+ "{
+ a: '', // Inline
+
+ // Comment
+ }",
+ "{
+ a: '', // Inline
+
+ // Comment
+ }"
+ );
+}
+
+#[test]
+fn complex_comments_snapshot() {
+ insta::assert_display_snapshot!(reformat(indoc!(
+ "{
+ comments: {
+ _: '',
+ // Plain comment
+ a: '',
+
+ # Plain comment with empty line before
+ b: '',
+ /*Single-line multiline comment
+
+ */
+ c: '',
+
+ /**Single-line multiline doc comment
+
+ */
+ c: '',
+
+ /**Multiline doc
+ Comment
+ */
+ c: '',
+
+ /*
+
+ Multi-line
+
+ comment
+ */
+ d: '',
+
+ e: '', // Inline comment
+
+ k: '',
+
+ // Text after everything
+ },
+ comments2: {
+ k: '',
+ // Text after everything, but no newline above
+ },
+ spacing: {
+ a: '',
+
+ b: '',
+ },
+ noSpacing: {
+ a: '',
+ b: '',
+ },
+ }"
+ )))
+}
cmds/jrsonnet-lsp/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-lsp/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "jrsonnet-lsp"
+version.workspace = true
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.48"
+jrsonnet-evaluator.workspace = true
+jrsonnet-rowan-parser.workspace = true
+lsp-server = "0.6.0"
+lsp-types = "0.93.0"
+serde = "1.0.130"
+serde_json = "1.0.71"
cmds/jrsonnet-lsp/src/main.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-lsp/src/main.rs
@@ -0,0 +1,188 @@
+use std::{fs::File, io::Write, path::PathBuf, str::FromStr};
+
+use lsp_server::{Connection, ErrorCode, Message, Request, RequestId, Response};
+use lsp_types::{
+ notification::{DidChangeTextDocument, DidOpenTextDocument, Notification},
+ request::{DocumentLinkRequest, HoverRequest},
+ CompletionOptions, DidChangeTextDocumentParams, DidOpenTextDocumentParams, DocumentLink,
+ DocumentLinkOptions, ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind,
+ TextDocumentSyncOptions, Url, WorkDoneProgressOptions,
+};
+
+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 {
+ reply(Response::new_err(
+ req.id,
+ ErrorCode::MethodNotFound as i32,
+ format!("unrecognized request {}", req.method),
+ ))
+ }
+ /*
+ 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
+ */
+ }
+ 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 (ast, errors) = jrsonnet_rowan_parser::parse(&text);
+ // 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()
+}
cmds/jrsonnet/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -12,10 +12,7 @@
# Use mimalloc as allocator
mimalloc = ["mimallocator"]
# Experimental feature, which allows to preserve order of object fields
-exp-preserve-order = [
- "jrsonnet-evaluator/exp-preserve-order",
- "jrsonnet-cli/exp-preserve-order",
-]
+exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order", "jrsonnet-cli/exp-preserve-order"]
# Destructuring of locals
exp-destruct = ["jrsonnet-evaluator/exp-destruct"]
# Iteration over objects yields [key, value] elements
@@ -44,3 +41,4 @@
clap_complete = { version = "4.1" }
serde_json = "1.0.104"
serde = { workspace = true, features = ["derive"] }
+ass-stroke = { git = "https://github.com/CertainLach/ass-stroke", version = "0.1.0" }
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -37,15 +37,15 @@
#[derive(Parser)]
#[clap(next_help_heading = "INPUT")]
struct InputOpts {
- /// Treat input as code, evaluate them instead of reading file
+ /// Treat input as code, evaluate it instead of reading file.
#[clap(long, short = 'e')]
pub exec: bool,
- /// Path to the file to be compiled if `--evaluate` is unset, otherwise code itself
+ /// Path to the file to be compiled if `--exec` is unset, otherwise code itself.
pub input: Option<String>,
/// After executing input, apply specified code.
- /// Output of the initial input will be accessible using `$`
+ /// Output of the initial input will be accessible using `_`.
#[cfg(feature = "exp-apply")]
#[clap(long)]
pub exp_apply: Vec<String>,
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -41,6 +41,10 @@
clippy::missing_const_for_fn,
// too many false-positives with .expect() calls
clippy::missing_panics_doc,
+ // false positive for IStr type. There is an configuration option for
+ // such cases, but it doesn't work:
+ // https://github.com/rust-lang/rust-clippy/issues/9801
+ clippy::mutable_key_type,
)]
// For jrsonnet-macros
crates/jrsonnet-rowan-parser/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "jrsonnet-rowan-parser"
+version.workspace = true
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0"
+backtrace = "0.3.63"
+drop_bomb = "0.1.5"
+indoc = "1.0"
+logos = "0.12"
+miette = { version = "5.5.0", features = ["fancy"] }
+rowan = "0.15"
+text-size = "1.1"
+thiserror = "1.0"
+
+[dev-dependencies]
+backtrace = "0.3.63"
+indoc = "1.0"
+insta = "1.15"
+anyhow = "1.0"
crates/jrsonnet-rowan-parser/jsonnet.ungramdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/jsonnet.ungram
@@ -0,0 +1,355 @@
+// This file describes structure of jsonnet source code
+// It is also used to generate files in src/generated
+
+// Token names ending with `!` are considered meta, and handled specifically
+
+SourceFile = Expr
+
+SuffixIndex =
+ '?'?
+ '.'
+ index:Name
+SuffixIndexExpr =
+ ('?' '.')?
+ '['
+ index:Expr
+ ']'
+SuffixSlice =
+ SliceDesc
+SuffixApply =
+ ArgsDesc
+ 'tailstrict'?
+Suffix =
+ SuffixIndex
+| SuffixIndexExpr
+| SuffixSlice
+| SuffixApply
+
+StmtLocal =
+ 'local'
+ (Bind (',' Bind)* ','?)
+ ';'
+StmtAssert =
+ Assertion
+ ';'
+Stmt =
+ StmtLocal
+| StmtAssert
+
+ExprBinary =
+ lhs:Expr
+ BinaryOperator
+ rhs:Expr
+ExprUnary =
+ UnaryOperator
+ rhs:Expr
+ExprObjExtend =
+ Expr
+ Expr
+ExprParened =
+ '('
+ Expr
+ ')'
+
+ExprLiteral =
+ Literal
+ExprString =
+ Text
+ExprNumber =
+ Number
+ExprArray =
+ '['
+ (Expr (',' Expr)* ','?)?
+ ']'
+ExprObject =
+ ObjBody
+ExprArrayComp =
+ '['
+ Expr
+ ','?
+ CompSpec*
+ ']'
+
+ExprImport =
+ ImportKind Text
+
+ImportKind =
+ 'importstr'
+| 'importbin'
+| 'import'
+
+ExprVar =
+ name:Name
+
+ExprIfThenElse =
+ 'if'
+ cond:Expr
+ 'then'
+ then:TrueExpr
+ ('else' else_:FalseExpr)?
+
+ExprFunction =
+ 'function'
+ '('
+ ParamsDesc
+ ')'
+ Expr
+ExprError =
+ 'error'
+ Expr
+
+Expr =
+ Stmt*
+ ExprBase
+ Suffix*
+
+ExprBase =
+ ExprBinary
+| ExprUnary
+| ExprObjExtend
+| ExprParened
+| ExprString
+| ExprNumber
+| ExprLiteral
+| ExprArray
+| ExprObject
+| ExprArrayComp
+| ExprImport
+| ExprVar
+| ExprIfThenElse
+| ExprFunction
+| ExprError
+
+BinaryOperator =
+ '||' | '??' | '&&'
+| '|' | '^' | '&'
+| '==' | '!=' | '<' | '>' | '<=' | '>=' | 'in'
+| '<<' | '>>'
+| '+' | '-'
+| '*' | '/' | '%'
+| 'META_OBJECT_APPLY!'
+| 'ERROR_NO_OPERATOR!'
+
+UnaryOperator =
+ '-' | '!' | '~'
+
+SliceDescEnd=Expr
+SliceDescStep=Expr
+SliceDesc =
+ '['
+ from:Expr?
+ ':'
+ (
+ end:SliceDescEnd?
+ (
+ ':'
+ step:SliceDescStep?
+ )?
+ )?
+ ']'
+
+Name =
+ 'LIT_IDENT!'
+
+ArgsDesc =
+ '('
+ (Arg (',' Arg)* ','?)?
+ ')'
+Arg =
+ (name:Name '=')? Expr
+
+ObjBodyComp =
+ '{'
+ (MemberComp (',' MemberComp)* ','?)?
+ CompSpec*
+ '}'
+ObjBodyMemberList =
+ '{'
+ (Member (',' Member)* ','?)?
+ '}'
+ObjBody =
+ ObjBodyComp
+| ObjBodyMemberList
+
+MemberBindStmt = ObjLocal
+MemberAssertStmt = Assertion
+MemberFieldNormal =
+ FieldName
+ '+'?
+ Visibility
+ Expr
+MemberFieldMethod =
+ FieldName
+ ParamsDesc
+ Visibility
+ Expr
+MemberComp =
+ MemberBindStmt
+| MemberFieldNormal
+| MemberFieldMethod
+Member =
+ MemberBindStmt
+| MemberAssertStmt
+| MemberFieldNormal
+| MemberFieldMethod
+
+ObjLocal =
+ 'local'
+ Bind
+
+FieldNameFixed =
+ id:Name
+| Text
+FieldNameDynamic =
+ '['
+ Expr
+ ']'
+FieldName =
+ FieldNameFixed
+| FieldNameDynamic
+
+Visibility =
+ ':::'
+| '::'
+| ':'
+
+Literal =
+ 'null'
+| 'true'
+| 'false'
+| 'self'
+| '$'
+| 'super'
+
+Text =
+ 'LIT_STRING_DOUBLE!'
+| 'ERROR_STRING_DOUBLE_UNTERMINATED!'
+| 'LIT_STRING_SINGLE!'
+| 'ERROR_STRING_SINGLE_UNTERMINATED!'
+| 'LIT_STRING_DOUBLE_VERBATIM!'
+| 'ERROR_STRING_DOUBLE_VERBATIM_UNTERMINATED!'
+| 'LIT_STRING_SINGLE_VERBATIM!'
+| 'ERROR_STRING_SINGLE_VERBATIM_UNTERMINATED!'
+| 'ERROR_STRING_VERBATIM_MISSING_QUOTES!'
+| 'LIT_STRING_BLOCK!'
+| 'ERROR_STRING_BLOCK_UNEXPECTED_END!'
+| 'ERROR_STRING_BLOCK_MISSING_NEW_LINE!'
+| 'ERROR_STRING_BLOCK_MISSING_TERMINATION!'
+| 'ERROR_STRING_BLOCK_MISSING_INDENT!'
+
+Number =
+ 'LIT_FLOAT!'
+| 'ERROR_FLOAT_JUNK_AFTER_POINT!'
+| 'ERROR_FLOAT_JUNK_AFTER_EXPONENT!'
+| 'ERROR_FLOAT_JUNK_AFTER_EXPONENT_SIGN!'
+
+ForSpec =
+ 'for'
+ bind:Destruct
+ 'in'
+ Expr
+IfSpec =
+ 'if'
+ Expr
+CompSpec =
+ ForSpec
+| IfSpec
+
+BindDestruct =
+ into:Destruct
+ '='
+ value:Expr
+BindFunction =
+ name:Name
+ params:ParamsDesc
+ '='
+ value:Expr
+Bind =
+ BindDestruct
+| BindFunction
+
+ParamsDesc =
+ '('
+ (Param (',' Param)* ','?)?
+ ')'
+Param =
+ Destruct
+ (
+ '='
+ Expr
+ )?
+
+Assertion =
+ 'assert'
+ condition:Expr
+ (
+ ':'
+ message:Expr
+ )?
+
+DestructFull =
+ Name
+DestructSkip =
+ '?'
+DestructArray =
+ '['
+ (
+ DestructArrayPart
+ (',' DestructArrayPart)*
+ ','?
+ )?
+ ']'
+DestructObject =
+ '{'
+ (
+ DestructObjectField
+ (',' DestructObjectField)*
+ ','?
+ )?
+ DestructRest?
+ ','?
+ '}'
+Destruct =
+ DestructFull
+| DestructSkip
+| DestructArray
+| DestructObject
+
+DestructArrayElement =
+ Destruct
+DestructArrayPart =
+ DestructArrayElement
+| DestructRest
+
+DestructRest =
+ '...'
+ into:Name?
+
+DestructObjectField =
+ field:Name
+ (
+ ':'
+ Destruct
+ )?
+ (
+ '='
+ Expr
+ )?
+
+// Aliases used to resolve node type conflicts
+TrueExpr=Expr
+FalseExpr=Expr
+
+// Trivia - tokens which will be implicitly skipped for parser
+Trivia =
+ 'LIT_WHITESPACE!'
+| 'LIT_MULTI_LINE_COMMENT!'
+| 'ERROR_COMMENT_TOO_SHORT!'
+| 'ERROR_COMMENT_UNTERMINATED!'
+| 'LIT_SINGLE_LINE_HASH_COMMENT!'
+| 'LIT_SINGLE_LINE_SLASH_COMMENT!'
+
+CustomError =
+ 'ERROR_MISSING_TOKEN!'
+| 'ERROR_UNEXPECTED_TOKEN!'
+| 'ERROR_CUSTOM!'
crates/jrsonnet-rowan-parser/src/ast.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/ast.rs
@@ -0,0 +1,95 @@
+use std::marker::PhantomData;
+
+use crate::{SyntaxKind, SyntaxNode, SyntaxNodeChildren, SyntaxToken};
+
+/// The main trait to go from untyped `SyntaxNode` to a typed ast. The
+/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
+/// the same representation: a pointer to the tree root and a pointer to the
+/// node itself.
+pub trait AstNode {
+ fn can_cast(kind: SyntaxKind) -> bool
+ where
+ Self: Sized;
+
+ fn cast(syntax: SyntaxNode) -> Option<Self>
+ where
+ Self: Sized;
+
+ fn syntax(&self) -> &SyntaxNode;
+ fn clone_for_update(&self) -> Self
+ where
+ Self: Sized,
+ {
+ Self::cast(self.syntax().clone_for_update()).unwrap()
+ }
+ fn clone_subtree(&self) -> Self
+ where
+ Self: Sized,
+ {
+ Self::cast(self.syntax().clone_subtree()).unwrap()
+ }
+}
+
+/// Like `AstNode`, but wraps tokens rather than interior nodes.
+pub trait AstToken {
+ fn can_cast(token: SyntaxKind) -> bool
+ where
+ Self: Sized;
+
+ fn cast(syntax: SyntaxToken) -> Option<Self>
+ where
+ Self: Sized;
+
+ fn syntax(&self) -> &SyntaxToken;
+
+ fn text(&self) -> &str {
+ self.syntax().text()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct AstChildren<N> {
+ inner: SyntaxNodeChildren,
+ ph: PhantomData<N>,
+}
+
+impl<N> AstChildren<N> {
+ fn new(parent: &SyntaxNode) -> Self {
+ AstChildren {
+ inner: parent.children(),
+ ph: PhantomData,
+ }
+ }
+}
+
+impl<N: AstNode> Iterator for AstChildren<N> {
+ type Item = N;
+ fn next(&mut self) -> Option<N> {
+ self.inner.find_map(N::cast)
+ }
+}
+
+pub mod support {
+ use super::{AstChildren, AstNode, AstToken, SyntaxKind, SyntaxNode, SyntaxToken};
+
+ pub fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
+ parent.children().find_map(N::cast)
+ }
+ pub fn token_child<N: AstToken>(parent: &SyntaxNode) -> Option<N> {
+ parent.children_with_tokens().find_map(|n| match n {
+ rowan::NodeOrToken::Node(_) => None,
+ rowan::NodeOrToken::Token(t) => N::cast(t),
+ })
+ }
+
+ pub fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
+ AstChildren::new(parent)
+ }
+
+ pub fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
+ parent
+ .children_with_tokens()
+ .filter_map(|it| it.into_token())
+ .find(|it| it.kind() == kind)
+ }
+}
crates/jrsonnet-rowan-parser/src/event.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/event.rs
@@ -0,0 +1,204 @@
+use std::{mem, num::NonZeroUsize};
+
+use rowan::{GreenNodeBuilder, Language, TextRange, TextSize};
+
+use crate::{
+ lex::Lexeme,
+ nodes::Trivia,
+ parser::{LocatedSyntaxError, Parse, SyntaxError},
+ AstToken, JsonnetLanguage, SyntaxKind,
+};
+
+#[derive(Clone, Debug)]
+pub enum Event {
+ /// Used for unfinished markers
+ Pending,
+ /// After marker is completed, Pending event is replaced with Start
+ Start {
+ kind: SyntaxKind,
+ /// If marker is preceded or wrapped - instead of reordering events, we
+ /// insert start event in the end of events Vec instead, and store relative offset to this event here
+ forward_parent: Option<NonZeroUsize>,
+ },
+ /// Eat token
+ Token { kind: SyntaxKind },
+ /// Push token, but do not eat anything,
+ VirtualToken { kind: SyntaxKind },
+ /// Position of finished node
+ Finish {
+ /// Same as forward_parent of Start, but for wrapping
+ wrapper: Option<NonZeroUsize>,
+ error: Option<Box<SyntaxError>>,
+ },
+ /// Used for dropped markers and other things
+ Noop,
+}
+
+pub(super) struct Sink<'i> {
+ pub builder: GreenNodeBuilder<'static>,
+ lexemes: &'i [Lexeme<'i>],
+ offset: usize,
+ events: Vec<Event>,
+ pub errors: Vec<LocatedSyntaxError>,
+}
+
+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![],
+ }
+ }
+
+ fn text_offset(&self) -> TextSize {
+ if self.offset == 0 {
+ return 0.into();
+ };
+ if let Some(lex) = self.lexemes.get(self.offset) {
+ lex.range.start()
+ } else if let Some(lex) = self.lexemes.get(self.offset - 1) {
+ lex.range.end()
+ } else {
+ panic!("hard oob")
+ }
+ }
+
+ pub(super) fn finish(mut self) -> Parse {
+ let mut eat_start_whitespace = false;
+ let mut depth = 0;
+ let mut error_starts_at = Vec::new();
+ for idx in 0..self.events.len() {
+ match mem::replace(&mut self.events[idx], Event::Noop) {
+ Event::Start {
+ kind,
+ forward_parent,
+ } => {
+ if depth != 0 {
+ self.skip_whitespace();
+ }
+ 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.get();
+
+ forward_parent = if let Event::Start {
+ kind,
+ forward_parent,
+ } = mem::replace(&mut self.events[idx], Event::Noop)
+ {
+ kinds.push(kind);
+ forward_parent
+ } else {
+ unreachable!()
+ };
+ }
+
+ for kind in kinds.into_iter().rev() {
+ self.builder.start_node(JsonnetLanguage::kind_to_raw(kind));
+ depth += 1;
+ if depth == 1 {
+ self.skip_whitespace();
+ }
+ error_starts_at.push(self.text_offset());
+ }
+
+ eat_start_whitespace = false;
+ }
+ Event::Token { kind } => {
+ if eat_start_whitespace {
+ self.skip_whitespace();
+ }
+ self.token(kind);
+ eat_start_whitespace = true;
+ }
+ Event::VirtualToken { kind } => {
+ if eat_start_whitespace {
+ self.skip_whitespace();
+ }
+ self.virtual_token(kind);
+ eat_start_whitespace = false;
+ }
+ Event::Finish { wrapper, error } => {
+ if depth == 1 {
+ self.skip_whitespace();
+ }
+ let range = (
+ error_starts_at.pop().expect("starts == finishes"),
+ self.text_offset(),
+ );
+ if let Some(error) = error {
+ self.errors.push(LocatedSyntaxError {
+ error: *error,
+ range: TextRange::new(range.0, range.1),
+ })
+ }
+ self.builder.finish_node();
+ depth -= 1;
+ let mut idx = idx;
+ let mut wrapper = wrapper;
+ while let Some(w) = wrapper {
+ idx += w.get();
+ wrapper = if let Event::Finish { wrapper, error } =
+ mem::replace(&mut self.events[idx], Event::Noop)
+ {
+ let range = (
+ error_starts_at.pop().expect("starts == finishes"),
+ self.text_offset(),
+ );
+ if let Some(error) = error {
+ self.errors.push(LocatedSyntaxError {
+ error: *error,
+ range: TextRange::new(range.0, range.1),
+ })
+ }
+
+ if depth == 1 {
+ self.skip_whitespace();
+ }
+ self.builder.finish_node();
+ depth -= 1;
+ wrapper
+ } else {
+ unreachable!()
+ }
+ }
+ eat_start_whitespace = true;
+ }
+ Event::Pending => panic!("pending event should not appear in finished events"),
+ Event::Noop => {}
+ }
+ }
+
+ Parse {
+ green_node: self.builder.finish(),
+ errors: self.errors,
+ }
+ }
+ fn virtual_token(&mut self, kind: SyntaxKind) {
+ self.builder.token(JsonnetLanguage::kind_to_raw(kind), "")
+ }
+ fn token(&mut self, kind: SyntaxKind) {
+ let lexeme = self.lexemes[self.offset];
+ self.builder
+ .token(JsonnetLanguage::kind_to_raw(kind), lexeme.text);
+ self.offset += 1;
+ }
+ fn skip_whitespace(&mut self) {
+ while let Some(lexeme) = self.lexemes.get(self.offset) {
+ if !Trivia::can_cast(lexeme.kind) {
+ break;
+ }
+
+ self.token(lexeme.kind);
+ }
+ }
+}
crates/jrsonnet-rowan-parser/src/generated/mod.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/generated/mod.rs
@@ -0,0 +1,2 @@
+pub mod nodes;
+pub mod syntax_kinds;
crates/jrsonnet-rowan-parser/src/generated/nodes.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/generated/nodes.rs
@@ -0,0 +1,3092 @@
+//! This is a generated file, please do not edit manually. Changes can be
+//! made in codegeneration that lives in `xtask` top-level dir.
+
+#![allow(non_snake_case, clippy::match_like_matches_macro)]
+use crate::{
+ ast::{support, AstChildren, AstNode, AstToken},
+ SyntaxKind::{self, *},
+ SyntaxNode, SyntaxToken, T,
+};
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SourceFile {
+ pub(crate) syntax: SyntaxNode,
+}
+impl SourceFile {
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Expr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl Expr {
+ pub fn stmts(&self) -> AstChildren<Stmt> {
+ support::children(&self.syntax)
+ }
+ pub fn expr_base(&self) -> Option<ExprBase> {
+ support::child(&self.syntax)
+ }
+ pub fn suffixs(&self) -> AstChildren<Suffix> {
+ support::children(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SuffixIndex {
+ pub(crate) syntax: SyntaxNode,
+}
+impl SuffixIndex {
+ pub fn question_mark_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![?])
+ }
+ pub fn dot_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![.])
+ }
+ pub fn index(&self) -> Option<Name> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Name {
+ pub(crate) syntax: SyntaxNode,
+}
+impl Name {
+ pub fn ident_lit(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, IDENT)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SuffixIndexExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl SuffixIndexExpr {
+ pub fn question_mark_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![?])
+ }
+ pub fn dot_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![.])
+ }
+ pub fn l_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['['])
+ }
+ pub fn index(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+ pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![']'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SuffixSlice {
+ pub(crate) syntax: SyntaxNode,
+}
+impl SuffixSlice {
+ pub fn slice_desc(&self) -> Option<SliceDesc> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SliceDesc {
+ pub(crate) syntax: SyntaxNode,
+}
+impl SliceDesc {
+ pub fn l_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['['])
+ }
+ pub fn from(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+ pub fn colon_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![:])
+ }
+ pub fn end(&self) -> Option<SliceDescEnd> {
+ support::child(&self.syntax)
+ }
+ pub fn step(&self) -> Option<SliceDescStep> {
+ support::child(&self.syntax)
+ }
+ pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![']'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SuffixApply {
+ pub(crate) syntax: SyntaxNode,
+}
+impl SuffixApply {
+ pub fn args_desc(&self) -> Option<ArgsDesc> {
+ support::child(&self.syntax)
+ }
+ pub fn tailstrict_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![tailstrict])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ArgsDesc {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ArgsDesc {
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['('])
+ }
+ pub fn args(&self) -> AstChildren<Arg> {
+ support::children(&self.syntax)
+ }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![')'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct StmtLocal {
+ pub(crate) syntax: SyntaxNode,
+}
+impl StmtLocal {
+ pub fn local_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![local])
+ }
+ pub fn binds(&self) -> AstChildren<Bind> {
+ support::children(&self.syntax)
+ }
+ pub fn semi_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![;])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct StmtAssert {
+ pub(crate) syntax: SyntaxNode,
+}
+impl StmtAssert {
+ pub fn assertion(&self) -> Option<Assertion> {
+ support::child(&self.syntax)
+ }
+ pub fn semi_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![;])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Assertion {
+ pub(crate) syntax: SyntaxNode,
+}
+impl Assertion {
+ pub fn assert_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![assert])
+ }
+ pub fn condition(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+ pub fn colon_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![:])
+ }
+ pub fn message(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprBinary {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprBinary {
+ pub fn lhs(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+ pub fn binary_operator(&self) -> Option<BinaryOperator> {
+ support::token_child(&self.syntax)
+ }
+ pub fn rhs(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprUnary {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprUnary {
+ pub fn unary_operator(&self) -> Option<UnaryOperator> {
+ support::token_child(&self.syntax)
+ }
+ pub fn rhs(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprObjExtend {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprObjExtend {
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprParened {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprParened {
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['('])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![')'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprLiteral {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprLiteral {
+ pub fn literal(&self) -> Option<Literal> {
+ support::token_child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprString {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprString {
+ pub fn text(&self) -> Option<Text> {
+ support::token_child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprNumber {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprNumber {
+ pub fn number(&self) -> Option<Number> {
+ support::token_child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprArray {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprArray {
+ pub fn l_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['['])
+ }
+ pub fn exprs(&self) -> AstChildren<Expr> {
+ support::children(&self.syntax)
+ }
+ pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![']'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprObject {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprObject {
+ pub fn obj_body(&self) -> Option<ObjBody> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprArrayComp {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprArrayComp {
+ pub fn l_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['['])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+ pub fn comma_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![,])
+ }
+ pub fn comp_specs(&self) -> AstChildren<CompSpec> {
+ support::children(&self.syntax)
+ }
+ pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![']'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprImport {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprImport {
+ pub fn import_kind(&self) -> Option<ImportKind> {
+ support::token_child(&self.syntax)
+ }
+ pub fn text(&self) -> Option<Text> {
+ support::token_child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprVar {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprVar {
+ pub fn name(&self) -> Option<Name> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprIfThenElse {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprIfThenElse {
+ pub fn if_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![if])
+ }
+ pub fn cond(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+ pub fn then_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![then])
+ }
+ pub fn then(&self) -> Option<TrueExpr> {
+ support::child(&self.syntax)
+ }
+ pub fn else_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![else])
+ }
+ pub fn else_(&self) -> Option<FalseExpr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct TrueExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl TrueExpr {
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FalseExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl FalseExpr {
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprFunction {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprFunction {
+ pub fn function_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![function])
+ }
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['('])
+ }
+ pub fn params_desc(&self) -> Option<ParamsDesc> {
+ support::child(&self.syntax)
+ }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![')'])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ParamsDesc {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ParamsDesc {
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['('])
+ }
+ pub fn params(&self) -> AstChildren<Param> {
+ support::children(&self.syntax)
+ }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![')'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprError {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ExprError {
+ pub fn error_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![error])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SliceDescEnd {
+ pub(crate) syntax: SyntaxNode,
+}
+impl SliceDescEnd {
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SliceDescStep {
+ pub(crate) syntax: SyntaxNode,
+}
+impl SliceDescStep {
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Arg {
+ pub(crate) syntax: SyntaxNode,
+}
+impl Arg {
+ pub fn name(&self) -> Option<Name> {
+ support::child(&self.syntax)
+ }
+ pub fn assign_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![=])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ObjBodyComp {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ObjBodyComp {
+ pub fn l_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['{'])
+ }
+ pub fn member_comps(&self) -> AstChildren<MemberComp> {
+ support::children(&self.syntax)
+ }
+ pub fn comp_specs(&self) -> AstChildren<CompSpec> {
+ support::children(&self.syntax)
+ }
+ pub fn r_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['}'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ObjBodyMemberList {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ObjBodyMemberList {
+ pub fn l_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['{'])
+ }
+ pub fn members(&self) -> AstChildren<Member> {
+ support::children(&self.syntax)
+ }
+ pub fn r_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['}'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MemberBindStmt {
+ pub(crate) syntax: SyntaxNode,
+}
+impl MemberBindStmt {
+ pub fn obj_local(&self) -> Option<ObjLocal> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ObjLocal {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ObjLocal {
+ pub fn local_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![local])
+ }
+ pub fn bind(&self) -> Option<Bind> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MemberAssertStmt {
+ pub(crate) syntax: SyntaxNode,
+}
+impl MemberAssertStmt {
+ pub fn assertion(&self) -> Option<Assertion> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MemberFieldNormal {
+ pub(crate) syntax: SyntaxNode,
+}
+impl MemberFieldNormal {
+ pub fn field_name(&self) -> Option<FieldName> {
+ support::child(&self.syntax)
+ }
+ pub fn plus_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![+])
+ }
+ pub fn visibility(&self) -> Option<Visibility> {
+ support::token_child(&self.syntax)
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MemberFieldMethod {
+ pub(crate) syntax: SyntaxNode,
+}
+impl MemberFieldMethod {
+ pub fn field_name(&self) -> Option<FieldName> {
+ support::child(&self.syntax)
+ }
+ pub fn params_desc(&self) -> Option<ParamsDesc> {
+ support::child(&self.syntax)
+ }
+ pub fn visibility(&self) -> Option<Visibility> {
+ support::token_child(&self.syntax)
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FieldNameFixed {
+ pub(crate) syntax: SyntaxNode,
+}
+impl FieldNameFixed {
+ pub fn id(&self) -> Option<Name> {
+ support::child(&self.syntax)
+ }
+ pub fn text(&self) -> Option<Text> {
+ support::token_child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FieldNameDynamic {
+ pub(crate) syntax: SyntaxNode,
+}
+impl FieldNameDynamic {
+ pub fn l_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['['])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+ pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![']'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ForSpec {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ForSpec {
+ pub fn for_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![for])
+ }
+ pub fn bind(&self) -> Option<Destruct> {
+ support::child(&self.syntax)
+ }
+ pub fn in_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![in])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct IfSpec {
+ pub(crate) syntax: SyntaxNode,
+}
+impl IfSpec {
+ pub fn if_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![if])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct BindDestruct {
+ pub(crate) syntax: SyntaxNode,
+}
+impl BindDestruct {
+ pub fn into(&self) -> Option<Destruct> {
+ support::child(&self.syntax)
+ }
+ pub fn assign_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![=])
+ }
+ pub fn value(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct BindFunction {
+ pub(crate) syntax: SyntaxNode,
+}
+impl BindFunction {
+ pub fn name(&self) -> Option<Name> {
+ support::child(&self.syntax)
+ }
+ pub fn params(&self) -> Option<ParamsDesc> {
+ support::child(&self.syntax)
+ }
+ pub fn assign_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![=])
+ }
+ pub fn value(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Param {
+ pub(crate) syntax: SyntaxNode,
+}
+impl Param {
+ pub fn destruct(&self) -> Option<Destruct> {
+ support::child(&self.syntax)
+ }
+ pub fn assign_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![=])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructFull {
+ pub(crate) syntax: SyntaxNode,
+}
+impl DestructFull {
+ pub fn name(&self) -> Option<Name> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructSkip {
+ pub(crate) syntax: SyntaxNode,
+}
+impl DestructSkip {
+ pub fn question_mark_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![?])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructArray {
+ pub(crate) syntax: SyntaxNode,
+}
+impl DestructArray {
+ pub fn l_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['['])
+ }
+ pub fn destruct_array_parts(&self) -> AstChildren<DestructArrayPart> {
+ support::children(&self.syntax)
+ }
+ pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![']'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructObject {
+ pub(crate) syntax: SyntaxNode,
+}
+impl DestructObject {
+ pub fn l_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['{'])
+ }
+ pub fn destruct_object_fields(&self) -> AstChildren<DestructObjectField> {
+ support::children(&self.syntax)
+ }
+ pub fn destruct_rest(&self) -> Option<DestructRest> {
+ support::child(&self.syntax)
+ }
+ pub fn comma_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![,])
+ }
+ pub fn r_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['}'])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructObjectField {
+ pub(crate) syntax: SyntaxNode,
+}
+impl DestructObjectField {
+ pub fn field(&self) -> Option<Name> {
+ support::child(&self.syntax)
+ }
+ pub fn colon_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![:])
+ }
+ pub fn destruct(&self) -> Option<Destruct> {
+ support::child(&self.syntax)
+ }
+ pub fn assign_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![=])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructRest {
+ pub(crate) syntax: SyntaxNode,
+}
+impl DestructRest {
+ pub fn dotdotdot_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![...])
+ }
+ pub fn into(&self) -> Option<Name> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructArrayElement {
+ pub(crate) syntax: SyntaxNode,
+}
+impl DestructArrayElement {
+ pub fn destruct(&self) -> Option<Destruct> {
+ support::child(&self.syntax)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Suffix {
+ SuffixIndex(SuffixIndex),
+ SuffixIndexExpr(SuffixIndexExpr),
+ SuffixSlice(SuffixSlice),
+ SuffixApply(SuffixApply),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Bind {
+ BindDestruct(BindDestruct),
+ BindFunction(BindFunction),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Stmt {
+ StmtLocal(StmtLocal),
+ StmtAssert(StmtAssert),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ObjBody {
+ ObjBodyComp(ObjBodyComp),
+ ObjBodyMemberList(ObjBodyMemberList),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum CompSpec {
+ ForSpec(ForSpec),
+ IfSpec(IfSpec),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ExprBase {
+ ExprBinary(ExprBinary),
+ ExprUnary(ExprUnary),
+ ExprObjExtend(ExprObjExtend),
+ ExprParened(ExprParened),
+ ExprString(ExprString),
+ ExprNumber(ExprNumber),
+ ExprLiteral(ExprLiteral),
+ ExprArray(ExprArray),
+ ExprObject(ExprObject),
+ ExprArrayComp(ExprArrayComp),
+ ExprImport(ExprImport),
+ ExprVar(ExprVar),
+ ExprIfThenElse(ExprIfThenElse),
+ ExprFunction(ExprFunction),
+ ExprError(ExprError),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum MemberComp {
+ MemberBindStmt(MemberBindStmt),
+ MemberFieldNormal(MemberFieldNormal),
+ MemberFieldMethod(MemberFieldMethod),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Member {
+ MemberBindStmt(MemberBindStmt),
+ MemberAssertStmt(MemberAssertStmt),
+ MemberFieldNormal(MemberFieldNormal),
+ MemberFieldMethod(MemberFieldMethod),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum FieldName {
+ FieldNameFixed(FieldNameFixed),
+ FieldNameDynamic(FieldNameDynamic),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Destruct {
+ DestructFull(DestructFull),
+ DestructSkip(DestructSkip),
+ DestructArray(DestructArray),
+ DestructObject(DestructObject),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum DestructArrayPart {
+ DestructArrayElement(DestructArrayElement),
+ DestructRest(DestructRest),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct BinaryOperator {
+ syntax: SyntaxToken,
+ kind: BinaryOperatorKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum BinaryOperatorKind {
+ Or,
+ NullCoaelse,
+ And,
+ BitOr,
+ BitXor,
+ BitAnd,
+ Eq,
+ Ne,
+ Lt,
+ Gt,
+ Le,
+ Ge,
+ InKw,
+ Lhs,
+ Rhs,
+ Plus,
+ Minus,
+ Mul,
+ Div,
+ Modulo,
+ MetaObjectApply,
+ ErrorNoOperator,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct UnaryOperator {
+ syntax: SyntaxToken,
+ kind: UnaryOperatorKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum UnaryOperatorKind {
+ Minus,
+ Not,
+ BitNot,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Literal {
+ syntax: SyntaxToken,
+ kind: LiteralKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum LiteralKind {
+ NullKw,
+ TrueKw,
+ FalseKw,
+ SelfKw,
+ Dollar,
+ SuperKw,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Text {
+ syntax: SyntaxToken,
+ kind: TextKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum TextKind {
+ StringDouble,
+ ErrorStringDoubleUnterminated,
+ StringSingle,
+ ErrorStringSingleUnterminated,
+ StringDoubleVerbatim,
+ ErrorStringDoubleVerbatimUnterminated,
+ StringSingleVerbatim,
+ ErrorStringSingleVerbatimUnterminated,
+ ErrorStringVerbatimMissingQuotes,
+ StringBlock,
+ ErrorStringBlockUnexpectedEnd,
+ ErrorStringBlockMissingNewLine,
+ ErrorStringBlockMissingTermination,
+ ErrorStringBlockMissingIndent,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Number {
+ syntax: SyntaxToken,
+ kind: NumberKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum NumberKind {
+ Float,
+ ErrorFloatJunkAfterPoint,
+ ErrorFloatJunkAfterExponent,
+ ErrorFloatJunkAfterExponentSign,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ImportKind {
+ syntax: SyntaxToken,
+ kind: ImportKindKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum ImportKindKind {
+ ImportstrKw,
+ ImportbinKw,
+ ImportKw,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Visibility {
+ syntax: SyntaxToken,
+ kind: VisibilityKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum VisibilityKind {
+ Coloncoloncolon,
+ Coloncolon,
+ Colon,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Trivia {
+ syntax: SyntaxToken,
+ kind: TriviaKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum TriviaKind {
+ Whitespace,
+ MultiLineComment,
+ ErrorCommentTooShort,
+ ErrorCommentUnterminated,
+ SingleLineHashComment,
+ SingleLineSlashComment,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct CustomError {
+ syntax: SyntaxToken,
+ kind: CustomErrorKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum CustomErrorKind {
+ ErrorMissingToken,
+ ErrorUnexpectedToken,
+ ErrorCustom,
+}
+impl AstNode for SourceFile {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == SOURCE_FILE
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for Expr {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for SuffixIndex {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == SUFFIX_INDEX
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for Name {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == NAME
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for SuffixIndexExpr {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == SUFFIX_INDEX_EXPR
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for SuffixSlice {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == SUFFIX_SLICE
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for SliceDesc {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == SLICE_DESC
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for SuffixApply {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == SUFFIX_APPLY
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ArgsDesc {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == ARGS_DESC
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for StmtLocal {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == STMT_LOCAL
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for StmtAssert {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == STMT_ASSERT
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for Assertion {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == ASSERTION
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprBinary {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_BINARY
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprUnary {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_UNARY
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprObjExtend {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_OBJ_EXTEND
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprParened {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_PARENED
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprLiteral {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_LITERAL
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprString {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_STRING
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprNumber {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_NUMBER
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprArray {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_ARRAY
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprObject {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_OBJECT
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprArrayComp {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_ARRAY_COMP
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprImport {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_IMPORT
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprVar {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_VAR
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprIfThenElse {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_IF_THEN_ELSE
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for TrueExpr {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == TRUE_EXPR
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for FalseExpr {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == FALSE_EXPR
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprFunction {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_FUNCTION
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ParamsDesc {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == PARAMS_DESC
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ExprError {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == EXPR_ERROR
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for SliceDescEnd {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == SLICE_DESC_END
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for SliceDescStep {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == SLICE_DESC_STEP
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for Arg {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == ARG
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ObjBodyComp {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == OBJ_BODY_COMP
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ObjBodyMemberList {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == OBJ_BODY_MEMBER_LIST
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for MemberBindStmt {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == MEMBER_BIND_STMT
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ObjLocal {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == OBJ_LOCAL
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for MemberAssertStmt {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == MEMBER_ASSERT_STMT
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for MemberFieldNormal {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == MEMBER_FIELD_NORMAL
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for MemberFieldMethod {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == MEMBER_FIELD_METHOD
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for FieldNameFixed {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == FIELD_NAME_FIXED
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for FieldNameDynamic {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == FIELD_NAME_DYNAMIC
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for ForSpec {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == FOR_SPEC
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for IfSpec {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == IF_SPEC
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for BindDestruct {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == BIND_DESTRUCT
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for BindFunction {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == BIND_FUNCTION
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for Param {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == PARAM
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for DestructFull {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == DESTRUCT_FULL
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for DestructSkip {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == DESTRUCT_SKIP
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for DestructArray {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == DESTRUCT_ARRAY
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for DestructObject {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == DESTRUCT_OBJECT
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for DestructObjectField {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == DESTRUCT_OBJECT_FIELD
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for DestructRest {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == DESTRUCT_REST
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl AstNode for DestructArrayElement {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == DESTRUCT_ARRAY_ELEMENT
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+}
+impl From<SuffixIndex> for Suffix {
+ fn from(node: SuffixIndex) -> Suffix {
+ Suffix::SuffixIndex(node)
+ }
+}
+impl From<SuffixIndexExpr> for Suffix {
+ fn from(node: SuffixIndexExpr) -> Suffix {
+ Suffix::SuffixIndexExpr(node)
+ }
+}
+impl From<SuffixSlice> for Suffix {
+ fn from(node: SuffixSlice) -> Suffix {
+ Suffix::SuffixSlice(node)
+ }
+}
+impl From<SuffixApply> for Suffix {
+ fn from(node: SuffixApply) -> Suffix {
+ Suffix::SuffixApply(node)
+ }
+}
+impl AstNode for Suffix {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ SUFFIX_INDEX | SUFFIX_INDEX_EXPR | SUFFIX_SLICE | SUFFIX_APPLY => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ SUFFIX_INDEX => Suffix::SuffixIndex(SuffixIndex { syntax }),
+ SUFFIX_INDEX_EXPR => Suffix::SuffixIndexExpr(SuffixIndexExpr { syntax }),
+ SUFFIX_SLICE => Suffix::SuffixSlice(SuffixSlice { syntax }),
+ SUFFIX_APPLY => Suffix::SuffixApply(SuffixApply { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ Suffix::SuffixIndex(it) => &it.syntax,
+ Suffix::SuffixIndexExpr(it) => &it.syntax,
+ Suffix::SuffixSlice(it) => &it.syntax,
+ Suffix::SuffixApply(it) => &it.syntax,
+ }
+ }
+}
+impl From<BindDestruct> for Bind {
+ fn from(node: BindDestruct) -> Bind {
+ Bind::BindDestruct(node)
+ }
+}
+impl From<BindFunction> for Bind {
+ fn from(node: BindFunction) -> Bind {
+ Bind::BindFunction(node)
+ }
+}
+impl AstNode for Bind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ BIND_DESTRUCT | BIND_FUNCTION => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ BIND_DESTRUCT => Bind::BindDestruct(BindDestruct { syntax }),
+ BIND_FUNCTION => Bind::BindFunction(BindFunction { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ Bind::BindDestruct(it) => &it.syntax,
+ Bind::BindFunction(it) => &it.syntax,
+ }
+ }
+}
+impl From<StmtLocal> for Stmt {
+ fn from(node: StmtLocal) -> Stmt {
+ Stmt::StmtLocal(node)
+ }
+}
+impl From<StmtAssert> for Stmt {
+ fn from(node: StmtAssert) -> Stmt {
+ Stmt::StmtAssert(node)
+ }
+}
+impl AstNode for Stmt {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ STMT_LOCAL | STMT_ASSERT => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ STMT_LOCAL => Stmt::StmtLocal(StmtLocal { syntax }),
+ STMT_ASSERT => Stmt::StmtAssert(StmtAssert { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ Stmt::StmtLocal(it) => &it.syntax,
+ Stmt::StmtAssert(it) => &it.syntax,
+ }
+ }
+}
+impl From<ObjBodyComp> for ObjBody {
+ fn from(node: ObjBodyComp) -> ObjBody {
+ ObjBody::ObjBodyComp(node)
+ }
+}
+impl From<ObjBodyMemberList> for ObjBody {
+ fn from(node: ObjBodyMemberList) -> ObjBody {
+ ObjBody::ObjBodyMemberList(node)
+ }
+}
+impl AstNode for ObjBody {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ OBJ_BODY_COMP | OBJ_BODY_MEMBER_LIST => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ OBJ_BODY_COMP => ObjBody::ObjBodyComp(ObjBodyComp { syntax }),
+ OBJ_BODY_MEMBER_LIST => ObjBody::ObjBodyMemberList(ObjBodyMemberList { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ ObjBody::ObjBodyComp(it) => &it.syntax,
+ ObjBody::ObjBodyMemberList(it) => &it.syntax,
+ }
+ }
+}
+impl From<ForSpec> for CompSpec {
+ fn from(node: ForSpec) -> CompSpec {
+ CompSpec::ForSpec(node)
+ }
+}
+impl From<IfSpec> for CompSpec {
+ fn from(node: IfSpec) -> CompSpec {
+ CompSpec::IfSpec(node)
+ }
+}
+impl AstNode for CompSpec {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ FOR_SPEC | IF_SPEC => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ FOR_SPEC => CompSpec::ForSpec(ForSpec { syntax }),
+ IF_SPEC => CompSpec::IfSpec(IfSpec { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ CompSpec::ForSpec(it) => &it.syntax,
+ CompSpec::IfSpec(it) => &it.syntax,
+ }
+ }
+}
+impl From<ExprBinary> for ExprBase {
+ fn from(node: ExprBinary) -> ExprBase {
+ ExprBase::ExprBinary(node)
+ }
+}
+impl From<ExprUnary> for ExprBase {
+ fn from(node: ExprUnary) -> ExprBase {
+ ExprBase::ExprUnary(node)
+ }
+}
+impl From<ExprObjExtend> for ExprBase {
+ fn from(node: ExprObjExtend) -> ExprBase {
+ ExprBase::ExprObjExtend(node)
+ }
+}
+impl From<ExprParened> for ExprBase {
+ fn from(node: ExprParened) -> ExprBase {
+ ExprBase::ExprParened(node)
+ }
+}
+impl From<ExprString> for ExprBase {
+ fn from(node: ExprString) -> ExprBase {
+ ExprBase::ExprString(node)
+ }
+}
+impl From<ExprNumber> for ExprBase {
+ fn from(node: ExprNumber) -> ExprBase {
+ ExprBase::ExprNumber(node)
+ }
+}
+impl From<ExprLiteral> for ExprBase {
+ fn from(node: ExprLiteral) -> ExprBase {
+ ExprBase::ExprLiteral(node)
+ }
+}
+impl From<ExprArray> for ExprBase {
+ fn from(node: ExprArray) -> ExprBase {
+ ExprBase::ExprArray(node)
+ }
+}
+impl From<ExprObject> for ExprBase {
+ fn from(node: ExprObject) -> ExprBase {
+ ExprBase::ExprObject(node)
+ }
+}
+impl From<ExprArrayComp> for ExprBase {
+ fn from(node: ExprArrayComp) -> ExprBase {
+ ExprBase::ExprArrayComp(node)
+ }
+}
+impl From<ExprImport> for ExprBase {
+ fn from(node: ExprImport) -> ExprBase {
+ ExprBase::ExprImport(node)
+ }
+}
+impl From<ExprVar> for ExprBase {
+ fn from(node: ExprVar) -> ExprBase {
+ ExprBase::ExprVar(node)
+ }
+}
+impl From<ExprIfThenElse> for ExprBase {
+ fn from(node: ExprIfThenElse) -> ExprBase {
+ ExprBase::ExprIfThenElse(node)
+ }
+}
+impl From<ExprFunction> for ExprBase {
+ fn from(node: ExprFunction) -> ExprBase {
+ ExprBase::ExprFunction(node)
+ }
+}
+impl From<ExprError> for ExprBase {
+ fn from(node: ExprError) -> ExprBase {
+ ExprBase::ExprError(node)
+ }
+}
+impl AstNode for ExprBase {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ EXPR_BINARY | EXPR_UNARY | EXPR_OBJ_EXTEND | EXPR_PARENED | EXPR_STRING
+ | EXPR_NUMBER | EXPR_LITERAL | EXPR_ARRAY | EXPR_OBJECT | EXPR_ARRAY_COMP
+ | EXPR_IMPORT | EXPR_VAR | EXPR_IF_THEN_ELSE | EXPR_FUNCTION | EXPR_ERROR => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ EXPR_BINARY => ExprBase::ExprBinary(ExprBinary { syntax }),
+ EXPR_UNARY => ExprBase::ExprUnary(ExprUnary { syntax }),
+ EXPR_OBJ_EXTEND => ExprBase::ExprObjExtend(ExprObjExtend { syntax }),
+ EXPR_PARENED => ExprBase::ExprParened(ExprParened { syntax }),
+ EXPR_STRING => ExprBase::ExprString(ExprString { syntax }),
+ EXPR_NUMBER => ExprBase::ExprNumber(ExprNumber { syntax }),
+ EXPR_LITERAL => ExprBase::ExprLiteral(ExprLiteral { syntax }),
+ EXPR_ARRAY => ExprBase::ExprArray(ExprArray { syntax }),
+ EXPR_OBJECT => ExprBase::ExprObject(ExprObject { syntax }),
+ EXPR_ARRAY_COMP => ExprBase::ExprArrayComp(ExprArrayComp { syntax }),
+ EXPR_IMPORT => ExprBase::ExprImport(ExprImport { syntax }),
+ EXPR_VAR => ExprBase::ExprVar(ExprVar { syntax }),
+ EXPR_IF_THEN_ELSE => ExprBase::ExprIfThenElse(ExprIfThenElse { syntax }),
+ EXPR_FUNCTION => ExprBase::ExprFunction(ExprFunction { syntax }),
+ EXPR_ERROR => ExprBase::ExprError(ExprError { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ ExprBase::ExprBinary(it) => &it.syntax,
+ ExprBase::ExprUnary(it) => &it.syntax,
+ ExprBase::ExprObjExtend(it) => &it.syntax,
+ ExprBase::ExprParened(it) => &it.syntax,
+ ExprBase::ExprString(it) => &it.syntax,
+ ExprBase::ExprNumber(it) => &it.syntax,
+ ExprBase::ExprLiteral(it) => &it.syntax,
+ ExprBase::ExprArray(it) => &it.syntax,
+ ExprBase::ExprObject(it) => &it.syntax,
+ ExprBase::ExprArrayComp(it) => &it.syntax,
+ ExprBase::ExprImport(it) => &it.syntax,
+ ExprBase::ExprVar(it) => &it.syntax,
+ ExprBase::ExprIfThenElse(it) => &it.syntax,
+ ExprBase::ExprFunction(it) => &it.syntax,
+ ExprBase::ExprError(it) => &it.syntax,
+ }
+ }
+}
+impl From<MemberBindStmt> for MemberComp {
+ fn from(node: MemberBindStmt) -> MemberComp {
+ MemberComp::MemberBindStmt(node)
+ }
+}
+impl From<MemberFieldNormal> for MemberComp {
+ fn from(node: MemberFieldNormal) -> MemberComp {
+ MemberComp::MemberFieldNormal(node)
+ }
+}
+impl From<MemberFieldMethod> for MemberComp {
+ fn from(node: MemberFieldMethod) -> MemberComp {
+ MemberComp::MemberFieldMethod(node)
+ }
+}
+impl AstNode for MemberComp {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ MEMBER_BIND_STMT | MEMBER_FIELD_NORMAL | MEMBER_FIELD_METHOD => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ MEMBER_BIND_STMT => MemberComp::MemberBindStmt(MemberBindStmt { syntax }),
+ MEMBER_FIELD_NORMAL => MemberComp::MemberFieldNormal(MemberFieldNormal { syntax }),
+ MEMBER_FIELD_METHOD => MemberComp::MemberFieldMethod(MemberFieldMethod { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ MemberComp::MemberBindStmt(it) => &it.syntax,
+ MemberComp::MemberFieldNormal(it) => &it.syntax,
+ MemberComp::MemberFieldMethod(it) => &it.syntax,
+ }
+ }
+}
+impl From<MemberBindStmt> for Member {
+ fn from(node: MemberBindStmt) -> Member {
+ Member::MemberBindStmt(node)
+ }
+}
+impl From<MemberAssertStmt> for Member {
+ fn from(node: MemberAssertStmt) -> Member {
+ Member::MemberAssertStmt(node)
+ }
+}
+impl From<MemberFieldNormal> for Member {
+ fn from(node: MemberFieldNormal) -> Member {
+ Member::MemberFieldNormal(node)
+ }
+}
+impl From<MemberFieldMethod> for Member {
+ fn from(node: MemberFieldMethod) -> Member {
+ Member::MemberFieldMethod(node)
+ }
+}
+impl AstNode for Member {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ MEMBER_BIND_STMT | MEMBER_ASSERT_STMT | MEMBER_FIELD_NORMAL | MEMBER_FIELD_METHOD => {
+ true
+ }
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ MEMBER_BIND_STMT => Member::MemberBindStmt(MemberBindStmt { syntax }),
+ MEMBER_ASSERT_STMT => Member::MemberAssertStmt(MemberAssertStmt { syntax }),
+ MEMBER_FIELD_NORMAL => Member::MemberFieldNormal(MemberFieldNormal { syntax }),
+ MEMBER_FIELD_METHOD => Member::MemberFieldMethod(MemberFieldMethod { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ Member::MemberBindStmt(it) => &it.syntax,
+ Member::MemberAssertStmt(it) => &it.syntax,
+ Member::MemberFieldNormal(it) => &it.syntax,
+ Member::MemberFieldMethod(it) => &it.syntax,
+ }
+ }
+}
+impl From<FieldNameFixed> for FieldName {
+ fn from(node: FieldNameFixed) -> FieldName {
+ FieldName::FieldNameFixed(node)
+ }
+}
+impl From<FieldNameDynamic> for FieldName {
+ fn from(node: FieldNameDynamic) -> FieldName {
+ FieldName::FieldNameDynamic(node)
+ }
+}
+impl AstNode for FieldName {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ FIELD_NAME_FIXED | FIELD_NAME_DYNAMIC => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ FIELD_NAME_FIXED => FieldName::FieldNameFixed(FieldNameFixed { syntax }),
+ FIELD_NAME_DYNAMIC => FieldName::FieldNameDynamic(FieldNameDynamic { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ FieldName::FieldNameFixed(it) => &it.syntax,
+ FieldName::FieldNameDynamic(it) => &it.syntax,
+ }
+ }
+}
+impl From<DestructFull> for Destruct {
+ fn from(node: DestructFull) -> Destruct {
+ Destruct::DestructFull(node)
+ }
+}
+impl From<DestructSkip> for Destruct {
+ fn from(node: DestructSkip) -> Destruct {
+ Destruct::DestructSkip(node)
+ }
+}
+impl From<DestructArray> for Destruct {
+ fn from(node: DestructArray) -> Destruct {
+ Destruct::DestructArray(node)
+ }
+}
+impl From<DestructObject> for Destruct {
+ fn from(node: DestructObject) -> Destruct {
+ Destruct::DestructObject(node)
+ }
+}
+impl AstNode for Destruct {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ DESTRUCT_FULL | DESTRUCT_SKIP | DESTRUCT_ARRAY | DESTRUCT_OBJECT => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ DESTRUCT_FULL => Destruct::DestructFull(DestructFull { syntax }),
+ DESTRUCT_SKIP => Destruct::DestructSkip(DestructSkip { syntax }),
+ DESTRUCT_ARRAY => Destruct::DestructArray(DestructArray { syntax }),
+ DESTRUCT_OBJECT => Destruct::DestructObject(DestructObject { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ Destruct::DestructFull(it) => &it.syntax,
+ Destruct::DestructSkip(it) => &it.syntax,
+ Destruct::DestructArray(it) => &it.syntax,
+ Destruct::DestructObject(it) => &it.syntax,
+ }
+ }
+}
+impl From<DestructArrayElement> for DestructArrayPart {
+ fn from(node: DestructArrayElement) -> DestructArrayPart {
+ DestructArrayPart::DestructArrayElement(node)
+ }
+}
+impl From<DestructRest> for DestructArrayPart {
+ fn from(node: DestructRest) -> DestructArrayPart {
+ DestructArrayPart::DestructRest(node)
+ }
+}
+impl AstNode for DestructArrayPart {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ DESTRUCT_ARRAY_ELEMENT | DESTRUCT_REST => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ DESTRUCT_ARRAY_ELEMENT => {
+ DestructArrayPart::DestructArrayElement(DestructArrayElement { syntax })
+ }
+ DESTRUCT_REST => DestructArrayPart::DestructRest(DestructRest { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ DestructArrayPart::DestructArrayElement(it) => &it.syntax,
+ DestructArrayPart::DestructRest(it) => &it.syntax,
+ }
+ }
+}
+impl AstToken for BinaryOperator {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ BinaryOperatorKind::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = BinaryOperatorKind::cast(syntax.kind())?;
+ Some(BinaryOperator { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+}
+impl BinaryOperatorKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ OR | NULL_COAELSE | AND | BIT_OR | BIT_XOR | BIT_AND | EQ | NE | LT | GT | LE | GE
+ | IN_KW | LHS | RHS | PLUS | MINUS | MUL | DIV | MODULO | META_OBJECT_APPLY
+ | ERROR_NO_OPERATOR => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ OR => Self::Or,
+ NULL_COAELSE => Self::NullCoaelse,
+ AND => Self::And,
+ BIT_OR => Self::BitOr,
+ BIT_XOR => Self::BitXor,
+ BIT_AND => Self::BitAnd,
+ EQ => Self::Eq,
+ NE => Self::Ne,
+ LT => Self::Lt,
+ GT => Self::Gt,
+ LE => Self::Le,
+ GE => Self::Ge,
+ IN_KW => Self::InKw,
+ LHS => Self::Lhs,
+ RHS => Self::Rhs,
+ PLUS => Self::Plus,
+ MINUS => Self::Minus,
+ MUL => Self::Mul,
+ DIV => Self::Div,
+ MODULO => Self::Modulo,
+ META_OBJECT_APPLY => Self::MetaObjectApply,
+ ERROR_NO_OPERATOR => Self::ErrorNoOperator,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+impl BinaryOperator {
+ pub fn kind(&self) -> BinaryOperatorKind {
+ self.kind
+ }
+}
+impl std::fmt::Display for BinaryOperator {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl AstToken for UnaryOperator {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ UnaryOperatorKind::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = UnaryOperatorKind::cast(syntax.kind())?;
+ Some(UnaryOperator { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+}
+impl UnaryOperatorKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ MINUS | NOT | BIT_NOT => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ MINUS => Self::Minus,
+ NOT => Self::Not,
+ BIT_NOT => Self::BitNot,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+impl UnaryOperator {
+ pub fn kind(&self) -> UnaryOperatorKind {
+ self.kind
+ }
+}
+impl std::fmt::Display for UnaryOperator {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl AstToken for Literal {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ LiteralKind::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = LiteralKind::cast(syntax.kind())?;
+ Some(Literal { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+}
+impl LiteralKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ NULL_KW | TRUE_KW | FALSE_KW | SELF_KW | DOLLAR | SUPER_KW => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ NULL_KW => Self::NullKw,
+ TRUE_KW => Self::TrueKw,
+ FALSE_KW => Self::FalseKw,
+ SELF_KW => Self::SelfKw,
+ DOLLAR => Self::Dollar,
+ SUPER_KW => Self::SuperKw,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+impl Literal {
+ pub fn kind(&self) -> LiteralKind {
+ self.kind
+ }
+}
+impl std::fmt::Display for Literal {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl AstToken for Text {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ TextKind::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = TextKind::cast(syntax.kind())?;
+ Some(Text { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+}
+impl TextKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ STRING_DOUBLE
+ | ERROR_STRING_DOUBLE_UNTERMINATED
+ | STRING_SINGLE
+ | ERROR_STRING_SINGLE_UNTERMINATED
+ | STRING_DOUBLE_VERBATIM
+ | ERROR_STRING_DOUBLE_VERBATIM_UNTERMINATED
+ | STRING_SINGLE_VERBATIM
+ | ERROR_STRING_SINGLE_VERBATIM_UNTERMINATED
+ | ERROR_STRING_VERBATIM_MISSING_QUOTES
+ | STRING_BLOCK
+ | ERROR_STRING_BLOCK_UNEXPECTED_END
+ | ERROR_STRING_BLOCK_MISSING_NEW_LINE
+ | ERROR_STRING_BLOCK_MISSING_TERMINATION
+ | ERROR_STRING_BLOCK_MISSING_INDENT => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ STRING_DOUBLE => Self::StringDouble,
+ ERROR_STRING_DOUBLE_UNTERMINATED => Self::ErrorStringDoubleUnterminated,
+ STRING_SINGLE => Self::StringSingle,
+ ERROR_STRING_SINGLE_UNTERMINATED => Self::ErrorStringSingleUnterminated,
+ STRING_DOUBLE_VERBATIM => Self::StringDoubleVerbatim,
+ ERROR_STRING_DOUBLE_VERBATIM_UNTERMINATED => {
+ Self::ErrorStringDoubleVerbatimUnterminated
+ }
+ STRING_SINGLE_VERBATIM => Self::StringSingleVerbatim,
+ ERROR_STRING_SINGLE_VERBATIM_UNTERMINATED => {
+ Self::ErrorStringSingleVerbatimUnterminated
+ }
+ ERROR_STRING_VERBATIM_MISSING_QUOTES => Self::ErrorStringVerbatimMissingQuotes,
+ STRING_BLOCK => Self::StringBlock,
+ ERROR_STRING_BLOCK_UNEXPECTED_END => Self::ErrorStringBlockUnexpectedEnd,
+ ERROR_STRING_BLOCK_MISSING_NEW_LINE => Self::ErrorStringBlockMissingNewLine,
+ ERROR_STRING_BLOCK_MISSING_TERMINATION => Self::ErrorStringBlockMissingTermination,
+ ERROR_STRING_BLOCK_MISSING_INDENT => Self::ErrorStringBlockMissingIndent,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+impl Text {
+ pub fn kind(&self) -> TextKind {
+ self.kind
+ }
+}
+impl std::fmt::Display for Text {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl AstToken for Number {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ NumberKind::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = NumberKind::cast(syntax.kind())?;
+ Some(Number { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+}
+impl NumberKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ FLOAT
+ | ERROR_FLOAT_JUNK_AFTER_POINT
+ | ERROR_FLOAT_JUNK_AFTER_EXPONENT
+ | ERROR_FLOAT_JUNK_AFTER_EXPONENT_SIGN => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ FLOAT => Self::Float,
+ ERROR_FLOAT_JUNK_AFTER_POINT => Self::ErrorFloatJunkAfterPoint,
+ ERROR_FLOAT_JUNK_AFTER_EXPONENT => Self::ErrorFloatJunkAfterExponent,
+ ERROR_FLOAT_JUNK_AFTER_EXPONENT_SIGN => Self::ErrorFloatJunkAfterExponentSign,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+impl Number {
+ pub fn kind(&self) -> NumberKind {
+ self.kind
+ }
+}
+impl std::fmt::Display for Number {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl AstToken for ImportKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ ImportKindKind::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = ImportKindKind::cast(syntax.kind())?;
+ Some(ImportKind { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+}
+impl ImportKindKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ IMPORTSTR_KW | IMPORTBIN_KW | IMPORT_KW => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ IMPORTSTR_KW => Self::ImportstrKw,
+ IMPORTBIN_KW => Self::ImportbinKw,
+ IMPORT_KW => Self::ImportKw,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+impl ImportKind {
+ pub fn kind(&self) -> ImportKindKind {
+ self.kind
+ }
+}
+impl std::fmt::Display for ImportKind {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl AstToken for Visibility {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ VisibilityKind::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = VisibilityKind::cast(syntax.kind())?;
+ Some(Visibility { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+}
+impl VisibilityKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ COLONCOLONCOLON | COLONCOLON | COLON => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ COLONCOLONCOLON => Self::Coloncoloncolon,
+ COLONCOLON => Self::Coloncolon,
+ COLON => Self::Colon,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+impl Visibility {
+ pub fn kind(&self) -> VisibilityKind {
+ self.kind
+ }
+}
+impl std::fmt::Display for Visibility {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl AstToken for Trivia {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ TriviaKind::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = TriviaKind::cast(syntax.kind())?;
+ Some(Trivia { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+}
+impl TriviaKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ WHITESPACE
+ | MULTI_LINE_COMMENT
+ | ERROR_COMMENT_TOO_SHORT
+ | ERROR_COMMENT_UNTERMINATED
+ | SINGLE_LINE_HASH_COMMENT
+ | SINGLE_LINE_SLASH_COMMENT => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ WHITESPACE => Self::Whitespace,
+ MULTI_LINE_COMMENT => Self::MultiLineComment,
+ ERROR_COMMENT_TOO_SHORT => Self::ErrorCommentTooShort,
+ ERROR_COMMENT_UNTERMINATED => Self::ErrorCommentUnterminated,
+ SINGLE_LINE_HASH_COMMENT => Self::SingleLineHashComment,
+ SINGLE_LINE_SLASH_COMMENT => Self::SingleLineSlashComment,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+impl Trivia {
+ pub fn kind(&self) -> TriviaKind {
+ self.kind
+ }
+}
+impl std::fmt::Display for Trivia {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl AstToken for CustomError {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ CustomErrorKind::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = CustomErrorKind::cast(syntax.kind())?;
+ Some(CustomError { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+}
+impl CustomErrorKind {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ ERROR_MISSING_TOKEN | ERROR_UNEXPECTED_TOKEN | ERROR_CUSTOM => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ ERROR_MISSING_TOKEN => Self::ErrorMissingToken,
+ ERROR_UNEXPECTED_TOKEN => Self::ErrorUnexpectedToken,
+ ERROR_CUSTOM => Self::ErrorCustom,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+impl CustomError {
+ pub fn kind(&self) -> CustomErrorKind {
+ self.kind
+ }
+}
+impl std::fmt::Display for CustomError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Suffix {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Bind {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Stmt {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ObjBody {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for CompSpec {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprBase {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for MemberComp {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Member {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for FieldName {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Destruct {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for DestructArrayPart {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for SourceFile {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Expr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for SuffixIndex {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Name {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for SuffixIndexExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for SuffixSlice {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for SliceDesc {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for SuffixApply {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ArgsDesc {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for StmtLocal {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for StmtAssert {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Assertion {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprBinary {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprUnary {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprObjExtend {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprParened {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprLiteral {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprString {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprNumber {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprArray {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprObject {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprArrayComp {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprImport {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprVar {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprIfThenElse {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for TrueExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for FalseExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprFunction {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ParamsDesc {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ExprError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for SliceDescEnd {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for SliceDescStep {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Arg {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ObjBodyComp {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ObjBodyMemberList {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for MemberBindStmt {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ObjLocal {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for MemberAssertStmt {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for MemberFieldNormal {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for MemberFieldMethod {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for FieldNameFixed {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for FieldNameDynamic {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for ForSpec {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for IfSpec {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for BindDestruct {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for BindFunction {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for Param {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for DestructFull {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for DestructSkip {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for DestructArray {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for DestructObject {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for DestructObjectField {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for DestructRest {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for DestructArrayElement {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs
@@ -0,0 +1,293 @@
+//! This is a generated file, please do not edit manually. Changes can be
+//! made in codegeneration that lives in `xtask` top-level dir.
+
+#![allow(
+ bad_style,
+ missing_docs,
+ unreachable_pub,
+ clippy::manual_non_exhaustive,
+ clippy::match_like_matches_macro
+)]
+use logos::Logos;
+#[doc = r" The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`."]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Logos)]
+#[repr(u16)]
+pub enum SyntaxKind {
+ #[doc(hidden)]
+ TOMBSTONE,
+ #[doc(hidden)]
+ EOF,
+ #[token("||")]
+ OR,
+ #[token("??")]
+ NULL_COAELSE,
+ #[token("&&")]
+ AND,
+ #[token("|")]
+ BIT_OR,
+ #[token("^")]
+ BIT_XOR,
+ #[token("&")]
+ BIT_AND,
+ #[token("==")]
+ EQ,
+ #[token("!=")]
+ NE,
+ #[token("<")]
+ LT,
+ #[token(">")]
+ GT,
+ #[token("<=")]
+ LE,
+ #[token(">=")]
+ GE,
+ #[token("<<")]
+ LHS,
+ #[token(">>")]
+ RHS,
+ #[token("+")]
+ PLUS,
+ #[token("-")]
+ MINUS,
+ #[token("*")]
+ MUL,
+ #[token("/")]
+ DIV,
+ #[token("%")]
+ MODULO,
+ #[token("!")]
+ NOT,
+ #[token("~")]
+ BIT_NOT,
+ #[token("[")]
+ L_BRACK,
+ #[token("]")]
+ R_BRACK,
+ #[token("(")]
+ L_PAREN,
+ #[token(")")]
+ R_PAREN,
+ #[token("{")]
+ L_BRACE,
+ #[token("}")]
+ R_BRACE,
+ #[token(":")]
+ COLON,
+ #[token("::")]
+ COLONCOLON,
+ #[token(":::")]
+ COLONCOLONCOLON,
+ #[token(";")]
+ SEMI,
+ #[token(".")]
+ DOT,
+ #[token("...")]
+ DOTDOTDOT,
+ #[token(",")]
+ COMMA,
+ #[token("$")]
+ DOLLAR,
+ #[token("=")]
+ ASSIGN,
+ #[token("?")]
+ QUESTION_MARK,
+ #[regex("(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?")]
+ FLOAT,
+ #[regex("(?:0|[1-9][0-9]*)\\.[^0-9]")]
+ ERROR_FLOAT_JUNK_AFTER_POINT,
+ #[regex("(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?[eE][^+\\-0-9]")]
+ ERROR_FLOAT_JUNK_AFTER_EXPONENT,
+ #[regex("(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?[eE][+-][^0-9]")]
+ ERROR_FLOAT_JUNK_AFTER_EXPONENT_SIGN,
+ #[regex("\"(?s:[^\"\\\\]|\\\\.)*\"")]
+ STRING_DOUBLE,
+ #[regex("\"(?s:[^\"\\\\]|\\\\.)*")]
+ ERROR_STRING_DOUBLE_UNTERMINATED,
+ #[regex("'(?s:[^'\\\\]|\\\\.)*'")]
+ STRING_SINGLE,
+ #[regex("'(?s:[^'\\\\]|\\\\.)*")]
+ ERROR_STRING_SINGLE_UNTERMINATED,
+ #[regex("@\"(?:[^\"]|\"\")*\"")]
+ STRING_DOUBLE_VERBATIM,
+ #[regex("@\"(?:[^\"]|\"\")*")]
+ ERROR_STRING_DOUBLE_VERBATIM_UNTERMINATED,
+ #[regex("@'(?:[^']|'')*'")]
+ STRING_SINGLE_VERBATIM,
+ #[regex("@'(?:[^']|'')*")]
+ ERROR_STRING_SINGLE_VERBATIM_UNTERMINATED,
+ #[regex("@[^\"'\\s]\\S+")]
+ ERROR_STRING_VERBATIM_MISSING_QUOTES,
+ #[regex("\\|\\|\\|", crate::string_block::lex_str_block_test)]
+ STRING_BLOCK,
+ ERROR_STRING_BLOCK_UNEXPECTED_END,
+ ERROR_STRING_BLOCK_MISSING_NEW_LINE,
+ ERROR_STRING_BLOCK_MISSING_TERMINATION,
+ ERROR_STRING_BLOCK_MISSING_INDENT,
+ #[regex("[_a-zA-Z][_a-zA-Z0-9]*")]
+ IDENT,
+ #[regex("[ \\t\\n\\r]+")]
+ WHITESPACE,
+ #[regex("//[^\\r\\n]*(\\r\\n|\\n)?")]
+ SINGLE_LINE_SLASH_COMMENT,
+ #[regex("#[^\\r\\n]*(\\r\\n|\\n)?")]
+ SINGLE_LINE_HASH_COMMENT,
+ #[regex("/\\*([^*]|\\*[^/])*\\*/")]
+ MULTI_LINE_COMMENT,
+ #[regex("/\\*/")]
+ ERROR_COMMENT_TOO_SHORT,
+ #[regex("/\\*([^*]|\\*[^/])+")]
+ ERROR_COMMENT_UNTERMINATED,
+ #[token("tailstrict")]
+ TAILSTRICT_KW,
+ #[token("local")]
+ LOCAL_KW,
+ #[token("importstr")]
+ IMPORTSTR_KW,
+ #[token("importbin")]
+ IMPORTBIN_KW,
+ #[token("import")]
+ IMPORT_KW,
+ #[token("if")]
+ IF_KW,
+ #[token("then")]
+ THEN_KW,
+ #[token("else")]
+ ELSE_KW,
+ #[token("function")]
+ FUNCTION_KW,
+ #[token("error")]
+ ERROR_KW,
+ #[token("in")]
+ IN_KW,
+ META_OBJECT_APPLY,
+ ERROR_NO_OPERATOR,
+ #[token("null")]
+ NULL_KW,
+ #[token("true")]
+ TRUE_KW,
+ #[token("false")]
+ FALSE_KW,
+ #[token("self")]
+ SELF_KW,
+ #[token("super")]
+ SUPER_KW,
+ #[token("for")]
+ FOR_KW,
+ #[token("assert")]
+ ASSERT_KW,
+ ERROR_MISSING_TOKEN,
+ ERROR_UNEXPECTED_TOKEN,
+ ERROR_CUSTOM,
+ #[doc = r" Also acts as __LAST_TOKEN"]
+ #[error]
+ LEXING_ERROR,
+ SOURCE_FILE,
+ EXPR,
+ SUFFIX_INDEX,
+ NAME,
+ SUFFIX_INDEX_EXPR,
+ SUFFIX_SLICE,
+ SLICE_DESC,
+ SUFFIX_APPLY,
+ ARGS_DESC,
+ STMT_LOCAL,
+ STMT_ASSERT,
+ ASSERTION,
+ EXPR_BINARY,
+ EXPR_UNARY,
+ EXPR_OBJ_EXTEND,
+ EXPR_PARENED,
+ EXPR_LITERAL,
+ EXPR_STRING,
+ EXPR_NUMBER,
+ EXPR_ARRAY,
+ EXPR_OBJECT,
+ EXPR_ARRAY_COMP,
+ EXPR_IMPORT,
+ EXPR_VAR,
+ EXPR_IF_THEN_ELSE,
+ TRUE_EXPR,
+ FALSE_EXPR,
+ EXPR_FUNCTION,
+ PARAMS_DESC,
+ EXPR_ERROR,
+ SLICE_DESC_END,
+ SLICE_DESC_STEP,
+ ARG,
+ OBJ_BODY_COMP,
+ OBJ_BODY_MEMBER_LIST,
+ MEMBER_BIND_STMT,
+ OBJ_LOCAL,
+ MEMBER_ASSERT_STMT,
+ MEMBER_FIELD_NORMAL,
+ MEMBER_FIELD_METHOD,
+ FIELD_NAME_FIXED,
+ FIELD_NAME_DYNAMIC,
+ FOR_SPEC,
+ IF_SPEC,
+ BIND_DESTRUCT,
+ BIND_FUNCTION,
+ PARAM,
+ DESTRUCT_FULL,
+ DESTRUCT_SKIP,
+ DESTRUCT_ARRAY,
+ DESTRUCT_OBJECT,
+ DESTRUCT_OBJECT_FIELD,
+ DESTRUCT_REST,
+ DESTRUCT_ARRAY_ELEMENT,
+ SUFFIX,
+ BIND,
+ STMT,
+ OBJ_BODY,
+ COMP_SPEC,
+ EXPR_BASE,
+ MEMBER_COMP,
+ MEMBER,
+ FIELD_NAME,
+ DESTRUCT,
+ DESTRUCT_ARRAY_PART,
+ BINARY_OPERATOR,
+ UNARY_OPERATOR,
+ LITERAL,
+ TEXT,
+ NUMBER,
+ IMPORT_KIND,
+ VISIBILITY,
+ TRIVIA,
+ CUSTOM_ERROR,
+ #[doc(hidden)]
+ __LAST,
+}
+use self::SyntaxKind::*;
+impl SyntaxKind {
+ pub fn is_keyword(self) -> bool {
+ match self {
+ OR | NULL_COAELSE | AND | BIT_OR | BIT_XOR | BIT_AND | EQ | NE | LT | GT | LE | GE
+ | LHS | RHS | PLUS | MINUS | MUL | DIV | MODULO | NOT | BIT_NOT | L_BRACK | R_BRACK
+ | L_PAREN | R_PAREN | L_BRACE | R_BRACE | COLON | COLONCOLON | COLONCOLONCOLON
+ | SEMI | DOT | DOTDOTDOT | COMMA | DOLLAR | ASSIGN | QUESTION_MARK | TAILSTRICT_KW
+ | LOCAL_KW | IMPORTSTR_KW | IMPORTBIN_KW | IMPORT_KW | IF_KW | THEN_KW | ELSE_KW
+ | FUNCTION_KW | ERROR_KW | IN_KW | NULL_KW | TRUE_KW | FALSE_KW | SELF_KW
+ | SUPER_KW | FOR_KW | ASSERT_KW => true,
+ _ => false,
+ }
+ }
+ pub fn is_enum(self) -> bool {
+ match self {
+ SUFFIX | BIND | STMT | OBJ_BODY | COMP_SPEC | EXPR_BASE | MEMBER_COMP | MEMBER
+ | FIELD_NAME | DESTRUCT | DESTRUCT_ARRAY_PART | BINARY_OPERATOR | UNARY_OPERATOR
+ | LITERAL | TEXT | NUMBER | IMPORT_KIND | VISIBILITY | TRIVIA | CUSTOM_ERROR => true,
+ _ => false,
+ }
+ }
+ pub fn from_raw(r: u16) -> Self {
+ assert!(r < Self::__LAST as u16);
+ unsafe { std::mem::transmute(r) }
+ }
+ pub fn into_raw(self) -> u16 {
+ self as u16
+ }
+}
+#[macro_export]
+macro_rules ! T { [||] => { $ crate :: SyntaxKind :: OR } ; [??] => { $ crate :: SyntaxKind :: NULL_COAELSE } ; [&&] => { $ crate :: SyntaxKind :: AND } ; [|] => { $ crate :: SyntaxKind :: BIT_OR } ; [^] => { $ crate :: SyntaxKind :: BIT_XOR } ; [&] => { $ crate :: SyntaxKind :: BIT_AND } ; [==] => { $ crate :: SyntaxKind :: EQ } ; [!=] => { $ crate :: SyntaxKind :: NE } ; [<] => { $ crate :: SyntaxKind :: LT } ; [>] => { $ crate :: SyntaxKind :: GT } ; [<=] => { $ crate :: SyntaxKind :: LE } ; [>=] => { $ crate :: SyntaxKind :: GE } ; [<<] => { $ crate :: SyntaxKind :: LHS } ; [>>] => { $ crate :: SyntaxKind :: RHS } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [*] => { $ crate :: SyntaxKind :: MUL } ; [/] => { $ crate :: SyntaxKind :: DIV } ; [%] => { $ crate :: SyntaxKind :: MODULO } ; [!] => { $ crate :: SyntaxKind :: NOT } ; [~] => { $ crate :: SyntaxKind :: BIT_NOT } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_BRACE } ; ['}'] => { $ crate :: SyntaxKind :: R_BRACE } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLONCOLON } ; [:::] => { $ crate :: SyntaxKind :: COLONCOLONCOLON } ; [;] => { $ crate :: SyntaxKind :: SEMI } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [...] => { $ crate :: SyntaxKind :: DOTDOTDOT } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['$'] => { $ crate :: SyntaxKind :: DOLLAR } ; [=] => { $ crate :: SyntaxKind :: ASSIGN } ; [?] => { $ crate :: SyntaxKind :: QUESTION_MARK } ; [tailstrict] => { $ crate :: SyntaxKind :: TAILSTRICT_KW } ; [local] => { $ crate :: SyntaxKind :: LOCAL_KW } ; [importstr] => { $ crate :: SyntaxKind :: IMPORTSTR_KW } ; [importbin] => { $ crate :: SyntaxKind :: IMPORTBIN_KW } ; [import] => { $ crate :: SyntaxKind :: IMPORT_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [then] => { $ crate :: SyntaxKind :: THEN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [function] => { $ crate :: SyntaxKind :: FUNCTION_KW } ; [error] => { $ crate :: SyntaxKind :: ERROR_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [null] => { $ crate :: SyntaxKind :: NULL_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [assert] => { $ crate :: SyntaxKind :: ASSERT_KW } }
+pub use T;
crates/jrsonnet-rowan-parser/src/language.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/language.rs
@@ -0,0 +1,24 @@
+use rowan::Language;
+
+use crate::SyntaxKind;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum JsonnetLanguage {}
+impl Language for JsonnetLanguage {
+ type Kind = SyntaxKind;
+
+ fn kind_from_raw(raw: rowan::SyntaxKind) -> SyntaxKind {
+ SyntaxKind::from_raw(raw.0)
+ }
+
+ fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind {
+ rowan::SyntaxKind(kind.into_raw())
+ }
+}
+
+pub type SyntaxNode = rowan::SyntaxNode<JsonnetLanguage>;
+pub type SyntaxToken = rowan::SyntaxToken<JsonnetLanguage>;
+pub type SyntaxElement = rowan::SyntaxElement<JsonnetLanguage>;
+pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<JsonnetLanguage>;
+pub type SyntaxElementChildren = rowan::SyntaxElementChildren<JsonnetLanguage>;
+pub type PreorderWithTokens = rowan::api::PreorderWithTokens<JsonnetLanguage>;
crates/jrsonnet-rowan-parser/src/lex.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/lex.rs
@@ -0,0 +1,80 @@
+use core::ops::Range;
+use std::convert::TryFrom;
+
+use logos::Logos;
+use rowan::{TextRange, TextSize};
+
+use crate::{
+ string_block::{lex_str_block, StringBlockError},
+ SyntaxKind,
+};
+
+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> {
+ use SyntaxKind::*;
+
+ let mut kind = self.inner.next()?;
+ let text = self.inner.slice();
+
+ if kind == STRING_BLOCK {
+ // We use custom lexer, which skips enough bytes, but not returns error
+ // Instead we should call lexer again to verify if there is something wrong with string block
+ let mut lexer = logos::Lexer::<SyntaxKind>::new(text);
+ // In kinds, string blocks is parsed at least as `|||`
+ lexer.bump(3);
+ let res = lex_str_block(&mut lexer);
+ debug_assert!(lexer.next().is_none(), "str_block is lexed");
+ match res {
+ Ok(_) => {}
+ Err(e) => {
+ kind = match e {
+ StringBlockError::UnexpectedEnd => ERROR_STRING_BLOCK_UNEXPECTED_END,
+ StringBlockError::MissingNewLine => ERROR_STRING_BLOCK_MISSING_NEW_LINE,
+ StringBlockError::MissingTermination => {
+ ERROR_STRING_BLOCK_MISSING_TERMINATION
+ }
+ StringBlockError::MissingIndent => ERROR_STRING_BLOCK_MISSING_INDENT,
+ }
+ }
+ }
+ }
+
+ 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, Debug)]
+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()
+}
crates/jrsonnet-rowan-parser/src/lib.rsdiffbeforeafterbothno changes
crates/jrsonnet-rowan-parser/src/marker.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/marker.rs
@@ -0,0 +1,187 @@
+use std::num::NonZeroUsize;
+
+use drop_bomb::DropBomb;
+
+use crate::{
+ event::Event,
+ parser::{ExpectedSyntax, Parser, SyntaxError},
+ SyntaxKind,
+};
+
+pub struct Ranger {
+ pub pos: usize,
+}
+impl Ranger {
+ pub fn finish(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 {
+ #[allow(dead_code)]
+ 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,
+ bomb: DropBomb,
+}
+impl Marker {
+ pub fn new(pos: usize) -> Self {
+ Self {
+ start_event_idx: pos,
+ bomb: DropBomb::new("marked dropped while not completed"),
+ }
+ }
+ fn complete_raw(
+ mut self,
+ p: &mut Parser,
+ kind: SyntaxKind,
+ error: Option<SyntaxError>,
+ ) -> CompletedMarker {
+ self.bomb.defuse();
+ assert!(
+ !kind.is_enum(),
+ "{kind:?} is a enum kind, you should use variant kinds instead"
+ );
+ // TODO: is_lexer should return true if enum variant has #[regex]/#[token] over it, or it is defined as lexer error explicitly
+ // debug_assert!(
+ // !kind.is_lexer(),
+ // "{kind:?} should be only emitted by lexer, not used directly"
+ // );
+ let event_at_pos = &mut p.events[self.start_event_idx];
+ assert!(matches!(event_at_pos, Event::Pending));
+
+ *event_at_pos = Event::Start {
+ kind,
+ forward_parent: None,
+ };
+
+ let finish_event_idx = p.events.len();
+ p.events.push(Event::Finish {
+ wrapper: None,
+ error: error.map(Box::new),
+ });
+ p.entered -= 1;
+ p.clear_outdated_hints();
+ CompletedMarker {
+ start_event_idx: self.start_event_idx,
+ finish_event_idx,
+ }
+ }
+ pub fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
+ self.complete_raw(p, kind, None)
+ }
+ pub fn complete_error(mut self, p: &mut Parser, msg: impl AsRef<str>) -> CompletedMarker {
+ self.complete_raw(
+ p,
+ SyntaxKind::ERROR_CUSTOM,
+ Some(SyntaxError::Custom {
+ error: msg.as_ref().to_owned(),
+ }),
+ )
+ }
+ pub fn complete_missing(mut self, p: &mut Parser, expected: ExpectedSyntax) -> CompletedMarker {
+ self.complete_raw(
+ p,
+ SyntaxKind::ERROR_MISSING_TOKEN,
+ Some(SyntaxError::Missing { expected }),
+ )
+ }
+ pub fn complete_unexpected(
+ mut self,
+ p: &mut Parser,
+ expected: ExpectedSyntax,
+ found: SyntaxKind,
+ ) -> CompletedMarker {
+ self.complete_raw(
+ p,
+ SyntaxKind::ERROR_UNEXPECTED_TOKEN,
+ Some(SyntaxError::Unexpected { expected, found }),
+ )
+ }
+
+ pub fn forget(mut self, p: &mut Parser) {
+ self.bomb.defuse();
+ let event_at_pos = &mut p.events[self.start_event_idx];
+ assert!(matches!(event_at_pos, Event::Pending));
+
+ *event_at_pos = Event::Noop;
+ p.entered -= 1;
+ p.clear_outdated_hints();
+ }
+}
+pub struct CompletedMarker {
+ start_event_idx: usize,
+ finish_event_idx: usize,
+}
+impl CompletedMarker {
+ pub(super) fn precede(self, p: &mut Parser) -> Marker {
+ let new_m = p.start();
+ match &mut p.events[self.start_event_idx] {
+ Event::Start { forward_parent, .. } => {
+ *forward_parent = Some(
+ NonZeroUsize::new(new_m.start_event_idx - self.start_event_idx).expect("!= 0"),
+ );
+ }
+ _ => unreachable!(),
+ }
+
+ new_m
+ }
+ /// Create new node around existing marker, not counting anything that comes after it
+ fn wrap_raw(
+ self,
+ p: &mut Parser,
+ kind: SyntaxKind,
+ error: Option<SyntaxError>,
+ ) -> CompletedMarker {
+ let new_m = p.start();
+ match &mut p.events[self.start_event_idx] {
+ Event::Start { forward_parent, .. } => {
+ *forward_parent = Some(
+ NonZeroUsize::new(new_m.start_event_idx - self.start_event_idx).expect("!= 0"),
+ );
+ }
+ _ => unreachable!(),
+ }
+
+ let completed = new_m.complete_raw(p, kind, error);
+
+ match &mut p.events[self.finish_event_idx] {
+ Event::Finish {
+ wrapper,
+ error: _error,
+ } => {
+ *wrapper = Some(
+ NonZeroUsize::new(completed.finish_event_idx - self.finish_event_idx)
+ .expect("!= 0"),
+ );
+ }
+ _ => unreachable!(),
+ }
+ completed
+ }
+ pub fn wrap(self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
+ self.wrap_raw(p, kind, None)
+ }
+ pub fn wrap_error(self, p: &mut Parser, msg: impl AsRef<str>) -> CompletedMarker {
+ self.wrap_raw(
+ p,
+ SyntaxKind::ERROR_CUSTOM,
+ Some(SyntaxError::Custom {
+ error: msg.as_ref().to_owned(),
+ }),
+ )
+ }
+}
crates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/parser.rs
@@ -0,0 +1,940 @@
+use std::{cell::Cell, fmt, rc::Rc};
+
+use miette::{LabeledSpan, SourceOffset, SourceSpan};
+use rowan::{GreenNode, TextRange};
+
+use crate::{
+ event::Event,
+ marker::{CompletedMarker, Marker, Ranger},
+ nodes::{BinaryOperatorKind, Literal, Number, Text, UnaryOperatorKind},
+ token_set::SyntaxKindSet,
+ AstToken, SyntaxKind,
+ SyntaxKind::*,
+ SyntaxNode, T, TS,
+};
+
+pub struct Parse {
+ pub green_node: GreenNode,
+ pub errors: Vec<LocatedSyntaxError>,
+}
+
+pub struct Parser {
+ // TODO: remove all trivia before feeding to parser?
+ kinds: Vec<SyntaxKind>,
+ pub offset: usize,
+ pub events: Vec<Event>,
+ pub entered: u32,
+ pub hints: Vec<(u32, TextRange, String)>,
+ pub last_error_token: usize,
+ expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,
+ steps: Cell<u64>,
+}
+
+#[derive(Clone, Debug)]
+pub enum SyntaxError {
+ Unexpected {
+ expected: ExpectedSyntax,
+ found: SyntaxKind,
+ },
+ Missing {
+ expected: ExpectedSyntax,
+ },
+ Custom {
+ error: String,
+ },
+ Hint {
+ error: String,
+ },
+}
+
+#[derive(Debug)]
+pub struct LocatedSyntaxError {
+ pub error: SyntaxError,
+ pub range: TextRange,
+}
+
+impl From<LocatedSyntaxError> for LabeledSpan {
+ fn from(val: LocatedSyntaxError) -> Self {
+ let span = SourceSpan::new(
+ SourceOffset::from(usize::from(val.range.start())),
+ SourceOffset::from(usize::from(val.range.end() - val.range.start())),
+ );
+ dbg!(&val);
+ match val.error {
+ SyntaxError::Unexpected { expected, found } => LabeledSpan::new_with_span(
+ Some(format!("expected {expected}, found {found:?}")),
+ span,
+ ),
+ SyntaxError::Missing { expected } => {
+ LabeledSpan::new_with_span(Some(format!("missing {expected}")), span)
+ }
+ SyntaxError::Custom { error } | SyntaxError::Hint { error } => {
+ LabeledSpan::new_with_span(Some(error), span)
+ }
+ }
+ }
+}
+
+impl Parser {
+ pub fn new(kinds: Vec<SyntaxKind>) -> Self {
+ Self {
+ kinds,
+ offset: 0,
+ events: vec![],
+ entered: 0,
+ last_error_token: 0,
+ hints: vec![],
+ expected_syntax_tracking_state: Rc::new(Cell::new(ExpectedSyntax::Unnamed(TS![]))),
+ steps: Cell::new(0),
+ }
+ }
+ 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_tracking_state
+ .set(ExpectedSyntax::Unnamed(TS![]));
+ }
+ pub fn start(&mut self) -> Marker {
+ let start_event_idx = self.events.len();
+ self.events.push(Event::Pending);
+ self.entered += 1;
+ Marker::new(start_event_idx)
+ }
+ pub fn start_ranger(&mut self) -> Ranger {
+ let pos = self.offset;
+ Ranger { pos }
+ }
+ pub fn parse(mut self) -> Vec<Event> {
+ let m = self.start();
+ expr(&mut self);
+ if !self.at(EOF) {
+ let m = self.start();
+ while !self.at(EOF) {
+ self.bump();
+ }
+ m.complete_error(&mut self, "unexpected tokens after end");
+ }
+ m.complete(&mut self, SOURCE_FILE);
+
+ self.events
+ }
+
+ pub(crate) fn expect(&mut self, kind: SyntaxKind) {
+ self.expect_with_recovery_set(kind, TS![])
+ }
+
+ pub(crate) fn expect_with_recovery_set(
+ &mut self,
+ kind: SyntaxKind,
+ recovery_set: SyntaxKindSet,
+ ) {
+ if self.at(kind) {
+ if kind != EOF {
+ 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 fn error_with_no_skip(&mut self) -> CompletedMarker {
+ self.error_with_recovery_set(SyntaxKindSet::ALL)
+ }
+
+ pub fn error_with_recovery_set(&mut self, recovery_set: SyntaxKindSet) -> CompletedMarker {
+ let expected = self.expected_syntax_tracking_state.get();
+ self.expected_syntax_tracking_state
+ .set(ExpectedSyntax::Unnamed(TS![]));
+
+ if self.at_end() || self.at_ts(recovery_set) {
+ let m = self.start();
+ return m.complete_missing(self, expected);
+ }
+
+ let current_token = self.current();
+
+ self.last_error_token = self.offset;
+
+ let m = self.start();
+ self.bump();
+ let m = m.complete_unexpected(self, expected, current_token);
+ self.clear_expected_syntaxes();
+ m
+ }
+ fn bump_assert(&mut self, kind: SyntaxKind) {
+ assert!(self.at(kind), "expected {:?}", kind);
+ self.bump_remap(self.current());
+ }
+ fn bump(&mut self) {
+ self.bump_remap(self.current());
+ }
+ fn bump_remap(&mut self, kind: SyntaxKind) {
+ assert_ne!(self.offset, self.kinds.len(), "already at end");
+ self.events.push(Event::Token { kind });
+ self.offset += 1;
+ self.clear_expected_syntaxes();
+ }
+ fn step(&self) {
+ use std::fmt::Write;
+ let steps = self.steps.get();
+ if steps >= 15000000 {
+ let mut out = "seems like parsing is stuck".to_owned();
+ {
+ let last = 20;
+ write!(out, "\n\nLast {} events:", last).unwrap();
+ for (i, event) in self
+ .events
+ .iter()
+ .skip(self.events.len().saturating_sub(last))
+ .enumerate()
+ {
+ write!(out, "\n{i}. {event:?}").unwrap();
+ }
+ }
+ {
+ let next = 20;
+ write!(out, "\n\nNext {next} tokens:").unwrap();
+ for (i, tok) in self.kinds.iter().skip(self.offset).take(next).enumerate() {
+ write!(out, "\n{i}. {tok:?}").unwrap();
+ }
+ }
+ panic!("{out}")
+ }
+ self.steps.set(steps + 1);
+ }
+ fn nth(&self, i: usize) -> SyntaxKind {
+ self.step();
+ let mut offset = self.offset;
+ for _ in 0..i {
+ offset += 1;
+ }
+ self.kinds.get(offset).copied().unwrap_or(EOF)
+ }
+ fn current(&self) -> SyntaxKind {
+ self.nth(0)
+ }
+ #[must_use]
+ pub(crate) fn expected_syntax_name(&mut self, name: &'static str) -> ExpectedSyntaxGuard {
+ self.expected_syntax_tracking_state
+ .set(ExpectedSyntax::Named(name));
+
+ ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state))
+ }
+ pub fn at(&mut self, kind: SyntaxKind) -> bool {
+ self.nth_at(0, kind)
+ }
+ pub fn nth_at(&mut self, n: usize, kind: SyntaxKind) -> bool {
+ if n == 0 {
+ if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {
+ let kinds = kinds.with(kind);
+ self.expected_syntax_tracking_state
+ .set(ExpectedSyntax::Unnamed(kinds))
+ }
+ }
+ self.nth(n) == kind
+ }
+ pub fn at_ts(&mut self, set: SyntaxKindSet) -> bool {
+ if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {
+ let kinds = kinds.union(set);
+ self.expected_syntax_tracking_state
+ .set(ExpectedSyntax::Unnamed(kinds))
+ }
+ set.contains(self.current())
+ }
+ pub fn at_end(&mut self) -> bool {
+ self.at(EOF)
+ }
+}
+pub(crate) struct ExpectedSyntaxGuard {
+ expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,
+}
+
+impl ExpectedSyntaxGuard {
+ fn new(expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>) -> Self {
+ Self {
+ expected_syntax_tracking_state,
+ }
+ }
+}
+
+impl Drop for ExpectedSyntaxGuard {
+ fn drop(&mut self) {
+ self.expected_syntax_tracking_state
+ .set(ExpectedSyntax::Unnamed(TS![]));
+ }
+}
+
+#[derive(Clone, Debug, Copy)]
+pub enum ExpectedSyntax {
+ Named(&'static str),
+ Unnamed(SyntaxKindSet),
+}
+impl fmt::Display for ExpectedSyntax {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ ExpectedSyntax::Named(name) => write!(f, "{name}"),
+ ExpectedSyntax::Unnamed(set) => write!(f, "{set}"),
+ }
+ }
+}
+
+fn expr(p: &mut Parser) -> CompletedMarker {
+ let m = p.start();
+ while p.at(T![local]) || p.at(T![assert]) {
+ let m = p.start();
+
+ if p.at(T![local]) {
+ p.bump();
+ loop {
+ if p.at(T![;]) {
+ p.bump();
+ break;
+ }
+ bind(p);
+
+ if p.at(T![,]) {
+ p.bump();
+ continue;
+ }
+ p.expect(T![;]);
+ break;
+ }
+ m.complete(p, STMT_LOCAL);
+ } else {
+ assertion(p);
+ p.expect(T![;]);
+ m.complete(p, STMT_ASSERT);
+ }
+ }
+ match expr_binding_power(p, 0) {
+ Ok(m) => m,
+ Err(m) => m,
+ };
+ m.complete(p, EXPR)
+}
+fn expr_binding_power(
+ p: &mut Parser,
+ minimum_binding_power: u8,
+) -> Result<CompletedMarker, CompletedMarker> {
+ let mut lhs = lhs(p)?;
+
+ while let Some(op) = BinaryOperatorKind::cast(p.current())
+ .or_else(|| p.at(T!['{']).then_some(BinaryOperatorKind::MetaObjectApply))
+ {
+ 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 != BinaryOperatorKind::MetaObjectApply {
+ p.bump();
+ }
+
+ let m = lhs.wrap(p, EXPR).precede(p);
+ let parsed_rhs = expr_binding_power(p, right_binding_power)
+ .map(|v| v.precede(p).complete(p, EXPR))
+ .is_ok();
+ lhs = m.complete(
+ p,
+ if op == BinaryOperatorKind::MetaObjectApply {
+ EXPR_OBJ_EXTEND
+ } else {
+ EXPR_BINARY
+ },
+ );
+
+ if !parsed_rhs {
+ break;
+ }
+ }
+ Ok(lhs)
+}
+
+const COMPSPEC: SyntaxKindSet = TS![for if];
+fn compspec(p: &mut Parser) -> CompletedMarker {
+ assert!(p.at_ts(COMPSPEC));
+ if p.at(T![for]) {
+ let m = p.start();
+ p.bump();
+ destruct(p);
+ p.expect(T![in]);
+ expr(p);
+ m.complete(p, FOR_SPEC)
+ } else if p.at(T![if]) {
+ let m = p.start();
+ p.bump();
+ expr(p);
+ m.complete(p, IF_SPEC)
+ } else {
+ unreachable!()
+ }
+}
+
+fn comma(p: &mut Parser) -> bool {
+ comma_with_alternatives(p, TS![])
+}
+fn comma_with_alternatives(p: &mut Parser, set: SyntaxKindSet) -> bool {
+ if p.at(T![,]) {
+ p.bump();
+ true
+ } else if p.at_ts(set) {
+ let _ex = p.expected_syntax_name("comma");
+ p.expect_with_recovery_set(T![,], TS![]);
+ true
+ } else {
+ false
+ }
+}
+
+fn field_name(p: &mut Parser) {
+ let _e = p.expected_syntax_name("field name");
+ let m = p.start();
+ if p.at(T!['[']) {
+ p.bump();
+ expr(p);
+ p.expect(T![']']);
+ m.complete(p, FIELD_NAME_DYNAMIC);
+ } else if p.at(IDENT) {
+ name(p);
+ m.complete(p, FIELD_NAME_FIXED);
+ } else if Text::can_cast(p.current()) {
+ text(p);
+ m.complete(p, FIELD_NAME_FIXED);
+ } else {
+ m.forget(p);
+ p.error_with_recovery_set(TS![; : :: ::: '(']);
+ }
+}
+fn visibility(p: &mut Parser) {
+ if p.at_ts(TS![: :: :::]) {
+ p.bump()
+ } else {
+ p.error_with_recovery_set(TS![=]);
+ }
+}
+fn assertion(p: &mut Parser) {
+ let m = p.start();
+ p.bump_assert(T![assert]);
+ expr(p);
+ if p.at(T![:]) {
+ p.bump();
+ expr(p);
+ }
+ m.complete(p, ASSERTION);
+}
+fn object(p: &mut Parser) -> CompletedMarker {
+ let m_t = p.start();
+ let m = p.start();
+ p.bump_assert(T!['{']);
+
+ let mut elems = 0;
+ let mut compspecs = Vec::new();
+ let mut asserts = Vec::new();
+ loop {
+ if p.at(T!['}']) {
+ p.bump();
+ break;
+ }
+ if p.at_ts(COMPSPEC) {
+ if elems == 0 {
+ let m = p.start();
+ m.complete_missing(p, ExpectedSyntax::Named("field definition"));
+ }
+ while p.at_ts(COMPSPEC) {
+ compspecs.push(compspec(p));
+ }
+ if comma_with_alternatives(p, TS![;]) {
+ continue;
+ }
+ p.expect(R_BRACE);
+ break;
+ }
+ let m = p.start();
+ if p.at(T![local]) {
+ obj_local(p);
+ m.complete(p, MEMBER_BIND_STMT);
+ } else if p.at(T![assert]) {
+ assertion(p);
+ asserts.push(m.complete(p, MEMBER_ASSERT_STMT));
+ } else {
+ field_name(p);
+ if p.at(T![+]) {
+ p.bump();
+ }
+ let params = if p.at(T!['(']) {
+ params_desc(p);
+ visibility(p);
+ expr(p);
+ true
+ } else if p.at_ts(TS![: :: :::]) && p.nth_at(1, T![function]) {
+ visibility(p);
+ p.bump_assert(T![function]);
+ params_desc(p);
+ expr(p);
+ true
+ } else {
+ visibility(p);
+ expr(p);
+ false
+ };
+ elems += 1;
+
+ if params {
+ m.complete(p, MEMBER_FIELD_METHOD)
+ } else {
+ m.complete(p, MEMBER_FIELD_NORMAL)
+ };
+ };
+ while p.at_ts(COMPSPEC) {
+ compspecs.push(compspec(p));
+ }
+ if comma_with_alternatives(p, TS![;]) {
+ continue;
+ }
+ p.expect(R_BRACE);
+ break;
+ }
+
+ if elems > 1 && !compspecs.is_empty() {
+ for errored in compspecs {
+ errored.wrap_error(
+ p,
+ "compspec may only be used if there is only one object element",
+ );
+ }
+ m.complete(p, OBJ_BODY_MEMBER_LIST);
+ } else if !compspecs.is_empty() {
+ for errored in asserts {
+ errored.wrap_error(p, "asserts can't be used in object comprehensions");
+ }
+ m.complete(p, OBJ_BODY_COMP);
+ } else {
+ m.complete(p, OBJ_BODY_MEMBER_LIST);
+ }
+ m_t.complete(p, EXPR_OBJECT)
+}
+fn param(p: &mut Parser) {
+ let m = p.start();
+ destruct(p);
+ if p.at(T![=]) {
+ p.bump();
+ expr(p);
+ }
+ m.complete(p, PARAM);
+}
+fn params_desc(p: &mut Parser) -> CompletedMarker {
+ let m = p.start();
+ p.bump_assert(T!['(']);
+
+ loop {
+ if p.at(T![')']) {
+ p.bump();
+ break;
+ }
+ param(p);
+ if comma(p) {
+ continue;
+ }
+ p.expect(T![')']);
+ break;
+ }
+
+ m.complete(p, PARAMS_DESC)
+}
+fn args_desc(p: &mut Parser) {
+ let m = p.start();
+ p.bump_assert(T!['(']);
+
+ let started_named = Cell::new(false);
+ let mut unnamed_after_named = Vec::new();
+
+ loop {
+ if p.at(T![')']) {
+ break;
+ }
+
+ let m = p.start();
+ if p.at(IDENT) && p.nth_at(1, T![=]) {
+ name(p);
+ p.bump();
+ expr(p);
+ m.complete(p, ARG);
+ started_named.set(true);
+ } else {
+ expr(p);
+ let arg = m.complete(p, ARG);
+ if started_named.get() {
+ unnamed_after_named.push(arg)
+ }
+ }
+ if comma(p) {
+ continue;
+ }
+ break;
+ }
+ p.expect(T![')']);
+ if p.at(T![tailstrict]) {
+ p.bump()
+ }
+
+ for errored in unnamed_after_named {
+ errored.wrap_error(p, "can't use positional arguments after named");
+ }
+
+ m.complete(p, ARGS_DESC);
+}
+
+fn array(p: &mut Parser) -> CompletedMarker {
+ // Start the list node
+ let m = p.start();
+ p.bump_assert(T!['[']);
+
+ let mut compspecs = Vec::new();
+ let mut elems = 0;
+
+ loop {
+ if p.at(T![']']) {
+ p.bump();
+ break;
+ }
+ if elems != 0 && p.at_ts(COMPSPEC) {
+ while p.at_ts(COMPSPEC) {
+ compspecs.push(compspec(p));
+ }
+ if comma(p) {
+ continue;
+ }
+ p.expect(T![']']);
+ break;
+ }
+ expr(p);
+ elems += 1;
+ while p.at_ts(COMPSPEC) {
+ compspecs.push(compspec(p));
+ }
+ if comma(p) {
+ continue;
+ }
+ p.expect(T![']']);
+ break;
+ }
+
+ if elems > 1 && !compspecs.is_empty() {
+ for spec in compspecs {
+ spec.wrap_error(
+ p,
+ "compspec may only be used if there is only one array element",
+ );
+ }
+
+ m.complete(p, EXPR_ARRAY)
+ } else if !compspecs.is_empty() {
+ m.complete(p, EXPR_ARRAY_COMP)
+ } else {
+ m.complete(p, EXPR_ARRAY)
+ }
+}
+/// Returns true if it was slice, false if just index
+#[must_use]
+fn slice_desc_or_index(p: &mut Parser) -> bool {
+ let m = p.start();
+ p.bump();
+ // TODO: do not treat :, ::, ::: as full tokens?
+ // Start
+ if !p.at(T![:]) && !p.at(T![::]) {
+ expr(p);
+ }
+ if p.at(T![:]) {
+ p.bump();
+ // End
+ if !p.at(T![']']) {
+ expr(p).wrap(p, SLICE_DESC_END);
+ }
+ if p.at(T![:]) {
+ p.bump();
+ // Step
+ if !p.at(T![']']) {
+ expr(p).wrap(p, SLICE_DESC_STEP);
+ }
+ }
+ } else if p.at(T![::]) {
+ p.bump();
+ // End
+ if !p.at(T![']']) {
+ expr(p).wrap(p, SLICE_DESC_END);
+ }
+ } else {
+ // It was not a slice
+ p.expect(T![']']);
+ m.forget(p);
+ return false;
+ }
+ p.expect(T![']']);
+ m.complete(p, SLICE_DESC);
+ true
+}
+
+fn suffix(p: &mut Parser) {
+ loop {
+ let start = p.start();
+ let _marker: CompletedMarker = if p.at(T![?]) {
+ p.bump();
+ p.expect(T![.]);
+ if p.at(IDENT) {
+ name(p);
+ start.complete(p, SUFFIX_INDEX)
+ } else if p.at(T!['[']) {
+ p.bump();
+ expr(p);
+ p.expect(T![']']);
+ start.complete(p, SUFFIX_INDEX_EXPR)
+ } else {
+ start.complete_missing(p, ExpectedSyntax::Named("index"))
+ }
+ } else if p.at(T![.]) {
+ p.bump();
+ name(p);
+ start.complete(p, SUFFIX_INDEX)
+ } else if p.at(T!['[']) {
+ if slice_desc_or_index(p) {
+ start.complete(p, SUFFIX_SLICE)
+ } else {
+ start.complete(p, SUFFIX_INDEX_EXPR)
+ }
+ } else if p.at(T!['(']) {
+ args_desc(p);
+ start.complete(p, SUFFIX_APPLY)
+ } else {
+ start.forget(p);
+ break;
+ };
+ }
+}
+
+fn lhs(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {
+ let lhs = lhs_basic(p)?;
+
+ suffix(p);
+
+ Ok(lhs)
+}
+fn name(p: &mut Parser) {
+ let m = p.start();
+ p.expect(IDENT);
+ m.complete(p, NAME);
+}
+fn destruct_rest(p: &mut Parser) {
+ let m = p.start();
+ p.bump_assert(T![...]);
+ if p.at(IDENT) {
+ p.bump()
+ }
+ m.complete(p, DESTRUCT_REST);
+}
+fn destruct_object_field(p: &mut Parser) {
+ let m = p.start();
+ name(p);
+ if p.at(T![:]) {
+ p.bump();
+ destruct(p);
+ };
+ if p.at(T![=]) {
+ p.bump();
+ expr(p);
+ }
+ m.complete(p, DESTRUCT_OBJECT_FIELD);
+}
+fn obj_local(p: &mut Parser) {
+ let m = p.start();
+ p.bump_assert(T![local]);
+ bind(p);
+ m.complete(p, OBJ_LOCAL);
+}
+fn destruct(p: &mut Parser) -> CompletedMarker {
+ let m = p.start();
+ let _ex = p.expected_syntax_name("destruction specifier");
+ if p.at(T![?]) {
+ p.bump();
+ m.complete(p, DESTRUCT_SKIP)
+ } else if p.at(T!['[']) {
+ p.bump();
+ let mut had_rest = false;
+ loop {
+ if p.at(T![']']) {
+ p.bump();
+ break;
+ } else if p.at(T![...]) {
+ let m_err = p.start_ranger();
+ destruct_rest(p);
+ // if had_rest {
+ // p.custom_error(m_err.finish(p), "only one rest can be present in array");
+ // }
+ had_rest = true;
+ } else {
+ destruct(p);
+ }
+ if p.at(T![,]) {
+ p.bump();
+ continue;
+ }
+ p.expect(T![']']);
+ break;
+ }
+ m.complete(p, DESTRUCT_ARRAY)
+ } else if p.at(T!['{']) {
+ p.bump();
+ let mut had_rest = false;
+ loop {
+ if p.at(T!['}']) {
+ p.bump();
+ break;
+ } else if p.at(T![...]) {
+ let m_err = p.start_ranger();
+ destruct_rest(p);
+ // if had_rest {
+ // p.custom_error(m_err.finish(p), "only one rest can be present in object");
+ // }
+ had_rest = true;
+ } else {
+ if had_rest {
+ p.error_with_recovery_set(TS![]);
+ }
+ destruct_object_field(p);
+ }
+ if p.at(T![,]) {
+ p.bump();
+ continue;
+ }
+ p.expect(T!['}']);
+ break;
+ }
+ m.complete(p, DESTRUCT_OBJECT)
+ } else if p.at(IDENT) {
+ name(p);
+ m.complete(p, DESTRUCT_FULL)
+ } else {
+ m.forget(p);
+ p.error_with_recovery_set(TS![; , '}', '(', :])
+ }
+}
+fn bind(p: &mut Parser) {
+ let m = p.start();
+ if p.at(IDENT) && p.nth_at(1, T!['(']) {
+ name(p);
+ params_desc(p);
+ p.expect(T![=]);
+ expr(p);
+ m.complete(p, BIND_FUNCTION)
+ } else if p.at(IDENT) && p.nth_at(1, T![=]) && p.nth_at(2, T![function]) {
+ name(p);
+ p.expect(T![=]);
+ p.expect(T![function]);
+ params_desc(p);
+ expr(p);
+ m.complete(p, BIND_FUNCTION)
+ } else {
+ destruct(p);
+ p.expect(T![=]);
+ expr(p);
+ m.complete(p, BIND_DESTRUCT)
+ };
+}
+fn text(p: &mut Parser) {
+ assert!(Text::can_cast(p.current()));
+ p.bump();
+}
+fn number(p: &mut Parser) {
+ assert!(Number::can_cast(p.current()));
+ p.bump();
+}
+fn literal(p: &mut Parser) {
+ assert!(Literal::can_cast(p.current()));
+ p.bump();
+}
+fn lhs_basic(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {
+ let _e = p.expected_syntax_name("expression");
+ Ok(if Literal::can_cast(p.current()) {
+ let m = p.start();
+ literal(p);
+ m.complete(p, EXPR_LITERAL)
+ } else if Text::can_cast(p.current()) {
+ let m = p.start();
+ text(p);
+ m.complete(p, EXPR_STRING)
+ } else if Number::can_cast(p.current()) {
+ let m = p.start();
+ number(p);
+ m.complete(p, EXPR_NUMBER)
+ } else if p.at(IDENT) {
+ let m = p.start();
+ name(p);
+ m.complete(p, EXPR_VAR)
+ } else if p.at(T![if]) {
+ let m = p.start();
+ p.bump();
+ expr(p);
+ p.expect(T![then]);
+ expr(p).wrap(p, TRUE_EXPR);
+ if p.at(T![else]) {
+ p.bump();
+ expr(p).wrap(p, FALSE_EXPR);
+ }
+ m.complete(p, EXPR_IF_THEN_ELSE)
+ } else if p.at(T!['[']) {
+ array(p)
+ } else if p.at(T!['{']) {
+ object(p)
+ } else if p.at(T![function]) {
+ let m = p.start();
+ p.bump();
+ params_desc(p);
+ expr(p);
+ m.complete(p, EXPR_FUNCTION)
+ } else if p.at(T![error]) {
+ let m = p.start();
+ p.bump();
+ expr(p);
+ m.complete(p, EXPR_ERROR)
+ } else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {
+ let m = p.start();
+ p.bump();
+ text(p);
+ m.complete(p, EXPR_IMPORT)
+ } else if let Some(op) = UnaryOperatorKind::cast(p.current()) {
+ let ((), right_binding_power) = op.binding_power();
+
+ let m = p.start();
+ p.bump();
+ let _ = expr_binding_power(p, right_binding_power);
+ m.complete(p, EXPR_UNARY)
+ } else if p.at(T!['(']) {
+ let m = p.start();
+ p.bump();
+ expr(p);
+ p.expect(T![')']);
+ m.complete(p, EXPR_PARENED)
+ } else {
+ return Err(p.error_with_no_skip());
+ })
+}
+
+impl Parse {
+ pub fn syntax(&self) -> SyntaxNode {
+ SyntaxNode::new_root(self.green_node.clone())
+ }
+}
crates/jrsonnet-rowan-parser/src/precedence.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/precedence.rs
@@ -0,0 +1,31 @@
+use crate::nodes::{BinaryOperatorKind, UnaryOperatorKind};
+
+impl BinaryOperatorKind {
+ pub fn binding_power(&self) -> (u8, u8) {
+ match self {
+ Self::MetaObjectApply => (22, 23),
+ Self::Mul | Self::Div | Self::Modulo => (20, 21),
+ Self::Plus | Self::Minus => (18, 19),
+ Self::Lhs | Self::Rhs => (16, 17),
+ Self::Lt | Self::Gt | Self::Le | Self::Ge | Self::InKw => (14, 15),
+ Self::Eq | Self::Ne => (12, 13),
+ Self::BitAnd => (10, 11),
+ Self::BitXor => (8, 9),
+ Self::BitOr => (6, 7),
+ Self::And => (4, 5),
+ Self::NullCoaelse => (2, 3),
+ Self::Or => (2, 3),
+ Self::ErrorNoOperator => (0, 1),
+ }
+ }
+}
+
+impl UnaryOperatorKind {
+ pub fn binding_power(&self) -> ((), u8) {
+ match self {
+ Self::Minus => ((), 20),
+ Self::Not => ((), 20),
+ Self::BitNot => ((), 20),
+ }
+ }
+}
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec.snap
@@ -0,0 +1,41 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "[a for a in [1, 2, 3]]\n"
+---
+SOURCE_FILE@0..23
+ EXPR@0..22
+ EXPR_ARRAY_COMP@0..22
+ L_BRACK@0..1 "["
+ EXPR@1..2
+ EXPR_VAR@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ WHITESPACE@2..3 " "
+ FOR_SPEC@3..21
+ FOR_KW@3..6 "for"
+ WHITESPACE@6..7 " "
+ DESTRUCT_FULL@7..8
+ NAME@7..8
+ IDENT@7..8 "a"
+ WHITESPACE@8..9 " "
+ IN_KW@9..11 "in"
+ WHITESPACE@11..12 " "
+ EXPR@12..21
+ EXPR_ARRAY@12..21
+ L_BRACK@12..13 "["
+ EXPR@13..14
+ EXPR_NUMBER@13..14
+ FLOAT@13..14 "1"
+ COMMA@14..15 ","
+ WHITESPACE@15..16 " "
+ EXPR@16..17
+ EXPR_NUMBER@16..17
+ FLOAT@16..17 "2"
+ COMMA@17..18 ","
+ WHITESPACE@18..19 " "
+ EXPR@19..20
+ EXPR_NUMBER@19..20
+ FLOAT@19..20 "3"
+ R_BRACK@20..21 "]"
+ R_BRACK@21..22 "]"
+ WHITESPACE@22..23 "\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec_comma.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec_comma.snap
@@ -0,0 +1,42 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "[a, for a in [1, 2, 3]]\n"
+---
+SOURCE_FILE@0..24
+ EXPR@0..23
+ EXPR_ARRAY_COMP@0..23
+ L_BRACK@0..1 "["
+ EXPR@1..2
+ EXPR_VAR@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ COMMA@2..3 ","
+ WHITESPACE@3..4 " "
+ FOR_SPEC@4..22
+ FOR_KW@4..7 "for"
+ WHITESPACE@7..8 " "
+ DESTRUCT_FULL@8..9
+ NAME@8..9
+ IDENT@8..9 "a"
+ WHITESPACE@9..10 " "
+ IN_KW@10..12 "in"
+ WHITESPACE@12..13 " "
+ EXPR@13..22
+ EXPR_ARRAY@13..22
+ L_BRACK@13..14 "["
+ EXPR@14..15
+ EXPR_NUMBER@14..15
+ FLOAT@14..15 "1"
+ COMMA@15..16 ","
+ WHITESPACE@16..17 " "
+ EXPR@17..18
+ EXPR_NUMBER@17..18
+ FLOAT@17..18 "2"
+ COMMA@18..19 ","
+ WHITESPACE@19..20 " "
+ EXPR@20..21
+ EXPR_NUMBER@20..21
+ FLOAT@20..21 "3"
+ R_BRACK@21..22 "]"
+ R_BRACK@22..23 "]"
+ WHITESPACE@23..24 "\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec_incompatible_with_multiple_elems.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec_incompatible_with_multiple_elems.snap
@@ -0,0 +1,57 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "[a for a in [1, 2, 3], b]\n"
+---
+SOURCE_FILE@0..26
+ EXPR@0..25
+ EXPR_ARRAY@0..25
+ L_BRACK@0..1 "["
+ EXPR@1..2
+ EXPR_VAR@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ WHITESPACE@2..3 " "
+ ERROR_CUSTOM@3..21
+ FOR_SPEC@3..21
+ FOR_KW@3..6 "for"
+ WHITESPACE@6..7 " "
+ DESTRUCT_FULL@7..8
+ NAME@7..8
+ IDENT@7..8 "a"
+ WHITESPACE@8..9 " "
+ IN_KW@9..11 "in"
+ WHITESPACE@11..12 " "
+ EXPR@12..21
+ EXPR_ARRAY@12..21
+ L_BRACK@12..13 "["
+ EXPR@13..14
+ EXPR_NUMBER@13..14
+ FLOAT@13..14 "1"
+ COMMA@14..15 ","
+ WHITESPACE@15..16 " "
+ EXPR@16..17
+ EXPR_NUMBER@16..17
+ FLOAT@16..17 "2"
+ COMMA@17..18 ","
+ WHITESPACE@18..19 " "
+ EXPR@19..20
+ EXPR_NUMBER@19..20
+ FLOAT@19..20 "3"
+ R_BRACK@20..21 "]"
+ COMMA@21..22 ","
+ WHITESPACE@22..23 " "
+ EXPR@23..24
+ EXPR_VAR@23..24
+ NAME@23..24
+ IDENT@23..24 "b"
+ R_BRACK@24..25 "]"
+ WHITESPACE@25..26 "\n"
+===
+LocatedSyntaxError { error: Custom { error: "compspec may only be used if there is only one array element" }, range: 3..21 }
+===
+ x syntax error
+ ,----
+ 1 | [a for a in [1, 2, 3], b]
+ : ^^^^^^^^^|^^^^^^^^
+ : `-- compspec may only be used if there is only one array element
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec_incompatible_with_multiple_elems_w.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec_incompatible_with_multiple_elems_w.snap
@@ -0,0 +1,64 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "[a, b, for a in [1, 2, 3], c]\n"
+---
+SOURCE_FILE@0..30
+ EXPR@0..29
+ EXPR_ARRAY@0..29
+ L_BRACK@0..1 "["
+ EXPR@1..2
+ EXPR_VAR@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ COMMA@2..3 ","
+ WHITESPACE@3..4 " "
+ EXPR@4..5
+ EXPR_VAR@4..5
+ NAME@4..5
+ IDENT@4..5 "b"
+ COMMA@5..6 ","
+ WHITESPACE@6..7 " "
+ ERROR_CUSTOM@7..25
+ FOR_SPEC@7..25
+ FOR_KW@7..10 "for"
+ WHITESPACE@10..11 " "
+ DESTRUCT_FULL@11..12
+ NAME@11..12
+ IDENT@11..12 "a"
+ WHITESPACE@12..13 " "
+ IN_KW@13..15 "in"
+ WHITESPACE@15..16 " "
+ EXPR@16..25
+ EXPR_ARRAY@16..25
+ L_BRACK@16..17 "["
+ EXPR@17..18
+ EXPR_NUMBER@17..18
+ FLOAT@17..18 "1"
+ COMMA@18..19 ","
+ WHITESPACE@19..20 " "
+ EXPR@20..21
+ EXPR_NUMBER@20..21
+ FLOAT@20..21 "2"
+ COMMA@21..22 ","
+ WHITESPACE@22..23 " "
+ EXPR@23..24
+ EXPR_NUMBER@23..24
+ FLOAT@23..24 "3"
+ R_BRACK@24..25 "]"
+ COMMA@25..26 ","
+ WHITESPACE@26..27 " "
+ EXPR@27..28
+ EXPR_VAR@27..28
+ NAME@27..28
+ IDENT@27..28 "c"
+ R_BRACK@28..29 "]"
+ WHITESPACE@29..30 "\n"
+===
+LocatedSyntaxError { error: Custom { error: "compspec may only be used if there is only one array element" }, range: 7..25 }
+===
+ x syntax error
+ ,----
+ 1 | [a, b, for a in [1, 2, 3], c]
+ : ^^^^^^^^^|^^^^^^^^
+ : `-- compspec may only be used if there is only one array element
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec_no_elems.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__arr_compspec_no_elems.snap
@@ -0,0 +1,47 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "[for a in [1, 2, 3]]\n"
+---
+SOURCE_FILE@0..21
+ EXPR@0..20
+ EXPR_ARRAY_COMP@0..20
+ L_BRACK@0..1 "["
+ EXPR@1..1
+ ERROR_MISSING_TOKEN@1..1
+ FOR_SPEC@1..19
+ FOR_KW@1..4 "for"
+ WHITESPACE@4..5 " "
+ DESTRUCT_FULL@5..6
+ NAME@5..6
+ IDENT@5..6 "a"
+ WHITESPACE@6..7 " "
+ IN_KW@7..9 "in"
+ WHITESPACE@9..10 " "
+ EXPR@10..19
+ EXPR_ARRAY@10..19
+ L_BRACK@10..11 "["
+ EXPR@11..12
+ EXPR_NUMBER@11..12
+ FLOAT@11..12 "1"
+ COMMA@12..13 ","
+ WHITESPACE@13..14 " "
+ EXPR@14..15
+ EXPR_NUMBER@14..15
+ FLOAT@14..15 "2"
+ COMMA@15..16 ","
+ WHITESPACE@16..17 " "
+ EXPR@17..18
+ EXPR_NUMBER@17..18
+ FLOAT@17..18 "3"
+ R_BRACK@18..19 "]"
+ R_BRACK@19..20 "]"
+ WHITESPACE@20..21 "\n"
+===
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 1..1 }
+===
+ x syntax error
+ ,----
+ 1 | [for a in [1, 2, 3]]
+ : ^
+ : `-- missing expression
+ `----
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,35 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "[a for a in [1, 2, 3]]\n"
+---
+SOURCE_FILE@0..23
+ EXPR_ARRAY_COMP@0..22
+ L_BRACK@0..1 "["
+ EXPR_VAR@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ WHITESPACE@2..3 " "
+ FOR_SPEC@3..21
+ FOR_KW@3..6 "for"
+ WHITESPACE@6..7 " "
+ NAME@7..8
+ IDENT@7..8 "a"
+ WHITESPACE@8..9 " "
+ IN_KW@9..11 "in"
+ WHITESPACE@11..12 " "
+ EXPR_ARRAY@12..21
+ L_BRACK@12..13 "["
+ EXPR_NUMBER@13..14
+ FLOAT@13..14 "1"
+ COMMA@14..15 ","
+ WHITESPACE@15..16 " "
+ EXPR_NUMBER@16..17
+ FLOAT@16..17 "2"
+ COMMA@17..18 ","
+ WHITESPACE@18..19 " "
+ EXPR_NUMBER@19..20
+ FLOAT@19..20 "3"
+ R_BRACK@20..21 "]"
+ R_BRACK@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,48 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "[a for a in [1, 2, 3], b]\n"
+---
+SOURCE_FILE@0..25
+ EXPR_ARRAY@0..25
+ L_BRACK@0..1 "["
+ EXPR_VAR@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ WHITESPACE@2..3 " "
+ FOR_SPEC@3..21
+ FOR_KW@3..6 "for"
+ WHITESPACE@6..7 " "
+ NAME@7..8
+ IDENT@7..8 "a"
+ WHITESPACE@8..9 " "
+ IN_KW@9..11 "in"
+ WHITESPACE@11..12 " "
+ EXPR_ARRAY@12..21
+ L_BRACK@12..13 "["
+ EXPR_NUMBER@13..14
+ FLOAT@13..14 "1"
+ COMMA@14..15 ","
+ WHITESPACE@15..16 " "
+ EXPR_NUMBER@16..17
+ FLOAT@16..17 "2"
+ COMMA@17..18 ","
+ WHITESPACE@18..19 " "
+ EXPR_NUMBER@19..20
+ FLOAT@19..20 "3"
+ R_BRACK@20..21 "]"
+ COMMA@21..22 ","
+ WHITESPACE@22..23 " "
+ EXPR_VAR@23..24
+ NAME@23..24
+ IDENT@23..24 "b"
+ R_BRACK@24..25 "]"
+===
+Custom { error: "compspec may only be used if there is only one array element", range: 3..21 }
+===
+ x syntax error
+ ,----
+ 1 | [a for a in [1, 2, 3], b]
+ : ^^^^^^^^^|^^^^^^^^
+ : `-- compspec may only be used if there is only one array element
+ `----
+
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__continue_after_total_failure.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__continue_after_total_failure.snap
@@ -0,0 +1,77 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "local intr = $intrinsic(test);\n\nlocal a = 1, b = 2, c = a + b;\n\n[c]\n"
+---
+SOURCE_FILE@0..68
+ EXPR@0..29
+ STMT_LOCAL@0..23
+ LOCAL_KW@0..5 "local"
+ WHITESPACE@5..6 " "
+ BIND_DESTRUCT@6..14
+ DESTRUCT_FULL@6..10
+ NAME@6..10
+ IDENT@6..10 "intr"
+ WHITESPACE@10..11 " "
+ ASSIGN@11..12 "="
+ WHITESPACE@12..13 " "
+ EXPR@13..14
+ EXPR_LITERAL@13..14
+ DOLLAR@13..14 "$"
+ ERROR_UNEXPECTED_TOKEN@14..23
+ IDENT@14..23 "intrinsic"
+ EXPR_PARENED@23..29
+ L_PAREN@23..24 "("
+ EXPR@24..28
+ EXPR_VAR@24..28
+ NAME@24..28
+ IDENT@24..28 "test"
+ R_PAREN@28..29 ")"
+ ERROR_CUSTOM@29..67
+ SEMI@29..30 ";"
+ WHITESPACE@30..32 "\n\n"
+ LOCAL_KW@32..37 "local"
+ WHITESPACE@37..38 " "
+ IDENT@38..39 "a"
+ WHITESPACE@39..40 " "
+ ASSIGN@40..41 "="
+ WHITESPACE@41..42 " "
+ FLOAT@42..43 "1"
+ COMMA@43..44 ","
+ WHITESPACE@44..45 " "
+ IDENT@45..46 "b"
+ WHITESPACE@46..47 " "
+ ASSIGN@47..48 "="
+ WHITESPACE@48..49 " "
+ FLOAT@49..50 "2"
+ COMMA@50..51 ","
+ WHITESPACE@51..52 " "
+ IDENT@52..53 "c"
+ WHITESPACE@53..54 " "
+ ASSIGN@54..55 "="
+ WHITESPACE@55..56 " "
+ IDENT@56..57 "a"
+ WHITESPACE@57..58 " "
+ PLUS@58..59 "+"
+ WHITESPACE@59..60 " "
+ IDENT@60..61 "b"
+ SEMI@61..62 ";"
+ WHITESPACE@62..64 "\n\n"
+ L_BRACK@64..65 "["
+ IDENT@65..66 "c"
+ R_BRACK@66..67 "]"
+ WHITESPACE@67..68 "\n"
+===
+LocatedSyntaxError { error: Unexpected { expected: Unnamed(SyntaxKindSet([L_BRACK, L_PAREN, L_BRACE, SEMI, DOT, COMMA, QUESTION_MARK])), found: IDENT }, range: 14..23 }
+LocatedSyntaxError { error: Custom { error: "unexpected tokens after end" }, range: 29..67 }
+===
+ x syntax error
+ ,-[1:1]
+ 1 | ,-> local intr = $intrinsic(test);
+ : || ^^^^|^^^^
+ : || `-- expected L_BRACK, L_PAREN, L_BRACE, SEMI, DOT, COMMA or QUESTION_MARK, found IDENT
+ 2 | |
+ 3 | | local a = 1, b = 2, c = a + b;
+ 4 | |
+ 5 | |-> [c]
+ : `---- unexpected tokens after end
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__destruct.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__destruct.snap
@@ -0,0 +1,265 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "local [a, b, c] = arr;\nlocal [a, ...] = arr_rest;\nlocal [..., a] = rest_arr;\nlocal [...] = rest_in_arr;\nlocal [a, ...n] = arr_rest_n;\nlocal [...n, a] = rest_arr_n;\nlocal [...n] = rest_in_arr_n;\n\nlocal {a, b, c} = obj;\nlocal {a, b, c, ...} = obj_rest;\nlocal {a, b, c, ...n} = obj_rest_n;\n\nnull\n"
+---
+SOURCE_FILE@0..293
+ EXPR@0..292
+ STMT_LOCAL@0..22
+ LOCAL_KW@0..5 "local"
+ WHITESPACE@5..6 " "
+ BIND_DESTRUCT@6..21
+ DESTRUCT_ARRAY@6..15
+ L_BRACK@6..7 "["
+ DESTRUCT_FULL@7..8
+ NAME@7..8
+ IDENT@7..8 "a"
+ COMMA@8..9 ","
+ WHITESPACE@9..10 " "
+ DESTRUCT_FULL@10..11
+ NAME@10..11
+ IDENT@10..11 "b"
+ COMMA@11..12 ","
+ WHITESPACE@12..13 " "
+ DESTRUCT_FULL@13..14
+ NAME@13..14
+ IDENT@13..14 "c"
+ R_BRACK@14..15 "]"
+ WHITESPACE@15..16 " "
+ ASSIGN@16..17 "="
+ WHITESPACE@17..18 " "
+ EXPR@18..21
+ EXPR_VAR@18..21
+ NAME@18..21
+ IDENT@18..21 "arr"
+ SEMI@21..22 ";"
+ WHITESPACE@22..23 "\n"
+ STMT_LOCAL@23..49
+ LOCAL_KW@23..28 "local"
+ WHITESPACE@28..29 " "
+ BIND_DESTRUCT@29..48
+ DESTRUCT_ARRAY@29..37
+ L_BRACK@29..30 "["
+ DESTRUCT_FULL@30..31
+ NAME@30..31
+ IDENT@30..31 "a"
+ COMMA@31..32 ","
+ WHITESPACE@32..33 " "
+ DESTRUCT_REST@33..36
+ DOTDOTDOT@33..36 "..."
+ R_BRACK@36..37 "]"
+ WHITESPACE@37..38 " "
+ ASSIGN@38..39 "="
+ WHITESPACE@39..40 " "
+ EXPR@40..48
+ EXPR_VAR@40..48
+ NAME@40..48
+ IDENT@40..48 "arr_rest"
+ SEMI@48..49 ";"
+ WHITESPACE@49..50 "\n"
+ STMT_LOCAL@50..76
+ LOCAL_KW@50..55 "local"
+ WHITESPACE@55..56 " "
+ BIND_DESTRUCT@56..75
+ DESTRUCT_ARRAY@56..64
+ L_BRACK@56..57 "["
+ DESTRUCT_REST@57..60
+ DOTDOTDOT@57..60 "..."
+ COMMA@60..61 ","
+ WHITESPACE@61..62 " "
+ DESTRUCT_FULL@62..63
+ NAME@62..63
+ IDENT@62..63 "a"
+ R_BRACK@63..64 "]"
+ WHITESPACE@64..65 " "
+ ASSIGN@65..66 "="
+ WHITESPACE@66..67 " "
+ EXPR@67..75
+ EXPR_VAR@67..75
+ NAME@67..75
+ IDENT@67..75 "rest_arr"
+ SEMI@75..76 ";"
+ WHITESPACE@76..77 "\n"
+ STMT_LOCAL@77..103
+ LOCAL_KW@77..82 "local"
+ WHITESPACE@82..83 " "
+ BIND_DESTRUCT@83..102
+ DESTRUCT_ARRAY@83..88
+ L_BRACK@83..84 "["
+ DESTRUCT_REST@84..87
+ DOTDOTDOT@84..87 "..."
+ R_BRACK@87..88 "]"
+ WHITESPACE@88..89 " "
+ ASSIGN@89..90 "="
+ WHITESPACE@90..91 " "
+ EXPR@91..102
+ EXPR_VAR@91..102
+ NAME@91..102
+ IDENT@91..102 "rest_in_arr"
+ SEMI@102..103 ";"
+ WHITESPACE@103..104 "\n"
+ STMT_LOCAL@104..133
+ LOCAL_KW@104..109 "local"
+ WHITESPACE@109..110 " "
+ BIND_DESTRUCT@110..132
+ DESTRUCT_ARRAY@110..119
+ L_BRACK@110..111 "["
+ DESTRUCT_FULL@111..112
+ NAME@111..112
+ IDENT@111..112 "a"
+ COMMA@112..113 ","
+ WHITESPACE@113..114 " "
+ DESTRUCT_REST@114..118
+ DOTDOTDOT@114..117 "..."
+ IDENT@117..118 "n"
+ R_BRACK@118..119 "]"
+ WHITESPACE@119..120 " "
+ ASSIGN@120..121 "="
+ WHITESPACE@121..122 " "
+ EXPR@122..132
+ EXPR_VAR@122..132
+ NAME@122..132
+ IDENT@122..132 "arr_rest_n"
+ SEMI@132..133 ";"
+ WHITESPACE@133..134 "\n"
+ STMT_LOCAL@134..163
+ LOCAL_KW@134..139 "local"
+ WHITESPACE@139..140 " "
+ BIND_DESTRUCT@140..162
+ DESTRUCT_ARRAY@140..149
+ L_BRACK@140..141 "["
+ DESTRUCT_REST@141..145
+ DOTDOTDOT@141..144 "..."
+ IDENT@144..145 "n"
+ COMMA@145..146 ","
+ WHITESPACE@146..147 " "
+ DESTRUCT_FULL@147..148
+ NAME@147..148
+ IDENT@147..148 "a"
+ R_BRACK@148..149 "]"
+ WHITESPACE@149..150 " "
+ ASSIGN@150..151 "="
+ WHITESPACE@151..152 " "
+ EXPR@152..162
+ EXPR_VAR@152..162
+ NAME@152..162
+ IDENT@152..162 "rest_arr_n"
+ SEMI@162..163 ";"
+ WHITESPACE@163..164 "\n"
+ STMT_LOCAL@164..193
+ LOCAL_KW@164..169 "local"
+ WHITESPACE@169..170 " "
+ BIND_DESTRUCT@170..192
+ DESTRUCT_ARRAY@170..176
+ L_BRACK@170..171 "["
+ DESTRUCT_REST@171..175
+ DOTDOTDOT@171..174 "..."
+ IDENT@174..175 "n"
+ R_BRACK@175..176 "]"
+ WHITESPACE@176..177 " "
+ ASSIGN@177..178 "="
+ WHITESPACE@178..179 " "
+ EXPR@179..192
+ EXPR_VAR@179..192
+ NAME@179..192
+ IDENT@179..192 "rest_in_arr_n"
+ SEMI@192..193 ";"
+ WHITESPACE@193..195 "\n\n"
+ STMT_LOCAL@195..217
+ LOCAL_KW@195..200 "local"
+ WHITESPACE@200..201 " "
+ BIND_DESTRUCT@201..216
+ DESTRUCT_OBJECT@201..210
+ L_BRACE@201..202 "{"
+ DESTRUCT_OBJECT_FIELD@202..203
+ NAME@202..203
+ IDENT@202..203 "a"
+ COMMA@203..204 ","
+ WHITESPACE@204..205 " "
+ DESTRUCT_OBJECT_FIELD@205..206
+ NAME@205..206
+ IDENT@205..206 "b"
+ COMMA@206..207 ","
+ WHITESPACE@207..208 " "
+ DESTRUCT_OBJECT_FIELD@208..209
+ NAME@208..209
+ IDENT@208..209 "c"
+ R_BRACE@209..210 "}"
+ WHITESPACE@210..211 " "
+ ASSIGN@211..212 "="
+ WHITESPACE@212..213 " "
+ EXPR@213..216
+ EXPR_VAR@213..216
+ NAME@213..216
+ IDENT@213..216 "obj"
+ SEMI@216..217 ";"
+ WHITESPACE@217..218 "\n"
+ STMT_LOCAL@218..250
+ LOCAL_KW@218..223 "local"
+ WHITESPACE@223..224 " "
+ BIND_DESTRUCT@224..249
+ DESTRUCT_OBJECT@224..238
+ L_BRACE@224..225 "{"
+ DESTRUCT_OBJECT_FIELD@225..226
+ NAME@225..226
+ IDENT@225..226 "a"
+ COMMA@226..227 ","
+ WHITESPACE@227..228 " "
+ DESTRUCT_OBJECT_FIELD@228..229
+ NAME@228..229
+ IDENT@228..229 "b"
+ COMMA@229..230 ","
+ WHITESPACE@230..231 " "
+ DESTRUCT_OBJECT_FIELD@231..232
+ NAME@231..232
+ IDENT@231..232 "c"
+ COMMA@232..233 ","
+ WHITESPACE@233..234 " "
+ DESTRUCT_REST@234..237
+ DOTDOTDOT@234..237 "..."
+ R_BRACE@237..238 "}"
+ WHITESPACE@238..239 " "
+ ASSIGN@239..240 "="
+ WHITESPACE@240..241 " "
+ EXPR@241..249
+ EXPR_VAR@241..249
+ NAME@241..249
+ IDENT@241..249 "obj_rest"
+ SEMI@249..250 ";"
+ WHITESPACE@250..251 "\n"
+ STMT_LOCAL@251..286
+ LOCAL_KW@251..256 "local"
+ WHITESPACE@256..257 " "
+ BIND_DESTRUCT@257..285
+ DESTRUCT_OBJECT@257..272
+ L_BRACE@257..258 "{"
+ DESTRUCT_OBJECT_FIELD@258..259
+ NAME@258..259
+ IDENT@258..259 "a"
+ COMMA@259..260 ","
+ WHITESPACE@260..261 " "
+ DESTRUCT_OBJECT_FIELD@261..262
+ NAME@261..262
+ IDENT@261..262 "b"
+ COMMA@262..263 ","
+ WHITESPACE@263..264 " "
+ DESTRUCT_OBJECT_FIELD@264..265
+ NAME@264..265
+ IDENT@264..265 "c"
+ COMMA@265..266 ","
+ WHITESPACE@266..267 " "
+ DESTRUCT_REST@267..271
+ DOTDOTDOT@267..270 "..."
+ IDENT@270..271 "n"
+ R_BRACE@271..272 "}"
+ WHITESPACE@272..273 " "
+ ASSIGN@273..274 "="
+ WHITESPACE@274..275 " "
+ EXPR@275..285
+ EXPR_VAR@275..285
+ NAME@275..285
+ IDENT@275..285 "obj_rest_n"
+ SEMI@285..286 ";"
+ WHITESPACE@286..288 "\n\n"
+ EXPR_LITERAL@288..292
+ NULL_KW@288..292 "null"
+ WHITESPACE@292..293 "\n"
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,17 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: " "
+---
+SOURCE_FILE@0..1
+ WHITESPACE@0..1 " "
+ EXPR@1..1
+ ERROR_MISSING_TOKEN@1..1
+===
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 1..1 }
+===
+ x syntax error
+ ,----
+ 1 |
+ : ^
+ : `-- missing expression
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function.snap
@@ -0,0 +1,42 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "function(a, b = 1) a + b\n"
+---
+SOURCE_FILE@0..25
+ EXPR@0..24
+ EXPR_FUNCTION@0..24
+ FUNCTION_KW@0..8 "function"
+ PARAMS_DESC@8..18
+ L_PAREN@8..9 "("
+ PARAM@9..10
+ DESTRUCT_FULL@9..10
+ NAME@9..10
+ IDENT@9..10 "a"
+ COMMA@10..11 ","
+ WHITESPACE@11..12 " "
+ PARAM@12..17
+ DESTRUCT_FULL@12..13
+ NAME@12..13
+ IDENT@12..13 "b"
+ WHITESPACE@13..14 " "
+ ASSIGN@14..15 "="
+ WHITESPACE@15..16 " "
+ EXPR@16..17
+ EXPR_NUMBER@16..17
+ FLOAT@16..17 "1"
+ R_PAREN@17..18 ")"
+ WHITESPACE@18..19 " "
+ EXPR@19..24
+ EXPR_BINARY@19..24
+ EXPR@19..20
+ EXPR_VAR@19..20
+ NAME@19..20
+ IDENT@19..20 "a"
+ WHITESPACE@20..21 " "
+ PLUS@21..22 "+"
+ WHITESPACE@22..23 " "
+ EXPR@23..24
+ EXPR_VAR@23..24
+ NAME@23..24
+ IDENT@23..24 "b"
+ WHITESPACE@24..25 "\n"
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,33 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "function(a, b)\n"
+---
+SOURCE_FILE@0..15
+ EXPR@0..15
+ EXPR_FUNCTION@0..15
+ FUNCTION_KW@0..8 "function"
+ PARAMS_DESC@8..14
+ L_PAREN@8..9 "("
+ PARAM@9..10
+ DESTRUCT_FULL@9..10
+ NAME@9..10
+ IDENT@9..10 "a"
+ COMMA@10..11 ","
+ WHITESPACE@11..12 " "
+ PARAM@12..13
+ DESTRUCT_FULL@12..13
+ NAME@12..13
+ IDENT@12..13 "b"
+ R_PAREN@13..14 ")"
+ WHITESPACE@14..15 "\n"
+ EXPR@15..15
+ ERROR_MISSING_TOKEN@15..15
+===
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 15..15 }
+===
+ x syntax error
+ ,----
+ 1 | function(a, b)
+ : ^
+ : `-- missing expression
+ `----
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,50 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "function(a, b = ) a + b\n"
+---
+SOURCE_FILE@0..24
+ EXPR@0..23
+ EXPR_FUNCTION@0..23
+ FUNCTION_KW@0..8 "function"
+ PARAMS_DESC@8..17
+ L_PAREN@8..9 "("
+ PARAM@9..10
+ DESTRUCT_FULL@9..10
+ NAME@9..10
+ IDENT@9..10 "a"
+ COMMA@10..11 ","
+ WHITESPACE@11..12 " "
+ PARAM@12..16
+ DESTRUCT_FULL@12..13
+ NAME@12..13
+ IDENT@12..13 "b"
+ WHITESPACE@13..14 " "
+ ASSIGN@14..15 "="
+ WHITESPACE@15..16 " "
+ EXPR@16..16
+ ERROR_MISSING_TOKEN@16..16
+ R_PAREN@16..17 ")"
+ WHITESPACE@17..18 " "
+ EXPR@18..23
+ EXPR_BINARY@18..23
+ EXPR@18..19
+ EXPR_VAR@18..19
+ NAME@18..19
+ IDENT@18..19 "a"
+ WHITESPACE@19..20 " "
+ PLUS@20..21 "+"
+ WHITESPACE@21..22 " "
+ EXPR@22..23
+ EXPR_VAR@22..23
+ NAME@22..23
+ IDENT@22..23 "b"
+ WHITESPACE@23..24 "\n"
+===
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 16..16 }
+===
+ x syntax error
+ ,----
+ 1 | function(a, b = ) a + b
+ : ^
+ : `-- missing expression
+ `----
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,35 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "function(a, b\n"
+---
+SOURCE_FILE@0..14
+ EXPR@0..14
+ EXPR_FUNCTION@0..14
+ FUNCTION_KW@0..8 "function"
+ PARAMS_DESC@8..14
+ L_PAREN@8..9 "("
+ PARAM@9..10
+ DESTRUCT_FULL@9..10
+ NAME@9..10
+ IDENT@9..10 "a"
+ COMMA@10..11 ","
+ WHITESPACE@11..12 " "
+ PARAM@12..13
+ DESTRUCT_FULL@12..13
+ NAME@12..13
+ IDENT@12..13 "b"
+ WHITESPACE@13..14 "\n"
+ ERROR_MISSING_TOKEN@14..14
+ EXPR@14..14
+ ERROR_MISSING_TOKEN@14..14
+===
+LocatedSyntaxError { error: Missing { expected: Unnamed(SyntaxKindSet([R_PAREN, COMMA, ASSIGN])) }, range: 14..14 }
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 14..14 }
+===
+ x syntax error
+ ,----
+ 1 | function(a, b
+ : ^^
+ : |`-- missing expression
+ : `-- missing R_PAREN, COMMA or ASSIGN
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_method.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_method.snap
@@ -0,0 +1,55 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "local\n\ta(x) = x,\n\ta = function(x) x,\n; c\n"
+---
+SOURCE_FILE@0..41
+ EXPR@0..40
+ STMT_LOCAL@0..38
+ LOCAL_KW@0..5 "local"
+ WHITESPACE@5..7 "\n\t"
+ BIND_FUNCTION@7..15
+ NAME@7..8
+ IDENT@7..8 "a"
+ PARAMS_DESC@8..11
+ L_PAREN@8..9 "("
+ PARAM@9..10
+ DESTRUCT_FULL@9..10
+ NAME@9..10
+ IDENT@9..10 "x"
+ R_PAREN@10..11 ")"
+ WHITESPACE@11..12 " "
+ ASSIGN@12..13 "="
+ WHITESPACE@13..14 " "
+ EXPR@14..15
+ EXPR_VAR@14..15
+ NAME@14..15
+ IDENT@14..15 "x"
+ COMMA@15..16 ","
+ WHITESPACE@16..18 "\n\t"
+ BIND_FUNCTION@18..35
+ NAME@18..19
+ IDENT@18..19 "a"
+ WHITESPACE@19..20 " "
+ ASSIGN@20..21 "="
+ WHITESPACE@21..22 " "
+ FUNCTION_KW@22..30 "function"
+ PARAMS_DESC@30..33
+ L_PAREN@30..31 "("
+ PARAM@31..32
+ DESTRUCT_FULL@31..32
+ NAME@31..32
+ IDENT@31..32 "x"
+ R_PAREN@32..33 ")"
+ WHITESPACE@33..34 " "
+ EXPR@34..35
+ EXPR_VAR@34..35
+ NAME@34..35
+ IDENT@34..35 "x"
+ COMMA@35..36 ","
+ WHITESPACE@36..37 "\n"
+ SEMI@37..38 ";"
+ WHITESPACE@38..39 " "
+ EXPR_VAR@39..40
+ NAME@39..40
+ IDENT@39..40 "c"
+ WHITESPACE@40..41 "\n"
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,49 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "local a =\nlocal b = 3;\n1\n"
+---
+SOURCE_FILE@0..25
+ EXPR@0..25
+ STMT_LOCAL@0..25
+ LOCAL_KW@0..5 "local"
+ WHITESPACE@5..6 " "
+ BIND_DESTRUCT@6..24
+ DESTRUCT_FULL@6..7
+ NAME@6..7
+ IDENT@6..7 "a"
+ WHITESPACE@7..8 " "
+ ASSIGN@8..9 "="
+ WHITESPACE@9..10 "\n"
+ EXPR@10..24
+ STMT_LOCAL@10..22
+ LOCAL_KW@10..15 "local"
+ WHITESPACE@15..16 " "
+ BIND_DESTRUCT@16..21
+ DESTRUCT_FULL@16..17
+ NAME@16..17
+ IDENT@16..17 "b"
+ WHITESPACE@17..18 " "
+ ASSIGN@18..19 "="
+ WHITESPACE@19..20 " "
+ EXPR@20..21
+ EXPR_NUMBER@20..21
+ FLOAT@20..21 "3"
+ SEMI@21..22 ";"
+ WHITESPACE@22..23 "\n"
+ EXPR_NUMBER@23..24
+ FLOAT@23..24 "1"
+ WHITESPACE@24..25 "\n"
+ ERROR_MISSING_TOKEN@25..25
+ ERROR_MISSING_TOKEN@25..25
+===
+LocatedSyntaxError { error: Missing { expected: Unnamed(SyntaxKindSet([L_BRACK, L_PAREN, L_BRACE, SEMI, DOT, COMMA, QUESTION_MARK])) }, range: 25..25 }
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 25..25 }
+===
+ x syntax error
+ ,-[2:1]
+ 2 | local b = 3;
+ 3 | 1
+ : ^^
+ : |`-- missing expression
+ : `-- missing L_BRACK, L_PAREN, L_BRACE, SEMI, DOT, COMMA or QUESTION_MARK
+ `----
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,33 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "local a =\n"
+---
+SOURCE_FILE@0..10
+ EXPR@0..10
+ STMT_LOCAL@0..10
+ LOCAL_KW@0..5 "local"
+ WHITESPACE@5..6 " "
+ BIND_DESTRUCT@6..10
+ DESTRUCT_FULL@6..7
+ NAME@6..7
+ IDENT@6..7 "a"
+ WHITESPACE@7..8 " "
+ ASSIGN@8..9 "="
+ WHITESPACE@9..10 "\n"
+ EXPR@10..10
+ ERROR_MISSING_TOKEN@10..10
+ ERROR_MISSING_TOKEN@10..10
+ ERROR_MISSING_TOKEN@10..10
+===
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 10..10 }
+LocatedSyntaxError { error: Missing { expected: Unnamed(SyntaxKindSet([SEMI, COMMA])) }, range: 10..10 }
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 10..10 }
+===
+ x syntax error
+ ,----
+ 1 | local a =
+ : ^^^
+ : `-- missing expression
+ : |`-- missing SEMI or COMMA
+ : `-- missing expression
+ `----
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,78 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "a(1, 2, b=4, 3, 5, k = 12, 6)\n"
+---
+SOURCE_FILE@0..30
+ EXPR@0..29
+ EXPR_VAR@0..1
+ NAME@0..1
+ IDENT@0..1 "a"
+ SUFFIX_APPLY@1..29
+ ARGS_DESC@1..29
+ L_PAREN@1..2 "("
+ ARG@2..3
+ EXPR@2..3
+ EXPR_NUMBER@2..3
+ FLOAT@2..3 "1"
+ COMMA@3..4 ","
+ WHITESPACE@4..5 " "
+ ARG@5..6
+ EXPR@5..6
+ EXPR_NUMBER@5..6
+ FLOAT@5..6 "2"
+ COMMA@6..7 ","
+ WHITESPACE@7..8 " "
+ ARG@8..11
+ NAME@8..9
+ IDENT@8..9 "b"
+ ASSIGN@9..10 "="
+ EXPR@10..11
+ EXPR_NUMBER@10..11
+ FLOAT@10..11 "4"
+ COMMA@11..12 ","
+ WHITESPACE@12..13 " "
+ ERROR_CUSTOM@13..14
+ ARG@13..14
+ EXPR@13..14
+ EXPR_NUMBER@13..14
+ FLOAT@13..14 "3"
+ COMMA@14..15 ","
+ WHITESPACE@15..16 " "
+ ERROR_CUSTOM@16..17
+ ARG@16..17
+ EXPR@16..17
+ EXPR_NUMBER@16..17
+ FLOAT@16..17 "5"
+ COMMA@17..18 ","
+ WHITESPACE@18..19 " "
+ ARG@19..25
+ NAME@19..20
+ IDENT@19..20 "k"
+ WHITESPACE@20..21 " "
+ ASSIGN@21..22 "="
+ WHITESPACE@22..23 " "
+ EXPR@23..25
+ EXPR_NUMBER@23..25
+ FLOAT@23..25 "12"
+ COMMA@25..26 ","
+ WHITESPACE@26..27 " "
+ ERROR_CUSTOM@27..28
+ ARG@27..28
+ EXPR@27..28
+ EXPR_NUMBER@27..28
+ FLOAT@27..28 "6"
+ R_PAREN@28..29 ")"
+ WHITESPACE@29..30 "\n"
+===
+LocatedSyntaxError { error: Custom { error: "can't use positional arguments after named" }, range: 13..14 }
+LocatedSyntaxError { error: Custom { error: "can't use positional arguments after named" }, range: 16..17 }
+LocatedSyntaxError { error: Custom { error: "can't use positional arguments after named" }, range: 27..28 }
+===
+ x syntax error
+ ,----
+ 1 | a(1, 2, b=4, 3, 5, k = 12, 6)
+ : | | |
+ : | | `-- can't use positional arguments after named
+ : | `-- can't use positional arguments after named
+ : `-- can't use positional arguments after named
+ `----
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/tests.rs
+expression: "+ 2\n"
+---
+SOURCE_FILE@0..4
+ EXPR@0..0
+ ERROR_MISSING_TOKEN@0..0
+ ERROR_CUSTOM@0..3
+ PLUS@0..1 "+"
+ WHITESPACE@1..2 " "
+ FLOAT@2..3 "2"
+ WHITESPACE@3..4 "\n"
+===
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 0..0 }
+LocatedSyntaxError { error: Custom { error: "unexpected tokens after end" }, range: 0..3 }
+===
+ x syntax error
+ ,----
+ 1 | + 2
+ : ^^|
+ : | `-- unexpected tokens after end
+ : `-- missing expression
+ `----
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,21 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "2 2\n"
+---
+SOURCE_FILE@0..4
+ EXPR@0..1
+ EXPR_NUMBER@0..1
+ FLOAT@0..1 "2"
+ WHITESPACE@1..2 " "
+ ERROR_CUSTOM@2..3
+ FLOAT@2..3 "2"
+ WHITESPACE@3..4 "\n"
+===
+LocatedSyntaxError { error: Custom { error: "unexpected tokens after end" }, range: 2..3 }
+===
+ x syntax error
+ ,----
+ 1 | 2 2
+ : |
+ : `-- unexpected tokens after end
+ `----
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,24 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "a +\n"
+---
+SOURCE_FILE@0..4
+ EXPR@0..4
+ EXPR_BINARY@0..4
+ EXPR@0..1
+ EXPR_VAR@0..1
+ NAME@0..1
+ IDENT@0..1 "a"
+ WHITESPACE@1..2 " "
+ PLUS@2..3 "+"
+ WHITESPACE@3..4 "\n"
+ ERROR_MISSING_TOKEN@4..4
+===
+LocatedSyntaxError { error: Missing { expected: Named("expression") }, range: 4..4 }
+===
+ x syntax error
+ ,----
+ 1 | a +
+ : ^
+ : `-- missing expression
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec.snap
@@ -0,0 +1,46 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{a:1 for a in [1, 2, 3]}\n"
+---
+SOURCE_FILE@0..25
+ EXPR@0..24
+ EXPR_OBJECT@0..24
+ OBJ_BODY_COMP@0..24
+ L_BRACE@0..1 "{"
+ MEMBER_FIELD_NORMAL@1..4
+ FIELD_NAME_FIXED@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ COLON@2..3 ":"
+ EXPR@3..4
+ EXPR_NUMBER@3..4
+ FLOAT@3..4 "1"
+ WHITESPACE@4..5 " "
+ FOR_SPEC@5..23
+ FOR_KW@5..8 "for"
+ WHITESPACE@8..9 " "
+ DESTRUCT_FULL@9..10
+ NAME@9..10
+ IDENT@9..10 "a"
+ WHITESPACE@10..11 " "
+ IN_KW@11..13 "in"
+ WHITESPACE@13..14 " "
+ EXPR@14..23
+ EXPR_ARRAY@14..23
+ L_BRACK@14..15 "["
+ EXPR@15..16
+ EXPR_NUMBER@15..16
+ FLOAT@15..16 "1"
+ COMMA@16..17 ","
+ WHITESPACE@17..18 " "
+ EXPR@18..19
+ EXPR_NUMBER@18..19
+ FLOAT@18..19 "2"
+ COMMA@19..20 ","
+ WHITESPACE@20..21 " "
+ EXPR@21..22
+ EXPR_NUMBER@21..22
+ FLOAT@21..22 "3"
+ R_BRACK@22..23 "]"
+ R_BRACE@23..24 "}"
+ WHITESPACE@24..25 "\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_comma.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_comma.snap
@@ -0,0 +1,47 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{a:1, for a in [1, 2, 3]}\n"
+---
+SOURCE_FILE@0..26
+ EXPR@0..25
+ EXPR_OBJECT@0..25
+ OBJ_BODY_COMP@0..25
+ L_BRACE@0..1 "{"
+ MEMBER_FIELD_NORMAL@1..4
+ FIELD_NAME_FIXED@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ COLON@2..3 ":"
+ EXPR@3..4
+ EXPR_NUMBER@3..4
+ FLOAT@3..4 "1"
+ COMMA@4..5 ","
+ WHITESPACE@5..6 " "
+ FOR_SPEC@6..24
+ FOR_KW@6..9 "for"
+ WHITESPACE@9..10 " "
+ DESTRUCT_FULL@10..11
+ NAME@10..11
+ IDENT@10..11 "a"
+ WHITESPACE@11..12 " "
+ IN_KW@12..14 "in"
+ WHITESPACE@14..15 " "
+ EXPR@15..24
+ EXPR_ARRAY@15..24
+ L_BRACK@15..16 "["
+ EXPR@16..17
+ EXPR_NUMBER@16..17
+ FLOAT@16..17 "1"
+ COMMA@17..18 ","
+ WHITESPACE@18..19 " "
+ EXPR@19..20
+ EXPR_NUMBER@19..20
+ FLOAT@19..20 "2"
+ COMMA@20..21 ","
+ WHITESPACE@21..22 " "
+ EXPR@22..23
+ EXPR_NUMBER@22..23
+ FLOAT@22..23 "3"
+ R_BRACK@23..24 "]"
+ R_BRACE@24..25 "}"
+ WHITESPACE@25..26 "\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_incompatible_with_asserts.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_incompatible_with_asserts.snap
@@ -0,0 +1,64 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{assert 1, a: 1 for a in [1,2,3]}\n"
+---
+SOURCE_FILE@0..34
+ EXPR@0..33
+ EXPR_OBJECT@0..33
+ OBJ_BODY_COMP@0..33
+ L_BRACE@0..1 "{"
+ ERROR_CUSTOM@1..9
+ MEMBER_ASSERT_STMT@1..9
+ ASSERTION@1..9
+ ASSERT_KW@1..7 "assert"
+ WHITESPACE@7..8 " "
+ EXPR@8..9
+ EXPR_NUMBER@8..9
+ FLOAT@8..9 "1"
+ COMMA@9..10 ","
+ WHITESPACE@10..11 " "
+ MEMBER_FIELD_NORMAL@11..15
+ FIELD_NAME_FIXED@11..12
+ NAME@11..12
+ IDENT@11..12 "a"
+ COLON@12..13 ":"
+ WHITESPACE@13..14 " "
+ EXPR@14..15
+ EXPR_NUMBER@14..15
+ FLOAT@14..15 "1"
+ WHITESPACE@15..16 " "
+ FOR_SPEC@16..32
+ FOR_KW@16..19 "for"
+ WHITESPACE@19..20 " "
+ DESTRUCT_FULL@20..21
+ NAME@20..21
+ IDENT@20..21 "a"
+ WHITESPACE@21..22 " "
+ IN_KW@22..24 "in"
+ WHITESPACE@24..25 " "
+ EXPR@25..32
+ EXPR_ARRAY@25..32
+ L_BRACK@25..26 "["
+ EXPR@26..27
+ EXPR_NUMBER@26..27
+ FLOAT@26..27 "1"
+ COMMA@27..28 ","
+ EXPR@28..29
+ EXPR_NUMBER@28..29
+ FLOAT@28..29 "2"
+ COMMA@29..30 ","
+ EXPR@30..31
+ EXPR_NUMBER@30..31
+ FLOAT@30..31 "3"
+ R_BRACK@31..32 "]"
+ R_BRACE@32..33 "}"
+ WHITESPACE@33..34 "\n"
+===
+LocatedSyntaxError { error: Custom { error: "asserts can't be used in object comprehensions" }, range: 1..9 }
+===
+ x syntax error
+ ,----
+ 1 | {assert 1, a: 1 for a in [1,2,3]}
+ : ^^^^|^^^
+ : `-- asserts can't be used in object comprehensions
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_incompatible_with_multiple_elems.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_incompatible_with_multiple_elems.snap
@@ -0,0 +1,66 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{a:1 for a in [1, 2, 3], b:1}\n"
+---
+SOURCE_FILE@0..30
+ EXPR@0..29
+ EXPR_OBJECT@0..29
+ OBJ_BODY_MEMBER_LIST@0..29
+ L_BRACE@0..1 "{"
+ MEMBER_FIELD_NORMAL@1..4
+ FIELD_NAME_FIXED@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ COLON@2..3 ":"
+ EXPR@3..4
+ EXPR_NUMBER@3..4
+ FLOAT@3..4 "1"
+ WHITESPACE@4..5 " "
+ ERROR_CUSTOM@5..23
+ FOR_SPEC@5..23
+ FOR_KW@5..8 "for"
+ WHITESPACE@8..9 " "
+ DESTRUCT_FULL@9..10
+ NAME@9..10
+ IDENT@9..10 "a"
+ WHITESPACE@10..11 " "
+ IN_KW@11..13 "in"
+ WHITESPACE@13..14 " "
+ EXPR@14..23
+ EXPR_ARRAY@14..23
+ L_BRACK@14..15 "["
+ EXPR@15..16
+ EXPR_NUMBER@15..16
+ FLOAT@15..16 "1"
+ COMMA@16..17 ","
+ WHITESPACE@17..18 " "
+ EXPR@18..19
+ EXPR_NUMBER@18..19
+ FLOAT@18..19 "2"
+ COMMA@19..20 ","
+ WHITESPACE@20..21 " "
+ EXPR@21..22
+ EXPR_NUMBER@21..22
+ FLOAT@21..22 "3"
+ R_BRACK@22..23 "]"
+ COMMA@23..24 ","
+ WHITESPACE@24..25 " "
+ MEMBER_FIELD_NORMAL@25..28
+ FIELD_NAME_FIXED@25..26
+ NAME@25..26
+ IDENT@25..26 "b"
+ COLON@26..27 ":"
+ EXPR@27..28
+ EXPR_NUMBER@27..28
+ FLOAT@27..28 "1"
+ R_BRACE@28..29 "}"
+ WHITESPACE@29..30 "\n"
+===
+LocatedSyntaxError { error: Custom { error: "compspec may only be used if there is only one object element" }, range: 5..23 }
+===
+ x syntax error
+ ,----
+ 1 | {a:1 for a in [1, 2, 3], b:1}
+ : ^^^^^^^^^|^^^^^^^^
+ : `-- compspec may only be used if there is only one object element
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_incompatible_with_multiple_elems_w.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_incompatible_with_multiple_elems_w.snap
@@ -0,0 +1,77 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{a:1, b:1, for a in [1, 2, 3], c:1}\n"
+---
+SOURCE_FILE@0..36
+ EXPR@0..35
+ EXPR_OBJECT@0..35
+ OBJ_BODY_MEMBER_LIST@0..35
+ L_BRACE@0..1 "{"
+ MEMBER_FIELD_NORMAL@1..4
+ FIELD_NAME_FIXED@1..2
+ NAME@1..2
+ IDENT@1..2 "a"
+ COLON@2..3 ":"
+ EXPR@3..4
+ EXPR_NUMBER@3..4
+ FLOAT@3..4 "1"
+ COMMA@4..5 ","
+ WHITESPACE@5..6 " "
+ MEMBER_FIELD_NORMAL@6..9
+ FIELD_NAME_FIXED@6..7
+ NAME@6..7
+ IDENT@6..7 "b"
+ COLON@7..8 ":"
+ EXPR@8..9
+ EXPR_NUMBER@8..9
+ FLOAT@8..9 "1"
+ COMMA@9..10 ","
+ WHITESPACE@10..11 " "
+ ERROR_CUSTOM@11..29
+ FOR_SPEC@11..29
+ FOR_KW@11..14 "for"
+ WHITESPACE@14..15 " "
+ DESTRUCT_FULL@15..16
+ NAME@15..16
+ IDENT@15..16 "a"
+ WHITESPACE@16..17 " "
+ IN_KW@17..19 "in"
+ WHITESPACE@19..20 " "
+ EXPR@20..29
+ EXPR_ARRAY@20..29
+ L_BRACK@20..21 "["
+ EXPR@21..22
+ EXPR_NUMBER@21..22
+ FLOAT@21..22 "1"
+ COMMA@22..23 ","
+ WHITESPACE@23..24 " "
+ EXPR@24..25
+ EXPR_NUMBER@24..25
+ FLOAT@24..25 "2"
+ COMMA@25..26 ","
+ WHITESPACE@26..27 " "
+ EXPR@27..28
+ EXPR_NUMBER@27..28
+ FLOAT@27..28 "3"
+ R_BRACK@28..29 "]"
+ COMMA@29..30 ","
+ WHITESPACE@30..31 " "
+ MEMBER_FIELD_NORMAL@31..34
+ FIELD_NAME_FIXED@31..32
+ NAME@31..32
+ IDENT@31..32 "c"
+ COLON@32..33 ":"
+ EXPR@33..34
+ EXPR_NUMBER@33..34
+ FLOAT@33..34 "1"
+ R_BRACE@34..35 "}"
+ WHITESPACE@35..36 "\n"
+===
+LocatedSyntaxError { error: Custom { error: "compspec may only be used if there is only one object element" }, range: 11..29 }
+===
+ x syntax error
+ ,----
+ 1 | {a:1, b:1, for a in [1, 2, 3], c:1}
+ : ^^^^^^^^^|^^^^^^^^
+ : `-- compspec may only be used if there is only one object element
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_no_elems.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_no_elems.snap
@@ -0,0 +1,47 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{for a in [1, 2, 3]}\n"
+---
+SOURCE_FILE@0..21
+ EXPR@0..20
+ EXPR_OBJECT@0..20
+ OBJ_BODY_COMP@0..20
+ L_BRACE@0..1 "{"
+ ERROR_MISSING_TOKEN@1..1
+ FOR_SPEC@1..19
+ FOR_KW@1..4 "for"
+ WHITESPACE@4..5 " "
+ DESTRUCT_FULL@5..6
+ NAME@5..6
+ IDENT@5..6 "a"
+ WHITESPACE@6..7 " "
+ IN_KW@7..9 "in"
+ WHITESPACE@9..10 " "
+ EXPR@10..19
+ EXPR_ARRAY@10..19
+ L_BRACK@10..11 "["
+ EXPR@11..12
+ EXPR_NUMBER@11..12
+ FLOAT@11..12 "1"
+ COMMA@12..13 ","
+ WHITESPACE@13..14 " "
+ EXPR@14..15
+ EXPR_NUMBER@14..15
+ FLOAT@14..15 "2"
+ COMMA@15..16 ","
+ WHITESPACE@16..17 " "
+ EXPR@17..18
+ EXPR_NUMBER@17..18
+ FLOAT@17..18 "3"
+ R_BRACK@18..19 "]"
+ R_BRACE@19..20 "}"
+ WHITESPACE@20..21 "\n"
+===
+LocatedSyntaxError { error: Missing { expected: Named("field definition") }, range: 1..1 }
+===
+ x syntax error
+ ,----
+ 1 | {for a in [1, 2, 3]}
+ : ^
+ : `-- missing field definition
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_method.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_method.snap
@@ -0,0 +1,52 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{\n\ta(x): x,\n\ta: function(x) x,\n}\n"
+---
+SOURCE_FILE@0..33
+ EXPR@0..32
+ EXPR_OBJECT@0..32
+ OBJ_BODY_MEMBER_LIST@0..32
+ L_BRACE@0..1 "{"
+ WHITESPACE@1..3 "\n\t"
+ MEMBER_FIELD_METHOD@3..10
+ FIELD_NAME_FIXED@3..4
+ NAME@3..4
+ IDENT@3..4 "a"
+ PARAMS_DESC@4..7
+ L_PAREN@4..5 "("
+ PARAM@5..6
+ DESTRUCT_FULL@5..6
+ NAME@5..6
+ IDENT@5..6 "x"
+ R_PAREN@6..7 ")"
+ COLON@7..8 ":"
+ WHITESPACE@8..9 " "
+ EXPR@9..10
+ EXPR_VAR@9..10
+ NAME@9..10
+ IDENT@9..10 "x"
+ COMMA@10..11 ","
+ WHITESPACE@11..13 "\n\t"
+ MEMBER_FIELD_METHOD@13..29
+ FIELD_NAME_FIXED@13..14
+ NAME@13..14
+ IDENT@13..14 "a"
+ COLON@14..15 ":"
+ WHITESPACE@15..16 " "
+ FUNCTION_KW@16..24 "function"
+ PARAMS_DESC@24..27
+ L_PAREN@24..25 "("
+ PARAM@25..26
+ DESTRUCT_FULL@25..26
+ NAME@25..26
+ IDENT@25..26 "x"
+ R_PAREN@26..27 ")"
+ WHITESPACE@27..28 " "
+ EXPR@28..29
+ EXPR_VAR@28..29
+ NAME@28..29
+ IDENT@28..29 "x"
+ COMMA@29..30 ","
+ WHITESPACE@30..31 "\n"
+ R_BRACE@31..32 "}"
+ WHITESPACE@32..33 "\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__plain_call.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__plain_call.snap
@@ -0,0 +1,58 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "std.substr(a, 0, std.length(b)) == b\n"
+---
+SOURCE_FILE@0..37
+ EXPR@0..36
+ EXPR_BINARY@0..36
+ EXPR@0..3
+ EXPR_VAR@0..3
+ NAME@0..3
+ IDENT@0..3 "std"
+ SUFFIX_INDEX@3..10
+ DOT@3..4 "."
+ NAME@4..10
+ IDENT@4..10 "substr"
+ SUFFIX_APPLY@10..31
+ ARGS_DESC@10..31
+ L_PAREN@10..11 "("
+ ARG@11..12
+ EXPR@11..12
+ EXPR_VAR@11..12
+ NAME@11..12
+ IDENT@11..12 "a"
+ COMMA@12..13 ","
+ WHITESPACE@13..14 " "
+ ARG@14..15
+ EXPR@14..15
+ EXPR_NUMBER@14..15
+ FLOAT@14..15 "0"
+ COMMA@15..16 ","
+ WHITESPACE@16..17 " "
+ ARG@17..30
+ EXPR@17..30
+ EXPR_VAR@17..20
+ NAME@17..20
+ IDENT@17..20 "std"
+ SUFFIX_INDEX@20..27
+ DOT@20..21 "."
+ NAME@21..27
+ IDENT@21..27 "length"
+ SUFFIX_APPLY@27..30
+ ARGS_DESC@27..30
+ L_PAREN@27..28 "("
+ ARG@28..29
+ EXPR@28..29
+ EXPR_VAR@28..29
+ NAME@28..29
+ IDENT@28..29 "b"
+ R_PAREN@29..30 ")"
+ R_PAREN@30..31 ")"
+ WHITESPACE@31..32 " "
+ EQ@32..34 "=="
+ WHITESPACE@34..35 " "
+ EXPR@35..36
+ EXPR_VAR@35..36
+ NAME@35..36
+ IDENT@35..36 "b"
+ WHITESPACE@36..37 "\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__stdlib.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__stdlib.snap
@@ -0,0 +1,5686 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{\n local std = self,\n local id = std.id,\n\n thisFile:: error 'std.thisFile is deprecated, to enable its support in jrsonnet - recompile it with \"legacy-this-file\" support.\\nThis will slow down stdlib caching a bit, though',\n\n lstripChars(str, chars)::\n if std.length(str) > 0 && std.member(chars, str[0]) then\n std.lstripChars(str[1:], chars)\n else\n str,\n\n rstripChars(str, chars)::\n local len = std.length(str);\n if len > 0 && std.member(chars, str[len - 1]) then\n std.rstripChars(str[:len - 1], chars)\n else\n str,\n\n stripChars(str, chars)::\n std.lstripChars(std.rstripChars(str, chars), chars),\n\n splitLimitR(str, c, maxsplits)::\n if maxsplits == -1 then\n std.splitLimit(str, c, -1)\n else\n local revStr(str) = std.join('', std.reverse(std.stringChars(str)));\n std.map(function(e) revStr(e), std.reverse(std.splitLimit(revStr(str), revStr(c), maxsplits))),\n\n split(str, c):: std.splitLimit(str, c, -1),\n\n mapWithIndex(func, arr)::\n if !std.isFunction(func) then\n error ('std.mapWithIndex first param must be function, got ' + std.type(func))\n else if !std.isArray(arr) && !std.isString(arr) then\n error ('std.mapWithIndex second param must be array, got ' + std.type(arr))\n else\n std.makeArray(std.length(arr), function(i) func(i, arr[i])),\n\n mapWithKey(func, obj)::\n if !std.isFunction(func) then\n error ('std.mapWithKey first param must be function, got ' + std.type(func))\n else if !std.isObject(obj) then\n error ('std.mapWithKey second param must be object, got ' + std.type(obj))\n else\n { [k]: func(k, obj[k]) for k in std.objectFields(obj) },\n\n lines(arr)::\n std.join('\\n', arr + ['']),\n\n deepJoin(arr)::\n if std.isString(arr) then\n arr\n else if std.isArray(arr) then\n std.join('', [std.deepJoin(x) for x in arr])\n else\n error 'Expected string or array, got %s' % std.type(arr),\n\n assertEqual(a, b)::\n if a == b then\n true\n else\n error 'Assertion failed. ' + a + ' != ' + b,\n\n clamp(x, minVal, maxVal)::\n if x < minVal then minVal\n else if x > maxVal then maxVal\n else x,\n\n manifestIni(ini)::\n local body_lines(body) =\n std.join([], [\n local value_or_values = body[k];\n if std.isArray(value_or_values) then\n ['%s = %s' % [k, value] for value in value_or_values]\n else\n ['%s = %s' % [k, value_or_values]]\n\n for k in std.objectFields(body)\n ]);\n\n local section_lines(sname, sbody) = ['[%s]' % [sname]] + body_lines(sbody),\n main_body = if std.objectHas(ini, 'main') then body_lines(ini.main) else [],\n all_sections = [\n section_lines(k, ini.sections[k])\n for k in std.objectFields(ini.sections)\n ];\n std.join('\\n', main_body + std.flattenArrays(all_sections) + ['']),\n\n manifestToml(value):: std.manifestTomlEx(value, ' '),\n\n escapeStringPython(str)::\n std.escapeStringJson(str),\n\n escapeStringBash(str_)::\n local str = std.toString(str_);\n local trans(ch) =\n if ch == \"'\" then\n \"'\\\"'\\\"'\"\n else\n ch;\n \"'%s'\" % std.join('', [trans(ch) for ch in std.stringChars(str)]),\n\n escapeStringDollars(str_)::\n local str = std.toString(str_);\n local trans(ch) =\n if ch == '$' then\n '$$'\n else\n ch;\n std.foldl(function(a, b) a + trans(b), std.stringChars(str), ''),\n\n local xml_escapes = {\n '<': '<',\n '>': '>',\n '&': '&',\n '\"': '"',\n \"'\": ''',\n },\n\n escapeStringXML(str_)::\n local str = std.toString(str_);\n std.join('', [std.get(xml_escapes, ch, ch) for ch in std.stringChars(str)]),\n\n manifestJson(value):: std.manifestJsonEx(value, ' ') tailstrict,\n\n manifestJsonMinified(value):: std.manifestJsonEx(value, '', '', ':'),\n\n manifestYamlStream(value, indent_array_in_object=false, c_document_end=true, quote_keys=true)::\n if !std.isArray(value) then\n error 'manifestYamlStream only takes arrays, got ' + std.type(value)\n else\n '---\\n' + std.join(\n '\\n---\\n', [std.manifestYamlDoc(e, indent_array_in_object, quote_keys) for e in value]\n ) + if c_document_end then '\\n...\\n' else '\\n',\n\n manifestPython(v)::\n if std.isObject(v) then\n local fields = [\n '%s: %s' % [std.escapeStringPython(k), std.manifestPython(v[k])]\n for k in std.objectFields(v)\n ];\n '{%s}' % [std.join(', ', fields)]\n else if std.isArray(v) then\n '[%s]' % [std.join(', ', [std.manifestPython(v2) for v2 in v])]\n else if std.isString(v) then\n '%s' % [std.escapeStringPython(v)]\n else if std.isFunction(v) then\n error 'cannot manifest function'\n else if std.isNumber(v) then\n std.toString(v)\n else if v == true then\n 'True'\n else if v == false then\n 'False'\n else if v == null then\n 'None',\n\n manifestPythonVars(conf)::\n local vars = ['%s = %s' % [k, std.manifestPython(conf[k])] for k in std.objectFields(conf)];\n std.join('\\n', vars + ['']),\n\n manifestXmlJsonml(value)::\n if !std.isArray(value) then\n error 'Expected a JSONML value (an array), got %s' % std.type(value)\n else\n local aux(v) =\n if std.isString(v) then\n v\n else\n local tag = v[0];\n local has_attrs = std.length(v) > 1 && std.isObject(v[1]);\n local attrs = if has_attrs then v[1] else {};\n local children = if has_attrs then v[2:] else v[1:];\n local attrs_str =\n std.join('', [' %s=\"%s\"' % [k, attrs[k]] for k in std.objectFields(attrs)]);\n std.deepJoin(['<', tag, attrs_str, '>', [aux(x) for x in children], '</', tag, '>']);\n\n aux(value),\n\n mergePatch(target, patch)::\n if std.isObject(patch) then\n local target_object =\n if std.isObject(target) then target else {};\n\n local target_fields =\n if std.isObject(target_object) then std.objectFields(target_object) else [];\n\n local null_fields = [k for k in std.objectFields(patch) if patch[k] == null];\n local both_fields = std.setUnion(target_fields, std.objectFields(patch));\n\n {\n [k]:\n if !std.objectHas(patch, k) then\n target_object[k]\n else if !std.objectHas(target_object, k) then\n std.mergePatch(null, patch[k]) tailstrict\n else\n std.mergePatch(target_object[k], patch[k]) tailstrict\n for k in std.setDiff(both_fields, null_fields)\n }\n else\n patch,\n\n get(o, f, default=null, inc_hidden=true)::\n if std.objectHasEx(o, f, inc_hidden) then o[f] else default,\n\n resolvePath(f, r)::\n local arr = std.split(f, '/');\n std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),\n\n prune(a)::\n local isContent(b) =\n if b == null then\n false\n else if std.isArray(b) then\n std.length(b) > 0\n else if std.isObject(b) then\n std.length(b) > 0\n else\n true;\n if std.isArray(a) then\n [std.prune(x) for x in a if isContent($.prune(x))]\n else if std.isObject(a) then {\n [x]: $.prune(a[x])\n for x in std.objectFields(a)\n if isContent(std.prune(a[x]))\n } else\n a,\n\n find(value, arr)::\n if !std.isArray(arr) then\n error 'find second parameter should be an array, got ' + std.type(arr)\n else\n std.filter(function(i) arr[i] == value, std.range(0, std.length(arr) - 1)),\n\n // Compat\n __compare_array(arr1, arr2)::\n assert std.isArray(arr1) && std.isArray(arr2);\n std.__compare(arr1, arr2),\n __array_less(arr1, arr2):: std.__compare_array(arr1, arr2) == -1,\n __array_greater(arr1, arr2):: std.__compare_array(arr1, arr2) == 1,\n __array_less_or_equal(arr1, arr2):: std.__compare_array(arr1, arr2) <= 0,\n __array_greater_or_equal(arr1, arr2):: std.__compare_array(arr1, arr2) >= 0,\n}\n"
+---
+SOURCE_FILE@0..7835
+ EXPR@0..7834
+ EXPR_OBJECT@0..7834
+ OBJ_BODY_MEMBER_LIST@0..7834
+ L_BRACE@0..1 "{"
+ WHITESPACE@1..4 "\n "
+ MEMBER_BIND_STMT@4..20
+ OBJ_LOCAL@4..20
+ LOCAL_KW@4..9 "local"
+ WHITESPACE@9..10 " "
+ BIND_DESTRUCT@10..20
+ DESTRUCT_FULL@10..13
+ NAME@10..13
+ IDENT@10..13 "std"
+ WHITESPACE@13..14 " "
+ ASSIGN@14..15 "="
+ WHITESPACE@15..16 " "
+ EXPR@16..20
+ EXPR_LITERAL@16..20
+ SELF_KW@16..20 "self"
+ COMMA@20..21 ","
+ WHITESPACE@21..24 "\n "
+ MEMBER_BIND_STMT@24..41
+ OBJ_LOCAL@24..41
+ LOCAL_KW@24..29 "local"
+ WHITESPACE@29..30 " "
+ BIND_DESTRUCT@30..41
+ DESTRUCT_FULL@30..32
+ NAME@30..32
+ IDENT@30..32 "id"
+ WHITESPACE@32..33 " "
+ ASSIGN@33..34 "="
+ WHITESPACE@34..35 " "
+ EXPR@35..41
+ EXPR_VAR@35..38
+ NAME@35..38
+ IDENT@35..38 "std"
+ SUFFIX_INDEX@38..41
+ DOT@38..39 "."
+ NAME@39..41
+ IDENT@39..41 "id"
+ COMMA@41..42 ","
+ WHITESPACE@42..46 "\n\n "
+ MEMBER_FIELD_NORMAL@46..224
+ FIELD_NAME_FIXED@46..54
+ NAME@46..54
+ IDENT@46..54 "thisFile"
+ COLONCOLON@54..56 "::"
+ WHITESPACE@56..57 " "
+ EXPR@57..224
+ EXPR_ERROR@57..224
+ ERROR_KW@57..62 "error"
+ WHITESPACE@62..63 " "
+ EXPR@63..224
+ EXPR_STRING@63..224
+ STRING_SINGLE@63..224 "'std.thisFile is depr ..."
+ COMMA@224..225 ","
+ WHITESPACE@225..229 "\n\n "
+ MEMBER_FIELD_METHOD@229..372
+ FIELD_NAME_FIXED@229..240
+ NAME@229..240
+ IDENT@229..240 "lstripChars"
+ PARAMS_DESC@240..252
+ L_PAREN@240..241 "("
+ PARAM@241..244
+ DESTRUCT_FULL@241..244
+ NAME@241..244
+ IDENT@241..244 "str"
+ COMMA@244..245 ","
+ WHITESPACE@245..246 " "
+ PARAM@246..251
+ DESTRUCT_FULL@246..251
+ NAME@246..251
+ IDENT@246..251 "chars"
+ R_PAREN@251..252 ")"
+ COLONCOLON@252..254 "::"
+ WHITESPACE@254..259 "\n "
+ EXPR@259..372
+ EXPR_IF_THEN_ELSE@259..372
+ IF_KW@259..261 "if"
+ WHITESPACE@261..262 " "
+ EXPR@262..310
+ EXPR_BINARY@262..310
+ EXPR@262..281
+ EXPR_BINARY@262..281
+ EXPR@262..265
+ EXPR_VAR@262..265
+ NAME@262..265
+ IDENT@262..265 "std"
+ SUFFIX_INDEX@265..272
+ DOT@265..266 "."
+ NAME@266..272
+ IDENT@266..272 "length"
+ SUFFIX_APPLY@272..277
+ ARGS_DESC@272..277
+ L_PAREN@272..273 "("
+ ARG@273..276
+ EXPR@273..276
+ EXPR_VAR@273..276
+ NAME@273..276
+ IDENT@273..276 "str"
+ R_PAREN@276..277 ")"
+ WHITESPACE@277..278 " "
+ GT@278..279 ">"
+ WHITESPACE@279..280 " "
+ EXPR@280..281
+ EXPR_NUMBER@280..281
+ FLOAT@280..281 "0"
+ WHITESPACE@281..282 " "
+ AND@282..284 "&&"
+ WHITESPACE@284..285 " "
+ EXPR@285..310
+ EXPR_VAR@285..288
+ NAME@285..288
+ IDENT@285..288 "std"
+ SUFFIX_INDEX@288..295
+ DOT@288..289 "."
+ NAME@289..295
+ IDENT@289..295 "member"
+ SUFFIX_APPLY@295..310
+ ARGS_DESC@295..310
+ L_PAREN@295..296 "("
+ ARG@296..301
+ EXPR@296..301
+ EXPR_VAR@296..301
+ NAME@296..301
+ IDENT@296..301 "chars"
+ COMMA@301..302 ","
+ WHITESPACE@302..303 " "
+ ARG@303..309
+ EXPR@303..309
+ EXPR_VAR@303..306
+ NAME@303..306
+ IDENT@303..306 "str"
+ SUFFIX_INDEX_EXPR@306..309
+ L_BRACK@306..307 "["
+ EXPR@307..308
+ EXPR_NUMBER@307..308
+ FLOAT@307..308 "0"
+ R_BRACK@308..309 "]"
+ R_PAREN@309..310 ")"
+ WHITESPACE@310..311 " "
+ THEN_KW@311..315 "then"
+ WHITESPACE@315..322 "\n "
+ TRUE_EXPR@322..353
+ EXPR@322..353
+ EXPR_VAR@322..325
+ NAME@322..325
+ IDENT@322..325 "std"
+ SUFFIX_INDEX@325..337
+ DOT@325..326 "."
+ NAME@326..337
+ IDENT@326..337 "lstripChars"
+ SUFFIX_APPLY@337..353
+ ARGS_DESC@337..353
+ L_PAREN@337..338 "("
+ ARG@338..345
+ EXPR@338..345
+ EXPR_VAR@338..341
+ NAME@338..341
+ IDENT@338..341 "str"
+ SUFFIX_SLICE@341..345
+ SLICE_DESC@341..345
+ L_BRACK@341..342 "["
+ EXPR@342..343
+ EXPR_NUMBER@342..343
+ FLOAT@342..343 "1"
+ COLON@343..344 ":"
+ R_BRACK@344..345 "]"
+ COMMA@345..346 ","
+ WHITESPACE@346..347 " "
+ ARG@347..352
+ EXPR@347..352
+ EXPR_VAR@347..352
+ NAME@347..352
+ IDENT@347..352 "chars"
+ R_PAREN@352..353 ")"
+ WHITESPACE@353..358 "\n "
+ ELSE_KW@358..362 "else"
+ WHITESPACE@362..369 "\n "
+ FALSE_EXPR@369..372
+ EXPR@369..372
+ EXPR_VAR@369..372
+ NAME@369..372
+ IDENT@369..372 "str"
+ COMMA@372..373 ","
+ WHITESPACE@373..377 "\n\n "
+ MEMBER_FIELD_METHOD@377..553
+ FIELD_NAME_FIXED@377..388
+ NAME@377..388
+ IDENT@377..388 "rstripChars"
+ PARAMS_DESC@388..400
+ L_PAREN@388..389 "("
+ PARAM@389..392
+ DESTRUCT_FULL@389..392
+ NAME@389..392
+ IDENT@389..392 "str"
+ COMMA@392..393 ","
+ WHITESPACE@393..394 " "
+ PARAM@394..399
+ DESTRUCT_FULL@394..399
+ NAME@394..399
+ IDENT@394..399 "chars"
+ R_PAREN@399..400 ")"
+ COLONCOLON@400..402 "::"
+ WHITESPACE@402..407 "\n "
+ EXPR@407..553
+ STMT_LOCAL@407..435
+ LOCAL_KW@407..412 "local"
+ WHITESPACE@412..413 " "
+ BIND_DESTRUCT@413..434
+ DESTRUCT_FULL@413..416
+ NAME@413..416
+ IDENT@413..416 "len"
+ WHITESPACE@416..417 " "
+ ASSIGN@417..418 "="
+ WHITESPACE@418..419 " "
+ EXPR@419..434
+ EXPR_VAR@419..422
+ NAME@419..422
+ IDENT@419..422 "std"
+ SUFFIX_INDEX@422..429
+ DOT@422..423 "."
+ NAME@423..429
+ IDENT@423..429 "length"
+ SUFFIX_APPLY@429..434
+ ARGS_DESC@429..434
+ L_PAREN@429..430 "("
+ ARG@430..433
+ EXPR@430..433
+ EXPR_VAR@430..433
+ NAME@430..433
+ IDENT@430..433 "str"
+ R_PAREN@433..434 ")"
+ SEMI@434..435 ";"
+ WHITESPACE@435..440 "\n "
+ EXPR_IF_THEN_ELSE@440..553
+ IF_KW@440..442 "if"
+ WHITESPACE@442..443 " "
+ EXPR@443..485
+ EXPR_BINARY@443..485
+ EXPR@443..450
+ EXPR_BINARY@443..450
+ EXPR@443..446
+ EXPR_VAR@443..446
+ NAME@443..446
+ IDENT@443..446 "len"
+ WHITESPACE@446..447 " "
+ GT@447..448 ">"
+ WHITESPACE@448..449 " "
+ EXPR@449..450
+ EXPR_NUMBER@449..450
+ FLOAT@449..450 "0"
+ WHITESPACE@450..451 " "
+ AND@451..453 "&&"
+ WHITESPACE@453..454 " "
+ EXPR@454..485
+ EXPR_VAR@454..457
+ NAME@454..457
+ IDENT@454..457 "std"
+ SUFFIX_INDEX@457..464
+ DOT@457..458 "."
+ NAME@458..464
+ IDENT@458..464 "member"
+ SUFFIX_APPLY@464..485
+ ARGS_DESC@464..485
+ L_PAREN@464..465 "("
+ ARG@465..470
+ EXPR@465..470
+ EXPR_VAR@465..470
+ NAME@465..470
+ IDENT@465..470 "chars"
+ COMMA@470..471 ","
+ WHITESPACE@471..472 " "
+ ARG@472..484
+ EXPR@472..484
+ EXPR_VAR@472..475
+ NAME@472..475
+ IDENT@472..475 "str"
+ SUFFIX_INDEX_EXPR@475..484
+ L_BRACK@475..476 "["
+ EXPR@476..483
+ EXPR_BINARY@476..483
+ EXPR@476..479
+ EXPR_VAR@476..479
+ NAME@476..479
+ IDENT@476..479 "len"
+ WHITESPACE@479..480 " "
+ MINUS@480..481 "-"
+ WHITESPACE@481..482 " "
+ EXPR@482..483
+ EXPR_NUMBER@482..483
+ FLOAT@482..483 "1"
+ R_BRACK@483..484 "]"
+ R_PAREN@484..485 ")"
+ WHITESPACE@485..486 " "
+ THEN_KW@486..490 "then"
+ WHITESPACE@490..497 "\n "
+ TRUE_EXPR@497..534
+ EXPR@497..534
+ EXPR_VAR@497..500
+ NAME@497..500
+ IDENT@497..500 "std"
+ SUFFIX_INDEX@500..512
+ DOT@500..501 "."
+ NAME@501..512
+ IDENT@501..512 "rstripChars"
+ SUFFIX_APPLY@512..534
+ ARGS_DESC@512..534
+ L_PAREN@512..513 "("
+ ARG@513..526
+ EXPR@513..526
+ EXPR_VAR@513..516
+ NAME@513..516
+ IDENT@513..516 "str"
+ SUFFIX_SLICE@516..526
+ SLICE_DESC@516..526
+ L_BRACK@516..517 "["
+ COLON@517..518 ":"
+ SLICE_DESC_END@518..525
+ EXPR@518..525
+ EXPR_BINARY@518..525
+ EXPR@518..521
+ EXPR_VAR@518..521
+ NAME@518..521
+ IDENT@518..521 "len"
+ WHITESPACE@521..522 " "
+ MINUS@522..523 "-"
+ WHITESPACE@523..524 " "
+ EXPR@524..525
+ EXPR_NUMBER@524..525
+ FLOAT@524..525 "1"
+ R_BRACK@525..526 "]"
+ COMMA@526..527 ","
+ WHITESPACE@527..528 " "
+ ARG@528..533
+ EXPR@528..533
+ EXPR_VAR@528..533
+ NAME@528..533
+ IDENT@528..533 "chars"
+ R_PAREN@533..534 ")"
+ WHITESPACE@534..539 "\n "
+ ELSE_KW@539..543 "else"
+ WHITESPACE@543..550 "\n "
+ FALSE_EXPR@550..553
+ EXPR@550..553
+ EXPR_VAR@550..553
+ NAME@550..553
+ IDENT@550..553 "str"
+ COMMA@553..554 ","
+ WHITESPACE@554..558 "\n\n "
+ MEMBER_FIELD_METHOD@558..638
+ FIELD_NAME_FIXED@558..568
+ NAME@558..568
+ IDENT@558..568 "stripChars"
+ PARAMS_DESC@568..580
+ L_PAREN@568..569 "("
+ PARAM@569..572
+ DESTRUCT_FULL@569..572
+ NAME@569..572
+ IDENT@569..572 "str"
+ COMMA@572..573 ","
+ WHITESPACE@573..574 " "
+ PARAM@574..579
+ DESTRUCT_FULL@574..579
+ NAME@574..579
+ IDENT@574..579 "chars"
+ R_PAREN@579..580 ")"
+ COLONCOLON@580..582 "::"
+ WHITESPACE@582..587 "\n "
+ EXPR@587..638
+ EXPR_VAR@587..590
+ NAME@587..590
+ IDENT@587..590 "std"
+ SUFFIX_INDEX@590..602
+ DOT@590..591 "."
+ NAME@591..602
+ IDENT@591..602 "lstripChars"
+ SUFFIX_APPLY@602..638
+ ARGS_DESC@602..638
+ L_PAREN@602..603 "("
+ ARG@603..630
+ EXPR@603..630
+ EXPR_VAR@603..606
+ NAME@603..606
+ IDENT@603..606 "std"
+ SUFFIX_INDEX@606..618
+ DOT@606..607 "."
+ NAME@607..618
+ IDENT@607..618 "rstripChars"
+ SUFFIX_APPLY@618..630
+ ARGS_DESC@618..630
+ L_PAREN@618..619 "("
+ ARG@619..622
+ EXPR@619..622
+ EXPR_VAR@619..622
+ NAME@619..622
+ IDENT@619..622 "str"
+ COMMA@622..623 ","
+ WHITESPACE@623..624 " "
+ ARG@624..629
+ EXPR@624..629
+ EXPR_VAR@624..629
+ NAME@624..629
+ IDENT@624..629 "chars"
+ R_PAREN@629..630 ")"
+ COMMA@630..631 ","
+ WHITESPACE@631..632 " "
+ ARG@632..637
+ EXPR@632..637
+ EXPR_VAR@632..637
+ NAME@632..637
+ IDENT@632..637 "chars"
+ R_PAREN@637..638 ")"
+ COMMA@638..639 ","
+ WHITESPACE@639..643 "\n\n "
+ MEMBER_FIELD_METHOD@643..921
+ FIELD_NAME_FIXED@643..654
+ NAME@643..654
+ IDENT@643..654 "splitLimitR"
+ PARAMS_DESC@654..673
+ L_PAREN@654..655 "("
+ PARAM@655..658
+ DESTRUCT_FULL@655..658
+ NAME@655..658
+ IDENT@655..658 "str"
+ COMMA@658..659 ","
+ WHITESPACE@659..660 " "
+ PARAM@660..661
+ DESTRUCT_FULL@660..661
+ NAME@660..661
+ IDENT@660..661 "c"
+ COMMA@661..662 ","
+ WHITESPACE@662..663 " "
+ PARAM@663..672
+ DESTRUCT_FULL@663..672
+ NAME@663..672
+ IDENT@663..672 "maxsplits"
+ R_PAREN@672..673 ")"
+ COLONCOLON@673..675 "::"
+ WHITESPACE@675..680 "\n "
+ EXPR@680..921
+ EXPR_IF_THEN_ELSE@680..921
+ IF_KW@680..682 "if"
+ WHITESPACE@682..683 " "
+ EXPR@683..698
+ EXPR_BINARY@683..698
+ EXPR@683..692
+ EXPR_VAR@683..692
+ NAME@683..692
+ IDENT@683..692 "maxsplits"
+ WHITESPACE@692..693 " "
+ EQ@693..695 "=="
+ WHITESPACE@695..696 " "
+ EXPR@696..698
+ EXPR_UNARY@696..698
+ MINUS@696..697 "-"
+ EXPR_NUMBER@697..698
+ FLOAT@697..698 "1"
+ WHITESPACE@698..699 " "
+ THEN_KW@699..703 "then"
+ WHITESPACE@703..710 "\n "
+ TRUE_EXPR@710..736
+ EXPR@710..736
+ EXPR_VAR@710..713
+ NAME@710..713
+ IDENT@710..713 "std"
+ SUFFIX_INDEX@713..724
+ DOT@713..714 "."
+ NAME@714..724
+ IDENT@714..724 "splitLimit"
+ SUFFIX_APPLY@724..736
+ ARGS_DESC@724..736
+ L_PAREN@724..725 "("
+ ARG@725..728
+ EXPR@725..728
+ EXPR_VAR@725..728
+ NAME@725..728
+ IDENT@725..728 "str"
+ COMMA@728..729 ","
+ WHITESPACE@729..730 " "
+ ARG@730..731
+ EXPR@730..731
+ EXPR_VAR@730..731
+ NAME@730..731
+ IDENT@730..731 "c"
+ COMMA@731..732 ","
+ WHITESPACE@732..733 " "
+ ARG@733..735
+ EXPR@733..735
+ EXPR_UNARY@733..735
+ MINUS@733..734 "-"
+ EXPR_NUMBER@734..735
+ FLOAT@734..735 "1"
+ R_PAREN@735..736 ")"
+ WHITESPACE@736..741 "\n "
+ ELSE_KW@741..745 "else"
+ WHITESPACE@745..752 "\n "
+ FALSE_EXPR@752..921
+ EXPR@752..921
+ STMT_LOCAL@752..820
+ LOCAL_KW@752..757 "local"
+ WHITESPACE@757..758 " "
+ BIND_FUNCTION@758..819
+ NAME@758..764
+ IDENT@758..764 "revStr"
+ PARAMS_DESC@764..769
+ L_PAREN@764..765 "("
+ PARAM@765..768
+ DESTRUCT_FULL@765..768
+ NAME@765..768
+ IDENT@765..768 "str"
+ R_PAREN@768..769 ")"
+ WHITESPACE@769..770 " "
+ ASSIGN@770..771 "="
+ WHITESPACE@771..772 " "
+ EXPR@772..819
+ EXPR_VAR@772..775
+ NAME@772..775
+ IDENT@772..775 "std"
+ SUFFIX_INDEX@775..780
+ DOT@775..776 "."
+ NAME@776..780
+ IDENT@776..780 "join"
+ SUFFIX_APPLY@780..819
+ ARGS_DESC@780..819
+ L_PAREN@780..781 "("
+ ARG@781..783
+ EXPR@781..783
+ EXPR_STRING@781..783
+ STRING_SINGLE@781..783 "''"
+ COMMA@783..784 ","
+ WHITESPACE@784..785 " "
+ ARG@785..818
+ EXPR@785..818
+ EXPR_VAR@785..788
+ NAME@785..788
+ IDENT@785..788 "std"
+ SUFFIX_INDEX@788..796
+ DOT@788..789 "."
+ NAME@789..796
+ IDENT@789..796 "reverse"
+ SUFFIX_APPLY@796..818
+ ARGS_DESC@796..818
+ L_PAREN@796..797 "("
+ ARG@797..817
+ EXPR@797..817
+ EXPR_VAR@797..800
+ NAME@797..800
+ IDENT@797..800 "std"
+ SUFFIX_INDEX@800..812
+ DOT@800..801 "."
+ NAME@801..812
+ IDENT@801..812 "stringChars"
+ SUFFIX_APPLY@812..817
+ ARGS_DESC@812..817
+ L_PAREN@812..813 "("
+ ARG@813..816
+ EXPR@813..816
+ EXPR_VAR@813..816
+ NAME@813..816
+ IDENT@813..816 "str"
+ R_PAREN@816..817 ")"
+ R_PAREN@817..818 ")"
+ R_PAREN@818..819 ")"
+ SEMI@819..820 ";"
+ WHITESPACE@820..827 "\n "
+ EXPR_VAR@827..830
+ NAME@827..830
+ IDENT@827..830 "std"
+ SUFFIX_INDEX@830..834
+ DOT@830..831 "."
+ NAME@831..834
+ IDENT@831..834 "map"
+ SUFFIX_APPLY@834..921
+ ARGS_DESC@834..921
+ L_PAREN@834..835 "("
+ ARG@835..856
+ EXPR@835..856
+ EXPR_FUNCTION@835..856
+ FUNCTION_KW@835..843 "function"
+ PARAMS_DESC@843..846
+ L_PAREN@843..844 "("
+ PARAM@844..845
+ DESTRUCT_FULL@844..845
+ NAME@844..845
+ IDENT@844..845 "e"
+ R_PAREN@845..846 ")"
+ WHITESPACE@846..847 " "
+ EXPR@847..856
+ EXPR_VAR@847..853
+ NAME@847..853
+ IDENT@847..853 "revStr"
+ SUFFIX_APPLY@853..856
+ ARGS_DESC@853..856
+ L_PAREN@853..854 "("
+ ARG@854..855
+ EXPR@854..855
+ EXPR_VAR@854..855
+ NAME@854..855
+ IDENT@854..855 "e"
+ R_PAREN@855..856 ")"
+ COMMA@856..857 ","
+ WHITESPACE@857..858 " "
+ ARG@858..920
+ EXPR@858..920
+ EXPR_VAR@858..861
+ NAME@858..861
+ IDENT@858..861 "std"
+ SUFFIX_INDEX@861..869
+ DOT@861..862 "."
+ NAME@862..869
+ IDENT@862..869 "reverse"
+ SUFFIX_APPLY@869..920
+ ARGS_DESC@869..920
+ L_PAREN@869..870 "("
+ ARG@870..919
+ EXPR@870..919
+ EXPR_VAR@870..873
+ NAME@870..873
+ IDENT@870..873 "std"
+ SUFFIX_INDEX@873..884
+ DOT@873..874 "."
+ NAME@874..884
+ IDENT@874..884 "splitLimit"
+ SUFFIX_APPLY@884..919
+ ARGS_DESC@884..919
+ L_PAREN@884..885 "("
+ ARG@885..896
+ EXPR@885..896
+ EXPR_VAR@885..891
+ NAME@885..891
+ IDENT@885..891 "revStr"
+ SUFFIX_APPLY@891..896
+ ARGS_DESC@891..896
+ L_PAREN@891..892 "("
+ ARG@892..895
+ EXPR@892..895
+ EXPR_VAR@892..895
+ NAME@892..895
+ IDENT@892..895 "str"
+ R_PAREN@895..896 ")"
+ COMMA@896..897 ","
+ WHITESPACE@897..898 " "
+ ARG@898..907
+ EXPR@898..907
+ EXPR_VAR@898..904
+ NAME@898..904
+ IDENT@898..904 "revStr"
+ SUFFIX_APPLY@904..907
+ ARGS_DESC@904..907
+ L_PAREN@904..905 "("
+ ARG@905..906
+ EXPR@905..906
+ EXPR_VAR@905..906
+ NAME@905..906
+ IDENT@905..906 "c"
+ R_PAREN@906..907 ")"
+ COMMA@907..908 ","
+ WHITESPACE@908..909 " "
+ ARG@909..918
+ EXPR@909..918
+ EXPR_VAR@909..918
+ NAME@909..918
+ IDENT@909..918 "maxsplits"
+ R_PAREN@918..919 ")"
+ R_PAREN@919..920 ")"
+ R_PAREN@920..921 ")"
+ COMMA@921..922 ","
+ WHITESPACE@922..926 "\n\n "
+ MEMBER_FIELD_METHOD@926..968
+ FIELD_NAME_FIXED@926..931
+ NAME@926..931
+ IDENT@926..931 "split"
+ PARAMS_DESC@931..939
+ L_PAREN@931..932 "("
+ PARAM@932..935
+ DESTRUCT_FULL@932..935
+ NAME@932..935
+ IDENT@932..935 "str"
+ COMMA@935..936 ","
+ WHITESPACE@936..937 " "
+ PARAM@937..938
+ DESTRUCT_FULL@937..938
+ NAME@937..938
+ IDENT@937..938 "c"
+ R_PAREN@938..939 ")"
+ COLONCOLON@939..941 "::"
+ WHITESPACE@941..942 " "
+ EXPR@942..968
+ EXPR_VAR@942..945
+ NAME@942..945
+ IDENT@942..945 "std"
+ SUFFIX_INDEX@945..956
+ DOT@945..946 "."
+ NAME@946..956
+ IDENT@946..956 "splitLimit"
+ SUFFIX_APPLY@956..968
+ ARGS_DESC@956..968
+ L_PAREN@956..957 "("
+ ARG@957..960
+ EXPR@957..960
+ EXPR_VAR@957..960
+ NAME@957..960
+ IDENT@957..960 "str"
+ COMMA@960..961 ","
+ WHITESPACE@961..962 " "
+ ARG@962..963
+ EXPR@962..963
+ EXPR_VAR@962..963
+ NAME@962..963
+ IDENT@962..963 "c"
+ COMMA@963..964 ","
+ WHITESPACE@964..965 " "
+ ARG@965..967
+ EXPR@965..967
+ EXPR_UNARY@965..967
+ MINUS@965..966 "-"
+ EXPR_NUMBER@966..967
+ FLOAT@966..967 "1"
+ R_PAREN@967..968 ")"
+ COMMA@968..969 ","
+ WHITESPACE@969..973 "\n\n "
+ MEMBER_FIELD_METHOD@973..1331
+ FIELD_NAME_FIXED@973..985
+ NAME@973..985
+ IDENT@973..985 "mapWithIndex"
+ PARAMS_DESC@985..996
+ L_PAREN@985..986 "("
+ PARAM@986..990
+ DESTRUCT_FULL@986..990
+ NAME@986..990
+ IDENT@986..990 "func"
+ COMMA@990..991 ","
+ WHITESPACE@991..992 " "
+ PARAM@992..995
+ DESTRUCT_FULL@992..995
+ NAME@992..995
+ IDENT@992..995 "arr"
+ R_PAREN@995..996 ")"
+ COLONCOLON@996..998 "::"
+ WHITESPACE@998..1003 "\n "
+ EXPR@1003..1331
+ EXPR_IF_THEN_ELSE@1003..1331
+ IF_KW@1003..1005 "if"
+ WHITESPACE@1005..1006 " "
+ EXPR@1006..1027
+ EXPR_UNARY@1006..1027
+ NOT@1006..1007 "!"
+ EXPR_VAR@1007..1010
+ NAME@1007..1010
+ IDENT@1007..1010 "std"
+ SUFFIX_INDEX@1010..1021
+ DOT@1010..1011 "."
+ NAME@1011..1021
+ IDENT@1011..1021 "isFunction"
+ SUFFIX_APPLY@1021..1027
+ ARGS_DESC@1021..1027
+ L_PAREN@1021..1022 "("
+ ARG@1022..1026
+ EXPR@1022..1026
+ EXPR_VAR@1022..1026
+ NAME@1022..1026
+ IDENT@1022..1026 "func"
+ R_PAREN@1026..1027 ")"
+ WHITESPACE@1027..1028 " "
+ THEN_KW@1028..1032 "then"
+ WHITESPACE@1032..1039 "\n "
+ TRUE_EXPR@1039..1117
+ EXPR@1039..1117
+ EXPR_ERROR@1039..1117
+ ERROR_KW@1039..1044 "error"
+ WHITESPACE@1044..1045 " "
+ EXPR@1045..1117
+ EXPR_PARENED@1045..1117
+ L_PAREN@1045..1046 "("
+ EXPR@1046..1116
+ EXPR_BINARY@1046..1116
+ EXPR@1046..1099
+ EXPR_STRING@1046..1099
+ STRING_SINGLE@1046..1099 "'std.mapWithIndex fir ..."
+ WHITESPACE@1099..1100 " "
+ PLUS@1100..1101 "+"
+ WHITESPACE@1101..1102 " "
+ EXPR@1102..1116
+ EXPR_VAR@1102..1105
+ NAME@1102..1105
+ IDENT@1102..1105 "std"
+ SUFFIX_INDEX@1105..1110
+ DOT@1105..1106 "."
+ NAME@1106..1110
+ IDENT@1106..1110 "type"
+ SUFFIX_APPLY@1110..1116
+ ARGS_DESC@1110..1116
+ L_PAREN@1110..1111 "("
+ ARG@1111..1115
+ EXPR@1111..1115
+ EXPR_VAR@1111..1115
+ NAME@1111..1115
+ IDENT@1111..1115 "func"
+ R_PAREN@1115..1116 ")"
+ R_PAREN@1116..1117 ")"
+ WHITESPACE@1117..1122 "\n "
+ ELSE_KW@1122..1126 "else"
+ WHITESPACE@1126..1127 " "
+ FALSE_EXPR@1127..1331
+ EXPR@1127..1331
+ EXPR_IF_THEN_ELSE@1127..1331
+ IF_KW@1127..1129 "if"
+ WHITESPACE@1129..1130 " "
+ EXPR@1130..1169
+ EXPR_BINARY@1130..1169
+ EXPR@1130..1147
+ EXPR_UNARY@1130..1147
+ NOT@1130..1131 "!"
+ EXPR_VAR@1131..1134
+ NAME@1131..1134
+ IDENT@1131..1134 "std"
+ SUFFIX_INDEX@1134..1142
+ DOT@1134..1135 "."
+ NAME@1135..1142
+ IDENT@1135..1142 "isArray"
+ SUFFIX_APPLY@1142..1147
+ ARGS_DESC@1142..1147
+ L_PAREN@1142..1143 "("
+ ARG@1143..1146
+ EXPR@1143..1146
+ EXPR_VAR@1143..1146
+ NAME@1143..1146
+ IDENT@1143..1146 "arr"
+ R_PAREN@1146..1147 ")"
+ WHITESPACE@1147..1148 " "
+ AND@1148..1150 "&&"
+ WHITESPACE@1150..1151 " "
+ EXPR@1151..1169
+ EXPR_UNARY@1151..1169
+ NOT@1151..1152 "!"
+ EXPR_VAR@1152..1155
+ NAME@1152..1155
+ IDENT@1152..1155 "std"
+ SUFFIX_INDEX@1155..1164
+ DOT@1155..1156 "."
+ NAME@1156..1164
+ IDENT@1156..1164 "isString"
+ SUFFIX_APPLY@1164..1169
+ ARGS_DESC@1164..1169
+ L_PAREN@1164..1165 "("
+ ARG@1165..1168
+ EXPR@1165..1168
+ EXPR_VAR@1165..1168
+ NAME@1165..1168
+ IDENT@1165..1168 "arr"
+ R_PAREN@1168..1169 ")"
+ WHITESPACE@1169..1170 " "
+ THEN_KW@1170..1174 "then"
+ WHITESPACE@1174..1181 "\n "
+ TRUE_EXPR@1181..1256
+ EXPR@1181..1256
+ EXPR_ERROR@1181..1256
+ ERROR_KW@1181..1186 "error"
+ WHITESPACE@1186..1187 " "
+ EXPR@1187..1256
+ EXPR_PARENED@1187..1256
+ L_PAREN@1187..1188 "("
+ EXPR@1188..1255
+ EXPR_BINARY@1188..1255
+ EXPR@1188..1239
+ EXPR_STRING@1188..1239
+ STRING_SINGLE@1188..1239 "'std.mapWithIndex sec ..."
+ WHITESPACE@1239..1240 " "
+ PLUS@1240..1241 "+"
+ WHITESPACE@1241..1242 " "
+ EXPR@1242..1255
+ EXPR_VAR@1242..1245
+ NAME@1242..1245
+ IDENT@1242..1245 "std"
+ SUFFIX_INDEX@1245..1250
+ DOT@1245..1246 "."
+ NAME@1246..1250
+ IDENT@1246..1250 "type"
+ SUFFIX_APPLY@1250..1255
+ ARGS_DESC@1250..1255
+ L_PAREN@1250..1251 "("
+ ARG@1251..1254
+ EXPR@1251..1254
+ EXPR_VAR@1251..1254
+ NAME@1251..1254
+ IDENT@1251..1254 "arr"
+ R_PAREN@1254..1255 ")"
+ R_PAREN@1255..1256 ")"
+ WHITESPACE@1256..1261 "\n "
+ ELSE_KW@1261..1265 "else"
+ WHITESPACE@1265..1272 "\n "
+ FALSE_EXPR@1272..1331
+ EXPR@1272..1331
+ EXPR_VAR@1272..1275
+ NAME@1272..1275
+ IDENT@1272..1275 "std"
+ SUFFIX_INDEX@1275..1285
+ DOT@1275..1276 "."
+ NAME@1276..1285
+ IDENT@1276..1285 "makeArray"
+ SUFFIX_APPLY@1285..1331
+ ARGS_DESC@1285..1331
+ L_PAREN@1285..1286 "("
+ ARG@1286..1301
+ EXPR@1286..1301
+ EXPR_VAR@1286..1289
+ NAME@1286..1289
+ IDENT@1286..1289 "std"
+ SUFFIX_INDEX@1289..1296
+ DOT@1289..1290 "."
+ NAME@1290..1296
+ IDENT@1290..1296 "length"
+ SUFFIX_APPLY@1296..1301
+ ARGS_DESC@1296..1301
+ L_PAREN@1296..1297 "("
+ ARG@1297..1300
+ EXPR@1297..1300
+ EXPR_VAR@1297..1300
+ NAME@1297..1300
+ IDENT@1297..1300 "arr"
+ R_PAREN@1300..1301 ")"
+ COMMA@1301..1302 ","
+ WHITESPACE@1302..1303 " "
+ ARG@1303..1330
+ EXPR@1303..1330
+ EXPR_FUNCTION@1303..1330
+ FUNCTION_KW@1303..1311 "function"
+ PARAMS_DESC@1311..1314
+ L_PAREN@1311..1312 "("
+ PARAM@1312..1313
+ DESTRUCT_FULL@1312..1313
+ NAME@1312..1313
+ IDENT@1312..1313 "i"
+ R_PAREN@1313..1314 ")"
+ WHITESPACE@1314..1315 " "
+ EXPR@1315..1330
+ EXPR_VAR@1315..1319
+ NAME@1315..1319
+ IDENT@1315..1319 "func"
+ SUFFIX_APPLY@1319..1330
+ ARGS_DESC@1319..1330
+ L_PAREN@1319..1320 "("
+ ARG@1320..1321
+ EXPR@1320..1321
+ EXPR_VAR@1320..1321
+ NAME@1320..1321
+ IDENT@1320..1321 "i"
+ COMMA@1321..1322 ","
+ WHITESPACE@1322..1323 " "
+ ARG@1323..1329
+ EXPR@1323..1329
+ EXPR_VAR@1323..1326
+ NAME@1323..1326
+ IDENT@1323..1326 "arr"
+ SUFFIX_INDEX_EXPR@1326..1329
+ L_BRACK@1326..1327 "["
+ EXPR@1327..1328
+ EXPR_VAR@1327..1328
+ NAME@1327..1328
+ IDENT@1327..1328 "i"
+ R_BRACK@1328..1329 "]"
+ R_PAREN@1329..1330 ")"
+ R_PAREN@1330..1331 ")"
+ COMMA@1331..1332 ","
+ WHITESPACE@1332..1336 "\n\n "
+ MEMBER_FIELD_METHOD@1336..1664
+ FIELD_NAME_FIXED@1336..1346
+ NAME@1336..1346
+ IDENT@1336..1346 "mapWithKey"
+ PARAMS_DESC@1346..1357
+ L_PAREN@1346..1347 "("
+ PARAM@1347..1351
+ DESTRUCT_FULL@1347..1351
+ NAME@1347..1351
+ IDENT@1347..1351 "func"
+ COMMA@1351..1352 ","
+ WHITESPACE@1352..1353 " "
+ PARAM@1353..1356
+ DESTRUCT_FULL@1353..1356
+ NAME@1353..1356
+ IDENT@1353..1356 "obj"
+ R_PAREN@1356..1357 ")"
+ COLONCOLON@1357..1359 "::"
+ WHITESPACE@1359..1364 "\n "
+ EXPR@1364..1664
+ EXPR_IF_THEN_ELSE@1364..1664
+ IF_KW@1364..1366 "if"
+ WHITESPACE@1366..1367 " "
+ EXPR@1367..1388
+ EXPR_UNARY@1367..1388
+ NOT@1367..1368 "!"
+ EXPR_VAR@1368..1371
+ NAME@1368..1371
+ IDENT@1368..1371 "std"
+ SUFFIX_INDEX@1371..1382
+ DOT@1371..1372 "."
+ NAME@1372..1382
+ IDENT@1372..1382 "isFunction"
+ SUFFIX_APPLY@1382..1388
+ ARGS_DESC@1382..1388
+ L_PAREN@1382..1383 "("
+ ARG@1383..1387
+ EXPR@1383..1387
+ EXPR_VAR@1383..1387
+ NAME@1383..1387
+ IDENT@1383..1387 "func"
+ R_PAREN@1387..1388 ")"
+ WHITESPACE@1388..1389 " "
+ THEN_KW@1389..1393 "then"
+ WHITESPACE@1393..1400 "\n "
+ TRUE_EXPR@1400..1476
+ EXPR@1400..1476
+ EXPR_ERROR@1400..1476
+ ERROR_KW@1400..1405 "error"
+ WHITESPACE@1405..1406 " "
+ EXPR@1406..1476
+ EXPR_PARENED@1406..1476
+ L_PAREN@1406..1407 "("
+ EXPR@1407..1475
+ EXPR_BINARY@1407..1475
+ EXPR@1407..1458
+ EXPR_STRING@1407..1458
+ STRING_SINGLE@1407..1458 "'std.mapWithKey first ..."
+ WHITESPACE@1458..1459 " "
+ PLUS@1459..1460 "+"
+ WHITESPACE@1460..1461 " "
+ EXPR@1461..1475
+ EXPR_VAR@1461..1464
+ NAME@1461..1464
+ IDENT@1461..1464 "std"
+ SUFFIX_INDEX@1464..1469
+ DOT@1464..1465 "."
+ NAME@1465..1469
+ IDENT@1465..1469 "type"
+ SUFFIX_APPLY@1469..1475
+ ARGS_DESC@1469..1475
+ L_PAREN@1469..1470 "("
+ ARG@1470..1474
+ EXPR@1470..1474
+ EXPR_VAR@1470..1474
+ NAME@1470..1474
+ IDENT@1470..1474 "func"
+ R_PAREN@1474..1475 ")"
+ R_PAREN@1475..1476 ")"
+ WHITESPACE@1476..1481 "\n "
+ ELSE_KW@1481..1485 "else"
+ WHITESPACE@1485..1486 " "
+ FALSE_EXPR@1486..1664
+ EXPR@1486..1664
+ EXPR_IF_THEN_ELSE@1486..1664
+ IF_KW@1486..1488 "if"
+ WHITESPACE@1488..1489 " "
+ EXPR@1489..1507
+ EXPR_UNARY@1489..1507
+ NOT@1489..1490 "!"
+ EXPR_VAR@1490..1493
+ NAME@1490..1493
+ IDENT@1490..1493 "std"
+ SUFFIX_INDEX@1493..1502
+ DOT@1493..1494 "."
+ NAME@1494..1502
+ IDENT@1494..1502 "isObject"
+ SUFFIX_APPLY@1502..1507
+ ARGS_DESC@1502..1507
+ L_PAREN@1502..1503 "("
+ ARG@1503..1506
+ EXPR@1503..1506
+ EXPR_VAR@1503..1506
+ NAME@1503..1506
+ IDENT@1503..1506 "obj"
+ R_PAREN@1506..1507 ")"
+ WHITESPACE@1507..1508 " "
+ THEN_KW@1508..1512 "then"
+ WHITESPACE@1512..1519 "\n "
+ TRUE_EXPR@1519..1593
+ EXPR@1519..1593
+ EXPR_ERROR@1519..1593
+ ERROR_KW@1519..1524 "error"
+ WHITESPACE@1524..1525 " "
+ EXPR@1525..1593
+ EXPR_PARENED@1525..1593
+ L_PAREN@1525..1526 "("
+ EXPR@1526..1592
+ EXPR_BINARY@1526..1592
+ EXPR@1526..1576
+ EXPR_STRING@1526..1576
+ STRING_SINGLE@1526..1576 "'std.mapWithKey secon ..."
+ WHITESPACE@1576..1577 " "
+ PLUS@1577..1578 "+"
+ WHITESPACE@1578..1579 " "
+ EXPR@1579..1592
+ EXPR_VAR@1579..1582
+ NAME@1579..1582
+ IDENT@1579..1582 "std"
+ SUFFIX_INDEX@1582..1587
+ DOT@1582..1583 "."
+ NAME@1583..1587
+ IDENT@1583..1587 "type"
+ SUFFIX_APPLY@1587..1592
+ ARGS_DESC@1587..1592
+ L_PAREN@1587..1588 "("
+ ARG@1588..1591
+ EXPR@1588..1591
+ EXPR_VAR@1588..1591
+ NAME@1588..1591
+ IDENT@1588..1591 "obj"
+ R_PAREN@1591..1592 ")"
+ R_PAREN@1592..1593 ")"
+ WHITESPACE@1593..1598 "\n "
+ ELSE_KW@1598..1602 "else"
+ WHITESPACE@1602..1609 "\n "
+ FALSE_EXPR@1609..1664
+ EXPR@1609..1664
+ EXPR_OBJECT@1609..1664
+ OBJ_BODY_COMP@1609..1664
+ L_BRACE@1609..1610 "{"
+ WHITESPACE@1610..1611 " "
+ MEMBER_FIELD_NORMAL@1611..1631
+ FIELD_NAME_DYNAMIC@1611..1614
+ L_BRACK@1611..1612 "["
+ EXPR@1612..1613
+ EXPR_VAR@1612..1613
+ NAME@1612..1613
+ IDENT@1612..1613 "k"
+ R_BRACK@1613..1614 "]"
+ COLON@1614..1615 ":"
+ WHITESPACE@1615..1616 " "
+ EXPR@1616..1631
+ EXPR_VAR@1616..1620
+ NAME@1616..1620
+ IDENT@1616..1620 "func"
+ SUFFIX_APPLY@1620..1631
+ ARGS_DESC@1620..1631
+ L_PAREN@1620..1621 "("
+ ARG@1621..1622
+ EXPR@1621..1622
+ EXPR_VAR@1621..1622
+ NAME@1621..1622
+ IDENT@1621..1622 "k"
+ COMMA@1622..1623 ","
+ WHITESPACE@1623..1624 " "
+ ARG@1624..1630
+ EXPR@1624..1630
+ EXPR_VAR@1624..1627
+ NAME@1624..1627
+ IDENT@1624..1627 "obj"
+ SUFFIX_INDEX_EXPR@1627..1630
+ L_BRACK@1627..1628 "["
+ EXPR@1628..1629
+ EXPR_VAR@1628..1629
+ NAME@1628..1629
+ IDENT@1628..1629 "k"
+ R_BRACK@1629..1630 "]"
+ R_PAREN@1630..1631 ")"
+ WHITESPACE@1631..1632 " "
+ FOR_SPEC@1632..1662
+ FOR_KW@1632..1635 "for"
+ WHITESPACE@1635..1636 " "
+ DESTRUCT_FULL@1636..1637
+ NAME@1636..1637
+ IDENT@1636..1637 "k"
+ WHITESPACE@1637..1638 " "
+ IN_KW@1638..1640 "in"
+ WHITESPACE@1640..1641 " "
+ EXPR@1641..1662
+ EXPR_VAR@1641..1644
+ NAME@1641..1644
+ IDENT@1641..1644 "std"
+ SUFFIX_INDEX@1644..1657
+ DOT@1644..1645 "."
+ NAME@1645..1657
+ IDENT@1645..1657 "objectFields"
+ SUFFIX_APPLY@1657..1662
+ ARGS_DESC@1657..1662
+ L_PAREN@1657..1658 "("
+ ARG@1658..1661
+ EXPR@1658..1661
+ EXPR_VAR@1658..1661
+ NAME@1658..1661
+ IDENT@1658..1661 "obj"
+ R_PAREN@1661..1662 ")"
+ WHITESPACE@1662..1663 " "
+ R_BRACE@1663..1664 "}"
+ COMMA@1664..1665 ","
+ WHITESPACE@1665..1669 "\n\n "
+ MEMBER_FIELD_METHOD@1669..1712
+ FIELD_NAME_FIXED@1669..1674
+ NAME@1669..1674
+ IDENT@1669..1674 "lines"
+ PARAMS_DESC@1674..1679
+ L_PAREN@1674..1675 "("
+ PARAM@1675..1678
+ DESTRUCT_FULL@1675..1678
+ NAME@1675..1678
+ IDENT@1675..1678 "arr"
+ R_PAREN@1678..1679 ")"
+ COLONCOLON@1679..1681 "::"
+ WHITESPACE@1681..1686 "\n "
+ EXPR@1686..1712
+ EXPR_VAR@1686..1689
+ NAME@1686..1689
+ IDENT@1686..1689 "std"
+ SUFFIX_INDEX@1689..1694
+ DOT@1689..1690 "."
+ NAME@1690..1694
+ IDENT@1690..1694 "join"
+ SUFFIX_APPLY@1694..1712
+ ARGS_DESC@1694..1712
+ L_PAREN@1694..1695 "("
+ ARG@1695..1699
+ EXPR@1695..1699
+ EXPR_STRING@1695..1699
+ STRING_SINGLE@1695..1699 "'\\n'"
+ COMMA@1699..1700 ","
+ WHITESPACE@1700..1701 " "
+ ARG@1701..1711
+ EXPR@1701..1711
+ EXPR_BINARY@1701..1711
+ EXPR@1701..1704
+ EXPR_VAR@1701..1704
+ NAME@1701..1704
+ IDENT@1701..1704 "arr"
+ WHITESPACE@1704..1705 " "
+ PLUS@1705..1706 "+"
+ WHITESPACE@1706..1707 " "
+ EXPR@1707..1711
+ EXPR_ARRAY@1707..1711
+ L_BRACK@1707..1708 "["
+ EXPR@1708..1710
+ EXPR_STRING@1708..1710
+ STRING_SINGLE@1708..1710 "''"
+ R_BRACK@1710..1711 "]"
+ R_PAREN@1711..1712 ")"
+ COMMA@1712..1713 ","
+ WHITESPACE@1713..1717 "\n\n "
+ MEMBER_FIELD_METHOD@1717..1929
+ FIELD_NAME_FIXED@1717..1725
+ NAME@1717..1725
+ IDENT@1717..1725 "deepJoin"
+ PARAMS_DESC@1725..1730
+ L_PAREN@1725..1726 "("
+ PARAM@1726..1729
+ DESTRUCT_FULL@1726..1729
+ NAME@1726..1729
+ IDENT@1726..1729 "arr"
+ R_PAREN@1729..1730 ")"
+ COLONCOLON@1730..1732 "::"
+ WHITESPACE@1732..1737 "\n "
+ EXPR@1737..1929
+ EXPR_IF_THEN_ELSE@1737..1929
+ IF_KW@1737..1739 "if"
+ WHITESPACE@1739..1740 " "
+ EXPR@1740..1757
+ EXPR_VAR@1740..1743
+ NAME@1740..1743
+ IDENT@1740..1743 "std"
+ SUFFIX_INDEX@1743..1752
+ DOT@1743..1744 "."
+ NAME@1744..1752
+ IDENT@1744..1752 "isString"
+ SUFFIX_APPLY@1752..1757
+ ARGS_DESC@1752..1757
+ L_PAREN@1752..1753 "("
+ ARG@1753..1756
+ EXPR@1753..1756
+ EXPR_VAR@1753..1756
+ NAME@1753..1756
+ IDENT@1753..1756 "arr"
+ R_PAREN@1756..1757 ")"
+ WHITESPACE@1757..1758 " "
+ THEN_KW@1758..1762 "then"
+ WHITESPACE@1762..1769 "\n "
+ TRUE_EXPR@1769..1772
+ EXPR@1769..1772
+ EXPR_VAR@1769..1772
+ NAME@1769..1772
+ IDENT@1769..1772 "arr"
+ WHITESPACE@1772..1777 "\n "
+ ELSE_KW@1777..1781 "else"
+ WHITESPACE@1781..1782 " "
+ FALSE_EXPR@1782..1929
+ EXPR@1782..1929
+ EXPR_IF_THEN_ELSE@1782..1929
+ IF_KW@1782..1784 "if"
+ WHITESPACE@1784..1785 " "
+ EXPR@1785..1801
+ EXPR_VAR@1785..1788
+ NAME@1785..1788
+ IDENT@1785..1788 "std"
+ SUFFIX_INDEX@1788..1796
+ DOT@1788..1789 "."
+ NAME@1789..1796
+ IDENT@1789..1796 "isArray"
+ SUFFIX_APPLY@1796..1801
+ ARGS_DESC@1796..1801
+ L_PAREN@1796..1797 "("
+ ARG@1797..1800
+ EXPR@1797..1800
+ EXPR_VAR@1797..1800
+ NAME@1797..1800
+ IDENT@1797..1800 "arr"
+ R_PAREN@1800..1801 ")"
+ WHITESPACE@1801..1802 " "
+ THEN_KW@1802..1806 "then"
+ WHITESPACE@1806..1813 "\n "
+ TRUE_EXPR@1813..1857
+ EXPR@1813..1857
+ EXPR_VAR@1813..1816
+ NAME@1813..1816
+ IDENT@1813..1816 "std"
+ SUFFIX_INDEX@1816..1821
+ DOT@1816..1817 "."
+ NAME@1817..1821
+ IDENT@1817..1821 "join"
+ SUFFIX_APPLY@1821..1857
+ ARGS_DESC@1821..1857
+ L_PAREN@1821..1822 "("
+ ARG@1822..1824
+ EXPR@1822..1824
+ EXPR_STRING@1822..1824
+ STRING_SINGLE@1822..1824 "''"
+ COMMA@1824..1825 ","
+ WHITESPACE@1825..1826 " "
+ ARG@1826..1856
+ EXPR@1826..1856
+ EXPR_ARRAY_COMP@1826..1856
+ L_BRACK@1826..1827 "["
+ EXPR@1827..1842
+ EXPR_VAR@1827..1830
+ NAME@1827..1830
+ IDENT@1827..1830 "std"
+ SUFFIX_INDEX@1830..1839
+ DOT@1830..1831 "."
+ NAME@1831..1839
+ IDENT@1831..1839 "deepJoin"
+ SUFFIX_APPLY@1839..1842
+ ARGS_DESC@1839..1842
+ L_PAREN@1839..1840 "("
+ ARG@1840..1841
+ EXPR@1840..1841
+ EXPR_VAR@1840..1841
+ NAME@1840..1841
+ IDENT@1840..1841 "x"
+ R_PAREN@1841..1842 ")"
+ WHITESPACE@1842..1843 " "
+ FOR_SPEC@1843..1855
+ FOR_KW@1843..1846 "for"
+ WHITESPACE@1846..1847 " "
+ DESTRUCT_FULL@1847..1848
+ NAME@1847..1848
+ IDENT@1847..1848 "x"
+ WHITESPACE@1848..1849 " "
+ IN_KW@1849..1851 "in"
+ WHITESPACE@1851..1852 " "
+ EXPR@1852..1855
+ EXPR_VAR@1852..1855
+ NAME@1852..1855
+ IDENT@1852..1855 "arr"
+ R_BRACK@1855..1856 "]"
+ R_PAREN@1856..1857 ")"
+ WHITESPACE@1857..1862 "\n "
+ ELSE_KW@1862..1866 "else"
+ WHITESPACE@1866..1873 "\n "
+ FALSE_EXPR@1873..1929
+ EXPR@1873..1929
+ EXPR_ERROR@1873..1929
+ ERROR_KW@1873..1878 "error"
+ WHITESPACE@1878..1879 " "
+ EXPR@1879..1929
+ EXPR_BINARY@1879..1929
+ EXPR@1879..1913
+ EXPR_STRING@1879..1913
+ STRING_SINGLE@1879..1913 "'Expected string or a ..."
+ WHITESPACE@1913..1914 " "
+ MODULO@1914..1915 "%"
+ WHITESPACE@1915..1916 " "
+ EXPR@1916..1929
+ EXPR_VAR@1916..1919
+ NAME@1916..1919
+ IDENT@1916..1919 "std"
+ SUFFIX_INDEX@1919..1924
+ DOT@1919..1920 "."
+ NAME@1920..1924
+ IDENT@1920..1924 "type"
+ SUFFIX_APPLY@1924..1929
+ ARGS_DESC@1924..1929
+ L_PAREN@1924..1925 "("
+ ARG@1925..1928
+ EXPR@1925..1928
+ EXPR_VAR@1925..1928
+ NAME@1925..1928
+ IDENT@1925..1928 "arr"
+ R_PAREN@1928..1929 ")"
+ COMMA@1929..1930 ","
+ WHITESPACE@1930..1934 "\n\n "
+ MEMBER_FIELD_METHOD@1934..2042
+ FIELD_NAME_FIXED@1934..1945
+ NAME@1934..1945
+ IDENT@1934..1945 "assertEqual"
+ PARAMS_DESC@1945..1951
+ L_PAREN@1945..1946 "("
+ PARAM@1946..1947
+ DESTRUCT_FULL@1946..1947
+ NAME@1946..1947
+ IDENT@1946..1947 "a"
+ COMMA@1947..1948 ","
+ WHITESPACE@1948..1949 " "
+ PARAM@1949..1950
+ DESTRUCT_FULL@1949..1950
+ NAME@1949..1950
+ IDENT@1949..1950 "b"
+ R_PAREN@1950..1951 ")"
+ COLONCOLON@1951..1953 "::"
+ WHITESPACE@1953..1958 "\n "
+ EXPR@1958..2042
+ EXPR_IF_THEN_ELSE@1958..2042
+ IF_KW@1958..1960 "if"
+ WHITESPACE@1960..1961 " "
+ EXPR@1961..1967
+ EXPR_BINARY@1961..1967
+ EXPR@1961..1962
+ EXPR_VAR@1961..1962
+ NAME@1961..1962
+ IDENT@1961..1962 "a"
+ WHITESPACE@1962..1963 " "
+ EQ@1963..1965 "=="
+ WHITESPACE@1965..1966 " "
+ EXPR@1966..1967
+ EXPR_VAR@1966..1967
+ NAME@1966..1967
+ IDENT@1966..1967 "b"
+ WHITESPACE@1967..1968 " "
+ THEN_KW@1968..1972 "then"
+ WHITESPACE@1972..1979 "\n "
+ TRUE_EXPR@1979..1983
+ EXPR@1979..1983
+ EXPR_LITERAL@1979..1983
+ TRUE_KW@1979..1983 "true"
+ WHITESPACE@1983..1988 "\n "
+ ELSE_KW@1988..1992 "else"
+ WHITESPACE@1992..1999 "\n "
+ FALSE_EXPR@1999..2042
+ EXPR@1999..2042
+ EXPR_ERROR@1999..2042
+ ERROR_KW@1999..2004 "error"
+ WHITESPACE@2004..2005 " "
+ EXPR@2005..2042
+ EXPR_BINARY@2005..2042
+ EXPR@2005..2038
+ EXPR_BINARY@2005..2038
+ EXPR@2005..2029
+ EXPR_BINARY@2005..2029
+ EXPR@2005..2025
+ EXPR_STRING@2005..2025
+ STRING_SINGLE@2005..2025 "'Assertion failed. '"
+ WHITESPACE@2025..2026 " "
+ PLUS@2026..2027 "+"
+ WHITESPACE@2027..2028 " "
+ EXPR@2028..2029
+ EXPR_VAR@2028..2029
+ NAME@2028..2029
+ IDENT@2028..2029 "a"
+ WHITESPACE@2029..2030 " "
+ PLUS@2030..2031 "+"
+ WHITESPACE@2031..2032 " "
+ EXPR@2032..2038
+ EXPR_STRING@2032..2038
+ STRING_SINGLE@2032..2038 "' != '"
+ WHITESPACE@2038..2039 " "
+ PLUS@2039..2040 "+"
+ WHITESPACE@2040..2041 " "
+ EXPR@2041..2042
+ EXPR_VAR@2041..2042
+ NAME@2041..2042
+ IDENT@2041..2042 "b"
+ COMMA@2042..2043 ","
+ WHITESPACE@2043..2047 "\n\n "
+ MEMBER_FIELD_METHOD@2047..2149
+ FIELD_NAME_FIXED@2047..2052
+ NAME@2047..2052
+ IDENT@2047..2052 "clamp"
+ PARAMS_DESC@2052..2071
+ L_PAREN@2052..2053 "("
+ PARAM@2053..2054
+ DESTRUCT_FULL@2053..2054
+ NAME@2053..2054
+ IDENT@2053..2054 "x"
+ COMMA@2054..2055 ","
+ WHITESPACE@2055..2056 " "
+ PARAM@2056..2062
+ DESTRUCT_FULL@2056..2062
+ NAME@2056..2062
+ IDENT@2056..2062 "minVal"
+ COMMA@2062..2063 ","
+ WHITESPACE@2063..2064 " "
+ PARAM@2064..2070
+ DESTRUCT_FULL@2064..2070
+ NAME@2064..2070
+ IDENT@2064..2070 "maxVal"
+ R_PAREN@2070..2071 ")"
+ COLONCOLON@2071..2073 "::"
+ WHITESPACE@2073..2078 "\n "
+ EXPR@2078..2149
+ EXPR_IF_THEN_ELSE@2078..2149
+ IF_KW@2078..2080 "if"
+ WHITESPACE@2080..2081 " "
+ EXPR@2081..2091
+ EXPR_BINARY@2081..2091
+ EXPR@2081..2082
+ EXPR_VAR@2081..2082
+ NAME@2081..2082
+ IDENT@2081..2082 "x"
+ WHITESPACE@2082..2083 " "
+ LT@2083..2084 "<"
+ WHITESPACE@2084..2085 " "
+ EXPR@2085..2091
+ EXPR_VAR@2085..2091
+ NAME@2085..2091
+ IDENT@2085..2091 "minVal"
+ WHITESPACE@2091..2092 " "
+ THEN_KW@2092..2096 "then"
+ WHITESPACE@2096..2097 " "
+ TRUE_EXPR@2097..2103
+ EXPR@2097..2103
+ EXPR_VAR@2097..2103
+ NAME@2097..2103
+ IDENT@2097..2103 "minVal"
+ WHITESPACE@2103..2108 "\n "
+ ELSE_KW@2108..2112 "else"
+ WHITESPACE@2112..2113 " "
+ FALSE_EXPR@2113..2149
+ EXPR@2113..2149
+ EXPR_IF_THEN_ELSE@2113..2149
+ IF_KW@2113..2115 "if"
+ WHITESPACE@2115..2116 " "
+ EXPR@2116..2126
+ EXPR_BINARY@2116..2126
+ EXPR@2116..2117
+ EXPR_VAR@2116..2117
+ NAME@2116..2117
+ IDENT@2116..2117 "x"
+ WHITESPACE@2117..2118 " "
+ GT@2118..2119 ">"
+ WHITESPACE@2119..2120 " "
+ EXPR@2120..2126
+ EXPR_VAR@2120..2126
+ NAME@2120..2126
+ IDENT@2120..2126 "maxVal"
+ WHITESPACE@2126..2127 " "
+ THEN_KW@2127..2131 "then"
+ WHITESPACE@2131..2132 " "
+ TRUE_EXPR@2132..2138
+ EXPR@2132..2138
+ EXPR_VAR@2132..2138
+ NAME@2132..2138
+ IDENT@2132..2138 "maxVal"
+ WHITESPACE@2138..2143 "\n "
+ ELSE_KW@2143..2147 "else"
+ WHITESPACE@2147..2148 " "
+ FALSE_EXPR@2148..2149
+ EXPR@2148..2149
+ EXPR_VAR@2148..2149
+ NAME@2148..2149
+ IDENT@2148..2149 "x"
+ COMMA@2149..2150 ","
+ WHITESPACE@2150..2154 "\n\n "
+ MEMBER_FIELD_METHOD@2154..2840
+ FIELD_NAME_FIXED@2154..2165
+ NAME@2154..2165
+ IDENT@2154..2165 "manifestIni"
+ PARAMS_DESC@2165..2170
+ L_PAREN@2165..2166 "("
+ PARAM@2166..2169
+ DESTRUCT_FULL@2166..2169
+ NAME@2166..2169
+ IDENT@2166..2169 "ini"
+ R_PAREN@2169..2170 ")"
+ COLONCOLON@2170..2172 "::"
+ WHITESPACE@2172..2177 "\n "
+ EXPR@2177..2840
+ STMT_LOCAL@2177..2481
+ LOCAL_KW@2177..2182 "local"
+ WHITESPACE@2182..2183 " "
+ BIND_FUNCTION@2183..2480
+ NAME@2183..2193
+ IDENT@2183..2193 "body_lines"
+ PARAMS_DESC@2193..2199
+ L_PAREN@2193..2194 "("
+ PARAM@2194..2198
+ DESTRUCT_FULL@2194..2198
+ NAME@2194..2198
+ IDENT@2194..2198 "body"
+ R_PAREN@2198..2199 ")"
+ WHITESPACE@2199..2200 " "
+ ASSIGN@2200..2201 "="
+ WHITESPACE@2201..2208 "\n "
+ EXPR@2208..2480
+ EXPR_VAR@2208..2211
+ NAME@2208..2211
+ IDENT@2208..2211 "std"
+ SUFFIX_INDEX@2211..2216
+ DOT@2211..2212 "."
+ NAME@2212..2216
+ IDENT@2212..2216 "join"
+ SUFFIX_APPLY@2216..2480
+ ARGS_DESC@2216..2480
+ L_PAREN@2216..2217 "("
+ ARG@2217..2219
+ EXPR@2217..2219
+ EXPR_ARRAY@2217..2219
+ L_BRACK@2217..2218 "["
+ R_BRACK@2218..2219 "]"
+ COMMA@2219..2220 ","
+ WHITESPACE@2220..2221 " "
+ ARG@2221..2479
+ EXPR@2221..2479
+ EXPR_ARRAY_COMP@2221..2479
+ L_BRACK@2221..2222 "["
+ WHITESPACE@2222..2231 "\n "
+ EXPR@2231..2430
+ STMT_LOCAL@2231..2263
+ LOCAL_KW@2231..2236 "local"
+ WHITESPACE@2236..2237 " "
+ BIND_DESTRUCT@2237..2262
+ DESTRUCT_FULL@2237..2252
+ NAME@2237..2252
+ IDENT@2237..2252 "value_or_values"
+ WHITESPACE@2252..2253 " "
+ ASSIGN@2253..2254 "="
+ WHITESPACE@2254..2255 " "
+ EXPR@2255..2262
+ EXPR_VAR@2255..2259
+ NAME@2255..2259
+ IDENT@2255..2259 "body"
+ SUFFIX_INDEX_EXPR@2259..2262
+ L_BRACK@2259..2260 "["
+ EXPR@2260..2261
+ EXPR_VAR@2260..2261
+ NAME@2260..2261
+ IDENT@2260..2261 "k"
+ R_BRACK@2261..2262 "]"
+ SEMI@2262..2263 ";"
+ WHITESPACE@2263..2272 "\n "
+ EXPR_IF_THEN_ELSE@2272..2430
+ IF_KW@2272..2274 "if"
+ WHITESPACE@2274..2275 " "
+ EXPR@2275..2303
+ EXPR_VAR@2275..2278
+ NAME@2275..2278
+ IDENT@2275..2278 "std"
+ SUFFIX_INDEX@2278..2286
+ DOT@2278..2279 "."
+ NAME@2279..2286
+ IDENT@2279..2286 "isArray"
+ SUFFIX_APPLY@2286..2303
+ ARGS_DESC@2286..2303
+ L_PAREN@2286..2287 "("
+ ARG@2287..2302
+ EXPR@2287..2302
+ EXPR_VAR@2287..2302
+ NAME@2287..2302
+ IDENT@2287..2302 "value_or_values"
+ R_PAREN@2302..2303 ")"
+ WHITESPACE@2303..2304 " "
+ THEN_KW@2304..2308 "then"
+ WHITESPACE@2308..2319 "\n "
+ TRUE_EXPR@2319..2372
+ EXPR@2319..2372
+ EXPR_ARRAY_COMP@2319..2372
+ L_BRACK@2319..2320 "["
+ EXPR@2320..2342
+ EXPR_BINARY@2320..2342
+ EXPR@2320..2329
+ EXPR_STRING@2320..2329
+ STRING_SINGLE@2320..2329 "'%s = %s'"
+ WHITESPACE@2329..2330 " "
+ MODULO@2330..2331 "%"
+ WHITESPACE@2331..2332 " "
+ EXPR@2332..2342
+ EXPR_ARRAY@2332..2342
+ L_BRACK@2332..2333 "["
+ EXPR@2333..2334
+ EXPR_VAR@2333..2334
+ NAME@2333..2334
+ IDENT@2333..2334 "k"
+ COMMA@2334..2335 ","
+ WHITESPACE@2335..2336 " "
+ EXPR@2336..2341
+ EXPR_VAR@2336..2341
+ NAME@2336..2341
+ IDENT@2336..2341 "value"
+ R_BRACK@2341..2342 "]"
+ WHITESPACE@2342..2343 " "
+ FOR_SPEC@2343..2371
+ FOR_KW@2343..2346 "for"
+ WHITESPACE@2346..2347 " "
+ DESTRUCT_FULL@2347..2352
+ NAME@2347..2352
+ IDENT@2347..2352 "value"
+ WHITESPACE@2352..2353 " "
+ IN_KW@2353..2355 "in"
+ WHITESPACE@2355..2356 " "
+ EXPR@2356..2371
+ EXPR_VAR@2356..2371
+ NAME@2356..2371
+ IDENT@2356..2371 "value_or_values"
+ R_BRACK@2371..2372 "]"
+ WHITESPACE@2372..2381 "\n "
+ ELSE_KW@2381..2385 "else"
+ WHITESPACE@2385..2396 "\n "
+ FALSE_EXPR@2396..2430
+ EXPR@2396..2430
+ EXPR_ARRAY@2396..2430
+ L_BRACK@2396..2397 "["
+ EXPR@2397..2429
+ EXPR_BINARY@2397..2429
+ EXPR@2397..2406
+ EXPR_STRING@2397..2406
+ STRING_SINGLE@2397..2406 "'%s = %s'"
+ WHITESPACE@2406..2407 " "
+ MODULO@2407..2408 "%"
+ WHITESPACE@2408..2409 " "
+ EXPR@2409..2429
+ EXPR_ARRAY@2409..2429
+ L_BRACK@2409..2410 "["
+ EXPR@2410..2411
+ EXPR_VAR@2410..2411
+ NAME@2410..2411
+ IDENT@2410..2411 "k"
+ COMMA@2411..2412 ","
+ WHITESPACE@2412..2413 " "
+ EXPR@2413..2428
+ EXPR_VAR@2413..2428
+ NAME@2413..2428
+ IDENT@2413..2428 "value_or_values"
+ R_BRACK@2428..2429 "]"
+ R_BRACK@2429..2430 "]"
+ WHITESPACE@2430..2440 "\n\n "
+ FOR_SPEC@2440..2471
+ FOR_KW@2440..2443 "for"
+ WHITESPACE@2443..2444 " "
+ DESTRUCT_FULL@2444..2445
+ NAME@2444..2445
+ IDENT@2444..2445 "k"
+ WHITESPACE@2445..2446 " "
+ IN_KW@2446..2448 "in"
+ WHITESPACE@2448..2449 " "
+ EXPR@2449..2471
+ EXPR_VAR@2449..2452
+ NAME@2449..2452
+ IDENT@2449..2452 "std"
+ SUFFIX_INDEX@2452..2465
+ DOT@2452..2453 "."
+ NAME@2453..2465
+ IDENT@2453..2465 "objectFields"
+ SUFFIX_APPLY@2465..2471
+ ARGS_DESC@2465..2471
+ L_PAREN@2465..2466 "("
+ ARG@2466..2470
+ EXPR@2466..2470
+ EXPR_VAR@2466..2470
+ NAME@2466..2470
+ IDENT@2466..2470 "body"
+ R_PAREN@2470..2471 ")"
+ WHITESPACE@2471..2478 "\n "
+ R_BRACK@2478..2479 "]"
+ R_PAREN@2479..2480 ")"
+ SEMI@2480..2481 ";"
+ WHITESPACE@2481..2487 "\n\n "
+ STMT_LOCAL@2487..2769
+ LOCAL_KW@2487..2492 "local"
+ WHITESPACE@2492..2493 " "
+ BIND_FUNCTION@2493..2561
+ NAME@2493..2506
+ IDENT@2493..2506 "section_lines"
+ PARAMS_DESC@2506..2520
+ L_PAREN@2506..2507 "("
+ PARAM@2507..2512
+ DESTRUCT_FULL@2507..2512
+ NAME@2507..2512
+ IDENT@2507..2512 "sname"
+ COMMA@2512..2513 ","
+ WHITESPACE@2513..2514 " "
+ PARAM@2514..2519
+ DESTRUCT_FULL@2514..2519
+ NAME@2514..2519
+ IDENT@2514..2519 "sbody"
+ R_PAREN@2519..2520 ")"
+ WHITESPACE@2520..2521 " "
+ ASSIGN@2521..2522 "="
+ WHITESPACE@2522..2523 " "
+ EXPR@2523..2561
+ EXPR_BINARY@2523..2561
+ EXPR@2523..2541
+ EXPR_ARRAY@2523..2541
+ L_BRACK@2523..2524 "["
+ EXPR@2524..2540
+ EXPR_BINARY@2524..2540
+ EXPR@2524..2530
+ EXPR_STRING@2524..2530
+ STRING_SINGLE@2524..2530 "'[%s]'"
+ WHITESPACE@2530..2531 " "
+ MODULO@2531..2532 "%"
+ WHITESPACE@2532..2533 " "
+ EXPR@2533..2540
+ EXPR_ARRAY@2533..2540
+ L_BRACK@2533..2534 "["
+ EXPR@2534..2539
+ EXPR_VAR@2534..2539
+ NAME@2534..2539
+ IDENT@2534..2539 "sname"
+ R_BRACK@2539..2540 "]"
+ R_BRACK@2540..2541 "]"
+ WHITESPACE@2541..2542 " "
+ PLUS@2542..2543 "+"
+ WHITESPACE@2543..2544 " "
+ EXPR@2544..2561
+ EXPR_VAR@2544..2554
+ NAME@2544..2554
+ IDENT@2544..2554 "body_lines"
+ SUFFIX_APPLY@2554..2561
+ ARGS_DESC@2554..2561
+ L_PAREN@2554..2555 "("
+ ARG@2555..2560
+ EXPR@2555..2560
+ EXPR_VAR@2555..2560
+ NAME@2555..2560
+ IDENT@2555..2560 "sbody"
+ R_PAREN@2560..2561 ")"
+ COMMA@2561..2562 ","
+ WHITESPACE@2562..2573 "\n "
+ BIND_DESTRUCT@2573..2648
+ DESTRUCT_FULL@2573..2582
+ NAME@2573..2582
+ IDENT@2573..2582 "main_body"
+ WHITESPACE@2582..2583 " "
+ ASSIGN@2583..2584 "="
+ WHITESPACE@2584..2585 " "
+ EXPR@2585..2648
+ EXPR_IF_THEN_ELSE@2585..2648
+ IF_KW@2585..2587 "if"
+ WHITESPACE@2587..2588 " "
+ EXPR@2588..2614
+ EXPR_VAR@2588..2591
+ NAME@2588..2591
+ IDENT@2588..2591 "std"
+ SUFFIX_INDEX@2591..2601
+ DOT@2591..2592 "."
+ NAME@2592..2601
+ IDENT@2592..2601 "objectHas"
+ SUFFIX_APPLY@2601..2614
+ ARGS_DESC@2601..2614
+ L_PAREN@2601..2602 "("
+ ARG@2602..2605
+ EXPR@2602..2605
+ EXPR_VAR@2602..2605
+ NAME@2602..2605
+ IDENT@2602..2605 "ini"
+ COMMA@2605..2606 ","
+ WHITESPACE@2606..2607 " "
+ ARG@2607..2613
+ EXPR@2607..2613
+ EXPR_STRING@2607..2613
+ STRING_SINGLE@2607..2613 "'main'"
+ R_PAREN@2613..2614 ")"
+ WHITESPACE@2614..2615 " "
+ THEN_KW@2615..2619 "then"
+ WHITESPACE@2619..2620 " "
+ TRUE_EXPR@2620..2640
+ EXPR@2620..2640
+ EXPR_VAR@2620..2630
+ NAME@2620..2630
+ IDENT@2620..2630 "body_lines"
+ SUFFIX_APPLY@2630..2640
+ ARGS_DESC@2630..2640
+ L_PAREN@2630..2631 "("
+ ARG@2631..2639
+ EXPR@2631..2639
+ EXPR_VAR@2631..2634
+ NAME@2631..2634
+ IDENT@2631..2634 "ini"
+ SUFFIX_INDEX@2634..2639
+ DOT@2634..2635 "."
+ NAME@2635..2639
+ IDENT@2635..2639 "main"
+ R_PAREN@2639..2640 ")"
+ WHITESPACE@2640..2641 " "
+ ELSE_KW@2641..2645 "else"
+ WHITESPACE@2645..2646 " "
+ FALSE_EXPR@2646..2648
+ EXPR@2646..2648
+ EXPR_ARRAY@2646..2648
+ L_BRACK@2646..2647 "["
+ R_BRACK@2647..2648 "]"
+ COMMA@2648..2649 ","
+ WHITESPACE@2649..2660 "\n "
+ BIND_DESTRUCT@2660..2768
+ DESTRUCT_FULL@2660..2672
+ NAME@2660..2672
+ IDENT@2660..2672 "all_sections"
+ WHITESPACE@2672..2673 " "
+ ASSIGN@2673..2674 "="
+ WHITESPACE@2674..2675 " "
+ EXPR@2675..2768
+ EXPR_ARRAY_COMP@2675..2768
+ L_BRACK@2675..2676 "["
+ WHITESPACE@2676..2683 "\n "
+ EXPR@2683..2716
+ EXPR_VAR@2683..2696
+ NAME@2683..2696
+ IDENT@2683..2696 "section_lines"
+ SUFFIX_APPLY@2696..2716
+ ARGS_DESC@2696..2716
+ L_PAREN@2696..2697 "("
+ ARG@2697..2698
+ EXPR@2697..2698
+ EXPR_VAR@2697..2698
+ NAME@2697..2698
+ IDENT@2697..2698 "k"
+ COMMA@2698..2699 ","
+ WHITESPACE@2699..2700 " "
+ ARG@2700..2715
+ EXPR@2700..2715
+ EXPR_VAR@2700..2703
+ NAME@2700..2703
+ IDENT@2700..2703 "ini"
+ SUFFIX_INDEX@2703..2712
+ DOT@2703..2704 "."
+ NAME@2704..2712
+ IDENT@2704..2712 "sections"
+ SUFFIX_INDEX_EXPR@2712..2715
+ L_BRACK@2712..2713 "["
+ EXPR@2713..2714
+ EXPR_VAR@2713..2714
+ NAME@2713..2714
+ IDENT@2713..2714 "k"
+ R_BRACK@2714..2715 "]"
+ R_PAREN@2715..2716 ")"
+ WHITESPACE@2716..2723 "\n "
+ FOR_SPEC@2723..2762
+ FOR_KW@2723..2726 "for"
+ WHITESPACE@2726..2727 " "
+ DESTRUCT_FULL@2727..2728
+ NAME@2727..2728
+ IDENT@2727..2728 "k"
+ WHITESPACE@2728..2729 " "
+ IN_KW@2729..2731 "in"
+ WHITESPACE@2731..2732 " "
+ EXPR@2732..2762
+ EXPR_VAR@2732..2735
+ NAME@2732..2735
+ IDENT@2732..2735 "std"
+ SUFFIX_INDEX@2735..2748
+ DOT@2735..2736 "."
+ NAME@2736..2748
+ IDENT@2736..2748 "objectFields"
+ SUFFIX_APPLY@2748..2762
+ ARGS_DESC@2748..2762
+ L_PAREN@2748..2749 "("
+ ARG@2749..2761
+ EXPR@2749..2761
+ EXPR_VAR@2749..2752
+ NAME@2749..2752
+ IDENT@2749..2752 "ini"
+ SUFFIX_INDEX@2752..2761
+ DOT@2752..2753 "."
+ NAME@2753..2761
+ IDENT@2753..2761 "sections"
+ R_PAREN@2761..2762 ")"
+ WHITESPACE@2762..2767 "\n "
+ R_BRACK@2767..2768 "]"
+ SEMI@2768..2769 ";"
+ WHITESPACE@2769..2774 "\n "
+ EXPR_VAR@2774..2777
+ NAME@2774..2777
+ IDENT@2774..2777 "std"
+ SUFFIX_INDEX@2777..2782
+ DOT@2777..2778 "."
+ NAME@2778..2782
+ IDENT@2778..2782 "join"
+ SUFFIX_APPLY@2782..2840
+ ARGS_DESC@2782..2840
+ L_PAREN@2782..2783 "("
+ ARG@2783..2787
+ EXPR@2783..2787
+ EXPR_STRING@2783..2787
+ STRING_SINGLE@2783..2787 "'\\n'"
+ COMMA@2787..2788 ","
+ WHITESPACE@2788..2789 " "
+ ARG@2789..2839
+ EXPR@2789..2839
+ EXPR_BINARY@2789..2839
+ EXPR@2789..2832
+ EXPR_BINARY@2789..2832
+ EXPR@2789..2798
+ EXPR_VAR@2789..2798
+ NAME@2789..2798
+ IDENT@2789..2798 "main_body"
+ WHITESPACE@2798..2799 " "
+ PLUS@2799..2800 "+"
+ WHITESPACE@2800..2801 " "
+ EXPR@2801..2832
+ EXPR_VAR@2801..2804
+ NAME@2801..2804
+ IDENT@2801..2804 "std"
+ SUFFIX_INDEX@2804..2818
+ DOT@2804..2805 "."
+ NAME@2805..2818
+ IDENT@2805..2818 "flattenArrays"
+ SUFFIX_APPLY@2818..2832
+ ARGS_DESC@2818..2832
+ L_PAREN@2818..2819 "("
+ ARG@2819..2831
+ EXPR@2819..2831
+ EXPR_VAR@2819..2831
+ NAME@2819..2831
+ IDENT@2819..2831 "all_sections"
+ R_PAREN@2831..2832 ")"
+ WHITESPACE@2832..2833 " "
+ PLUS@2833..2834 "+"
+ WHITESPACE@2834..2835 " "
+ EXPR@2835..2839
+ EXPR_ARRAY@2835..2839
+ L_BRACK@2835..2836 "["
+ EXPR@2836..2838
+ EXPR_STRING@2836..2838
+ STRING_SINGLE@2836..2838 "''"
+ R_BRACK@2838..2839 "]"
+ R_PAREN@2839..2840 ")"
+ COMMA@2840..2841 ","
+ WHITESPACE@2841..2845 "\n\n "
+ MEMBER_FIELD_METHOD@2845..2898
+ FIELD_NAME_FIXED@2845..2857
+ NAME@2845..2857
+ IDENT@2845..2857 "manifestToml"
+ PARAMS_DESC@2857..2864
+ L_PAREN@2857..2858 "("
+ PARAM@2858..2863
+ DESTRUCT_FULL@2858..2863
+ NAME@2858..2863
+ IDENT@2858..2863 "value"
+ R_PAREN@2863..2864 ")"
+ COLONCOLON@2864..2866 "::"
+ WHITESPACE@2866..2867 " "
+ EXPR@2867..2898
+ EXPR_VAR@2867..2870
+ NAME@2867..2870
+ IDENT@2867..2870 "std"
+ SUFFIX_INDEX@2870..2885
+ DOT@2870..2871 "."
+ NAME@2871..2885
+ IDENT@2871..2885 "manifestTomlEx"
+ SUFFIX_APPLY@2885..2898
+ ARGS_DESC@2885..2898
+ L_PAREN@2885..2886 "("
+ ARG@2886..2891
+ EXPR@2886..2891
+ EXPR_VAR@2886..2891
+ NAME@2886..2891
+ IDENT@2886..2891 "value"
+ COMMA@2891..2892 ","
+ WHITESPACE@2892..2893 " "
+ ARG@2893..2897
+ EXPR@2893..2897
+ EXPR_STRING@2893..2897
+ STRING_SINGLE@2893..2897 "' '"
+ R_PAREN@2897..2898 ")"
+ COMMA@2898..2899 ","
+ WHITESPACE@2899..2903 "\n\n "
+ MEMBER_FIELD_METHOD@2903..2958
+ FIELD_NAME_FIXED@2903..2921
+ NAME@2903..2921
+ IDENT@2903..2921 "escapeStringPython"
+ PARAMS_DESC@2921..2926
+ L_PAREN@2921..2922 "("
+ PARAM@2922..2925
+ DESTRUCT_FULL@2922..2925
+ NAME@2922..2925
+ IDENT@2922..2925 "str"
+ R_PAREN@2925..2926 ")"
+ COLONCOLON@2926..2928 "::"
+ WHITESPACE@2928..2933 "\n "
+ EXPR@2933..2958
+ EXPR_VAR@2933..2936
+ NAME@2933..2936
+ IDENT@2933..2936 "std"
+ SUFFIX_INDEX@2936..2953
+ DOT@2936..2937 "."
+ NAME@2937..2953
+ IDENT@2937..2953 "escapeStringJson"
+ SUFFIX_APPLY@2953..2958
+ ARGS_DESC@2953..2958
+ L_PAREN@2953..2954 "("
+ ARG@2954..2957
+ EXPR@2954..2957
+ EXPR_VAR@2954..2957
+ NAME@2954..2957
+ IDENT@2954..2957 "str"
+ R_PAREN@2957..2958 ")"
+ COMMA@2958..2959 ","
+ WHITESPACE@2959..2963 "\n\n "
+ MEMBER_FIELD_METHOD@2963..3180
+ FIELD_NAME_FIXED@2963..2979
+ NAME@2963..2979
+ IDENT@2963..2979 "escapeStringBash"
+ PARAMS_DESC@2979..2985
+ L_PAREN@2979..2980 "("
+ PARAM@2980..2984
+ DESTRUCT_FULL@2980..2984
+ NAME@2980..2984
+ IDENT@2980..2984 "str_"
+ R_PAREN@2984..2985 ")"
+ COLONCOLON@2985..2987 "::"
+ WHITESPACE@2987..2992 "\n "
+ EXPR@2992..3180
+ STMT_LOCAL@2992..3023
+ LOCAL_KW@2992..2997 "local"
+ WHITESPACE@2997..2998 " "
+ BIND_DESTRUCT@2998..3022
+ DESTRUCT_FULL@2998..3001
+ NAME@2998..3001
+ IDENT@2998..3001 "str"
+ WHITESPACE@3001..3002 " "
+ ASSIGN@3002..3003 "="
+ WHITESPACE@3003..3004 " "
+ EXPR@3004..3022
+ EXPR_VAR@3004..3007
+ NAME@3004..3007
+ IDENT@3004..3007 "std"
+ SUFFIX_INDEX@3007..3016
+ DOT@3007..3008 "."
+ NAME@3008..3016
+ IDENT@3008..3016 "toString"
+ SUFFIX_APPLY@3016..3022
+ ARGS_DESC@3016..3022
+ L_PAREN@3016..3017 "("
+ ARG@3017..3021
+ EXPR@3017..3021
+ EXPR_VAR@3017..3021
+ NAME@3017..3021
+ IDENT@3017..3021 "str_"
+ R_PAREN@3021..3022 ")"
+ SEMI@3022..3023 ";"
+ WHITESPACE@3023..3028 "\n "
+ STMT_LOCAL@3028..3110
+ LOCAL_KW@3028..3033 "local"
+ WHITESPACE@3033..3034 " "
+ BIND_FUNCTION@3034..3109
+ NAME@3034..3039
+ IDENT@3034..3039 "trans"
+ PARAMS_DESC@3039..3043
+ L_PAREN@3039..3040 "("
+ PARAM@3040..3042
+ DESTRUCT_FULL@3040..3042
+ NAME@3040..3042
+ IDENT@3040..3042 "ch"
+ R_PAREN@3042..3043 ")"
+ WHITESPACE@3043..3044 " "
+ ASSIGN@3044..3045 "="
+ WHITESPACE@3045..3052 "\n "
+ EXPR@3052..3109
+ EXPR_IF_THEN_ELSE@3052..3109
+ IF_KW@3052..3054 "if"
+ WHITESPACE@3054..3055 " "
+ EXPR@3055..3064
+ EXPR_BINARY@3055..3064
+ EXPR@3055..3057
+ EXPR_VAR@3055..3057
+ NAME@3055..3057
+ IDENT@3055..3057 "ch"
+ WHITESPACE@3057..3058 " "
+ EQ@3058..3060 "=="
+ WHITESPACE@3060..3061 " "
+ EXPR@3061..3064
+ EXPR_STRING@3061..3064
+ STRING_DOUBLE@3061..3064 "\"'\""
+ WHITESPACE@3064..3065 " "
+ THEN_KW@3065..3069 "then"
+ WHITESPACE@3069..3078 "\n "
+ TRUE_EXPR@3078..3087
+ EXPR@3078..3087
+ EXPR_STRING@3078..3087
+ STRING_DOUBLE@3078..3087 "\"'\\\"'\\\"'\""
+ WHITESPACE@3087..3094 "\n "
+ ELSE_KW@3094..3098 "else"
+ WHITESPACE@3098..3107 "\n "
+ FALSE_EXPR@3107..3109
+ EXPR@3107..3109
+ EXPR_VAR@3107..3109
+ NAME@3107..3109
+ IDENT@3107..3109 "ch"
+ SEMI@3109..3110 ";"
+ WHITESPACE@3110..3115 "\n "
+ EXPR_BINARY@3115..3180
+ EXPR@3115..3121
+ EXPR_STRING@3115..3121
+ STRING_DOUBLE@3115..3121 "\"'%s'\""
+ WHITESPACE@3121..3122 " "
+ MODULO@3122..3123 "%"
+ WHITESPACE@3123..3124 " "
+ EXPR@3124..3180
+ EXPR_VAR@3124..3127
+ NAME@3124..3127
+ IDENT@3124..3127 "std"
+ SUFFIX_INDEX@3127..3132
+ DOT@3127..3128 "."
+ NAME@3128..3132
+ IDENT@3128..3132 "join"
+ SUFFIX_APPLY@3132..3180
+ ARGS_DESC@3132..3180
+ L_PAREN@3132..3133 "("
+ ARG@3133..3135
+ EXPR@3133..3135
+ EXPR_STRING@3133..3135
+ STRING_SINGLE@3133..3135 "''"
+ COMMA@3135..3136 ","
+ WHITESPACE@3136..3137 " "
+ ARG@3137..3179
+ EXPR@3137..3179
+ EXPR_ARRAY_COMP@3137..3179
+ L_BRACK@3137..3138 "["
+ EXPR@3138..3147
+ EXPR_VAR@3138..3143
+ NAME@3138..3143
+ IDENT@3138..3143 "trans"
+ SUFFIX_APPLY@3143..3147
+ ARGS_DESC@3143..3147
+ L_PAREN@3143..3144 "("
+ ARG@3144..3146
+ EXPR@3144..3146
+ EXPR_VAR@3144..3146
+ NAME@3144..3146
+ IDENT@3144..3146 "ch"
+ R_PAREN@3146..3147 ")"
+ WHITESPACE@3147..3148 " "
+ FOR_SPEC@3148..3178
+ FOR_KW@3148..3151 "for"
+ WHITESPACE@3151..3152 " "
+ DESTRUCT_FULL@3152..3154
+ NAME@3152..3154
+ IDENT@3152..3154 "ch"
+ WHITESPACE@3154..3155 " "
+ IN_KW@3155..3157 "in"
+ WHITESPACE@3157..3158 " "
+ EXPR@3158..3178
+ EXPR_VAR@3158..3161
+ NAME@3158..3161
+ IDENT@3158..3161 "std"
+ SUFFIX_INDEX@3161..3173
+ DOT@3161..3162 "."
+ NAME@3162..3173
+ IDENT@3162..3173 "stringChars"
+ SUFFIX_APPLY@3173..3178
+ ARGS_DESC@3173..3178
+ L_PAREN@3173..3174 "("
+ ARG@3174..3177
+ EXPR@3174..3177
+ EXPR_VAR@3174..3177
+ NAME@3174..3177
+ IDENT@3174..3177 "str"
+ R_PAREN@3177..3178 ")"
+ R_BRACK@3178..3179 "]"
+ R_PAREN@3179..3180 ")"
+ COMMA@3180..3181 ","
+ WHITESPACE@3181..3185 "\n\n "
+ MEMBER_FIELD_METHOD@3185..3399
+ FIELD_NAME_FIXED@3185..3204
+ NAME@3185..3204
+ IDENT@3185..3204 "escapeStringDollars"
+ PARAMS_DESC@3204..3210
+ L_PAREN@3204..3205 "("
+ PARAM@3205..3209
+ DESTRUCT_FULL@3205..3209
+ NAME@3205..3209
+ IDENT@3205..3209 "str_"
+ R_PAREN@3209..3210 ")"
+ COLONCOLON@3210..3212 "::"
+ WHITESPACE@3212..3217 "\n "
+ EXPR@3217..3399
+ STMT_LOCAL@3217..3248
+ LOCAL_KW@3217..3222 "local"
+ WHITESPACE@3222..3223 " "
+ BIND_DESTRUCT@3223..3247
+ DESTRUCT_FULL@3223..3226
+ NAME@3223..3226
+ IDENT@3223..3226 "str"
+ WHITESPACE@3226..3227 " "
+ ASSIGN@3227..3228 "="
+ WHITESPACE@3228..3229 " "
+ EXPR@3229..3247
+ EXPR_VAR@3229..3232
+ NAME@3229..3232
+ IDENT@3229..3232 "std"
+ SUFFIX_INDEX@3232..3241
+ DOT@3232..3233 "."
+ NAME@3233..3241
+ IDENT@3233..3241 "toString"
+ SUFFIX_APPLY@3241..3247
+ ARGS_DESC@3241..3247
+ L_PAREN@3241..3242 "("
+ ARG@3242..3246
+ EXPR@3242..3246
+ EXPR_VAR@3242..3246
+ NAME@3242..3246
+ IDENT@3242..3246 "str_"
+ R_PAREN@3246..3247 ")"
+ SEMI@3247..3248 ";"
+ WHITESPACE@3248..3253 "\n "
+ STMT_LOCAL@3253..3330
+ LOCAL_KW@3253..3258 "local"
+ WHITESPACE@3258..3259 " "
+ BIND_FUNCTION@3259..3329
+ NAME@3259..3264
+ IDENT@3259..3264 "trans"
+ PARAMS_DESC@3264..3268
+ L_PAREN@3264..3265 "("
+ PARAM@3265..3267
+ DESTRUCT_FULL@3265..3267
+ NAME@3265..3267
+ IDENT@3265..3267 "ch"
+ R_PAREN@3267..3268 ")"
+ WHITESPACE@3268..3269 " "
+ ASSIGN@3269..3270 "="
+ WHITESPACE@3270..3277 "\n "
+ EXPR@3277..3329
+ EXPR_IF_THEN_ELSE@3277..3329
+ IF_KW@3277..3279 "if"
+ WHITESPACE@3279..3280 " "
+ EXPR@3280..3289
+ EXPR_BINARY@3280..3289
+ EXPR@3280..3282
+ EXPR_VAR@3280..3282
+ NAME@3280..3282
+ IDENT@3280..3282 "ch"
+ WHITESPACE@3282..3283 " "
+ EQ@3283..3285 "=="
+ WHITESPACE@3285..3286 " "
+ EXPR@3286..3289
+ EXPR_STRING@3286..3289
+ STRING_SINGLE@3286..3289 "'$'"
+ WHITESPACE@3289..3290 " "
+ THEN_KW@3290..3294 "then"
+ WHITESPACE@3294..3303 "\n "
+ TRUE_EXPR@3303..3307
+ EXPR@3303..3307
+ EXPR_STRING@3303..3307
+ STRING_SINGLE@3303..3307 "'$$'"
+ WHITESPACE@3307..3314 "\n "
+ ELSE_KW@3314..3318 "else"
+ WHITESPACE@3318..3327 "\n "
+ FALSE_EXPR@3327..3329
+ EXPR@3327..3329
+ EXPR_VAR@3327..3329
+ NAME@3327..3329
+ IDENT@3327..3329 "ch"
+ SEMI@3329..3330 ";"
+ WHITESPACE@3330..3335 "\n "
+ EXPR_VAR@3335..3338
+ NAME@3335..3338
+ IDENT@3335..3338 "std"
+ SUFFIX_INDEX@3338..3344
+ DOT@3338..3339 "."
+ NAME@3339..3344
+ IDENT@3339..3344 "foldl"
+ SUFFIX_APPLY@3344..3399
+ ARGS_DESC@3344..3399
+ L_PAREN@3344..3345 "("
+ ARG@3345..3372
+ EXPR@3345..3372
+ EXPR_FUNCTION@3345..3372
+ FUNCTION_KW@3345..3353 "function"
+ PARAMS_DESC@3353..3359
+ L_PAREN@3353..3354 "("
+ PARAM@3354..3355
+ DESTRUCT_FULL@3354..3355
+ NAME@3354..3355
+ IDENT@3354..3355 "a"
+ COMMA@3355..3356 ","
+ WHITESPACE@3356..3357 " "
+ PARAM@3357..3358
+ DESTRUCT_FULL@3357..3358
+ NAME@3357..3358
+ IDENT@3357..3358 "b"
+ R_PAREN@3358..3359 ")"
+ WHITESPACE@3359..3360 " "
+ EXPR@3360..3372
+ EXPR_BINARY@3360..3372
+ EXPR@3360..3361
+ EXPR_VAR@3360..3361
+ NAME@3360..3361
+ IDENT@3360..3361 "a"
+ WHITESPACE@3361..3362 " "
+ PLUS@3362..3363 "+"
+ WHITESPACE@3363..3364 " "
+ EXPR@3364..3372
+ EXPR_VAR@3364..3369
+ NAME@3364..3369
+ IDENT@3364..3369 "trans"
+ SUFFIX_APPLY@3369..3372
+ ARGS_DESC@3369..3372
+ L_PAREN@3369..3370 "("
+ ARG@3370..3371
+ EXPR@3370..3371
+ EXPR_VAR@3370..3371
+ NAME@3370..3371
+ IDENT@3370..3371 "b"
+ R_PAREN@3371..3372 ")"
+ COMMA@3372..3373 ","
+ WHITESPACE@3373..3374 " "
+ ARG@3374..3394
+ EXPR@3374..3394
+ EXPR_VAR@3374..3377
+ NAME@3374..3377
+ IDENT@3374..3377 "std"
+ SUFFIX_INDEX@3377..3389
+ DOT@3377..3378 "."
+ NAME@3378..3389
+ IDENT@3378..3389 "stringChars"
+ SUFFIX_APPLY@3389..3394
+ ARGS_DESC@3389..3394
+ L_PAREN@3389..3390 "("
+ ARG@3390..3393
+ EXPR@3390..3393
+ EXPR_VAR@3390..3393
+ NAME@3390..3393
+ IDENT@3390..3393 "str"
+ R_PAREN@3393..3394 ")"
+ COMMA@3394..3395 ","
+ WHITESPACE@3395..3396 " "
+ ARG@3396..3398
+ EXPR@3396..3398
+ EXPR_STRING@3396..3398
+ STRING_SINGLE@3396..3398 "''"
+ R_PAREN@3398..3399 ")"
+ COMMA@3399..3400 ","
+ WHITESPACE@3400..3404 "\n\n "
+ MEMBER_BIND_STMT@3404..3519
+ OBJ_LOCAL@3404..3519
+ LOCAL_KW@3404..3409 "local"
+ WHITESPACE@3409..3410 " "
+ BIND_DESTRUCT@3410..3519
+ DESTRUCT_FULL@3410..3421
+ NAME@3410..3421
+ IDENT@3410..3421 "xml_escapes"
+ WHITESPACE@3421..3422 " "
+ ASSIGN@3422..3423 "="
+ WHITESPACE@3423..3424 " "
+ EXPR@3424..3519
+ EXPR_OBJECT@3424..3519
+ OBJ_BODY_MEMBER_LIST@3424..3519
+ L_BRACE@3424..3425 "{"
+ WHITESPACE@3425..3430 "\n "
+ MEMBER_FIELD_NORMAL@3430..3441
+ FIELD_NAME_FIXED@3430..3433
+ STRING_SINGLE@3430..3433 "'<'"
+ COLON@3433..3434 ":"
+ WHITESPACE@3434..3435 " "
+ EXPR@3435..3441
+ EXPR_STRING@3435..3441
+ STRING_SINGLE@3435..3441 "'<'"
+ COMMA@3441..3442 ","
+ WHITESPACE@3442..3447 "\n "
+ MEMBER_FIELD_NORMAL@3447..3458
+ FIELD_NAME_FIXED@3447..3450
+ STRING_SINGLE@3447..3450 "'>'"
+ COLON@3450..3451 ":"
+ WHITESPACE@3451..3452 " "
+ EXPR@3452..3458
+ EXPR_STRING@3452..3458
+ STRING_SINGLE@3452..3458 "'>'"
+ COMMA@3458..3459 ","
+ WHITESPACE@3459..3464 "\n "
+ MEMBER_FIELD_NORMAL@3464..3476
+ FIELD_NAME_FIXED@3464..3467
+ STRING_SINGLE@3464..3467 "'&'"
+ COLON@3467..3468 ":"
+ WHITESPACE@3468..3469 " "
+ EXPR@3469..3476
+ EXPR_STRING@3469..3476
+ STRING_SINGLE@3469..3476 "'&'"
+ COMMA@3476..3477 ","
+ WHITESPACE@3477..3482 "\n "
+ MEMBER_FIELD_NORMAL@3482..3495
+ FIELD_NAME_FIXED@3482..3485
+ STRING_SINGLE@3482..3485 "'\"'"
+ COLON@3485..3486 ":"
+ WHITESPACE@3486..3487 " "
+ EXPR@3487..3495
+ EXPR_STRING@3487..3495
+ STRING_SINGLE@3487..3495 "'"'"
+ COMMA@3495..3496 ","
+ WHITESPACE@3496..3501 "\n "
+ MEMBER_FIELD_NORMAL@3501..3514
+ FIELD_NAME_FIXED@3501..3504
+ STRING_DOUBLE@3501..3504 "\"'\""
+ COLON@3504..3505 ":"
+ WHITESPACE@3505..3506 " "
+ EXPR@3506..3514
+ EXPR_STRING@3506..3514
+ STRING_SINGLE@3506..3514 "'''"
+ COMMA@3514..3515 ","
+ WHITESPACE@3515..3518 "\n "
+ R_BRACE@3518..3519 "}"
+ COMMA@3519..3520 ","
+ WHITESPACE@3520..3524 "\n\n "
+ MEMBER_FIELD_METHOD@3524..3663
+ FIELD_NAME_FIXED@3524..3539
+ NAME@3524..3539
+ IDENT@3524..3539 "escapeStringXML"
+ PARAMS_DESC@3539..3545
+ L_PAREN@3539..3540 "("
+ PARAM@3540..3544
+ DESTRUCT_FULL@3540..3544
+ NAME@3540..3544
+ IDENT@3540..3544 "str_"
+ R_PAREN@3544..3545 ")"
+ COLONCOLON@3545..3547 "::"
+ WHITESPACE@3547..3552 "\n "
+ EXPR@3552..3663
+ STMT_LOCAL@3552..3583
+ LOCAL_KW@3552..3557 "local"
+ WHITESPACE@3557..3558 " "
+ BIND_DESTRUCT@3558..3582
+ DESTRUCT_FULL@3558..3561
+ NAME@3558..3561
+ IDENT@3558..3561 "str"
+ WHITESPACE@3561..3562 " "
+ ASSIGN@3562..3563 "="
+ WHITESPACE@3563..3564 " "
+ EXPR@3564..3582
+ EXPR_VAR@3564..3567
+ NAME@3564..3567
+ IDENT@3564..3567 "std"
+ SUFFIX_INDEX@3567..3576
+ DOT@3567..3568 "."
+ NAME@3568..3576
+ IDENT@3568..3576 "toString"
+ SUFFIX_APPLY@3576..3582
+ ARGS_DESC@3576..3582
+ L_PAREN@3576..3577 "("
+ ARG@3577..3581
+ EXPR@3577..3581
+ EXPR_VAR@3577..3581
+ NAME@3577..3581
+ IDENT@3577..3581 "str_"
+ R_PAREN@3581..3582 ")"
+ SEMI@3582..3583 ";"
+ WHITESPACE@3583..3588 "\n "
+ EXPR_VAR@3588..3591
+ NAME@3588..3591
+ IDENT@3588..3591 "std"
+ SUFFIX_INDEX@3591..3596
+ DOT@3591..3592 "."
+ NAME@3592..3596
+ IDENT@3592..3596 "join"
+ SUFFIX_APPLY@3596..3663
+ ARGS_DESC@3596..3663
+ L_PAREN@3596..3597 "("
+ ARG@3597..3599
+ EXPR@3597..3599
+ EXPR_STRING@3597..3599
+ STRING_SINGLE@3597..3599 "''"
+ COMMA@3599..3600 ","
+ WHITESPACE@3600..3601 " "
+ ARG@3601..3662
+ EXPR@3601..3662
+ EXPR_ARRAY_COMP@3601..3662
+ L_BRACK@3601..3602 "["
+ EXPR@3602..3630
+ EXPR_VAR@3602..3605
+ NAME@3602..3605
+ IDENT@3602..3605 "std"
+ SUFFIX_INDEX@3605..3609
+ DOT@3605..3606 "."
+ NAME@3606..3609
+ IDENT@3606..3609 "get"
+ SUFFIX_APPLY@3609..3630
+ ARGS_DESC@3609..3630
+ L_PAREN@3609..3610 "("
+ ARG@3610..3621
+ EXPR@3610..3621
+ EXPR_VAR@3610..3621
+ NAME@3610..3621
+ IDENT@3610..3621 "xml_escapes"
+ COMMA@3621..3622 ","
+ WHITESPACE@3622..3623 " "
+ ARG@3623..3625
+ EXPR@3623..3625
+ EXPR_VAR@3623..3625
+ NAME@3623..3625
+ IDENT@3623..3625 "ch"
+ COMMA@3625..3626 ","
+ WHITESPACE@3626..3627 " "
+ ARG@3627..3629
+ EXPR@3627..3629
+ EXPR_VAR@3627..3629
+ NAME@3627..3629
+ IDENT@3627..3629 "ch"
+ R_PAREN@3629..3630 ")"
+ WHITESPACE@3630..3631 " "
+ FOR_SPEC@3631..3661
+ FOR_KW@3631..3634 "for"
+ WHITESPACE@3634..3635 " "
+ DESTRUCT_FULL@3635..3637
+ NAME@3635..3637
+ IDENT@3635..3637 "ch"
+ WHITESPACE@3637..3638 " "
+ IN_KW@3638..3640 "in"
+ WHITESPACE@3640..3641 " "
+ EXPR@3641..3661
+ EXPR_VAR@3641..3644
+ NAME@3641..3644
+ IDENT@3641..3644 "std"
+ SUFFIX_INDEX@3644..3656
+ DOT@3644..3645 "."
+ NAME@3645..3656
+ IDENT@3645..3656 "stringChars"
+ SUFFIX_APPLY@3656..3661
+ ARGS_DESC@3656..3661
+ L_PAREN@3656..3657 "("
+ ARG@3657..3660
+ EXPR@3657..3660
+ EXPR_VAR@3657..3660
+ NAME@3657..3660
+ IDENT@3657..3660 "str"
+ R_PAREN@3660..3661 ")"
+ R_BRACK@3661..3662 "]"
+ R_PAREN@3662..3663 ")"
+ COMMA@3663..3664 ","
+ WHITESPACE@3664..3668 "\n\n "
+ MEMBER_FIELD_METHOD@3668..3734
+ FIELD_NAME_FIXED@3668..3680
+ NAME@3668..3680
+ IDENT@3668..3680 "manifestJson"
+ PARAMS_DESC@3680..3687
+ L_PAREN@3680..3681 "("
+ PARAM@3681..3686
+ DESTRUCT_FULL@3681..3686
+ NAME@3681..3686
+ IDENT@3681..3686 "value"
+ R_PAREN@3686..3687 ")"
+ COLONCOLON@3687..3689 "::"
+ WHITESPACE@3689..3690 " "
+ EXPR@3690..3734
+ EXPR_VAR@3690..3693
+ NAME@3690..3693
+ IDENT@3690..3693 "std"
+ SUFFIX_INDEX@3693..3708
+ DOT@3693..3694 "."
+ NAME@3694..3708
+ IDENT@3694..3708 "manifestJsonEx"
+ SUFFIX_APPLY@3708..3734
+ ARGS_DESC@3708..3734
+ L_PAREN@3708..3709 "("
+ ARG@3709..3714
+ EXPR@3709..3714
+ EXPR_VAR@3709..3714
+ NAME@3709..3714
+ IDENT@3709..3714 "value"
+ COMMA@3714..3715 ","
+ WHITESPACE@3715..3716 " "
+ ARG@3716..3722
+ EXPR@3716..3722
+ EXPR_STRING@3716..3722
+ STRING_SINGLE@3716..3722 "' '"
+ R_PAREN@3722..3723 ")"
+ WHITESPACE@3723..3724 " "
+ TAILSTRICT_KW@3724..3734 "tailstrict"
+ COMMA@3734..3735 ","
+ WHITESPACE@3735..3739 "\n\n "
+ MEMBER_FIELD_METHOD@3739..3807
+ FIELD_NAME_FIXED@3739..3759
+ NAME@3739..3759
+ IDENT@3739..3759 "manifestJsonMinified"
+ PARAMS_DESC@3759..3766
+ L_PAREN@3759..3760 "("
+ PARAM@3760..3765
+ DESTRUCT_FULL@3760..3765
+ NAME@3760..3765
+ IDENT@3760..3765 "value"
+ R_PAREN@3765..3766 ")"
+ COLONCOLON@3766..3768 "::"
+ WHITESPACE@3768..3769 " "
+ EXPR@3769..3807
+ EXPR_VAR@3769..3772
+ NAME@3769..3772
+ IDENT@3769..3772 "std"
+ SUFFIX_INDEX@3772..3787
+ DOT@3772..3773 "."
+ NAME@3773..3787
+ IDENT@3773..3787 "manifestJsonEx"
+ SUFFIX_APPLY@3787..3807
+ ARGS_DESC@3787..3807
+ L_PAREN@3787..3788 "("
+ ARG@3788..3793
+ EXPR@3788..3793
+ EXPR_VAR@3788..3793
+ NAME@3788..3793
+ IDENT@3788..3793 "value"
+ COMMA@3793..3794 ","
+ WHITESPACE@3794..3795 " "
+ ARG@3795..3797
+ EXPR@3795..3797
+ EXPR_STRING@3795..3797
+ STRING_SINGLE@3795..3797 "''"
+ COMMA@3797..3798 ","
+ WHITESPACE@3798..3799 " "
+ ARG@3799..3801
+ EXPR@3799..3801
+ EXPR_STRING@3799..3801
+ STRING_SINGLE@3799..3801 "''"
+ COMMA@3801..3802 ","
+ WHITESPACE@3802..3803 " "
+ ARG@3803..3806
+ EXPR@3803..3806
+ EXPR_STRING@3803..3806
+ STRING_SINGLE@3803..3806 "':'"
+ R_PAREN@3806..3807 ")"
+ COMMA@3807..3808 ","
+ WHITESPACE@3808..3812 "\n\n "
+ MEMBER_FIELD_METHOD@3812..4197
+ FIELD_NAME_FIXED@3812..3830
+ NAME@3812..3830
+ IDENT@3812..3830 "manifestYamlStream"
+ PARAMS_DESC@3830..3905
+ L_PAREN@3830..3831 "("
+ PARAM@3831..3836
+ DESTRUCT_FULL@3831..3836
+ NAME@3831..3836
+ IDENT@3831..3836 "value"
+ COMMA@3836..3837 ","
+ WHITESPACE@3837..3838 " "
+ PARAM@3838..3866
+ DESTRUCT_FULL@3838..3860
+ NAME@3838..3860
+ IDENT@3838..3860 "indent_array_in_object"
+ ASSIGN@3860..3861 "="
+ EXPR@3861..3866
+ EXPR_LITERAL@3861..3866
+ FALSE_KW@3861..3866 "false"
+ COMMA@3866..3867 ","
+ WHITESPACE@3867..3868 " "
+ PARAM@3868..3887
+ DESTRUCT_FULL@3868..3882
+ NAME@3868..3882
+ IDENT@3868..3882 "c_document_end"
+ ASSIGN@3882..3883 "="
+ EXPR@3883..3887
+ EXPR_LITERAL@3883..3887
+ TRUE_KW@3883..3887 "true"
+ COMMA@3887..3888 ","
+ WHITESPACE@3888..3889 " "
+ PARAM@3889..3904
+ DESTRUCT_FULL@3889..3899
+ NAME@3889..3899
+ IDENT@3889..3899 "quote_keys"
+ ASSIGN@3899..3900 "="
+ EXPR@3900..3904
+ EXPR_LITERAL@3900..3904
+ TRUE_KW@3900..3904 "true"
+ R_PAREN@3904..3905 ")"
+ COLONCOLON@3905..3907 "::"
+ WHITESPACE@3907..3912 "\n "
+ EXPR@3912..4197
+ EXPR_IF_THEN_ELSE@3912..4197
+ IF_KW@3912..3914 "if"
+ WHITESPACE@3914..3915 " "
+ EXPR@3915..3934
+ EXPR_UNARY@3915..3934
+ NOT@3915..3916 "!"
+ EXPR_VAR@3916..3919
+ NAME@3916..3919
+ IDENT@3916..3919 "std"
+ SUFFIX_INDEX@3919..3927
+ DOT@3919..3920 "."
+ NAME@3920..3927
+ IDENT@3920..3927 "isArray"
+ SUFFIX_APPLY@3927..3934
+ ARGS_DESC@3927..3934
+ L_PAREN@3927..3928 "("
+ ARG@3928..3933
+ EXPR@3928..3933
+ EXPR_VAR@3928..3933
+ NAME@3928..3933
+ IDENT@3928..3933 "value"
+ R_PAREN@3933..3934 ")"
+ WHITESPACE@3934..3935 " "
+ THEN_KW@3935..3939 "then"
+ WHITESPACE@3939..3946 "\n "
+ TRUE_EXPR@3946..4014
+ EXPR@3946..4014
+ EXPR_ERROR@3946..4014
+ ERROR_KW@3946..3951 "error"
+ WHITESPACE@3951..3952 " "
+ EXPR@3952..4014
+ EXPR_BINARY@3952..4014
+ EXPR@3952..3996
+ EXPR_STRING@3952..3996
+ STRING_SINGLE@3952..3996 "'manifestYamlStream o ..."
+ WHITESPACE@3996..3997 " "
+ PLUS@3997..3998 "+"
+ WHITESPACE@3998..3999 " "
+ EXPR@3999..4014
+ EXPR_VAR@3999..4002
+ NAME@3999..4002
+ IDENT@3999..4002 "std"
+ SUFFIX_INDEX@4002..4007
+ DOT@4002..4003 "."
+ NAME@4003..4007
+ IDENT@4003..4007 "type"
+ SUFFIX_APPLY@4007..4014
+ ARGS_DESC@4007..4014
+ L_PAREN@4007..4008 "("
+ ARG@4008..4013
+ EXPR@4008..4013
+ EXPR_VAR@4008..4013
+ NAME@4008..4013
+ IDENT@4008..4013 "value"
+ R_PAREN@4013..4014 ")"
+ WHITESPACE@4014..4019 "\n "
+ ELSE_KW@4019..4023 "else"
+ WHITESPACE@4023..4030 "\n "
+ FALSE_EXPR@4030..4197
+ EXPR@4030..4197
+ EXPR_BINARY@4030..4197
+ EXPR@4030..4152
+ EXPR_BINARY@4030..4152
+ EXPR@4030..4037
+ EXPR_STRING@4030..4037
+ STRING_SINGLE@4030..4037 "'---\\n'"
+ WHITESPACE@4037..4038 " "
+ PLUS@4038..4039 "+"
+ WHITESPACE@4039..4040 " "
+ EXPR@4040..4152
+ EXPR_VAR@4040..4043
+ NAME@4040..4043
+ IDENT@4040..4043 "std"
+ SUFFIX_INDEX@4043..4048
+ DOT@4043..4044 "."
+ NAME@4044..4048
+ IDENT@4044..4048 "join"
+ SUFFIX_APPLY@4048..4152
+ ARGS_DESC@4048..4152
+ L_PAREN@4048..4049 "("
+ WHITESPACE@4049..4058 "\n "
+ ARG@4058..4067
+ EXPR@4058..4067
+ EXPR_STRING@4058..4067
+ STRING_SINGLE@4058..4067 "'\\n---\\n'"
+ COMMA@4067..4068 ","
+ WHITESPACE@4068..4069 " "
+ ARG@4069..4144
+ EXPR@4069..4144
+ EXPR_ARRAY_COMP@4069..4144
+ L_BRACK@4069..4070 "["
+ EXPR@4070..4128
+ EXPR_VAR@4070..4073
+ NAME@4070..4073
+ IDENT@4070..4073 "std"
+ SUFFIX_INDEX@4073..4089
+ DOT@4073..4074 "."
+ NAME@4074..4089
+ IDENT@4074..4089 "manifestYamlDoc"
+ SUFFIX_APPLY@4089..4128
+ ARGS_DESC@4089..4128
+ L_PAREN@4089..4090 "("
+ ARG@4090..4091
+ EXPR@4090..4091
+ EXPR_VAR@4090..4091
+ NAME@4090..4091
+ IDENT@4090..4091 "e"
+ COMMA@4091..4092 ","
+ WHITESPACE@4092..4093 " "
+ ARG@4093..4115
+ EXPR@4093..4115
+ EXPR_VAR@4093..4115
+ NAME@4093..4115
+ IDENT@4093..4115 "indent_array_in_object"
+ COMMA@4115..4116 ","
+ WHITESPACE@4116..4117 " "
+ ARG@4117..4127
+ EXPR@4117..4127
+ EXPR_VAR@4117..4127
+ NAME@4117..4127
+ IDENT@4117..4127 "quote_keys"
+ R_PAREN@4127..4128 ")"
+ WHITESPACE@4128..4129 " "
+ FOR_SPEC@4129..4143
+ FOR_KW@4129..4132 "for"
+ WHITESPACE@4132..4133 " "
+ DESTRUCT_FULL@4133..4134
+ NAME@4133..4134
+ IDENT@4133..4134 "e"
+ WHITESPACE@4134..4135 " "
+ IN_KW@4135..4137 "in"
+ WHITESPACE@4137..4138 " "
+ EXPR@4138..4143
+ EXPR_VAR@4138..4143
+ NAME@4138..4143
+ IDENT@4138..4143 "value"
+ R_BRACK@4143..4144 "]"
+ WHITESPACE@4144..4151 "\n "
+ R_PAREN@4151..4152 ")"
+ WHITESPACE@4152..4153 " "
+ PLUS@4153..4154 "+"
+ WHITESPACE@4154..4155 " "
+ EXPR@4155..4197
+ EXPR_IF_THEN_ELSE@4155..4197
+ IF_KW@4155..4157 "if"
+ WHITESPACE@4157..4158 " "
+ EXPR@4158..4172
+ EXPR_VAR@4158..4172
+ NAME@4158..4172
+ IDENT@4158..4172 "c_document_end"
+ WHITESPACE@4172..4173 " "
+ THEN_KW@4173..4177 "then"
+ WHITESPACE@4177..4178 " "
+ TRUE_EXPR@4178..4187
+ EXPR@4178..4187
+ EXPR_STRING@4178..4187
+ STRING_SINGLE@4178..4187 "'\\n...\\n'"
+ WHITESPACE@4187..4188 " "
+ ELSE_KW@4188..4192 "else"
+ WHITESPACE@4192..4193 " "
+ FALSE_EXPR@4193..4197
+ EXPR@4193..4197
+ EXPR_STRING@4193..4197
+ STRING_SINGLE@4193..4197 "'\\n'"
+ COMMA@4197..4198 ","
+ WHITESPACE@4198..4202 "\n\n "
+ MEMBER_FIELD_METHOD@4202..4858
+ FIELD_NAME_FIXED@4202..4216
+ NAME@4202..4216
+ IDENT@4202..4216 "manifestPython"
+ PARAMS_DESC@4216..4219
+ L_PAREN@4216..4217 "("
+ PARAM@4217..4218
+ DESTRUCT_FULL@4217..4218
+ NAME@4217..4218
+ IDENT@4217..4218 "v"
+ R_PAREN@4218..4219 ")"
+ COLONCOLON@4219..4221 "::"
+ WHITESPACE@4221..4226 "\n "
+ EXPR@4226..4858
+ EXPR_IF_THEN_ELSE@4226..4858
+ IF_KW@4226..4228 "if"
+ WHITESPACE@4228..4229 " "
+ EXPR@4229..4244
+ EXPR_VAR@4229..4232
+ NAME@4229..4232
+ IDENT@4229..4232 "std"
+ SUFFIX_INDEX@4232..4241
+ DOT@4232..4233 "."
+ NAME@4233..4241
+ IDENT@4233..4241 "isObject"
+ SUFFIX_APPLY@4241..4244
+ ARGS_DESC@4241..4244
+ L_PAREN@4241..4242 "("
+ ARG@4242..4243
+ EXPR@4242..4243
+ EXPR_VAR@4242..4243
+ NAME@4242..4243
+ IDENT@4242..4243 "v"
+ R_PAREN@4243..4244 ")"
+ WHITESPACE@4244..4245 " "
+ THEN_KW@4245..4249 "then"
+ WHITESPACE@4249..4256 "\n "
+ TRUE_EXPR@4256..4431
+ EXPR@4256..4431
+ STMT_LOCAL@4256..4391
+ LOCAL_KW@4256..4261 "local"
+ WHITESPACE@4261..4262 " "
+ BIND_DESTRUCT@4262..4390
+ DESTRUCT_FULL@4262..4268
+ NAME@4262..4268
+ IDENT@4262..4268 "fields"
+ WHITESPACE@4268..4269 " "
+ ASSIGN@4269..4270 "="
+ WHITESPACE@4270..4271 " "
+ EXPR@4271..4390
+ EXPR_ARRAY_COMP@4271..4390
+ L_BRACK@4271..4272 "["
+ WHITESPACE@4272..4281 "\n "
+ EXPR@4281..4345
+ EXPR_BINARY@4281..4345
+ EXPR@4281..4289
+ EXPR_STRING@4281..4289
+ STRING_SINGLE@4281..4289 "'%s: %s'"
+ WHITESPACE@4289..4290 " "
+ MODULO@4290..4291 "%"
+ WHITESPACE@4291..4292 " "
+ EXPR@4292..4345
+ EXPR_ARRAY@4292..4345
+ L_BRACK@4292..4293 "["
+ EXPR@4293..4318
+ EXPR_VAR@4293..4296
+ NAME@4293..4296
+ IDENT@4293..4296 "std"
+ SUFFIX_INDEX@4296..4315
+ DOT@4296..4297 "."
+ NAME@4297..4315
+ IDENT@4297..4315 "escapeStringPython"
+ SUFFIX_APPLY@4315..4318
+ ARGS_DESC@4315..4318
+ L_PAREN@4315..4316 "("
+ ARG@4316..4317
+ EXPR@4316..4317
+ EXPR_VAR@4316..4317
+ NAME@4316..4317
+ IDENT@4316..4317 "k"
+ R_PAREN@4317..4318 ")"
+ COMMA@4318..4319 ","
+ WHITESPACE@4319..4320 " "
+ EXPR@4320..4344
+ EXPR_VAR@4320..4323
+ NAME@4320..4323
+ IDENT@4320..4323 "std"
+ SUFFIX_INDEX@4323..4338
+ DOT@4323..4324 "."
+ NAME@4324..4338
+ IDENT@4324..4338 "manifestPython"
+ SUFFIX_APPLY@4338..4344
+ ARGS_DESC@4338..4344
+ L_PAREN@4338..4339 "("
+ ARG@4339..4343
+ EXPR@4339..4343
+ EXPR_VAR@4339..4340
+ NAME@4339..4340
+ IDENT@4339..4340 "v"
+ SUFFIX_INDEX_EXPR@4340..4343
+ L_BRACK@4340..4341 "["
+ EXPR@4341..4342
+ EXPR_VAR@4341..4342
+ NAME@4341..4342
+ IDENT@4341..4342 "k"
+ R_BRACK@4342..4343 "]"
+ R_PAREN@4343..4344 ")"
+ R_BRACK@4344..4345 "]"
+ WHITESPACE@4345..4354 "\n "
+ FOR_SPEC@4354..4382
+ FOR_KW@4354..4357 "for"
+ WHITESPACE@4357..4358 " "
+ DESTRUCT_FULL@4358..4359
+ NAME@4358..4359
+ IDENT@4358..4359 "k"
+ WHITESPACE@4359..4360 " "
+ IN_KW@4360..4362 "in"
+ WHITESPACE@4362..4363 " "
+ EXPR@4363..4382
+ EXPR_VAR@4363..4366
+ NAME@4363..4366
+ IDENT@4363..4366 "std"
+ SUFFIX_INDEX@4366..4379
+ DOT@4366..4367 "."
+ NAME@4367..4379
+ IDENT@4367..4379 "objectFields"
+ SUFFIX_APPLY@4379..4382
+ ARGS_DESC@4379..4382
+ L_PAREN@4379..4380 "("
+ ARG@4380..4381
+ EXPR@4380..4381
+ EXPR_VAR@4380..4381
+ NAME@4380..4381
+ IDENT@4380..4381 "v"
+ R_PAREN@4381..4382 ")"
+ WHITESPACE@4382..4389 "\n "
+ R_BRACK@4389..4390 "]"
+ SEMI@4390..4391 ";"
+ WHITESPACE@4391..4398 "\n "
+ EXPR_BINARY@4398..4431
+ EXPR@4398..4404
+ EXPR_STRING@4398..4404
+ STRING_SINGLE@4398..4404 "'{%s}'"
+ WHITESPACE@4404..4405 " "
+ MODULO@4405..4406 "%"
+ WHITESPACE@4406..4407 " "
+ EXPR@4407..4431
+ EXPR_ARRAY@4407..4431
+ L_BRACK@4407..4408 "["
+ EXPR@4408..4430
+ EXPR_VAR@4408..4411
+ NAME@4408..4411
+ IDENT@4408..4411 "std"
+ SUFFIX_INDEX@4411..4416
+ DOT@4411..4412 "."
+ NAME@4412..4416
+ IDENT@4412..4416 "join"
+ SUFFIX_APPLY@4416..4430
+ ARGS_DESC@4416..4430
+ L_PAREN@4416..4417 "("
+ ARG@4417..4421
+ EXPR@4417..4421
+ EXPR_STRING@4417..4421
+ STRING_SINGLE@4417..4421 "', '"
+ COMMA@4421..4422 ","
+ WHITESPACE@4422..4423 " "
+ ARG@4423..4429
+ EXPR@4423..4429
+ EXPR_VAR@4423..4429
+ NAME@4423..4429
+ IDENT@4423..4429 "fields"
+ R_PAREN@4429..4430 ")"
+ R_BRACK@4430..4431 "]"
+ WHITESPACE@4431..4436 "\n "
+ ELSE_KW@4436..4440 "else"
+ WHITESPACE@4440..4441 " "
+ FALSE_EXPR@4441..4858
+ EXPR@4441..4858
+ EXPR_IF_THEN_ELSE@4441..4858
+ IF_KW@4441..4443 "if"
+ WHITESPACE@4443..4444 " "
+ EXPR@4444..4458
+ EXPR_VAR@4444..4447
+ NAME@4444..4447
+ IDENT@4444..4447 "std"
+ SUFFIX_INDEX@4447..4455
+ DOT@4447..4448 "."
+ NAME@4448..4455
+ IDENT@4448..4455 "isArray"
+ SUFFIX_APPLY@4455..4458
+ ARGS_DESC@4455..4458
+ L_PAREN@4455..4456 "("
+ ARG@4456..4457
+ EXPR@4456..4457
+ EXPR_VAR@4456..4457
+ NAME@4456..4457
+ IDENT@4456..4457 "v"
+ R_PAREN@4457..4458 ")"
+ WHITESPACE@4458..4459 " "
+ THEN_KW@4459..4463 "then"
+ WHITESPACE@4463..4470 "\n "
+ TRUE_EXPR@4470..4533
+ EXPR@4470..4533
+ EXPR_BINARY@4470..4533
+ EXPR@4470..4476
+ EXPR_STRING@4470..4476
+ STRING_SINGLE@4470..4476 "'[%s]'"
+ WHITESPACE@4476..4477 " "
+ MODULO@4477..4478 "%"
+ WHITESPACE@4478..4479 " "
+ EXPR@4479..4533
+ EXPR_ARRAY@4479..4533
+ L_BRACK@4479..4480 "["
+ EXPR@4480..4532
+ EXPR_VAR@4480..4483
+ NAME@4480..4483
+ IDENT@4480..4483 "std"
+ SUFFIX_INDEX@4483..4488
+ DOT@4483..4484 "."
+ NAME@4484..4488
+ IDENT@4484..4488 "join"
+ SUFFIX_APPLY@4488..4532
+ ARGS_DESC@4488..4532
+ L_PAREN@4488..4489 "("
+ ARG@4489..4493
+ EXPR@4489..4493
+ EXPR_STRING@4489..4493
+ STRING_SINGLE@4489..4493 "', '"
+ COMMA@4493..4494 ","
+ WHITESPACE@4494..4495 " "
+ ARG@4495..4531
+ EXPR@4495..4531
+ EXPR_ARRAY_COMP@4495..4531
+ L_BRACK@4495..4496 "["
+ EXPR@4496..4518
+ EXPR_VAR@4496..4499
+ NAME@4496..4499
+ IDENT@4496..4499 "std"
+ SUFFIX_INDEX@4499..4514
+ DOT@4499..4500 "."
+ NAME@4500..4514
+ IDENT@4500..4514 "manifestPython"
+ SUFFIX_APPLY@4514..4518
+ ARGS_DESC@4514..4518
+ L_PAREN@4514..4515 "("
+ ARG@4515..4517
+ EXPR@4515..4517
+ EXPR_VAR@4515..4517
+ NAME@4515..4517
+ IDENT@4515..4517 "v2"
+ R_PAREN@4517..4518 ")"
+ WHITESPACE@4518..4519 " "
+ FOR_SPEC@4519..4530
+ FOR_KW@4519..4522 "for"
+ WHITESPACE@4522..4523 " "
+ DESTRUCT_FULL@4523..4525
+ NAME@4523..4525
+ IDENT@4523..4525 "v2"
+ WHITESPACE@4525..4526 " "
+ IN_KW@4526..4528 "in"
+ WHITESPACE@4528..4529 " "
+ EXPR@4529..4530
+ EXPR_VAR@4529..4530
+ NAME@4529..4530
+ IDENT@4529..4530 "v"
+ R_BRACK@4530..4531 "]"
+ R_PAREN@4531..4532 ")"
+ R_BRACK@4532..4533 "]"
+ WHITESPACE@4533..4538 "\n "
+ ELSE_KW@4538..4542 "else"
+ WHITESPACE@4542..4543 " "
+ FALSE_EXPR@4543..4858
+ EXPR@4543..4858
+ EXPR_IF_THEN_ELSE@4543..4858
+ IF_KW@4543..4545 "if"
+ WHITESPACE@4545..4546 " "
+ EXPR@4546..4561
+ EXPR_VAR@4546..4549
+ NAME@4546..4549
+ IDENT@4546..4549 "std"
+ SUFFIX_INDEX@4549..4558
+ DOT@4549..4550 "."
+ NAME@4550..4558
+ IDENT@4550..4558 "isString"
+ SUFFIX_APPLY@4558..4561
+ ARGS_DESC@4558..4561
+ L_PAREN@4558..4559 "("
+ ARG@4559..4560
+ EXPR@4559..4560
+ EXPR_VAR@4559..4560
+ NAME@4559..4560
+ IDENT@4559..4560 "v"
+ R_PAREN@4560..4561 ")"
+ WHITESPACE@4561..4562 " "
+ THEN_KW@4562..4566 "then"
+ WHITESPACE@4566..4573 "\n "
+ TRUE_EXPR@4573..4607
+ EXPR@4573..4607
+ EXPR_BINARY@4573..4607
+ EXPR@4573..4577
+ EXPR_STRING@4573..4577
+ STRING_SINGLE@4573..4577 "'%s'"
+ WHITESPACE@4577..4578 " "
+ MODULO@4578..4579 "%"
+ WHITESPACE@4579..4580 " "
+ EXPR@4580..4607
+ EXPR_ARRAY@4580..4607
+ L_BRACK@4580..4581 "["
+ EXPR@4581..4606
+ EXPR_VAR@4581..4584
+ NAME@4581..4584
+ IDENT@4581..4584 "std"
+ SUFFIX_INDEX@4584..4603
+ DOT@4584..4585 "."
+ NAME@4585..4603
+ IDENT@4585..4603 "escapeStringPython"
+ SUFFIX_APPLY@4603..4606
+ ARGS_DESC@4603..4606
+ L_PAREN@4603..4604 "("
+ ARG@4604..4605
+ EXPR@4604..4605
+ EXPR_VAR@4604..4605
+ NAME@4604..4605
+ IDENT@4604..4605 "v"
+ R_PAREN@4605..4606 ")"
+ R_BRACK@4606..4607 "]"
+ WHITESPACE@4607..4612 "\n "
+ ELSE_KW@4612..4616 "else"
+ WHITESPACE@4616..4617 " "
+ FALSE_EXPR@4617..4858
+ EXPR@4617..4858
+ EXPR_IF_THEN_ELSE@4617..4858
+ IF_KW@4617..4619 "if"
+ WHITESPACE@4619..4620 " "
+ EXPR@4620..4637
+ EXPR_VAR@4620..4623
+ NAME@4620..4623
+ IDENT@4620..4623 "std"
+ SUFFIX_INDEX@4623..4634
+ DOT@4623..4624 "."
+ NAME@4624..4634
+ IDENT@4624..4634 "isFunction"
+ SUFFIX_APPLY@4634..4637
+ ARGS_DESC@4634..4637
+ L_PAREN@4634..4635 "("
+ ARG@4635..4636
+ EXPR@4635..4636
+ EXPR_VAR@4635..4636
+ NAME@4635..4636
+ IDENT@4635..4636 "v"
+ R_PAREN@4636..4637 ")"
+ WHITESPACE@4637..4638 " "
+ THEN_KW@4638..4642 "then"
+ WHITESPACE@4642..4649 "\n "
+ TRUE_EXPR@4649..4681
+ EXPR@4649..4681
+ EXPR_ERROR@4649..4681
+ ERROR_KW@4649..4654 "error"
+ WHITESPACE@4654..4655 " "
+ EXPR@4655..4681
+ EXPR_STRING@4655..4681
+ STRING_SINGLE@4655..4681 "'cannot manifest func ..."
+ WHITESPACE@4681..4686 "\n "
+ ELSE_KW@4686..4690 "else"
+ WHITESPACE@4690..4691 " "
+ FALSE_EXPR@4691..4858
+ EXPR@4691..4858
+ EXPR_IF_THEN_ELSE@4691..4858
+ IF_KW@4691..4693 "if"
+ WHITESPACE@4693..4694 " "
+ EXPR@4694..4709
+ EXPR_VAR@4694..4697
+ NAME@4694..4697
+ IDENT@4694..4697 "std"
+ SUFFIX_INDEX@4697..4706
+ DOT@4697..4698 "."
+ NAME@4698..4706
+ IDENT@4698..4706 "isNumber"
+ SUFFIX_APPLY@4706..4709
+ ARGS_DESC@4706..4709
+ L_PAREN@4706..4707 "("
+ ARG@4707..4708
+ EXPR@4707..4708
+ EXPR_VAR@4707..4708
+ NAME@4707..4708
+ IDENT@4707..4708 "v"
+ R_PAREN@4708..4709 ")"
+ WHITESPACE@4709..4710 " "
+ THEN_KW@4710..4714 "then"
+ WHITESPACE@4714..4721 "\n "
+ TRUE_EXPR@4721..4736
+ EXPR@4721..4736
+ EXPR_VAR@4721..4724
+ NAME@4721..4724
+ IDENT@4721..4724 "std"
+ SUFFIX_INDEX@4724..4733
+ DOT@4724..4725 "."
+ NAME@4725..4733
+ IDENT@4725..4733 "toString"
+ SUFFIX_APPLY@4733..4736
+ ARGS_DESC@4733..4736
+ L_PAREN@4733..4734 "("
+ ARG@4734..4735
+ EXPR@4734..4735
+ EXPR_VAR@4734..4735
+ NAME@4734..4735
+ IDENT@4734..4735 "v"
+ R_PAREN@4735..4736 ")"
+ WHITESPACE@4736..4741 "\n "
+ ELSE_KW@4741..4745 "else"
+ WHITESPACE@4745..4746 " "
+ FALSE_EXPR@4746..4858
+ EXPR@4746..4858
+ EXPR_IF_THEN_ELSE@4746..4858
+ IF_KW@4746..4748 "if"
+ WHITESPACE@4748..4749 " "
+ EXPR@4749..4758
+ EXPR_BINARY@4749..4758
+ EXPR@4749..4750
+ EXPR_VAR@4749..4750
+ NAME@4749..4750
+ IDENT@4749..4750 "v"
+ WHITESPACE@4750..4751 " "
+ EQ@4751..4753 "=="
+ WHITESPACE@4753..4754 " "
+ EXPR@4754..4758
+ EXPR_LITERAL@4754..4758
+ TRUE_KW@4754..4758 "true"
+ WHITESPACE@4758..4759 " "
+ THEN_KW@4759..4763 "then"
+ WHITESPACE@4763..4770 "\n "
+ TRUE_EXPR@4770..4776
+ EXPR@4770..4776
+ EXPR_STRING@4770..4776
+ STRING_SINGLE@4770..4776 "'True'"
+ WHITESPACE@4776..4781 "\n "
+ ELSE_KW@4781..4785 "else"
+ WHITESPACE@4785..4786 " "
+ FALSE_EXPR@4786..4858
+ EXPR@4786..4858
+ EXPR_IF_THEN_ELSE@4786..4858
+ IF_KW@4786..4788 "if"
+ WHITESPACE@4788..4789 " "
+ EXPR@4789..4799
+ EXPR_BINARY@4789..4799
+ EXPR@4789..4790
+ EXPR_VAR@4789..4790
+ NAME@4789..4790
+ IDENT@4789..4790 "v"
+ WHITESPACE@4790..4791 " "
+ EQ@4791..4793 "=="
+ WHITESPACE@4793..4794 " "
+ EXPR@4794..4799
+ EXPR_LITERAL@4794..4799
+ FALSE_KW@4794..4799 "false"
+ WHITESPACE@4799..4800 " "
+ THEN_KW@4800..4804 "then"
+ WHITESPACE@4804..4811 "\n "
+ TRUE_EXPR@4811..4818
+ EXPR@4811..4818
+ EXPR_STRING@4811..4818
+ STRING_SINGLE@4811..4818 "'False'"
+ WHITESPACE@4818..4823 "\n "
+ ELSE_KW@4823..4827 "else"
+ WHITESPACE@4827..4828 " "
+ FALSE_EXPR@4828..4858
+ EXPR@4828..4858
+ EXPR_IF_THEN_ELSE@4828..4858
+ IF_KW@4828..4830 "if"
+ WHITESPACE@4830..4831 " "
+ EXPR@4831..4840
+ EXPR_BINARY@4831..4840
+ EXPR@4831..4832
+ EXPR_VAR@4831..4832
+ NAME@4831..4832
+ IDENT@4831..4832 "v"
+ WHITESPACE@4832..4833 " "
+ EQ@4833..4835 "=="
+ WHITESPACE@4835..4836 " "
+ EXPR@4836..4840
+ EXPR_LITERAL@4836..4840
+ NULL_KW@4836..4840 "null"
+ WHITESPACE@4840..4841 " "
+ THEN_KW@4841..4845 "then"
+ WHITESPACE@4845..4852 "\n "
+ TRUE_EXPR@4852..4858
+ EXPR@4852..4858
+ EXPR_STRING@4852..4858
+ STRING_SINGLE@4852..4858 "'None'"
+ COMMA@4858..4859 ","
+ WHITESPACE@4859..4863 "\n\n "
+ MEMBER_FIELD_METHOD@4863..5018
+ FIELD_NAME_FIXED@4863..4881
+ NAME@4863..4881
+ IDENT@4863..4881 "manifestPythonVars"
+ PARAMS_DESC@4881..4887
+ L_PAREN@4881..4882 "("
+ PARAM@4882..4886
+ DESTRUCT_FULL@4882..4886
+ NAME@4882..4886
+ IDENT@4882..4886 "conf"
+ R_PAREN@4886..4887 ")"
+ COLONCOLON@4887..4889 "::"
+ WHITESPACE@4889..4894 "\n "
+ EXPR@4894..5018
+ STMT_LOCAL@4894..4986
+ LOCAL_KW@4894..4899 "local"
+ WHITESPACE@4899..4900 " "
+ BIND_DESTRUCT@4900..4985
+ DESTRUCT_FULL@4900..4904
+ NAME@4900..4904
+ IDENT@4900..4904 "vars"
+ WHITESPACE@4904..4905 " "
+ ASSIGN@4905..4906 "="
+ WHITESPACE@4906..4907 " "
+ EXPR@4907..4985
+ EXPR_ARRAY_COMP@4907..4985
+ L_BRACK@4907..4908 "["
+ EXPR@4908..4952
+ EXPR_BINARY@4908..4952
+ EXPR@4908..4917
+ EXPR_STRING@4908..4917
+ STRING_SINGLE@4908..4917 "'%s = %s'"
+ WHITESPACE@4917..4918 " "
+ MODULO@4918..4919 "%"
+ WHITESPACE@4919..4920 " "
+ EXPR@4920..4952
+ EXPR_ARRAY@4920..4952
+ L_BRACK@4920..4921 "["
+ EXPR@4921..4922
+ EXPR_VAR@4921..4922
+ NAME@4921..4922
+ IDENT@4921..4922 "k"
+ COMMA@4922..4923 ","
+ WHITESPACE@4923..4924 " "
+ EXPR@4924..4951
+ EXPR_VAR@4924..4927
+ NAME@4924..4927
+ IDENT@4924..4927 "std"
+ SUFFIX_INDEX@4927..4942
+ DOT@4927..4928 "."
+ NAME@4928..4942
+ IDENT@4928..4942 "manifestPython"
+ SUFFIX_APPLY@4942..4951
+ ARGS_DESC@4942..4951
+ L_PAREN@4942..4943 "("
+ ARG@4943..4950
+ EXPR@4943..4950
+ EXPR_VAR@4943..4947
+ NAME@4943..4947
+ IDENT@4943..4947 "conf"
+ SUFFIX_INDEX_EXPR@4947..4950
+ L_BRACK@4947..4948 "["
+ EXPR@4948..4949
+ EXPR_VAR@4948..4949
+ NAME@4948..4949
+ IDENT@4948..4949 "k"
+ R_BRACK@4949..4950 "]"
+ R_PAREN@4950..4951 ")"
+ R_BRACK@4951..4952 "]"
+ WHITESPACE@4952..4953 " "
+ FOR_SPEC@4953..4984
+ FOR_KW@4953..4956 "for"
+ WHITESPACE@4956..4957 " "
+ DESTRUCT_FULL@4957..4958
+ NAME@4957..4958
+ IDENT@4957..4958 "k"
+ WHITESPACE@4958..4959 " "
+ IN_KW@4959..4961 "in"
+ WHITESPACE@4961..4962 " "
+ EXPR@4962..4984
+ EXPR_VAR@4962..4965
+ NAME@4962..4965
+ IDENT@4962..4965 "std"
+ SUFFIX_INDEX@4965..4978
+ DOT@4965..4966 "."
+ NAME@4966..4978
+ IDENT@4966..4978 "objectFields"
+ SUFFIX_APPLY@4978..4984
+ ARGS_DESC@4978..4984
+ L_PAREN@4978..4979 "("
+ ARG@4979..4983
+ EXPR@4979..4983
+ EXPR_VAR@4979..4983
+ NAME@4979..4983
+ IDENT@4979..4983 "conf"
+ R_PAREN@4983..4984 ")"
+ R_BRACK@4984..4985 "]"
+ SEMI@4985..4986 ";"
+ WHITESPACE@4986..4991 "\n "
+ EXPR_VAR@4991..4994
+ NAME@4991..4994
+ IDENT@4991..4994 "std"
+ SUFFIX_INDEX@4994..4999
+ DOT@4994..4995 "."
+ NAME@4995..4999
+ IDENT@4995..4999 "join"
+ SUFFIX_APPLY@4999..5018
+ ARGS_DESC@4999..5018
+ L_PAREN@4999..5000 "("
+ ARG@5000..5004
+ EXPR@5000..5004
+ EXPR_STRING@5000..5004
+ STRING_SINGLE@5000..5004 "'\\n'"
+ COMMA@5004..5005 ","
+ WHITESPACE@5005..5006 " "
+ ARG@5006..5017
+ EXPR@5006..5017
+ EXPR_BINARY@5006..5017
+ EXPR@5006..5010
+ EXPR_VAR@5006..5010
+ NAME@5006..5010
+ IDENT@5006..5010 "vars"
+ WHITESPACE@5010..5011 " "
+ PLUS@5011..5012 "+"
+ WHITESPACE@5012..5013 " "
+ EXPR@5013..5017
+ EXPR_ARRAY@5013..5017
+ L_BRACK@5013..5014 "["
+ EXPR@5014..5016
+ EXPR_STRING@5014..5016
+ STRING_SINGLE@5014..5016 "''"
+ R_BRACK@5016..5017 "]"
+ R_PAREN@5017..5018 ")"
+ COMMA@5018..5019 ","
+ WHITESPACE@5019..5023 "\n\n "
+ MEMBER_FIELD_METHOD@5023..5690
+ FIELD_NAME_FIXED@5023..5040
+ NAME@5023..5040
+ IDENT@5023..5040 "manifestXmlJsonml"
+ PARAMS_DESC@5040..5047
+ L_PAREN@5040..5041 "("
+ PARAM@5041..5046
+ DESTRUCT_FULL@5041..5046
+ NAME@5041..5046
+ IDENT@5041..5046 "value"
+ R_PAREN@5046..5047 ")"
+ COLONCOLON@5047..5049 "::"
+ WHITESPACE@5049..5054 "\n "
+ EXPR@5054..5690
+ EXPR_IF_THEN_ELSE@5054..5690
+ IF_KW@5054..5056 "if"
+ WHITESPACE@5056..5057 " "
+ EXPR@5057..5076
+ EXPR_UNARY@5057..5076
+ NOT@5057..5058 "!"
+ EXPR_VAR@5058..5061
+ NAME@5058..5061
+ IDENT@5058..5061 "std"
+ SUFFIX_INDEX@5061..5069
+ DOT@5061..5062 "."
+ NAME@5062..5069
+ IDENT@5062..5069 "isArray"
+ SUFFIX_APPLY@5069..5076
+ ARGS_DESC@5069..5076
+ L_PAREN@5069..5070 "("
+ ARG@5070..5075
+ EXPR@5070..5075
+ EXPR_VAR@5070..5075
+ NAME@5070..5075
+ IDENT@5070..5075 "value"
+ R_PAREN@5075..5076 ")"
+ WHITESPACE@5076..5077 " "
+ THEN_KW@5077..5081 "then"
+ WHITESPACE@5081..5088 "\n "
+ TRUE_EXPR@5088..5156
+ EXPR@5088..5156
+ EXPR_ERROR@5088..5156
+ ERROR_KW@5088..5093 "error"
+ WHITESPACE@5093..5094 " "
+ EXPR@5094..5156
+ EXPR_BINARY@5094..5156
+ EXPR@5094..5138
+ EXPR_STRING@5094..5138
+ STRING_SINGLE@5094..5138 "'Expected a JSONML va ..."
+ WHITESPACE@5138..5139 " "
+ MODULO@5139..5140 "%"
+ WHITESPACE@5140..5141 " "
+ EXPR@5141..5156
+ EXPR_VAR@5141..5144
+ NAME@5141..5144
+ IDENT@5141..5144 "std"
+ SUFFIX_INDEX@5144..5149
+ DOT@5144..5145 "."
+ NAME@5145..5149
+ IDENT@5145..5149 "type"
+ SUFFIX_APPLY@5149..5156
+ ARGS_DESC@5149..5156
+ L_PAREN@5149..5150 "("
+ ARG@5150..5155
+ EXPR@5150..5155
+ EXPR_VAR@5150..5155
+ NAME@5150..5155
+ IDENT@5150..5155 "value"
+ R_PAREN@5155..5156 ")"
+ WHITESPACE@5156..5161 "\n "
+ ELSE_KW@5161..5165 "else"
+ WHITESPACE@5165..5172 "\n "
+ FALSE_EXPR@5172..5690
+ EXPR@5172..5690
+ STMT_LOCAL@5172..5672
+ LOCAL_KW@5172..5177 "local"
+ WHITESPACE@5177..5178 " "
+ BIND_FUNCTION@5178..5671
+ NAME@5178..5181
+ IDENT@5178..5181 "aux"
+ PARAMS_DESC@5181..5184
+ L_PAREN@5181..5182 "("
+ PARAM@5182..5183
+ DESTRUCT_FULL@5182..5183
+ NAME@5182..5183
+ IDENT@5182..5183 "v"
+ R_PAREN@5183..5184 ")"
+ WHITESPACE@5184..5185 " "
+ ASSIGN@5185..5186 "="
+ WHITESPACE@5186..5195 "\n "
+ EXPR@5195..5671
+ EXPR_IF_THEN_ELSE@5195..5671
+ IF_KW@5195..5197 "if"
+ WHITESPACE@5197..5198 " "
+ EXPR@5198..5213
+ EXPR_VAR@5198..5201
+ NAME@5198..5201
+ IDENT@5198..5201 "std"
+ SUFFIX_INDEX@5201..5210
+ DOT@5201..5202 "."
+ NAME@5202..5210
+ IDENT@5202..5210 "isString"
+ SUFFIX_APPLY@5210..5213
+ ARGS_DESC@5210..5213
+ L_PAREN@5210..5211 "("
+ ARG@5211..5212
+ EXPR@5211..5212
+ EXPR_VAR@5211..5212
+ NAME@5211..5212
+ IDENT@5211..5212 "v"
+ R_PAREN@5212..5213 ")"
+ WHITESPACE@5213..5214 " "
+ THEN_KW@5214..5218 "then"
+ WHITESPACE@5218..5229 "\n "
+ TRUE_EXPR@5229..5230
+ EXPR@5229..5230
+ EXPR_VAR@5229..5230
+ NAME@5229..5230
+ IDENT@5229..5230 "v"
+ WHITESPACE@5230..5239 "\n "
+ ELSE_KW@5239..5243 "else"
+ WHITESPACE@5243..5254 "\n "
+ FALSE_EXPR@5254..5671
+ EXPR@5254..5671
+ STMT_LOCAL@5254..5271
+ LOCAL_KW@5254..5259 "local"
+ WHITESPACE@5259..5260 " "
+ BIND_DESTRUCT@5260..5270
+ DESTRUCT_FULL@5260..5263
+ NAME@5260..5263
+ IDENT@5260..5263 "tag"
+ WHITESPACE@5263..5264 " "
+ ASSIGN@5264..5265 "="
+ WHITESPACE@5265..5266 " "
+ EXPR@5266..5270
+ EXPR_VAR@5266..5267
+ NAME@5266..5267
+ IDENT@5266..5267 "v"
+ SUFFIX_INDEX_EXPR@5267..5270
+ L_BRACK@5267..5268 "["
+ EXPR@5268..5269
+ EXPR_NUMBER@5268..5269
+ FLOAT@5268..5269 "0"
+ R_BRACK@5269..5270 "]"
+ SEMI@5270..5271 ";"
+ WHITESPACE@5271..5282 "\n "
+ STMT_LOCAL@5282..5340
+ LOCAL_KW@5282..5287 "local"
+ WHITESPACE@5287..5288 " "
+ BIND_DESTRUCT@5288..5339
+ DESTRUCT_FULL@5288..5297
+ NAME@5288..5297
+ IDENT@5288..5297 "has_attrs"
+ WHITESPACE@5297..5298 " "
+ ASSIGN@5298..5299 "="
+ WHITESPACE@5299..5300 " "
+ EXPR@5300..5339
+ EXPR_BINARY@5300..5339
+ EXPR@5300..5317
+ EXPR_BINARY@5300..5317
+ EXPR@5300..5303
+ EXPR_VAR@5300..5303
+ NAME@5300..5303
+ IDENT@5300..5303 "std"
+ SUFFIX_INDEX@5303..5310
+ DOT@5303..5304 "."
+ NAME@5304..5310
+ IDENT@5304..5310 "length"
+ SUFFIX_APPLY@5310..5313
+ ARGS_DESC@5310..5313
+ L_PAREN@5310..5311 "("
+ ARG@5311..5312
+ EXPR@5311..5312
+ EXPR_VAR@5311..5312
+ NAME@5311..5312
+ IDENT@5311..5312 "v"
+ R_PAREN@5312..5313 ")"
+ WHITESPACE@5313..5314 " "
+ GT@5314..5315 ">"
+ WHITESPACE@5315..5316 " "
+ EXPR@5316..5317
+ EXPR_NUMBER@5316..5317
+ FLOAT@5316..5317 "1"
+ WHITESPACE@5317..5318 " "
+ AND@5318..5320 "&&"
+ WHITESPACE@5320..5321 " "
+ EXPR@5321..5339
+ EXPR_VAR@5321..5324
+ NAME@5321..5324
+ IDENT@5321..5324 "std"
+ SUFFIX_INDEX@5324..5333
+ DOT@5324..5325 "."
+ NAME@5325..5333
+ IDENT@5325..5333 "isObject"
+ SUFFIX_APPLY@5333..5339
+ ARGS_DESC@5333..5339
+ L_PAREN@5333..5334 "("
+ ARG@5334..5338
+ EXPR@5334..5338
+ EXPR_VAR@5334..5335
+ NAME@5334..5335
+ IDENT@5334..5335 "v"
+ SUFFIX_INDEX_EXPR@5335..5338
+ L_BRACK@5335..5336 "["
+ EXPR@5336..5337
+ EXPR_NUMBER@5336..5337
+ FLOAT@5336..5337 "1"
+ R_BRACK@5337..5338 "]"
+ R_PAREN@5338..5339 ")"
+ SEMI@5339..5340 ";"
+ WHITESPACE@5340..5351 "\n "
+ STMT_LOCAL@5351..5396
+ LOCAL_KW@5351..5356 "local"
+ WHITESPACE@5356..5357 " "
+ BIND_DESTRUCT@5357..5395
+ DESTRUCT_FULL@5357..5362
+ NAME@5357..5362
+ IDENT@5357..5362 "attrs"
+ WHITESPACE@5362..5363 " "
+ ASSIGN@5363..5364 "="
+ WHITESPACE@5364..5365 " "
+ EXPR@5365..5395
+ EXPR_IF_THEN_ELSE@5365..5395
+ IF_KW@5365..5367 "if"
+ WHITESPACE@5367..5368 " "
+ EXPR@5368..5377
+ EXPR_VAR@5368..5377
+ NAME@5368..5377
+ IDENT@5368..5377 "has_attrs"
+ WHITESPACE@5377..5378 " "
+ THEN_KW@5378..5382 "then"
+ WHITESPACE@5382..5383 " "
+ TRUE_EXPR@5383..5387
+ EXPR@5383..5387
+ EXPR_VAR@5383..5384
+ NAME@5383..5384
+ IDENT@5383..5384 "v"
+ SUFFIX_INDEX_EXPR@5384..5387
+ L_BRACK@5384..5385 "["
+ EXPR@5385..5386
+ EXPR_NUMBER@5385..5386
+ FLOAT@5385..5386 "1"
+ R_BRACK@5386..5387 "]"
+ WHITESPACE@5387..5388 " "
+ ELSE_KW@5388..5392 "else"
+ WHITESPACE@5392..5393 " "
+ FALSE_EXPR@5393..5395
+ EXPR@5393..5395
+ EXPR_OBJECT@5393..5395
+ OBJ_BODY_MEMBER_LIST@5393..5395
+ L_BRACE@5393..5394 "{"
+ R_BRACE@5394..5395 "}"
+ SEMI@5395..5396 ";"
+ WHITESPACE@5396..5407 "\n "
+ STMT_LOCAL@5407..5459
+ LOCAL_KW@5407..5412 "local"
+ WHITESPACE@5412..5413 " "
+ BIND_DESTRUCT@5413..5458
+ DESTRUCT_FULL@5413..5421
+ NAME@5413..5421
+ IDENT@5413..5421 "children"
+ WHITESPACE@5421..5422 " "
+ ASSIGN@5422..5423 "="
+ WHITESPACE@5423..5424 " "
+ EXPR@5424..5458
+ EXPR_IF_THEN_ELSE@5424..5458
+ IF_KW@5424..5426 "if"
+ WHITESPACE@5426..5427 " "
+ EXPR@5427..5436
+ EXPR_VAR@5427..5436
+ NAME@5427..5436
+ IDENT@5427..5436 "has_attrs"
+ WHITESPACE@5436..5437 " "
+ THEN_KW@5437..5441 "then"
+ WHITESPACE@5441..5442 " "
+ TRUE_EXPR@5442..5447
+ EXPR@5442..5447
+ EXPR_VAR@5442..5443
+ NAME@5442..5443
+ IDENT@5442..5443 "v"
+ SUFFIX_SLICE@5443..5447
+ SLICE_DESC@5443..5447
+ L_BRACK@5443..5444 "["
+ EXPR@5444..5445
+ EXPR_NUMBER@5444..5445
+ FLOAT@5444..5445 "2"
+ COLON@5445..5446 ":"
+ R_BRACK@5446..5447 "]"
+ WHITESPACE@5447..5448 " "
+ ELSE_KW@5448..5452 "else"
+ WHITESPACE@5452..5453 " "
+ FALSE_EXPR@5453..5458
+ EXPR@5453..5458
+ EXPR_VAR@5453..5454
+ NAME@5453..5454
+ IDENT@5453..5454 "v"
+ SUFFIX_SLICE@5454..5458
+ SLICE_DESC@5454..5458
+ L_BRACK@5454..5455 "["
+ EXPR@5455..5456
+ EXPR_NUMBER@5455..5456
+ FLOAT@5455..5456 "1"
+ COLON@5456..5457 ":"
+ R_BRACK@5457..5458 "]"
+ SEMI@5458..5459 ";"
+ WHITESPACE@5459..5470 "\n "
+ STMT_LOCAL@5470..5576
+ LOCAL_KW@5470..5475 "local"
+ WHITESPACE@5475..5476 " "
+ BIND_DESTRUCT@5476..5575
+ DESTRUCT_FULL@5476..5485
+ NAME@5476..5485
+ IDENT@5476..5485 "attrs_str"
+ WHITESPACE@5485..5486 " "
+ ASSIGN@5486..5487 "="
+ WHITESPACE@5487..5500 "\n "
+ EXPR@5500..5575
+ EXPR_VAR@5500..5503
+ NAME@5500..5503
+ IDENT@5500..5503 "std"
+ SUFFIX_INDEX@5503..5508
+ DOT@5503..5504 "."
+ NAME@5504..5508
+ IDENT@5504..5508 "join"
+ SUFFIX_APPLY@5508..5575
+ ARGS_DESC@5508..5575
+ L_PAREN@5508..5509 "("
+ ARG@5509..5511
+ EXPR@5509..5511
+ EXPR_STRING@5509..5511
+ STRING_SINGLE@5509..5511 "''"
+ COMMA@5511..5512 ","
+ WHITESPACE@5512..5513 " "
+ ARG@5513..5574
+ EXPR@5513..5574
+ EXPR_ARRAY_COMP@5513..5574
+ L_BRACK@5513..5514 "["
+ EXPR@5514..5540
+ EXPR_BINARY@5514..5540
+ EXPR@5514..5524
+ EXPR_STRING@5514..5524
+ STRING_SINGLE@5514..5524 "' %s=\"%s\"'"
+ WHITESPACE@5524..5525 " "
+ MODULO@5525..5526 "%"
+ WHITESPACE@5526..5527 " "
+ EXPR@5527..5540
+ EXPR_ARRAY@5527..5540
+ L_BRACK@5527..5528 "["
+ EXPR@5528..5529
+ EXPR_VAR@5528..5529
+ NAME@5528..5529
+ IDENT@5528..5529 "k"
+ COMMA@5529..5530 ","
+ WHITESPACE@5530..5531 " "
+ EXPR@5531..5539
+ EXPR_VAR@5531..5536
+ NAME@5531..5536
+ IDENT@5531..5536 "attrs"
+ SUFFIX_INDEX_EXPR@5536..5539
+ L_BRACK@5536..5537 "["
+ EXPR@5537..5538
+ EXPR_VAR@5537..5538
+ NAME@5537..5538
+ IDENT@5537..5538 "k"
+ R_BRACK@5538..5539 "]"
+ R_BRACK@5539..5540 "]"
+ WHITESPACE@5540..5541 " "
+ FOR_SPEC@5541..5573
+ FOR_KW@5541..5544 "for"
+ WHITESPACE@5544..5545 " "
+ DESTRUCT_FULL@5545..5546
+ NAME@5545..5546
+ IDENT@5545..5546 "k"
+ WHITESPACE@5546..5547 " "
+ IN_KW@5547..5549 "in"
+ WHITESPACE@5549..5550 " "
+ EXPR@5550..5573
+ EXPR_VAR@5550..5553
+ NAME@5550..5553
+ IDENT@5550..5553 "std"
+ SUFFIX_INDEX@5553..5566
+ DOT@5553..5554 "."
+ NAME@5554..5566
+ IDENT@5554..5566 "objectFields"
+ SUFFIX_APPLY@5566..5573
+ ARGS_DESC@5566..5573
+ L_PAREN@5566..5567 "("
+ ARG@5567..5572
+ EXPR@5567..5572
+ EXPR_VAR@5567..5572
+ NAME@5567..5572
+ IDENT@5567..5572 "attrs"
+ R_PAREN@5572..5573 ")"
+ R_BRACK@5573..5574 "]"
+ R_PAREN@5574..5575 ")"
+ SEMI@5575..5576 ";"
+ WHITESPACE@5576..5587 "\n "
+ EXPR_VAR@5587..5590
+ NAME@5587..5590
+ IDENT@5587..5590 "std"
+ SUFFIX_INDEX@5590..5599
+ DOT@5590..5591 "."
+ NAME@5591..5599
+ IDENT@5591..5599 "deepJoin"
+ SUFFIX_APPLY@5599..5671
+ ARGS_DESC@5599..5671
+ L_PAREN@5599..5600 "("
+ ARG@5600..5670
+ EXPR@5600..5670
+ EXPR_ARRAY@5600..5670
+ L_BRACK@5600..5601 "["
+ EXPR@5601..5604
+ EXPR_STRING@5601..5604
+ STRING_SINGLE@5601..5604 "'<'"
+ COMMA@5604..5605 ","
+ WHITESPACE@5605..5606 " "
+ EXPR@5606..5609
+ EXPR_VAR@5606..5609
+ NAME@5606..5609
+ IDENT@5606..5609 "tag"
+ COMMA@5609..5610 ","
+ WHITESPACE@5610..5611 " "
+ EXPR@5611..5620
+ EXPR_VAR@5611..5620
+ NAME@5611..5620
+ IDENT@5611..5620 "attrs_str"
+ COMMA@5620..5621 ","
+ WHITESPACE@5621..5622 " "
+ EXPR@5622..5625
+ EXPR_STRING@5622..5625
+ STRING_SINGLE@5622..5625 "'>'"
+ COMMA@5625..5626 ","
+ WHITESPACE@5626..5627 " "
+ EXPR@5627..5653
+ EXPR_ARRAY_COMP@5627..5653
+ L_BRACK@5627..5628 "["
+ EXPR@5628..5634
+ EXPR_VAR@5628..5631
+ NAME@5628..5631
+ IDENT@5628..5631 "aux"
+ SUFFIX_APPLY@5631..5634
+ ARGS_DESC@5631..5634
+ L_PAREN@5631..5632 "("
+ ARG@5632..5633
+ EXPR@5632..5633
+ EXPR_VAR@5632..5633
+ NAME@5632..5633
+ IDENT@5632..5633 "x"
+ R_PAREN@5633..5634 ")"
+ WHITESPACE@5634..5635 " "
+ FOR_SPEC@5635..5652
+ FOR_KW@5635..5638 "for"
+ WHITESPACE@5638..5639 " "
+ DESTRUCT_FULL@5639..5640
+ NAME@5639..5640
+ IDENT@5639..5640 "x"
+ WHITESPACE@5640..5641 " "
+ IN_KW@5641..5643 "in"
+ WHITESPACE@5643..5644 " "
+ EXPR@5644..5652
+ EXPR_VAR@5644..5652
+ NAME@5644..5652
+ IDENT@5644..5652 "children"
+ R_BRACK@5652..5653 "]"
+ COMMA@5653..5654 ","
+ WHITESPACE@5654..5655 " "
+ EXPR@5655..5659
+ EXPR_STRING@5655..5659
+ STRING_SINGLE@5655..5659 "'</'"
+ COMMA@5659..5660 ","
+ WHITESPACE@5660..5661 " "
+ EXPR@5661..5664
+ EXPR_VAR@5661..5664
+ NAME@5661..5664
+ IDENT@5661..5664 "tag"
+ COMMA@5664..5665 ","
+ WHITESPACE@5665..5666 " "
+ EXPR@5666..5669
+ EXPR_STRING@5666..5669
+ STRING_SINGLE@5666..5669 "'>'"
+ R_BRACK@5669..5670 "]"
+ R_PAREN@5670..5671 ")"
+ SEMI@5671..5672 ";"
+ WHITESPACE@5672..5680 "\n\n "
+ EXPR_VAR@5680..5683
+ NAME@5680..5683
+ IDENT@5680..5683 "aux"
+ SUFFIX_APPLY@5683..5690
+ ARGS_DESC@5683..5690
+ L_PAREN@5683..5684 "("
+ ARG@5684..5689
+ EXPR@5684..5689
+ EXPR_VAR@5684..5689
+ NAME@5684..5689
+ IDENT@5684..5689 "value"
+ R_PAREN@5689..5690 ")"
+ COMMA@5690..5691 ","
+ WHITESPACE@5691..5695 "\n\n "
+ MEMBER_FIELD_METHOD@5695..6483
+ FIELD_NAME_FIXED@5695..5705
+ NAME@5695..5705
+ IDENT@5695..5705 "mergePatch"
+ PARAMS_DESC@5705..5720
+ L_PAREN@5705..5706 "("
+ PARAM@5706..5712
+ DESTRUCT_FULL@5706..5712
+ NAME@5706..5712
+ IDENT@5706..5712 "target"
+ COMMA@5712..5713 ","
+ WHITESPACE@5713..5714 " "
+ PARAM@5714..5719
+ DESTRUCT_FULL@5714..5719
+ NAME@5714..5719
+ IDENT@5714..5719 "patch"
+ R_PAREN@5719..5720 ")"
+ COLONCOLON@5720..5722 "::"
+ WHITESPACE@5722..5727 "\n "
+ EXPR@5727..6483
+ EXPR_IF_THEN_ELSE@5727..6483
+ IF_KW@5727..5729 "if"
+ WHITESPACE@5729..5730 " "
+ EXPR@5730..5749
+ EXPR_VAR@5730..5733
+ NAME@5730..5733
+ IDENT@5730..5733 "std"
+ SUFFIX_INDEX@5733..5742
+ DOT@5733..5734 "."
+ NAME@5734..5742
+ IDENT@5734..5742 "isObject"
+ SUFFIX_APPLY@5742..5749
+ ARGS_DESC@5742..5749
+ L_PAREN@5742..5743 "("
+ ARG@5743..5748
+ EXPR@5743..5748
+ EXPR_VAR@5743..5748
+ NAME@5743..5748
+ IDENT@5743..5748 "patch"
+ R_PAREN@5748..5749 ")"
+ WHITESPACE@5749..5750 " "
+ THEN_KW@5750..5754 "then"
+ WHITESPACE@5754..5761 "\n "
+ TRUE_EXPR@5761..6462
+ EXPR@5761..6462
+ STMT_LOCAL@5761..5835
+ LOCAL_KW@5761..5766 "local"
+ WHITESPACE@5766..5767 " "
+ BIND_DESTRUCT@5767..5834
+ DESTRUCT_FULL@5767..5780
+ NAME@5767..5780
+ IDENT@5767..5780 "target_object"
+ WHITESPACE@5780..5781 " "
+ ASSIGN@5781..5782 "="
+ WHITESPACE@5782..5791 "\n "
+ EXPR@5791..5834
+ EXPR_IF_THEN_ELSE@5791..5834
+ IF_KW@5791..5793 "if"
+ WHITESPACE@5793..5794 " "
+ EXPR@5794..5814
+ EXPR_VAR@5794..5797
+ NAME@5794..5797
+ IDENT@5794..5797 "std"
+ SUFFIX_INDEX@5797..5806
+ DOT@5797..5798 "."
+ NAME@5798..5806
+ IDENT@5798..5806 "isObject"
+ SUFFIX_APPLY@5806..5814
+ ARGS_DESC@5806..5814
+ L_PAREN@5806..5807 "("
+ ARG@5807..5813
+ EXPR@5807..5813
+ EXPR_VAR@5807..5813
+ NAME@5807..5813
+ IDENT@5807..5813 "target"
+ R_PAREN@5813..5814 ")"
+ WHITESPACE@5814..5815 " "
+ THEN_KW@5815..5819 "then"
+ WHITESPACE@5819..5820 " "
+ TRUE_EXPR@5820..5826
+ EXPR@5820..5826
+ EXPR_VAR@5820..5826
+ NAME@5820..5826
+ IDENT@5820..5826 "target"
+ WHITESPACE@5826..5827 " "
+ ELSE_KW@5827..5831 "else"
+ WHITESPACE@5831..5832 " "
+ FALSE_EXPR@5832..5834
+ EXPR@5832..5834
+ EXPR_OBJECT@5832..5834
+ OBJ_BODY_MEMBER_LIST@5832..5834
+ L_BRACE@5832..5833 "{"
+ R_BRACE@5833..5834 "}"
+ SEMI@5834..5835 ";"
+ WHITESPACE@5835..5843 "\n\n "
+ STMT_LOCAL@5843..5949
+ LOCAL_KW@5843..5848 "local"
+ WHITESPACE@5848..5849 " "
+ BIND_DESTRUCT@5849..5948
+ DESTRUCT_FULL@5849..5862
+ NAME@5849..5862
+ IDENT@5849..5862 "target_fields"
+ WHITESPACE@5862..5863 " "
+ ASSIGN@5863..5864 "="
+ WHITESPACE@5864..5873 "\n "
+ EXPR@5873..5948
+ EXPR_IF_THEN_ELSE@5873..5948
+ IF_KW@5873..5875 "if"
+ WHITESPACE@5875..5876 " "
+ EXPR@5876..5903
+ EXPR_VAR@5876..5879
+ NAME@5876..5879
+ IDENT@5876..5879 "std"
+ SUFFIX_INDEX@5879..5888
+ DOT@5879..5880 "."
+ NAME@5880..5888
+ IDENT@5880..5888 "isObject"
+ SUFFIX_APPLY@5888..5903
+ ARGS_DESC@5888..5903
+ L_PAREN@5888..5889 "("
+ ARG@5889..5902
+ EXPR@5889..5902
+ EXPR_VAR@5889..5902
+ NAME@5889..5902
+ IDENT@5889..5902 "target_object"
+ R_PAREN@5902..5903 ")"
+ WHITESPACE@5903..5904 " "
+ THEN_KW@5904..5908 "then"
+ WHITESPACE@5908..5909 " "
+ TRUE_EXPR@5909..5940
+ EXPR@5909..5940
+ EXPR_VAR@5909..5912
+ NAME@5909..5912
+ IDENT@5909..5912 "std"
+ SUFFIX_INDEX@5912..5925
+ DOT@5912..5913 "."
+ NAME@5913..5925
+ IDENT@5913..5925 "objectFields"
+ SUFFIX_APPLY@5925..5940
+ ARGS_DESC@5925..5940
+ L_PAREN@5925..5926 "("
+ ARG@5926..5939
+ EXPR@5926..5939
+ EXPR_VAR@5926..5939
+ NAME@5926..5939
+ IDENT@5926..5939 "target_object"
+ R_PAREN@5939..5940 ")"
+ WHITESPACE@5940..5941 " "
+ ELSE_KW@5941..5945 "else"
+ WHITESPACE@5945..5946 " "
+ FALSE_EXPR@5946..5948
+ EXPR@5946..5948
+ EXPR_ARRAY@5946..5948
+ L_BRACK@5946..5947 "["
+ R_BRACK@5947..5948 "]"
+ SEMI@5948..5949 ";"
+ WHITESPACE@5949..5957 "\n\n "
+ STMT_LOCAL@5957..6034
+ LOCAL_KW@5957..5962 "local"
+ WHITESPACE@5962..5963 " "
+ BIND_DESTRUCT@5963..6033
+ DESTRUCT_FULL@5963..5974
+ NAME@5963..5974
+ IDENT@5963..5974 "null_fields"
+ WHITESPACE@5974..5975 " "
+ ASSIGN@5975..5976 "="
+ WHITESPACE@5976..5977 " "
+ EXPR@5977..6033
+ EXPR_ARRAY_COMP@5977..6033
+ L_BRACK@5977..5978 "["
+ EXPR@5978..5979
+ EXPR_VAR@5978..5979
+ NAME@5978..5979
+ IDENT@5978..5979 "k"
+ WHITESPACE@5979..5980 " "
+ FOR_SPEC@5980..6012
+ FOR_KW@5980..5983 "for"
+ WHITESPACE@5983..5984 " "
+ DESTRUCT_FULL@5984..5985
+ NAME@5984..5985
+ IDENT@5984..5985 "k"
+ WHITESPACE@5985..5986 " "
+ IN_KW@5986..5988 "in"
+ WHITESPACE@5988..5989 " "
+ EXPR@5989..6012
+ EXPR_VAR@5989..5992
+ NAME@5989..5992
+ IDENT@5989..5992 "std"
+ SUFFIX_INDEX@5992..6005
+ DOT@5992..5993 "."
+ NAME@5993..6005
+ IDENT@5993..6005 "objectFields"
+ SUFFIX_APPLY@6005..6012
+ ARGS_DESC@6005..6012
+ L_PAREN@6005..6006 "("
+ ARG@6006..6011
+ EXPR@6006..6011
+ EXPR_VAR@6006..6011
+ NAME@6006..6011
+ IDENT@6006..6011 "patch"
+ R_PAREN@6011..6012 ")"
+ WHITESPACE@6012..6013 " "
+ IF_SPEC@6013..6032
+ IF_KW@6013..6015 "if"
+ WHITESPACE@6015..6016 " "
+ EXPR@6016..6032
+ EXPR_BINARY@6016..6032
+ EXPR@6016..6021
+ EXPR_VAR@6016..6021
+ NAME@6016..6021
+ IDENT@6016..6021 "patch"
+ SUFFIX_INDEX_EXPR@6021..6024
+ L_BRACK@6021..6022 "["
+ EXPR@6022..6023
+ EXPR_VAR@6022..6023
+ NAME@6022..6023
+ IDENT@6022..6023 "k"
+ R_BRACK@6023..6024 "]"
+ WHITESPACE@6024..6025 " "
+ EQ@6025..6027 "=="
+ WHITESPACE@6027..6028 " "
+ EXPR@6028..6032
+ EXPR_LITERAL@6028..6032
+ NULL_KW@6028..6032 "null"
+ R_BRACK@6032..6033 "]"
+ SEMI@6033..6034 ";"
+ WHITESPACE@6034..6041 "\n "
+ STMT_LOCAL@6041..6114
+ LOCAL_KW@6041..6046 "local"
+ WHITESPACE@6046..6047 " "
+ BIND_DESTRUCT@6047..6113
+ DESTRUCT_FULL@6047..6058
+ NAME@6047..6058
+ IDENT@6047..6058 "both_fields"
+ WHITESPACE@6058..6059 " "
+ ASSIGN@6059..6060 "="
+ WHITESPACE@6060..6061 " "
+ EXPR@6061..6113
+ EXPR_VAR@6061..6064
+ NAME@6061..6064
+ IDENT@6061..6064 "std"
+ SUFFIX_INDEX@6064..6073
+ DOT@6064..6065 "."
+ NAME@6065..6073
+ IDENT@6065..6073 "setUnion"
+ SUFFIX_APPLY@6073..6113
+ ARGS_DESC@6073..6113
+ L_PAREN@6073..6074 "("
+ ARG@6074..6087
+ EXPR@6074..6087
+ EXPR_VAR@6074..6087
+ NAME@6074..6087
+ IDENT@6074..6087 "target_fields"
+ COMMA@6087..6088 ","
+ WHITESPACE@6088..6089 " "
+ ARG@6089..6112
+ EXPR@6089..6112
+ EXPR_VAR@6089..6092
+ NAME@6089..6092
+ IDENT@6089..6092 "std"
+ SUFFIX_INDEX@6092..6105
+ DOT@6092..6093 "."
+ NAME@6093..6105
+ IDENT@6093..6105 "objectFields"
+ SUFFIX_APPLY@6105..6112
+ ARGS_DESC@6105..6112
+ L_PAREN@6105..6106 "("
+ ARG@6106..6111
+ EXPR@6106..6111
+ EXPR_VAR@6106..6111
+ NAME@6106..6111
+ IDENT@6106..6111 "patch"
+ R_PAREN@6111..6112 ")"
+ R_PAREN@6112..6113 ")"
+ SEMI@6113..6114 ";"
+ WHITESPACE@6114..6122 "\n\n "
+ EXPR_OBJECT@6122..6462
+ OBJ_BODY_COMP@6122..6462
+ L_BRACE@6122..6123 "{"
+ WHITESPACE@6123..6132 "\n "
+ MEMBER_FIELD_NORMAL@6132..6399
+ FIELD_NAME_DYNAMIC@6132..6135
+ L_BRACK@6132..6133 "["
+ EXPR@6133..6134
+ EXPR_VAR@6133..6134
+ NAME@6133..6134
+ IDENT@6133..6134 "k"
+ R_BRACK@6134..6135 "]"
+ COLON@6135..6136 ":"
+ WHITESPACE@6136..6147 "\n "
+ EXPR@6147..6399
+ EXPR_IF_THEN_ELSE@6147..6399
+ IF_KW@6147..6149 "if"
+ WHITESPACE@6149..6150 " "
+ EXPR@6150..6174
+ EXPR_UNARY@6150..6174
+ NOT@6150..6151 "!"
+ EXPR_VAR@6151..6154
+ NAME@6151..6154
+ IDENT@6151..6154 "std"
+ SUFFIX_INDEX@6154..6164
+ DOT@6154..6155 "."
+ NAME@6155..6164
+ IDENT@6155..6164 "objectHas"
+ SUFFIX_APPLY@6164..6174
+ ARGS_DESC@6164..6174
+ L_PAREN@6164..6165 "("
+ ARG@6165..6170
+ EXPR@6165..6170
+ EXPR_VAR@6165..6170
+ NAME@6165..6170
+ IDENT@6165..6170 "patch"
+ COMMA@6170..6171 ","
+ WHITESPACE@6171..6172 " "
+ ARG@6172..6173
+ EXPR@6172..6173
+ EXPR_VAR@6172..6173
+ NAME@6172..6173
+ IDENT@6172..6173 "k"
+ R_PAREN@6173..6174 ")"
+ WHITESPACE@6174..6175 " "
+ THEN_KW@6175..6179 "then"
+ WHITESPACE@6179..6192 "\n "
+ TRUE_EXPR@6192..6208
+ EXPR@6192..6208
+ EXPR_VAR@6192..6205
+ NAME@6192..6205
+ IDENT@6192..6205 "target_object"
+ SUFFIX_INDEX_EXPR@6205..6208
+ L_BRACK@6205..6206 "["
+ EXPR@6206..6207
+ EXPR_VAR@6206..6207
+ NAME@6206..6207
+ IDENT@6206..6207 "k"
+ R_BRACK@6207..6208 "]"
+ WHITESPACE@6208..6219 "\n "
+ ELSE_KW@6219..6223 "else"
+ WHITESPACE@6223..6224 " "
+ FALSE_EXPR@6224..6399
+ EXPR@6224..6399
+ EXPR_IF_THEN_ELSE@6224..6399
+ IF_KW@6224..6226 "if"
+ WHITESPACE@6226..6227 " "
+ EXPR@6227..6259
+ EXPR_UNARY@6227..6259
+ NOT@6227..6228 "!"
+ EXPR_VAR@6228..6231
+ NAME@6228..6231
+ IDENT@6228..6231 "std"
+ SUFFIX_INDEX@6231..6241
+ DOT@6231..6232 "."
+ NAME@6232..6241
+ IDENT@6232..6241 "objectHas"
+ SUFFIX_APPLY@6241..6259
+ ARGS_DESC@6241..6259
+ L_PAREN@6241..6242 "("
+ ARG@6242..6255
+ EXPR@6242..6255
+ EXPR_VAR@6242..6255
+ NAME@6242..6255
+ IDENT@6242..6255 "target_object"
+ COMMA@6255..6256 ","
+ WHITESPACE@6256..6257 " "
+ ARG@6257..6258
+ EXPR@6257..6258
+ EXPR_VAR@6257..6258
+ NAME@6257..6258
+ IDENT@6257..6258 "k"
+ R_PAREN@6258..6259 ")"
+ WHITESPACE@6259..6260 " "
+ THEN_KW@6260..6264 "then"
+ WHITESPACE@6264..6277 "\n "
+ TRUE_EXPR@6277..6318
+ EXPR@6277..6318
+ EXPR_VAR@6277..6280
+ NAME@6277..6280
+ IDENT@6277..6280 "std"
+ SUFFIX_INDEX@6280..6291
+ DOT@6280..6281 "."
+ NAME@6281..6291
+ IDENT@6281..6291 "mergePatch"
+ SUFFIX_APPLY@6291..6318
+ ARGS_DESC@6291..6318
+ L_PAREN@6291..6292 "("
+ ARG@6292..6296
+ EXPR@6292..6296
+ EXPR_LITERAL@6292..6296
+ NULL_KW@6292..6296 "null"
+ COMMA@6296..6297 ","
+ WHITESPACE@6297..6298 " "
+ ARG@6298..6306
+ EXPR@6298..6306
+ EXPR_VAR@6298..6303
+ NAME@6298..6303
+ IDENT@6298..6303 "patch"
+ SUFFIX_INDEX_EXPR@6303..6306
+ L_BRACK@6303..6304 "["
+ EXPR@6304..6305
+ EXPR_VAR@6304..6305
+ NAME@6304..6305
+ IDENT@6304..6305 "k"
+ R_BRACK@6305..6306 "]"
+ R_PAREN@6306..6307 ")"
+ WHITESPACE@6307..6308 " "
+ TAILSTRICT_KW@6308..6318 "tailstrict"
+ WHITESPACE@6318..6329 "\n "
+ ELSE_KW@6329..6333 "else"
+ WHITESPACE@6333..6346 "\n "
+ FALSE_EXPR@6346..6399
+ EXPR@6346..6399
+ EXPR_VAR@6346..6349
+ NAME@6346..6349
+ IDENT@6346..6349 "std"
+ SUFFIX_INDEX@6349..6360
+ DOT@6349..6350 "."
+ NAME@6350..6360
+ IDENT@6350..6360 "mergePatch"
+ SUFFIX_APPLY@6360..6399
+ ARGS_DESC@6360..6399
+ L_PAREN@6360..6361 "("
+ ARG@6361..6377
+ EXPR@6361..6377
+ EXPR_VAR@6361..6374
+ NAME@6361..6374
+ IDENT@6361..6374 "target_object"
+ SUFFIX_INDEX_EXPR@6374..6377
+ L_BRACK@6374..6375 "["
+ EXPR@6375..6376
+ EXPR_VAR@6375..6376
+ NAME@6375..6376
+ IDENT@6375..6376 "k"
+ R_BRACK@6376..6377 "]"
+ COMMA@6377..6378 ","
+ WHITESPACE@6378..6379 " "
+ ARG@6379..6387
+ EXPR@6379..6387
+ EXPR_VAR@6379..6384
+ NAME@6379..6384
+ IDENT@6379..6384 "patch"
+ SUFFIX_INDEX_EXPR@6384..6387
+ L_BRACK@6384..6385 "["
+ EXPR@6385..6386
+ EXPR_VAR@6385..6386
+ NAME@6385..6386
+ IDENT@6385..6386 "k"
+ R_BRACK@6386..6387 "]"
+ R_PAREN@6387..6388 ")"
+ WHITESPACE@6388..6389 " "
+ TAILSTRICT_KW@6389..6399 "tailstrict"
+ WHITESPACE@6399..6408 "\n "
+ FOR_SPEC@6408..6454
+ FOR_KW@6408..6411 "for"
+ WHITESPACE@6411..6412 " "
+ DESTRUCT_FULL@6412..6413
+ NAME@6412..6413
+ IDENT@6412..6413 "k"
+ WHITESPACE@6413..6414 " "
+ IN_KW@6414..6416 "in"
+ WHITESPACE@6416..6417 " "
+ EXPR@6417..6454
+ EXPR_VAR@6417..6420
+ NAME@6417..6420
+ IDENT@6417..6420 "std"
+ SUFFIX_INDEX@6420..6428
+ DOT@6420..6421 "."
+ NAME@6421..6428
+ IDENT@6421..6428 "setDiff"
+ SUFFIX_APPLY@6428..6454
+ ARGS_DESC@6428..6454
+ L_PAREN@6428..6429 "("
+ ARG@6429..6440
+ EXPR@6429..6440
+ EXPR_VAR@6429..6440
+ NAME@6429..6440
+ IDENT@6429..6440 "both_fields"
+ COMMA@6440..6441 ","
+ WHITESPACE@6441..6442 " "
+ ARG@6442..6453
+ EXPR@6442..6453
+ EXPR_VAR@6442..6453
+ NAME@6442..6453
+ IDENT@6442..6453 "null_fields"
+ R_PAREN@6453..6454 ")"
+ WHITESPACE@6454..6461 "\n "
+ R_BRACE@6461..6462 "}"
+ WHITESPACE@6462..6467 "\n "
+ ELSE_KW@6467..6471 "else"
+ WHITESPACE@6471..6478 "\n "
+ FALSE_EXPR@6478..6483
+ EXPR@6478..6483
+ EXPR_VAR@6478..6483
+ NAME@6478..6483
+ IDENT@6478..6483 "patch"
+ COMMA@6483..6484 ","
+ WHITESPACE@6484..6488 "\n\n "
+ MEMBER_FIELD_METHOD@6488..6594
+ FIELD_NAME_FIXED@6488..6491
+ NAME@6488..6491
+ IDENT@6488..6491 "get"
+ PARAMS_DESC@6491..6528
+ L_PAREN@6491..6492 "("
+ PARAM@6492..6493
+ DESTRUCT_FULL@6492..6493
+ NAME@6492..6493
+ IDENT@6492..6493 "o"
+ COMMA@6493..6494 ","
+ WHITESPACE@6494..6495 " "
+ PARAM@6495..6496
+ DESTRUCT_FULL@6495..6496
+ NAME@6495..6496
+ IDENT@6495..6496 "f"
+ COMMA@6496..6497 ","
+ WHITESPACE@6497..6498 " "
+ PARAM@6498..6510
+ DESTRUCT_FULL@6498..6505
+ NAME@6498..6505
+ IDENT@6498..6505 "default"
+ ASSIGN@6505..6506 "="
+ EXPR@6506..6510
+ EXPR_LITERAL@6506..6510
+ NULL_KW@6506..6510 "null"
+ COMMA@6510..6511 ","
+ WHITESPACE@6511..6512 " "
+ PARAM@6512..6527
+ DESTRUCT_FULL@6512..6522
+ NAME@6512..6522
+ IDENT@6512..6522 "inc_hidden"
+ ASSIGN@6522..6523 "="
+ EXPR@6523..6527
+ EXPR_LITERAL@6523..6527
+ TRUE_KW@6523..6527 "true"
+ R_PAREN@6527..6528 ")"
+ COLONCOLON@6528..6530 "::"
+ WHITESPACE@6530..6535 "\n "
+ EXPR@6535..6594
+ EXPR_IF_THEN_ELSE@6535..6594
+ IF_KW@6535..6537 "if"
+ WHITESPACE@6537..6538 " "
+ EXPR@6538..6571
+ EXPR_VAR@6538..6541
+ NAME@6538..6541
+ IDENT@6538..6541 "std"
+ SUFFIX_INDEX@6541..6553
+ DOT@6541..6542 "."
+ NAME@6542..6553
+ IDENT@6542..6553 "objectHasEx"
+ SUFFIX_APPLY@6553..6571
+ ARGS_DESC@6553..6571
+ L_PAREN@6553..6554 "("
+ ARG@6554..6555
+ EXPR@6554..6555
+ EXPR_VAR@6554..6555
+ NAME@6554..6555
+ IDENT@6554..6555 "o"
+ COMMA@6555..6556 ","
+ WHITESPACE@6556..6557 " "
+ ARG@6557..6558
+ EXPR@6557..6558
+ EXPR_VAR@6557..6558
+ NAME@6557..6558
+ IDENT@6557..6558 "f"
+ COMMA@6558..6559 ","
+ WHITESPACE@6559..6560 " "
+ ARG@6560..6570
+ EXPR@6560..6570
+ EXPR_VAR@6560..6570
+ NAME@6560..6570
+ IDENT@6560..6570 "inc_hidden"
+ R_PAREN@6570..6571 ")"
+ WHITESPACE@6571..6572 " "
+ THEN_KW@6572..6576 "then"
+ WHITESPACE@6576..6577 " "
+ TRUE_EXPR@6577..6581
+ EXPR@6577..6581
+ EXPR_VAR@6577..6578
+ NAME@6577..6578
+ IDENT@6577..6578 "o"
+ SUFFIX_INDEX_EXPR@6578..6581
+ L_BRACK@6578..6579 "["
+ EXPR@6579..6580
+ EXPR_VAR@6579..6580
+ NAME@6579..6580
+ IDENT@6579..6580 "f"
+ R_BRACK@6580..6581 "]"
+ WHITESPACE@6581..6582 " "
+ ELSE_KW@6582..6586 "else"
+ WHITESPACE@6586..6587 " "
+ FALSE_EXPR@6587..6594
+ EXPR@6587..6594
+ EXPR_VAR@6587..6594
+ NAME@6587..6594
+ IDENT@6587..6594 "default"
+ COMMA@6594..6595 ","
+ WHITESPACE@6595..6599 "\n\n "
+ MEMBER_FIELD_METHOD@6599..6733
+ FIELD_NAME_FIXED@6599..6610
+ NAME@6599..6610
+ IDENT@6599..6610 "resolvePath"
+ PARAMS_DESC@6610..6616
+ L_PAREN@6610..6611 "("
+ PARAM@6611..6612
+ DESTRUCT_FULL@6611..6612
+ NAME@6611..6612
+ IDENT@6611..6612 "f"
+ COMMA@6612..6613 ","
+ WHITESPACE@6613..6614 " "
+ PARAM@6614..6615
+ DESTRUCT_FULL@6614..6615
+ NAME@6614..6615
+ IDENT@6614..6615 "r"
+ R_PAREN@6615..6616 ")"
+ COLONCOLON@6616..6618 "::"
+ WHITESPACE@6618..6623 "\n "
+ EXPR@6623..6733
+ STMT_LOCAL@6623..6653
+ LOCAL_KW@6623..6628 "local"
+ WHITESPACE@6628..6629 " "
+ BIND_DESTRUCT@6629..6652
+ DESTRUCT_FULL@6629..6632
+ NAME@6629..6632
+ IDENT@6629..6632 "arr"
+ WHITESPACE@6632..6633 " "
+ ASSIGN@6633..6634 "="
+ WHITESPACE@6634..6635 " "
+ EXPR@6635..6652
+ EXPR_VAR@6635..6638
+ NAME@6635..6638
+ IDENT@6635..6638 "std"
+ SUFFIX_INDEX@6638..6644
+ DOT@6638..6639 "."
+ NAME@6639..6644
+ IDENT@6639..6644 "split"
+ SUFFIX_APPLY@6644..6652
+ ARGS_DESC@6644..6652
+ L_PAREN@6644..6645 "("
+ ARG@6645..6646
+ EXPR@6645..6646
+ EXPR_VAR@6645..6646
+ NAME@6645..6646
+ IDENT@6645..6646 "f"
+ COMMA@6646..6647 ","
+ WHITESPACE@6647..6648 " "
+ ARG@6648..6651
+ EXPR@6648..6651
+ EXPR_STRING@6648..6651
+ STRING_SINGLE@6648..6651 "'/'"
+ R_PAREN@6651..6652 ")"
+ SEMI@6652..6653 ";"
+ WHITESPACE@6653..6658 "\n "
+ EXPR_VAR@6658..6661
+ NAME@6658..6661
+ IDENT@6658..6661 "std"
+ SUFFIX_INDEX@6661..6666
+ DOT@6661..6662 "."
+ NAME@6662..6666
+ IDENT@6662..6666 "join"
+ SUFFIX_APPLY@6666..6733
+ ARGS_DESC@6666..6733
+ L_PAREN@6666..6667 "("
+ ARG@6667..6670
+ EXPR@6667..6670
+ EXPR_STRING@6667..6670
+ STRING_SINGLE@6667..6670 "'/'"
+ COMMA@6670..6671 ","
+ WHITESPACE@6671..6672 " "
+ ARG@6672..6732
+ EXPR@6672..6732
+ EXPR_BINARY@6672..6732
+ EXPR@6672..6675
+ EXPR_VAR@6672..6675
+ NAME@6672..6675
+ IDENT@6672..6675 "std"
+ SUFFIX_INDEX@6675..6685
+ DOT@6675..6676 "."
+ NAME@6676..6685
+ IDENT@6676..6685 "makeArray"
+ SUFFIX_APPLY@6685..6726
+ ARGS_DESC@6685..6726
+ L_PAREN@6685..6686 "("
+ ARG@6686..6705
+ EXPR@6686..6705
+ EXPR_BINARY@6686..6705
+ EXPR@6686..6689
+ EXPR_VAR@6686..6689
+ NAME@6686..6689
+ IDENT@6686..6689 "std"
+ SUFFIX_INDEX@6689..6696
+ DOT@6689..6690 "."
+ NAME@6690..6696
+ IDENT@6690..6696 "length"
+ SUFFIX_APPLY@6696..6701
+ ARGS_DESC@6696..6701
+ L_PAREN@6696..6697 "("
+ ARG@6697..6700
+ EXPR@6697..6700
+ EXPR_VAR@6697..6700
+ NAME@6697..6700
+ IDENT@6697..6700 "arr"
+ R_PAREN@6700..6701 ")"
+ WHITESPACE@6701..6702 " "
+ MINUS@6702..6703 "-"
+ WHITESPACE@6703..6704 " "
+ EXPR@6704..6705
+ EXPR_NUMBER@6704..6705
+ FLOAT@6704..6705 "1"
+ COMMA@6705..6706 ","
+ WHITESPACE@6706..6707 " "
+ ARG@6707..6725
+ EXPR@6707..6725
+ EXPR_FUNCTION@6707..6725
+ FUNCTION_KW@6707..6715 "function"
+ PARAMS_DESC@6715..6718
+ L_PAREN@6715..6716 "("
+ PARAM@6716..6717
+ DESTRUCT_FULL@6716..6717
+ NAME@6716..6717
+ IDENT@6716..6717 "i"
+ R_PAREN@6717..6718 ")"
+ WHITESPACE@6718..6719 " "
+ EXPR@6719..6725
+ EXPR_VAR@6719..6722
+ NAME@6719..6722
+ IDENT@6719..6722 "arr"
+ SUFFIX_INDEX_EXPR@6722..6725
+ L_BRACK@6722..6723 "["
+ EXPR@6723..6724
+ EXPR_VAR@6723..6724
+ NAME@6723..6724
+ IDENT@6723..6724 "i"
+ R_BRACK@6724..6725 "]"
+ R_PAREN@6725..6726 ")"
+ WHITESPACE@6726..6727 " "
+ PLUS@6727..6728 "+"
+ WHITESPACE@6728..6729 " "
+ EXPR@6729..6732
+ EXPR_ARRAY@6729..6732
+ L_BRACK@6729..6730 "["
+ EXPR@6730..6731
+ EXPR_VAR@6730..6731
+ NAME@6730..6731
+ IDENT@6730..6731 "r"
+ R_BRACK@6731..6732 "]"
+ R_PAREN@6732..6733 ")"
+ COMMA@6733..6734 ","
+ WHITESPACE@6734..6738 "\n\n "
+ MEMBER_FIELD_METHOD@6738..7191
+ FIELD_NAME_FIXED@6738..6743
+ NAME@6738..6743
+ IDENT@6738..6743 "prune"
+ PARAMS_DESC@6743..6746
+ L_PAREN@6743..6744 "("
+ PARAM@6744..6745
+ DESTRUCT_FULL@6744..6745
+ NAME@6744..6745
+ IDENT@6744..6745 "a"
+ R_PAREN@6745..6746 ")"
+ COLONCOLON@6746..6748 "::"
+ WHITESPACE@6748..6753 "\n "
+ EXPR@6753..7191
+ STMT_LOCAL@6753..6957
+ LOCAL_KW@6753..6758 "local"
+ WHITESPACE@6758..6759 " "
+ BIND_FUNCTION@6759..6956
+ NAME@6759..6768
+ IDENT@6759..6768 "isContent"
+ PARAMS_DESC@6768..6771
+ L_PAREN@6768..6769 "("
+ PARAM@6769..6770
+ DESTRUCT_FULL@6769..6770
+ NAME@6769..6770
+ IDENT@6769..6770 "b"
+ R_PAREN@6770..6771 ")"
+ WHITESPACE@6771..6772 " "
+ ASSIGN@6772..6773 "="
+ WHITESPACE@6773..6780 "\n "
+ EXPR@6780..6956
+ EXPR_IF_THEN_ELSE@6780..6956
+ IF_KW@6780..6782 "if"
+ WHITESPACE@6782..6783 " "
+ EXPR@6783..6792
+ EXPR_BINARY@6783..6792
+ EXPR@6783..6784
+ EXPR_VAR@6783..6784
+ NAME@6783..6784
+ IDENT@6783..6784 "b"
+ WHITESPACE@6784..6785 " "
+ EQ@6785..6787 "=="
+ WHITESPACE@6787..6788 " "
+ EXPR@6788..6792
+ EXPR_LITERAL@6788..6792
+ NULL_KW@6788..6792 "null"
+ WHITESPACE@6792..6793 " "
+ THEN_KW@6793..6797 "then"
+ WHITESPACE@6797..6806 "\n "
+ TRUE_EXPR@6806..6811
+ EXPR@6806..6811
+ EXPR_LITERAL@6806..6811
+ FALSE_KW@6806..6811 "false"
+ WHITESPACE@6811..6818 "\n "
+ ELSE_KW@6818..6822 "else"
+ WHITESPACE@6822..6823 " "
+ FALSE_EXPR@6823..6956
+ EXPR@6823..6956
+ EXPR_IF_THEN_ELSE@6823..6956
+ IF_KW@6823..6825 "if"
+ WHITESPACE@6825..6826 " "
+ EXPR@6826..6840
+ EXPR_VAR@6826..6829
+ NAME@6826..6829
+ IDENT@6826..6829 "std"
+ SUFFIX_INDEX@6829..6837
+ DOT@6829..6830 "."
+ NAME@6830..6837
+ IDENT@6830..6837 "isArray"
+ SUFFIX_APPLY@6837..6840
+ ARGS_DESC@6837..6840
+ L_PAREN@6837..6838 "("
+ ARG@6838..6839
+ EXPR@6838..6839
+ EXPR_VAR@6838..6839
+ NAME@6838..6839
+ IDENT@6838..6839 "b"
+ R_PAREN@6839..6840 ")"
+ WHITESPACE@6840..6841 " "
+ THEN_KW@6841..6845 "then"
+ WHITESPACE@6845..6854 "\n "
+ TRUE_EXPR@6854..6871
+ EXPR@6854..6871
+ EXPR_BINARY@6854..6871
+ EXPR@6854..6857
+ EXPR_VAR@6854..6857
+ NAME@6854..6857
+ IDENT@6854..6857 "std"
+ SUFFIX_INDEX@6857..6864
+ DOT@6857..6858 "."
+ NAME@6858..6864
+ IDENT@6858..6864 "length"
+ SUFFIX_APPLY@6864..6867
+ ARGS_DESC@6864..6867
+ L_PAREN@6864..6865 "("
+ ARG@6865..6866
+ EXPR@6865..6866
+ EXPR_VAR@6865..6866
+ NAME@6865..6866
+ IDENT@6865..6866 "b"
+ R_PAREN@6866..6867 ")"
+ WHITESPACE@6867..6868 " "
+ GT@6868..6869 ">"
+ WHITESPACE@6869..6870 " "
+ EXPR@6870..6871
+ EXPR_NUMBER@6870..6871
+ FLOAT@6870..6871 "0"
+ WHITESPACE@6871..6878 "\n "
+ ELSE_KW@6878..6882 "else"
+ WHITESPACE@6882..6883 " "
+ FALSE_EXPR@6883..6956
+ EXPR@6883..6956
+ EXPR_IF_THEN_ELSE@6883..6956
+ IF_KW@6883..6885 "if"
+ WHITESPACE@6885..6886 " "
+ EXPR@6886..6901
+ EXPR_VAR@6886..6889
+ NAME@6886..6889
+ IDENT@6886..6889 "std"
+ SUFFIX_INDEX@6889..6898
+ DOT@6889..6890 "."
+ NAME@6890..6898
+ IDENT@6890..6898 "isObject"
+ SUFFIX_APPLY@6898..6901
+ ARGS_DESC@6898..6901
+ L_PAREN@6898..6899 "("
+ ARG@6899..6900
+ EXPR@6899..6900
+ EXPR_VAR@6899..6900
+ NAME@6899..6900
+ IDENT@6899..6900 "b"
+ R_PAREN@6900..6901 ")"
+ WHITESPACE@6901..6902 " "
+ THEN_KW@6902..6906 "then"
+ WHITESPACE@6906..6915 "\n "
+ TRUE_EXPR@6915..6932
+ EXPR@6915..6932
+ EXPR_BINARY@6915..6932
+ EXPR@6915..6918
+ EXPR_VAR@6915..6918
+ NAME@6915..6918
+ IDENT@6915..6918 "std"
+ SUFFIX_INDEX@6918..6925
+ DOT@6918..6919 "."
+ NAME@6919..6925
+ IDENT@6919..6925 "length"
+ SUFFIX_APPLY@6925..6928
+ ARGS_DESC@6925..6928
+ L_PAREN@6925..6926 "("
+ ARG@6926..6927
+ EXPR@6926..6927
+ EXPR_VAR@6926..6927
+ NAME@6926..6927
+ IDENT@6926..6927 "b"
+ R_PAREN@6927..6928 ")"
+ WHITESPACE@6928..6929 " "
+ GT@6929..6930 ">"
+ WHITESPACE@6930..6931 " "
+ EXPR@6931..6932
+ EXPR_NUMBER@6931..6932
+ FLOAT@6931..6932 "0"
+ WHITESPACE@6932..6939 "\n "
+ ELSE_KW@6939..6943 "else"
+ WHITESPACE@6943..6952 "\n "
+ FALSE_EXPR@6952..6956
+ EXPR@6952..6956
+ EXPR_LITERAL@6952..6956
+ TRUE_KW@6952..6956 "true"
+ SEMI@6956..6957 ";"
+ WHITESPACE@6957..6962 "\n "
+ EXPR_IF_THEN_ELSE@6962..7191
+ IF_KW@6962..6964 "if"
+ WHITESPACE@6964..6965 " "
+ EXPR@6965..6979
+ EXPR_VAR@6965..6968
+ NAME@6965..6968
+ IDENT@6965..6968 "std"
+ SUFFIX_INDEX@6968..6976
+ DOT@6968..6969 "."
+ NAME@6969..6976
+ IDENT@6969..6976 "isArray"
+ SUFFIX_APPLY@6976..6979
+ ARGS_DESC@6976..6979
+ L_PAREN@6976..6977 "("
+ ARG@6977..6978
+ EXPR@6977..6978
+ EXPR_VAR@6977..6978
+ NAME@6977..6978
+ IDENT@6977..6978 "a"
+ R_PAREN@6978..6979 ")"
+ WHITESPACE@6979..6980 " "
+ THEN_KW@6980..6984 "then"
+ WHITESPACE@6984..6991 "\n "
+ TRUE_EXPR@6991..7041
+ EXPR@6991..7041
+ EXPR_ARRAY_COMP@6991..7041
+ L_BRACK@6991..6992 "["
+ EXPR@6992..7004
+ EXPR_VAR@6992..6995
+ NAME@6992..6995
+ IDENT@6992..6995 "std"
+ SUFFIX_INDEX@6995..7001
+ DOT@6995..6996 "."
+ NAME@6996..7001
+ IDENT@6996..7001 "prune"
+ SUFFIX_APPLY@7001..7004
+ ARGS_DESC@7001..7004
+ L_PAREN@7001..7002 "("
+ ARG@7002..7003
+ EXPR@7002..7003
+ EXPR_VAR@7002..7003
+ NAME@7002..7003
+ IDENT@7002..7003 "x"
+ R_PAREN@7003..7004 ")"
+ WHITESPACE@7004..7005 " "
+ FOR_SPEC@7005..7015
+ FOR_KW@7005..7008 "for"
+ WHITESPACE@7008..7009 " "
+ DESTRUCT_FULL@7009..7010
+ NAME@7009..7010
+ IDENT@7009..7010 "x"
+ WHITESPACE@7010..7011 " "
+ IN_KW@7011..7013 "in"
+ WHITESPACE@7013..7014 " "
+ EXPR@7014..7015
+ EXPR_VAR@7014..7015
+ NAME@7014..7015
+ IDENT@7014..7015 "a"
+ WHITESPACE@7015..7016 " "
+ IF_SPEC@7016..7040
+ IF_KW@7016..7018 "if"
+ WHITESPACE@7018..7019 " "
+ EXPR@7019..7040
+ EXPR_VAR@7019..7028
+ NAME@7019..7028
+ IDENT@7019..7028 "isContent"
+ SUFFIX_APPLY@7028..7040
+ ARGS_DESC@7028..7040
+ L_PAREN@7028..7029 "("
+ ARG@7029..7039
+ EXPR@7029..7039
+ EXPR_LITERAL@7029..7030
+ DOLLAR@7029..7030 "$"
+ SUFFIX_INDEX@7030..7036
+ DOT@7030..7031 "."
+ NAME@7031..7036
+ IDENT@7031..7036 "prune"
+ SUFFIX_APPLY@7036..7039
+ ARGS_DESC@7036..7039
+ L_PAREN@7036..7037 "("
+ ARG@7037..7038
+ EXPR@7037..7038
+ EXPR_VAR@7037..7038
+ NAME@7037..7038
+ IDENT@7037..7038 "x"
+ R_PAREN@7038..7039 ")"
+ R_PAREN@7039..7040 ")"
+ R_BRACK@7040..7041 "]"
+ WHITESPACE@7041..7046 "\n "
+ ELSE_KW@7046..7050 "else"
+ WHITESPACE@7050..7051 " "
+ FALSE_EXPR@7051..7191
+ EXPR@7051..7191
+ EXPR_IF_THEN_ELSE@7051..7191
+ IF_KW@7051..7053 "if"
+ WHITESPACE@7053..7054 " "
+ EXPR@7054..7069
+ EXPR_VAR@7054..7057
+ NAME@7054..7057
+ IDENT@7054..7057 "std"
+ SUFFIX_INDEX@7057..7066
+ DOT@7057..7058 "."
+ NAME@7058..7066
+ IDENT@7058..7066 "isObject"
+ SUFFIX_APPLY@7066..7069
+ ARGS_DESC@7066..7069
+ L_PAREN@7066..7067 "("
+ ARG@7067..7068
+ EXPR@7067..7068
+ EXPR_VAR@7067..7068
+ NAME@7067..7068
+ IDENT@7067..7068 "a"
+ R_PAREN@7068..7069 ")"
+ WHITESPACE@7069..7070 " "
+ THEN_KW@7070..7074 "then"
+ WHITESPACE@7074..7075 " "
+ TRUE_EXPR@7075..7178
+ EXPR@7075..7178
+ EXPR_OBJECT@7075..7178
+ OBJ_BODY_COMP@7075..7178
+ L_BRACE@7075..7076 "{"
+ WHITESPACE@7076..7083 "\n "
+ MEMBER_FIELD_NORMAL@7083..7101
+ FIELD_NAME_DYNAMIC@7083..7086
+ L_BRACK@7083..7084 "["
+ EXPR@7084..7085
+ EXPR_VAR@7084..7085
+ NAME@7084..7085
+ IDENT@7084..7085 "x"
+ R_BRACK@7085..7086 "]"
+ COLON@7086..7087 ":"
+ WHITESPACE@7087..7088 " "
+ EXPR@7088..7101
+ EXPR_LITERAL@7088..7089
+ DOLLAR@7088..7089 "$"
+ SUFFIX_INDEX@7089..7095
+ DOT@7089..7090 "."
+ NAME@7090..7095
+ IDENT@7090..7095 "prune"
+ SUFFIX_APPLY@7095..7101
+ ARGS_DESC@7095..7101
+ L_PAREN@7095..7096 "("
+ ARG@7096..7100
+ EXPR@7096..7100
+ EXPR_VAR@7096..7097
+ NAME@7096..7097
+ IDENT@7096..7097 "a"
+ SUFFIX_INDEX_EXPR@7097..7100
+ L_BRACK@7097..7098 "["
+ EXPR@7098..7099
+ EXPR_VAR@7098..7099
+ NAME@7098..7099
+ IDENT@7098..7099 "x"
+ R_BRACK@7099..7100 "]"
+ R_PAREN@7100..7101 ")"
+ WHITESPACE@7101..7108 "\n "
+ FOR_SPEC@7108..7136
+ FOR_KW@7108..7111 "for"
+ WHITESPACE@7111..7112 " "
+ DESTRUCT_FULL@7112..7113
+ NAME@7112..7113
+ IDENT@7112..7113 "x"
+ WHITESPACE@7113..7114 " "
+ IN_KW@7114..7116 "in"
+ WHITESPACE@7116..7117 " "
+ EXPR@7117..7136
+ EXPR_VAR@7117..7120
+ NAME@7117..7120
+ IDENT@7117..7120 "std"
+ SUFFIX_INDEX@7120..7133
+ DOT@7120..7121 "."
+ NAME@7121..7133
+ IDENT@7121..7133 "objectFields"
+ SUFFIX_APPLY@7133..7136
+ ARGS_DESC@7133..7136
+ L_PAREN@7133..7134 "("
+ ARG@7134..7135
+ EXPR@7134..7135
+ EXPR_VAR@7134..7135
+ NAME@7134..7135
+ IDENT@7134..7135 "a"
+ R_PAREN@7135..7136 ")"
+ WHITESPACE@7136..7143 "\n "
+ IF_SPEC@7143..7172
+ IF_KW@7143..7145 "if"
+ WHITESPACE@7145..7146 " "
+ EXPR@7146..7172
+ EXPR_VAR@7146..7155
+ NAME@7146..7155
+ IDENT@7146..7155 "isContent"
+ SUFFIX_APPLY@7155..7172
+ ARGS_DESC@7155..7172
+ L_PAREN@7155..7156 "("
+ ARG@7156..7171
+ EXPR@7156..7171
+ EXPR_VAR@7156..7159
+ NAME@7156..7159
+ IDENT@7156..7159 "std"
+ SUFFIX_INDEX@7159..7165
+ DOT@7159..7160 "."
+ NAME@7160..7165
+ IDENT@7160..7165 "prune"
+ SUFFIX_APPLY@7165..7171
+ ARGS_DESC@7165..7171
+ L_PAREN@7165..7166 "("
+ ARG@7166..7170
+ EXPR@7166..7170
+ EXPR_VAR@7166..7167
+ NAME@7166..7167
+ IDENT@7166..7167 "a"
+ SUFFIX_INDEX_EXPR@7167..7170
+ L_BRACK@7167..7168 "["
+ EXPR@7168..7169
+ EXPR_VAR@7168..7169
+ NAME@7168..7169
+ IDENT@7168..7169 "x"
+ R_BRACK@7169..7170 "]"
+ R_PAREN@7170..7171 ")"
+ R_PAREN@7171..7172 ")"
+ WHITESPACE@7172..7177 "\n "
+ R_BRACE@7177..7178 "}"
+ WHITESPACE@7178..7179 " "
+ ELSE_KW@7179..7183 "else"
+ WHITESPACE@7183..7190 "\n "
+ FALSE_EXPR@7190..7191
+ EXPR@7190..7191
+ EXPR_VAR@7190..7191
+ NAME@7190..7191
+ IDENT@7190..7191 "a"
+ COMMA@7191..7192 ","
+ WHITESPACE@7192..7196 "\n\n "
+ MEMBER_FIELD_METHOD@7196..7411
+ FIELD_NAME_FIXED@7196..7200
+ NAME@7196..7200
+ IDENT@7196..7200 "find"
+ PARAMS_DESC@7200..7212
+ L_PAREN@7200..7201 "("
+ PARAM@7201..7206
+ DESTRUCT_FULL@7201..7206
+ NAME@7201..7206
+ IDENT@7201..7206 "value"
+ COMMA@7206..7207 ","
+ WHITESPACE@7207..7208 " "
+ PARAM@7208..7211
+ DESTRUCT_FULL@7208..7211
+ NAME@7208..7211
+ IDENT@7208..7211 "arr"
+ R_PAREN@7211..7212 ")"
+ COLONCOLON@7212..7214 "::"
+ WHITESPACE@7214..7219 "\n "
+ EXPR@7219..7411
+ EXPR_IF_THEN_ELSE@7219..7411
+ IF_KW@7219..7221 "if"
+ WHITESPACE@7221..7222 " "
+ EXPR@7222..7239
+ EXPR_UNARY@7222..7239
+ NOT@7222..7223 "!"
+ EXPR_VAR@7223..7226
+ NAME@7223..7226
+ IDENT@7223..7226 "std"
+ SUFFIX_INDEX@7226..7234
+ DOT@7226..7227 "."
+ NAME@7227..7234
+ IDENT@7227..7234 "isArray"
+ SUFFIX_APPLY@7234..7239
+ ARGS_DESC@7234..7239
+ L_PAREN@7234..7235 "("
+ ARG@7235..7238
+ EXPR@7235..7238
+ EXPR_VAR@7235..7238
+ NAME@7235..7238
+ IDENT@7235..7238 "arr"
+ R_PAREN@7238..7239 ")"
+ WHITESPACE@7239..7240 " "
+ THEN_KW@7240..7244 "then"
+ WHITESPACE@7244..7251 "\n "
+ TRUE_EXPR@7251..7321
+ EXPR@7251..7321
+ EXPR_ERROR@7251..7321
+ ERROR_KW@7251..7256 "error"
+ WHITESPACE@7256..7257 " "
+ EXPR@7257..7321
+ EXPR_BINARY@7257..7321
+ EXPR@7257..7305
+ EXPR_STRING@7257..7305
+ STRING_SINGLE@7257..7305 "'find second paramete ..."
+ WHITESPACE@7305..7306 " "
+ PLUS@7306..7307 "+"
+ WHITESPACE@7307..7308 " "
+ EXPR@7308..7321
+ EXPR_VAR@7308..7311
+ NAME@7308..7311
+ IDENT@7308..7311 "std"
+ SUFFIX_INDEX@7311..7316
+ DOT@7311..7312 "."
+ NAME@7312..7316
+ IDENT@7312..7316 "type"
+ SUFFIX_APPLY@7316..7321
+ ARGS_DESC@7316..7321
+ L_PAREN@7316..7317 "("
+ ARG@7317..7320
+ EXPR@7317..7320
+ EXPR_VAR@7317..7320
+ NAME@7317..7320
+ IDENT@7317..7320 "arr"
+ R_PAREN@7320..7321 ")"
+ WHITESPACE@7321..7326 "\n "
+ ELSE_KW@7326..7330 "else"
+ WHITESPACE@7330..7337 "\n "
+ FALSE_EXPR@7337..7411
+ EXPR@7337..7411
+ EXPR_VAR@7337..7340
+ NAME@7337..7340
+ IDENT@7337..7340 "std"
+ SUFFIX_INDEX@7340..7347
+ DOT@7340..7341 "."
+ NAME@7341..7347
+ IDENT@7341..7347 "filter"
+ SUFFIX_APPLY@7347..7411
+ ARGS_DESC@7347..7411
+ L_PAREN@7347..7348 "("
+ ARG@7348..7375
+ EXPR@7348..7375
+ EXPR_FUNCTION@7348..7375
+ FUNCTION_KW@7348..7356 "function"
+ PARAMS_DESC@7356..7359
+ L_PAREN@7356..7357 "("
+ PARAM@7357..7358
+ DESTRUCT_FULL@7357..7358
+ NAME@7357..7358
+ IDENT@7357..7358 "i"
+ R_PAREN@7358..7359 ")"
+ WHITESPACE@7359..7360 " "
+ EXPR@7360..7375
+ EXPR_BINARY@7360..7375
+ EXPR@7360..7363
+ EXPR_VAR@7360..7363
+ NAME@7360..7363
+ IDENT@7360..7363 "arr"
+ SUFFIX_INDEX_EXPR@7363..7366
+ L_BRACK@7363..7364 "["
+ EXPR@7364..7365
+ EXPR_VAR@7364..7365
+ NAME@7364..7365
+ IDENT@7364..7365 "i"
+ R_BRACK@7365..7366 "]"
+ WHITESPACE@7366..7367 " "
+ EQ@7367..7369 "=="
+ WHITESPACE@7369..7370 " "
+ EXPR@7370..7375
+ EXPR_VAR@7370..7375
+ NAME@7370..7375
+ IDENT@7370..7375 "value"
+ COMMA@7375..7376 ","
+ WHITESPACE@7376..7377 " "
+ ARG@7377..7410
+ EXPR@7377..7410
+ EXPR_VAR@7377..7380
+ NAME@7377..7380
+ IDENT@7377..7380 "std"
+ SUFFIX_INDEX@7380..7386
+ DOT@7380..7381 "."
+ NAME@7381..7386
+ IDENT@7381..7386 "range"
+ SUFFIX_APPLY@7386..7410
+ ARGS_DESC@7386..7410
+ L_PAREN@7386..7387 "("
+ ARG@7387..7388
+ EXPR@7387..7388
+ EXPR_NUMBER@7387..7388
+ FLOAT@7387..7388 "0"
+ COMMA@7388..7389 ","
+ WHITESPACE@7389..7390 " "
+ ARG@7390..7409
+ EXPR@7390..7409
+ EXPR_BINARY@7390..7409
+ EXPR@7390..7393
+ EXPR_VAR@7390..7393
+ NAME@7390..7393
+ IDENT@7390..7393 "std"
+ SUFFIX_INDEX@7393..7400
+ DOT@7393..7394 "."
+ NAME@7394..7400
+ IDENT@7394..7400 "length"
+ SUFFIX_APPLY@7400..7405
+ ARGS_DESC@7400..7405
+ L_PAREN@7400..7401 "("
+ ARG@7401..7404
+ EXPR@7401..7404
+ EXPR_VAR@7401..7404
+ NAME@7401..7404
+ IDENT@7401..7404 "arr"
+ R_PAREN@7404..7405 ")"
+ WHITESPACE@7405..7406 " "
+ MINUS@7406..7407 "-"
+ WHITESPACE@7407..7408 " "
+ EXPR@7408..7409
+ EXPR_NUMBER@7408..7409
+ FLOAT@7408..7409 "1"
+ R_PAREN@7409..7410 ")"
+ R_PAREN@7410..7411 ")"
+ COMMA@7411..7412 ","
+ WHITESPACE@7412..7416 "\n\n "
+ SINGLE_LINE_SLASH_COMMENT@7416..7426 "// Compat\n"
+ WHITESPACE@7426..7428 " "
+ MEMBER_FIELD_METHOD@7428..7538
+ FIELD_NAME_FIXED@7428..7443
+ NAME@7428..7443
+ IDENT@7428..7443 "__compare_array"
+ PARAMS_DESC@7443..7455
+ L_PAREN@7443..7444 "("
+ PARAM@7444..7448
+ DESTRUCT_FULL@7444..7448
+ NAME@7444..7448
+ IDENT@7444..7448 "arr1"
+ COMMA@7448..7449 ","
+ WHITESPACE@7449..7450 " "
+ PARAM@7450..7454
+ DESTRUCT_FULL@7450..7454
+ NAME@7450..7454
+ IDENT@7450..7454 "arr2"
+ R_PAREN@7454..7455 ")"
+ COLONCOLON@7455..7457 "::"
+ WHITESPACE@7457..7462 "\n "
+ EXPR@7462..7538
+ STMT_ASSERT@7462..7508
+ ASSERTION@7462..7507
+ ASSERT_KW@7462..7468 "assert"
+ WHITESPACE@7468..7469 " "
+ EXPR@7469..7507
+ EXPR_BINARY@7469..7507
+ EXPR@7469..7472
+ EXPR_VAR@7469..7472
+ NAME@7469..7472
+ IDENT@7469..7472 "std"
+ SUFFIX_INDEX@7472..7480
+ DOT@7472..7473 "."
+ NAME@7473..7480
+ IDENT@7473..7480 "isArray"
+ SUFFIX_APPLY@7480..7486
+ ARGS_DESC@7480..7486
+ L_PAREN@7480..7481 "("
+ ARG@7481..7485
+ EXPR@7481..7485
+ EXPR_VAR@7481..7485
+ NAME@7481..7485
+ IDENT@7481..7485 "arr1"
+ R_PAREN@7485..7486 ")"
+ WHITESPACE@7486..7487 " "
+ AND@7487..7489 "&&"
+ WHITESPACE@7489..7490 " "
+ EXPR@7490..7507
+ EXPR_VAR@7490..7493
+ NAME@7490..7493
+ IDENT@7490..7493 "std"
+ SUFFIX_INDEX@7493..7501
+ DOT@7493..7494 "."
+ NAME@7494..7501
+ IDENT@7494..7501 "isArray"
+ SUFFIX_APPLY@7501..7507
+ ARGS_DESC@7501..7507
+ L_PAREN@7501..7502 "("
+ ARG@7502..7506
+ EXPR@7502..7506
+ EXPR_VAR@7502..7506
+ NAME@7502..7506
+ IDENT@7502..7506 "arr2"
+ R_PAREN@7506..7507 ")"
+ SEMI@7507..7508 ";"
+ WHITESPACE@7508..7513 "\n "
+ EXPR_VAR@7513..7516
+ NAME@7513..7516
+ IDENT@7513..7516 "std"
+ SUFFIX_INDEX@7516..7526
+ DOT@7516..7517 "."
+ NAME@7517..7526
+ IDENT@7517..7526 "__compare"
+ SUFFIX_APPLY@7526..7538
+ ARGS_DESC@7526..7538
+ L_PAREN@7526..7527 "("
+ ARG@7527..7531
+ EXPR@7527..7531
+ EXPR_VAR@7527..7531
+ NAME@7527..7531
+ IDENT@7527..7531 "arr1"
+ COMMA@7531..7532 ","
+ WHITESPACE@7532..7533 " "
+ ARG@7533..7537
+ EXPR@7533..7537
+ EXPR_VAR@7533..7537
+ NAME@7533..7537
+ IDENT@7533..7537 "arr2"
+ R_PAREN@7537..7538 ")"
+ COMMA@7538..7539 ","
+ WHITESPACE@7539..7542 "\n "
+ MEMBER_FIELD_METHOD@7542..7606
+ FIELD_NAME_FIXED@7542..7554
+ NAME@7542..7554
+ IDENT@7542..7554 "__array_less"
+ PARAMS_DESC@7554..7566
+ L_PAREN@7554..7555 "("
+ PARAM@7555..7559
+ DESTRUCT_FULL@7555..7559
+ NAME@7555..7559
+ IDENT@7555..7559 "arr1"
+ COMMA@7559..7560 ","
+ WHITESPACE@7560..7561 " "
+ PARAM@7561..7565
+ DESTRUCT_FULL@7561..7565
+ NAME@7561..7565
+ IDENT@7561..7565 "arr2"
+ R_PAREN@7565..7566 ")"
+ COLONCOLON@7566..7568 "::"
+ WHITESPACE@7568..7569 " "
+ EXPR@7569..7606
+ EXPR_BINARY@7569..7606
+ EXPR@7569..7572
+ EXPR_VAR@7569..7572
+ NAME@7569..7572
+ IDENT@7569..7572 "std"
+ SUFFIX_INDEX@7572..7588
+ DOT@7572..7573 "."
+ NAME@7573..7588
+ IDENT@7573..7588 "__compare_array"
+ SUFFIX_APPLY@7588..7600
+ ARGS_DESC@7588..7600
+ L_PAREN@7588..7589 "("
+ ARG@7589..7593
+ EXPR@7589..7593
+ EXPR_VAR@7589..7593
+ NAME@7589..7593
+ IDENT@7589..7593 "arr1"
+ COMMA@7593..7594 ","
+ WHITESPACE@7594..7595 " "
+ ARG@7595..7599
+ EXPR@7595..7599
+ EXPR_VAR@7595..7599
+ NAME@7595..7599
+ IDENT@7595..7599 "arr2"
+ R_PAREN@7599..7600 ")"
+ WHITESPACE@7600..7601 " "
+ EQ@7601..7603 "=="
+ WHITESPACE@7603..7604 " "
+ EXPR@7604..7606
+ EXPR_UNARY@7604..7606
+ MINUS@7604..7605 "-"
+ EXPR_NUMBER@7605..7606
+ FLOAT@7605..7606 "1"
+ COMMA@7606..7607 ","
+ WHITESPACE@7607..7610 "\n "
+ MEMBER_FIELD_METHOD@7610..7676
+ FIELD_NAME_FIXED@7610..7625
+ NAME@7610..7625
+ IDENT@7610..7625 "__array_greater"
+ PARAMS_DESC@7625..7637
+ L_PAREN@7625..7626 "("
+ PARAM@7626..7630
+ DESTRUCT_FULL@7626..7630
+ NAME@7626..7630
+ IDENT@7626..7630 "arr1"
+ COMMA@7630..7631 ","
+ WHITESPACE@7631..7632 " "
+ PARAM@7632..7636
+ DESTRUCT_FULL@7632..7636
+ NAME@7632..7636
+ IDENT@7632..7636 "arr2"
+ R_PAREN@7636..7637 ")"
+ COLONCOLON@7637..7639 "::"
+ WHITESPACE@7639..7640 " "
+ EXPR@7640..7676
+ EXPR_BINARY@7640..7676
+ EXPR@7640..7643
+ EXPR_VAR@7640..7643
+ NAME@7640..7643
+ IDENT@7640..7643 "std"
+ SUFFIX_INDEX@7643..7659
+ DOT@7643..7644 "."
+ NAME@7644..7659
+ IDENT@7644..7659 "__compare_array"
+ SUFFIX_APPLY@7659..7671
+ ARGS_DESC@7659..7671
+ L_PAREN@7659..7660 "("
+ ARG@7660..7664
+ EXPR@7660..7664
+ EXPR_VAR@7660..7664
+ NAME@7660..7664
+ IDENT@7660..7664 "arr1"
+ COMMA@7664..7665 ","
+ WHITESPACE@7665..7666 " "
+ ARG@7666..7670
+ EXPR@7666..7670
+ EXPR_VAR@7666..7670
+ NAME@7666..7670
+ IDENT@7666..7670 "arr2"
+ R_PAREN@7670..7671 ")"
+ WHITESPACE@7671..7672 " "
+ EQ@7672..7674 "=="
+ WHITESPACE@7674..7675 " "
+ EXPR@7675..7676
+ EXPR_NUMBER@7675..7676
+ FLOAT@7675..7676 "1"
+ COMMA@7676..7677 ","
+ WHITESPACE@7677..7680 "\n "
+ MEMBER_FIELD_METHOD@7680..7752
+ FIELD_NAME_FIXED@7680..7701
+ NAME@7680..7701
+ IDENT@7680..7701 "__array_less_or_equal"
+ PARAMS_DESC@7701..7713
+ L_PAREN@7701..7702 "("
+ PARAM@7702..7706
+ DESTRUCT_FULL@7702..7706
+ NAME@7702..7706
+ IDENT@7702..7706 "arr1"
+ COMMA@7706..7707 ","
+ WHITESPACE@7707..7708 " "
+ PARAM@7708..7712
+ DESTRUCT_FULL@7708..7712
+ NAME@7708..7712
+ IDENT@7708..7712 "arr2"
+ R_PAREN@7712..7713 ")"
+ COLONCOLON@7713..7715 "::"
+ WHITESPACE@7715..7716 " "
+ EXPR@7716..7752
+ EXPR_BINARY@7716..7752
+ EXPR@7716..7719
+ EXPR_VAR@7716..7719
+ NAME@7716..7719
+ IDENT@7716..7719 "std"
+ SUFFIX_INDEX@7719..7735
+ DOT@7719..7720 "."
+ NAME@7720..7735
+ IDENT@7720..7735 "__compare_array"
+ SUFFIX_APPLY@7735..7747
+ ARGS_DESC@7735..7747
+ L_PAREN@7735..7736 "("
+ ARG@7736..7740
+ EXPR@7736..7740
+ EXPR_VAR@7736..7740
+ NAME@7736..7740
+ IDENT@7736..7740 "arr1"
+ COMMA@7740..7741 ","
+ WHITESPACE@7741..7742 " "
+ ARG@7742..7746
+ EXPR@7742..7746
+ EXPR_VAR@7742..7746
+ NAME@7742..7746
+ IDENT@7742..7746 "arr2"
+ R_PAREN@7746..7747 ")"
+ WHITESPACE@7747..7748 " "
+ LE@7748..7750 "<="
+ WHITESPACE@7750..7751 " "
+ EXPR@7751..7752
+ EXPR_NUMBER@7751..7752
+ FLOAT@7751..7752 "0"
+ COMMA@7752..7753 ","
+ WHITESPACE@7753..7756 "\n "
+ MEMBER_FIELD_METHOD@7756..7831
+ FIELD_NAME_FIXED@7756..7780
+ NAME@7756..7780
+ IDENT@7756..7780 "__array_greater_or_equal"
+ PARAMS_DESC@7780..7792
+ L_PAREN@7780..7781 "("
+ PARAM@7781..7785
+ DESTRUCT_FULL@7781..7785
+ NAME@7781..7785
+ IDENT@7781..7785 "arr1"
+ COMMA@7785..7786 ","
+ WHITESPACE@7786..7787 " "
+ PARAM@7787..7791
+ DESTRUCT_FULL@7787..7791
+ NAME@7787..7791
+ IDENT@7787..7791 "arr2"
+ R_PAREN@7791..7792 ")"
+ COLONCOLON@7792..7794 "::"
+ WHITESPACE@7794..7795 " "
+ EXPR@7795..7831
+ EXPR_BINARY@7795..7831
+ EXPR@7795..7798
+ EXPR_VAR@7795..7798
+ NAME@7795..7798
+ IDENT@7795..7798 "std"
+ SUFFIX_INDEX@7798..7814
+ DOT@7798..7799 "."
+ NAME@7799..7814
+ IDENT@7799..7814 "__compare_array"
+ SUFFIX_APPLY@7814..7826
+ ARGS_DESC@7814..7826
+ L_PAREN@7814..7815 "("
+ ARG@7815..7819
+ EXPR@7815..7819
+ EXPR_VAR@7815..7819
+ NAME@7815..7819
+ IDENT@7815..7819 "arr1"
+ COMMA@7819..7820 ","
+ WHITESPACE@7820..7821 " "
+ ARG@7821..7825
+ EXPR@7821..7825
+ EXPR_VAR@7821..7825
+ NAME@7821..7825
+ IDENT@7821..7825 "arr2"
+ R_PAREN@7825..7826 ")"
+ WHITESPACE@7826..7827 " "
+ GE@7827..7829 ">="
+ WHITESPACE@7829..7830 " "
+ EXPR@7830..7831
+ EXPR_NUMBER@7830..7831
+ FLOAT@7830..7831 "0"
+ COMMA@7831..7832 ","
+ WHITESPACE@7832..7833 "\n"
+ R_BRACE@7833..7834 "}"
+ WHITESPACE@7834..7835 "\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_indent.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_indent.snap
@@ -0,0 +1,8 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "|||\n"
+---
+SOURCE_FILE@0..4
+ EXPR@0..4
+ EXPR_STRING@0..4
+ ERROR_STRING_BLOCK_MISSING_INDENT@0..4 "|||\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_indent_text.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_indent_text.snap
@@ -0,0 +1,8 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "|||\nhello\n"
+---
+SOURCE_FILE@0..10
+ EXPR@0..10
+ EXPR_STRING@0..10
+ ERROR_STRING_BLOCK_MISSING_INDENT@0..10 "|||\nhello\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_newline.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_newline.snap
@@ -0,0 +1,8 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "|||hello\n"
+---
+SOURCE_FILE@0..9
+ EXPR@0..9
+ EXPR_STRING@0..9
+ ERROR_STRING_BLOCK_MISSING_NEW_LINE@0..9 "|||hello\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_termination.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_termination.snap
@@ -0,0 +1,8 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "|||\n\thello\n"
+---
+SOURCE_FILE@0..11
+ EXPR@0..11
+ EXPR_STRING@0..11
+ ERROR_STRING_BLOCK_UNEXPECTED_END@0..11 "|||\n\thello\n"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__unexpected_destruct.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__unexpected_destruct.snap
@@ -0,0 +1,34 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "local * = 1;\na\n"
+---
+SOURCE_FILE@0..15
+ EXPR@0..14
+ STMT_LOCAL@0..12
+ LOCAL_KW@0..5 "local"
+ WHITESPACE@5..6 " "
+ BIND_DESTRUCT@6..11
+ ERROR_UNEXPECTED_TOKEN@6..7
+ MUL@6..7 "*"
+ WHITESPACE@7..8 " "
+ ASSIGN@8..9 "="
+ WHITESPACE@9..10 " "
+ EXPR@10..11
+ EXPR_NUMBER@10..11
+ FLOAT@10..11 "1"
+ SEMI@11..12 ";"
+ WHITESPACE@12..13 "\n"
+ EXPR_VAR@13..14
+ NAME@13..14
+ IDENT@13..14 "a"
+ WHITESPACE@14..15 "\n"
+===
+LocatedSyntaxError { error: Unexpected { expected: Named("destruction specifier"), found: MUL }, range: 6..7 }
+===
+ x syntax error
+ ,-[1:1]
+ 1 | local * = 1;
+ : |
+ : `-- expected destruction specifier, found MUL
+ 2 | a
+ `----
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__wrong_field_end.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__wrong_field_end.snap
@@ -0,0 +1,51 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{\n\ta: 1;\n\tb: 2;\n}\n"
+---
+SOURCE_FILE@0..18
+ EXPR@0..17
+ EXPR_OBJECT@0..17
+ OBJ_BODY_MEMBER_LIST@0..17
+ L_BRACE@0..1 "{"
+ WHITESPACE@1..3 "\n\t"
+ MEMBER_FIELD_NORMAL@3..7
+ FIELD_NAME_FIXED@3..4
+ NAME@3..4
+ IDENT@3..4 "a"
+ COLON@4..5 ":"
+ WHITESPACE@5..6 " "
+ EXPR@6..7
+ EXPR_NUMBER@6..7
+ FLOAT@6..7 "1"
+ ERROR_UNEXPECTED_TOKEN@7..8
+ SEMI@7..8 ";"
+ WHITESPACE@8..10 "\n\t"
+ MEMBER_FIELD_NORMAL@10..14
+ FIELD_NAME_FIXED@10..11
+ NAME@10..11
+ IDENT@10..11 "b"
+ COLON@11..12 ":"
+ WHITESPACE@12..13 " "
+ EXPR@13..14
+ EXPR_NUMBER@13..14
+ FLOAT@13..14 "2"
+ ERROR_UNEXPECTED_TOKEN@14..15
+ SEMI@14..15 ";"
+ WHITESPACE@15..16 "\n"
+ R_BRACE@16..17 "}"
+ WHITESPACE@17..18 "\n"
+===
+LocatedSyntaxError { error: Unexpected { expected: Named("comma"), found: SEMI }, range: 7..8 }
+LocatedSyntaxError { error: Unexpected { expected: Named("comma"), found: SEMI }, range: 14..15 }
+===
+ x syntax error
+ ,-[1:1]
+ 1 | {
+ 2 | a: 1;
+ : |
+ : `-- expected comma, found SEMI
+ 3 | b: 2;
+ : |
+ : `-- expected comma, found SEMI
+ 4 | }
+ `----
crates/jrsonnet-rowan-parser/src/string_block.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/string_block.rs
@@ -0,0 +1,216 @@
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum StringBlockError {
+ UnexpectedEnd,
+ MissingNewLine,
+ MissingTermination,
+ MissingIndent,
+}
+
+use std::ops::Range;
+
+use logos::Lexer;
+use StringBlockError::*;
+
+use crate::SyntaxKind;
+
+pub fn lex_str_block_test(lex: &mut Lexer<SyntaxKind>) {
+ let _ = lex_str_block(lex);
+}
+
+pub fn lex_str_block(lex: &mut Lexer<SyntaxKind>) -> Result<(), StringBlockError> {
+ 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 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 Err(UnexpectedEnd);
+ }
+ // Text block requires new line after |||.
+ Some(_) => {
+ guess_token_end_and_bump(lex, &ctx);
+ return Err(MissingNewLine);
+ }
+ }
+
+ // 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 Err(MissingIndent);
+ }
+
+ 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 Err(UnexpectedEnd);
+ }
+ 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);
+ while let Some(' ' | '\t') = ctx.peek() {
+ term_indent.push(ctx.next().unwrap());
+ }
+
+ if !ctx.rest().starts_with("|||") {
+ // Text block not terminated with |||
+ let pos = ctx.pos();
+ if pos.is_empty() {
+ // eof
+ lex.bump(ctx.index);
+ return Err(UnexpectedEnd);
+ }
+
+ guess_token_end_and_bump(lex, &ctx);
+ return Err(MissingTermination);
+ }
+
+ // Skip '|||'
+ ctx.skip(3);
+ break;
+ }
+ }
+
+ lex.bump(ctx.index);
+ Ok(())
+}
crates/jrsonnet-rowan-parser/src/tests.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/tests.rs
@@ -0,0 +1,253 @@
+#![cfg(test)]
+
+use miette::{
+ Diagnostic, GraphicalReportHandler, GraphicalTheme, LabeledSpan, ThemeCharacters, ThemeStyles,
+};
+use thiserror::Error;
+
+use crate::{parse, AstNode};
+
+#[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, errors) = parse(text);
+ write!(out, "{:#?}", node.syntax()).unwrap();
+ if !errors.is_empty() && !text.is_empty() {
+ writeln!(out, "===").unwrap();
+ for err in &errors {
+ writeln!(out, "{:?}", err).unwrap();
+ }
+ let mut code = text.to_string();
+
+ // Prettier errors at EOF position
+ if code.ends_with('\n') {
+ code.truncate(code.len() - 1);
+ code += " ";
+ }
+ code += " ";
+
+ let diag = MyDiagnostic {
+ code,
+ spans: errors.into_iter().map(|e| e.into()).collect(),
+ };
+
+ let handler = GraphicalReportHandler::new_themed(GraphicalTheme {
+ characters: ThemeCharacters::ascii(),
+ styles: ThemeStyles::none(),
+ });
+
+ writeln!(out, "===").unwrap();
+ handler
+ .render_report(&mut out, &diag)
+ .expect("fmt error?..");
+ }
+ out.split('\n')
+ .map(|s| s.trim_end().to_string())
+ .collect::<Vec<String>>()
+ .join("\n")
+ .trim_end()
+ .to_string()
+}
+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
+ "#
+
+
+ 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;
+ }
+ "
+
+
+ plain_call => "
+ std.substr(a, 0, std.length(b)) == b
+ "
+
+ destruct => "
+ local [a, b, c] = arr;
+ local [a, ...] = arr_rest;
+ local [..., a] = rest_arr;
+ local [...] = rest_in_arr;
+ local [a, ...n] = arr_rest_n;
+ local [...n, a] = rest_arr_n;
+ local [...n] = rest_in_arr_n;
+
+ local {a, b, c} = obj;
+ local {a, b, c, ...} = obj_rest;
+ local {a, b, c, ...n} = obj_rest_n;
+
+ null
+ "
+
+ str_block_missing_indent => "
+ |||
+ "
+ str_block_missing_termination => "
+ |||
+ hello
+ "
+ str_block_missing_newline => "
+ |||hello
+ "
+ str_block_missing_indent_text => "
+ |||
+ hello
+ "
+
+ unexpected_destruct => "
+ local * = 1;
+ a
+ "
+ arr_compspec => r#"
+ [a for a in [1, 2, 3]]
+ "#
+ arr_compspec_comma => "
+ [a, for a in [1, 2, 3]]
+ "
+ arr_compspec_no_elems => "
+ [for a in [1, 2, 3]]
+ "
+ arr_compspec_incompatible_with_multiple_elems => r#"
+ [a for a in [1, 2, 3], b]
+ "#
+ arr_compspec_incompatible_with_multiple_elems_w => r#"
+ [a, b, for a in [1, 2, 3], c]
+ "#
+
+ obj_compspec => r#"
+ {a:1 for a in [1, 2, 3]}
+ "#
+ obj_compspec_comma => "
+ {a:1, for a in [1, 2, 3]}
+ "
+ obj_compspec_no_elems => "
+ {for a in [1, 2, 3]}
+ "
+ obj_compspec_incompatible_with_multiple_elems => r#"
+ {a:1 for a in [1, 2, 3], b:1}
+ "#
+ obj_compspec_incompatible_with_multiple_elems_w => r#"
+ {a:1, b:1, for a in [1, 2, 3], c:1}
+ "#
+
+ obj_compspec_incompatible_with_asserts => r#"
+ {assert 1, a: 1 for a in [1,2,3]}
+ "#
+
+ local_method => r#"
+ local
+ a(x) = x,
+ a = function(x) x,
+ ; c
+ "#
+ obj_method => r#"
+ {
+ a(x): x,
+ a: function(x) x,
+ }
+ "#
+
+ continue_after_total_failure => r#"
+ local intr = $intrinsic(test);
+
+ local a = 1, b = 2, c = a + b;
+
+ [c]
+ "#
+);
+
+#[test]
+fn stdlib() {
+ let src = include_str!("../../jrsonnet-stdlib/src/std.jsonnet");
+ let result = process(src);
+ insta::assert_snapshot!("stdlib", result, src);
+}
+#[test]
+fn eval_simple() {
+ let src = "local a = 1, b = 2; a + local c = 1; c";
+ let (node, errors) = parse(src);
+
+ dbg!(node);
+}
crates/jrsonnet-rowan-parser/src/token_set.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/token_set.rs
@@ -0,0 +1,106 @@
+use std::fmt;
+
+use crate::SyntaxKind;
+
+#[derive(Clone, Copy, Default)]
+pub struct SyntaxKindSet(u128);
+
+impl SyntaxKindSet {
+ #[allow(dead_code)]
+ pub const EMPTY: Self = Self(0);
+ pub const ALL: Self = Self(u128::MAX);
+
+ pub const fn new(kinds: &[SyntaxKind]) -> SyntaxKindSet {
+ let mut res = 0u128;
+ let mut i = 0;
+ while i < kinds.len() {
+ res |= mask(kinds[i]);
+ i += 1
+ }
+ SyntaxKindSet(res)
+ }
+
+ pub const fn union(self, other: SyntaxKindSet) -> SyntaxKindSet {
+ SyntaxKindSet(self.0 | other.0)
+ }
+ pub const fn with(self, kind: SyntaxKind) -> SyntaxKindSet {
+ SyntaxKindSet(self.0 | mask(kind))
+ }
+
+ pub fn contains(&self, kind: SyntaxKind) -> bool {
+ if !is_token(kind) {
+ return false;
+ }
+ self.0 & mask(kind) != 0
+ }
+}
+impl fmt::Display for SyntaxKindSet {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut v = self.0;
+ let mut variants = <Vec<SyntaxKind>>::new();
+ for i in 0..128 {
+ if v & 1 == 1 {
+ variants.push(SyntaxKind::from_raw(i))
+ }
+ v >>= 1;
+ if v == 0 {
+ break;
+ }
+ }
+ for (i, v) in variants.iter().enumerate() {
+ if i == 0 {
+ } else if i == variants.len() - 1 {
+ write!(f, " or ")?;
+ } else {
+ write!(f, ", ")?;
+ }
+ write!(f, "{v:?}")?;
+ }
+ Ok(())
+ }
+}
+impl fmt::Debug for SyntaxKindSet {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut v = self.0;
+ let mut variants = <Vec<SyntaxKind>>::new();
+ for i in 0..128 {
+ if v & 1 == 1 {
+ variants.push(SyntaxKind::from_raw(i))
+ }
+ v >>= 1;
+ if v == 0 {
+ break;
+ }
+ }
+ f.debug_tuple("SyntaxKindSet").field(&variants).finish()
+ }
+}
+
+const fn mask(kind: SyntaxKind) -> u128 {
+ if kind as u32 > 128 {
+ panic!("mask for not a token kind")
+ }
+ 1u128 << (kind as u128)
+}
+
+#[macro_export]
+macro_rules! TS {
+ ($($tt:tt)*) => {
+ $crate::SyntaxKindSet::new(&[
+ $(
+ $crate::T![$tt]
+ ),*
+ ])
+ };
+}
+
+#[test]
+fn sanity() {
+ assert!(
+ (SyntaxKind::LEXING_ERROR as u32) < 127,
+ "can't keep KindSet as bitset"
+ );
+}
+fn is_token(kind: SyntaxKind) -> bool {
+ (kind as u32) < 127
+}
crates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -161,7 +161,11 @@
use jrsonnet_evaluator::runtime_error;
use Either2::*;
Ok(match v {
- A(a) => Val::BigInt(Box::new((a as i64).into())),
+ A(a) => {
+ Val::BigInt(Box::new(a.to_string().parse().map_err(|e| {
+ runtime_error!("number is not convertible to bigint: {e}")
+ })?))
+ }
B(b) => Val::BigInt(Box::new(
b.as_str()
.parse()
flake.nixdiffbeforeafterboth--- a/flake.nix
+++ b/flake.nix
@@ -131,6 +131,8 @@
cargo-edit
cargo-asm
cargo-outdated
+ cargo-watch
+ cargo-insta
lld
hyperfine
graphviz
xtask/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/xtask/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "xtask"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.57"
+indexmap = "1.9.0"
+itertools = "0.10.3"
+proc-macro2 = "1.0.39"
+quote = "1.0.18"
+ungrammar = "1.16.1"
+xshell = "0.2.2"
xtask/src/main.rsdiffbeforeafterboth--- /dev/null
+++ b/xtask/src/main.rs
@@ -0,0 +1,7 @@
+use anyhow::Result;
+
+mod sourcegen;
+
+fn main() -> Result<()> {
+ sourcegen::generate_ungrammar()
+}
xtask/src/sourcegen/ast.rsdiffbeforeafterboth--- /dev/null
+++ b/xtask/src/sourcegen/ast.rs
@@ -0,0 +1,399 @@
+use std::collections::{BTreeSet, HashMap};
+
+use proc_macro2::TokenStream;
+use quote::format_ident;
+use ungrammar::{Grammar, Rule};
+
+use super::{
+ util::{pluralize, to_lower_snake_case},
+ KindsSrc,
+};
+
+impl AstNodeSrc {
+ pub fn remove_field(&mut self, to_remove: Vec<usize>) {
+ to_remove.into_iter().rev().for_each(|idx| {
+ self.fields.remove(idx);
+ });
+ }
+}
+
+#[allow(dead_code)]
+#[derive(Default, Debug)]
+pub struct AstSrc {
+ pub nodes: Vec<AstNodeSrc>,
+ pub enums: Vec<AstEnumSrc>,
+ pub token_enums: Vec<AstTokenEnumSrc>,
+}
+#[derive(Debug)]
+pub struct AstNodeSrc {
+ pub doc: Vec<String>,
+ pub name: String,
+ pub traits: Vec<String>,
+ pub fields: Vec<Field>,
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum Field {
+ Token(String),
+ Node {
+ name: String,
+ ty: String,
+ cardinality: Cardinality,
+ },
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum Cardinality {
+ /// This field may not exist in code
+ Optional,
+ /// This field should exist in correctly parsed code
+ Required,
+ /// There may be multiple field values of this kind
+ Many,
+}
+
+#[derive(Debug, Clone)]
+pub struct AstEnumSrc {
+ pub doc: Vec<String>,
+ pub name: String,
+ pub traits: Vec<String>,
+ pub variants: Vec<String>,
+}
+
+#[derive(Debug, Clone)]
+pub struct AstTokenEnumSrc {
+ pub doc: Vec<String>,
+ pub name: String,
+ pub variants: Vec<String>,
+}
+
+impl Field {
+ pub fn is_many(&self) -> bool {
+ matches!(
+ self,
+ Field::Node {
+ cardinality: Cardinality::Many,
+ ..
+ }
+ )
+ }
+
+ pub fn token_name(&self) -> Option<String> {
+ match self {
+ Field::Token(token) => Some(token.clone()),
+ _ => None,
+ }
+ }
+ pub fn token_kind(&self, kinds: &KindsSrc) -> Option<TokenStream> {
+ match self {
+ Field::Token(token) => Some(kinds.token(token).expect("token exists").reference()),
+ _ => None,
+ }
+ }
+ pub fn is_token_enum(&self, grammar: &AstSrc) -> bool {
+ match self {
+ Field::Node { ty, .. } => grammar.token_enums.iter().any(|e| &e.name == ty),
+ _ => false,
+ }
+ }
+
+ pub fn method_name(&self, kinds: &KindsSrc) -> proc_macro2::Ident {
+ match self {
+ Field::Token(name) => kinds.token(name).expect("token exists").method_name(),
+ Field::Node { name, .. } => {
+ format_ident!("{}", name)
+ }
+ }
+ }
+ pub fn ty(&self) -> proc_macro2::Ident {
+ match self {
+ Field::Token(_) => format_ident!("SyntaxToken"),
+ Field::Node { ty, .. } => format_ident!("{}", ty),
+ }
+ }
+}
+
+pub fn lower(kinds: &KindsSrc, grammar: &Grammar) -> AstSrc {
+ let mut res = AstSrc {
+ // tokens,
+ ..Default::default()
+ };
+
+ let nodes = grammar.iter().collect::<Vec<_>>();
+
+ for &node in &nodes {
+ let name = grammar[node].name.clone();
+ let rule = &grammar[node].rule;
+ match lower_enum(grammar, rule) {
+ Some(variants) => {
+ let enum_src = AstEnumSrc {
+ doc: Vec::new(),
+ name,
+ traits: Vec::new(),
+ variants,
+ };
+ res.enums.push(enum_src);
+ }
+ None => match lower_token_enum(grammar, rule) {
+ Some(variants) => {
+ let tokens_enum_src = AstTokenEnumSrc {
+ doc: Vec::new(),
+ name,
+ variants,
+ };
+ res.token_enums.push(tokens_enum_src);
+ }
+ None => {
+ let mut fields = Vec::new();
+ lower_rule(&mut fields, grammar, None, rule, false);
+ let mut types = HashMap::new();
+ for field in fields.iter().filter(|f| f.token_name().is_none()) {
+ if let Some(old) = types.insert(field.ty(), field.method_name(kinds)) {
+ // panic!("{name}.{} has same type as {name}.{}, resolve conflict by wrapping one field: {}", old, field.method_name(kinds), field.ty());
+ }
+ // TODO: check for assignable field types, i.e you can have
+ // ```
+ // SomeEnum =
+ // SomeItem
+ // | SomeOtherItem
+ // ```
+ // And check above will fail to detect conflict in
+ // ```
+ // SomeStruct =
+ // SomeEnum
+ // SomeItem
+ // ```
+ // Despite generating getters, which will both return SomeEnum
+ }
+ res.nodes.push(AstNodeSrc {
+ doc: Vec::new(),
+ name,
+ traits: Vec::new(),
+ fields,
+ });
+ }
+ },
+ }
+ }
+
+ deduplicate_fields(&mut res);
+ extract_struct_traits(kinds, &mut res);
+ extract_enum_traits(&mut res);
+ res
+}
+
+fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {
+ let alternatives = match rule {
+ Rule::Alt(it) => it,
+ _ => return None,
+ };
+ let mut variants = Vec::new();
+ for alternative in alternatives {
+ match alternative {
+ Rule::Node(it) => variants.push(grammar[*it].name.clone()),
+ Rule::Token(it) if grammar[*it].name == ";" => (),
+ _ => return None,
+ }
+ }
+ Some(variants)
+}
+fn lower_token_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {
+ let alternatives = match rule {
+ Rule::Alt(it) => it,
+ _ => return None,
+ };
+ let mut variants = Vec::new();
+ for alternative in alternatives {
+ match alternative {
+ Rule::Token(it) => variants.push(grammar[*it].name.clone()),
+ _ => return None,
+ }
+ }
+ Some(variants)
+}
+
+fn lower_rule(
+ acc: &mut Vec<Field>,
+ grammar: &Grammar,
+ label: Option<&String>,
+ rule: &Rule,
+ in_optional: bool,
+) {
+ if lower_comma_list(acc, grammar, label, rule) {
+ return;
+ }
+
+ match rule {
+ Rule::Node(node) => {
+ let ty = grammar[*node].name.clone();
+ let name = label.cloned().unwrap_or_else(|| to_lower_snake_case(&ty));
+ let field = Field::Node {
+ name,
+ ty,
+ cardinality: if in_optional {
+ Cardinality::Optional
+ } else {
+ Cardinality::Required
+ },
+ };
+ acc.push(field);
+ }
+ Rule::Token(token) => {
+ assert!(label.is_none(), "uexpected label: {:?}", label);
+ let name = grammar[*token].name.clone();
+ let field = Field::Token(name);
+ acc.push(field);
+ }
+ Rule::Rep(inner) => {
+ if let Rule::Node(node) = &**inner {
+ let ty = grammar[*node].name.clone();
+ let name = label
+ .cloned()
+ .unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
+ let field = Field::Node {
+ name,
+ ty,
+ cardinality: Cardinality::Many,
+ };
+ acc.push(field);
+ return;
+ }
+ todo!("unsupported repitition: {:?}", rule)
+ }
+ Rule::Labeled { label: l, rule } => {
+ assert!(label.is_none());
+ lower_rule(acc, grammar, Some(l), rule, in_optional);
+ }
+ Rule::Seq(rules) | Rule::Alt(rules) => {
+ for rule in rules {
+ lower_rule(acc, grammar, label, rule, in_optional)
+ }
+ }
+ Rule::Opt(rule) => lower_rule(acc, grammar, label, rule, true),
+ }
+}
+
+// (T (',' T)* ','?)
+fn lower_comma_list(
+ acc: &mut Vec<Field>,
+ grammar: &Grammar,
+ label: Option<&String>,
+ rule: &Rule,
+) -> bool {
+ let rule = match rule {
+ Rule::Seq(it) => it,
+ _ => return false,
+ };
+ let (node, repeat, trailing_comma) = match rule.as_slice() {
+ [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => {
+ (node, repeat, trailing_comma)
+ }
+ _ => return false,
+ };
+ let repeat = match &**repeat {
+ Rule::Seq(it) => it,
+ _ => return false,
+ };
+ match repeat.as_slice() {
+ [comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (),
+ _ => return false,
+ }
+ let ty = grammar[*node].name.clone();
+ let name = label
+ .cloned()
+ .unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
+ let field = Field::Node {
+ name,
+ ty,
+ cardinality: Cardinality::Many,
+ };
+ acc.push(field);
+ true
+}
+
+fn deduplicate_fields(ast: &mut AstSrc) {
+ for node in &mut ast.nodes {
+ let mut i = 0;
+ 'outer: while i < node.fields.len() {
+ for j in 0..i {
+ let f1 = &node.fields[i];
+ let f2 = &node.fields[j];
+ if f1 == f2 {
+ node.fields.remove(i);
+ continue 'outer;
+ }
+ }
+ i += 1;
+ }
+ }
+}
+
+fn extract_struct_traits(kinds: &KindsSrc, ast: &mut AstSrc) {
+ // TODO: add common accessor traits here.
+ let traits: &[(&str, &[&str])] = &[];
+
+ for node in &mut ast.nodes {
+ for (name, methods) in traits {
+ extract_struct_trait(kinds, node, name, methods);
+ }
+ }
+}
+
+fn extract_struct_trait(
+ kinds: &KindsSrc,
+ node: &mut AstNodeSrc,
+ trait_name: &str,
+ methods: &[&str],
+) {
+ let mut to_remove = Vec::new();
+ for (i, field) in node.fields.iter().enumerate() {
+ let method_name = field.method_name(kinds).to_string();
+ if methods.iter().any(|&it| it == method_name) {
+ to_remove.push(i);
+ }
+ }
+ if to_remove.len() == methods.len() {
+ node.traits.push(trait_name.to_string());
+ node.remove_field(to_remove);
+ }
+}
+
+fn extract_enum_traits(ast: &mut AstSrc) {
+ let enums = ast.enums.clone();
+ for enm in &mut ast.enums {
+ let nodes = &ast.nodes;
+
+ let mut variant_traits = enm.variants.iter().map(|var| {
+ nodes
+ .iter()
+ .find_map(|node| {
+ if &node.name != var {
+ return None;
+ }
+ Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())
+ })
+ .unwrap_or_else(|| {
+ enums
+ .iter()
+ .find_map(|node| {
+ if &node.name != var {
+ return None;
+ }
+ Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())
+ })
+ .unwrap_or_else(|| {
+ panic!("could not find struct {var} for enum {}::{var}", enm.name)
+ })
+ })
+ });
+
+ let mut enum_traits = match variant_traits.next() {
+ Some(it) => it,
+ None => continue,
+ };
+ for traits in variant_traits {
+ enum_traits = enum_traits.intersection(&traits).cloned().collect();
+ }
+ enm.traits = enum_traits.into_iter().collect();
+ }
+}
xtask/src/sourcegen/kinds.rsdiffbeforeafterboth--- /dev/null
+++ b/xtask/src/sourcegen/kinds.rs
@@ -0,0 +1,279 @@
+#[derive(Debug)]
+pub struct KindsSrc {
+ /// Key - how this token appears in ungrammar
+ defined_tokens: IndexMap<String, TokenKind>,
+ defined_node_names: HashSet<String>,
+ pub nodes: Vec<String>,
+}
+
+#[derive(Debug, Clone)]
+pub enum TokenKind {
+ /// May exist in token tree, but never in source code
+ Meta { grammar_name: String, name: String },
+ /// Specific parsing/lexing errors may be emitted as this type of kind
+ Error {
+ grammar_name: String,
+ name: String,
+ /// Is this error returned by lexer directly, or from lex.rs
+ is_lexer_error: bool,
+ regex: Option<String>,
+ priority: Option<u32>,
+ },
+ /// Keyword - literal match of token
+ Keyword {
+ /// How this keyword appears in grammar/code, should be same as Kinds key
+ code: String,
+ name: String,
+ },
+ /// Literal - something defined by user, i.e strings, identifiers, smth
+ Literal {
+ /// How this keyword appears in grammar, should be same as Kinds key
+ grammar_name: String,
+ name: String,
+ /// Regex for Logos lexer
+ regex: String,
+ /// Path to custom lexer
+ lexer: Option<String>,
+ },
+}
+
+impl TokenKind {
+ pub fn grammar_name(&self) -> &str {
+ match self {
+ TokenKind::Keyword { code, .. } => code,
+ TokenKind::Literal { grammar_name, .. } => grammar_name,
+ TokenKind::Meta { grammar_name, .. } => grammar_name,
+ TokenKind::Error { grammar_name, .. } => grammar_name,
+ }
+ }
+ /// How this keyword should appear in kinds enum, screaming snake cased
+ pub fn name(&self) -> &str {
+ match self {
+ TokenKind::Keyword { name, .. } => name,
+ TokenKind::Literal { name, .. } => name,
+ TokenKind::Meta { name, .. } => name,
+ TokenKind::Error { name, .. } => name,
+ }
+ }
+ pub fn expand_kind(&self) -> TokenStream {
+ let name = format_ident!("{}", self.name());
+ let attr = match self {
+ TokenKind::Keyword { code, .. } => quote! {#[token(#code)]},
+ TokenKind::Literal { regex, lexer, .. } => {
+ let lexer = lexer
+ .as_deref()
+ .map(TokenStream::from_str)
+ .map(|r| r.expect("path is correct"));
+ quote! {#[regex(#regex, #lexer)]}
+ }
+ TokenKind::Error {
+ regex, priority, ..
+ } if regex.is_some() => {
+ let priority = priority.map(|p| quote! {, priority = #p});
+ quote! {#[regex(#regex #priority)]}
+ }
+ _ => quote! {},
+ };
+ quote! {
+ #attr
+ #name
+ }
+ }
+ pub fn expand_t_macros(&self) -> Option<TokenStream> {
+ match self {
+ TokenKind::Keyword { code, name } => {
+ let code = escape_token_macro(code);
+ let name = format_ident!("{name}");
+ Some(quote! {
+ [#code] => {$crate::SyntaxKind::#name}
+ })
+ }
+ // Meta items should not appear in T![_]
+ _ => None,
+ }
+ }
+
+ /// How this token should be referenced in code
+ /// Keywords are referenced with `T![_]` macro,
+ /// and literals are referenced directly by name
+ pub fn reference(&self) -> TokenStream {
+ match self {
+ TokenKind::Keyword { code, .. } => {
+ let code = escape_token_macro(code);
+ quote! {T![#code]}
+ }
+ _ => {
+ let name = self.name();
+ let ident = format_ident!("{name}");
+ quote! {#ident}
+ }
+ }
+ }
+
+ pub fn method_name(&self) -> Ident {
+ match self {
+ TokenKind::Keyword { name, .. } => {
+ format_ident!("{}_token", name.to_lowercase())
+ }
+ TokenKind::Literal { name, .. } => {
+ format_ident!("{}_lit", name.to_lowercase())
+ }
+ TokenKind::Meta { name, .. } => format_ident!("{}_meta", name.to_lowercase()),
+ TokenKind::Error { name, .. } => format_ident!("{}_error", name.to_lowercase()),
+ }
+ }
+}
+
+#[macro_export]
+macro_rules! define_kinds {
+ ($into:ident = lit($name:literal) => $regex:literal $(, $lexer:literal)? $(; $($rest:tt)*)?) => {{
+ $into.define_token(TokenKind::Literal {
+ grammar_name: format!("LIT_{}!", $name),
+ name: $name.to_owned(),
+ regex: $regex.to_owned(),
+ lexer: None $(.or_else(|| Some($lexer.to_string())))?,
+ });
+ $(define_kinds!($into = $($rest)*))?
+ }};
+ ($into:ident = error($name:literal$(, priority = $priority:literal)? $(, lexer = $lexer:literal)?) $(=> $regex:literal)? $(; $($rest:tt)*)?) => {{
+ {
+ let regex = None$(.or(Some($regex.to_owned())))?;
+ let priority = None$(.or(Some($priority)))?;
+ $into.define_token(TokenKind::Error {
+ grammar_name: format!("ERROR_{}!", $name),
+ name: format!("ERROR_{}", $name),
+ is_lexer_error: false $(|| $lexer)? || regex.is_some() || priority.is_some(),
+ regex,
+ priority,
+ });
+ }
+ $(define_kinds!($into = $($rest)*))?
+ }};
+ ($into:ident = $tok:literal => $name:literal $(; $($rest:tt)*)?) => {{
+ $into.define_token(TokenKind::Keyword {
+ code: format!("{}", $tok),
+ name: $name.to_owned(),
+ });
+ $(define_kinds!($into = $($rest)*))?
+ }};
+ ($into:ident =) => {{}}
+}
+use std::{collections::HashSet, str::FromStr};
+
+pub use define_kinds;
+use indexmap::IndexMap;
+use proc_macro2::{Ident, TokenStream};
+use quote::{format_ident, quote};
+
+use super::escape_token_macro;
+
+impl KindsSrc {
+ pub fn new() -> Self {
+ Self {
+ defined_tokens: IndexMap::new(),
+ defined_node_names: HashSet::new(),
+ nodes: Vec::new(),
+ }
+ }
+ pub fn define_token(&mut self, token: TokenKind) {
+ assert!(
+ self.defined_node_names.insert(token.name().to_owned()),
+ "node name already defined: {}",
+ token.name()
+ );
+ assert!(
+ self.defined_tokens
+ .insert(token.grammar_name().to_owned(), token.clone())
+ .is_none(),
+ "token already defined: {}",
+ token.grammar_name()
+ )
+ }
+ pub fn define_node(&mut self, node: &str) {
+ assert!(
+ self.defined_node_names.insert(node.to_owned()),
+ "node name already defined: {}",
+ node
+ );
+ self.nodes.push(node.to_string())
+ }
+ pub fn token(&self, tok: &str) -> Option<&TokenKind> {
+ self.defined_tokens.get(tok)
+ }
+ pub fn is_token(&self, tok: &str) -> bool {
+ self.defined_tokens.contains_key(tok)
+ }
+ pub fn tokens(&self) -> impl Iterator<Item = &TokenKind> {
+ self.defined_tokens.iter().map(|(_, v)| v)
+ }
+}
+
+pub fn jsonnet_kinds() -> KindsSrc {
+ let mut kinds = KindsSrc::new();
+ define_kinds![kinds =
+ "||" => "OR";
+ "??" => "NULL_COAELSE";
+ "&&" => "AND";
+ "|" => "BIT_OR";
+ "^" => "BIT_XOR";
+ "&" => "BIT_AND";
+ "==" => "EQ";
+ "!=" => "NE";
+ "<" => "LT";
+ ">" => "GT";
+ "<=" => "LE";
+ ">=" => "GE";
+ "<<" => "LHS";
+ ">>" => "RHS";
+ "+" => "PLUS";
+ "-" => "MINUS";
+ "*" => "MUL";
+ "/" => "DIV";
+ "%" => "MODULO";
+ "!" => "NOT";
+ "~" => "BIT_NOT";
+ "[" => "L_BRACK";
+ "]" => "R_BRACK";
+ "(" => "L_PAREN";
+ ")" => "R_PAREN";
+ "{" => "L_BRACE";
+ "}" => "R_BRACE";
+ ":" => "COLON";
+ "::" => "COLONCOLON";
+ ":::" => "COLONCOLONCOLON";
+ ";" => "SEMI";
+ "." => "DOT";
+ "..." => "DOTDOTDOT";
+ "," => "COMMA";
+ "$" => "DOLLAR";
+ "=" => "ASSIGN";
+ "?" => "QUESTION_MARK";
+ // Literals
+ lit("FLOAT") => r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?";
+ error("FLOAT_JUNK_AFTER_POINT") => r"(?:0|[1-9][0-9]*)\.[^0-9]";
+ error("FLOAT_JUNK_AFTER_EXPONENT") => r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?[eE][^+\-0-9]";
+ error("FLOAT_JUNK_AFTER_EXPONENT_SIGN") => r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?[eE][+-][^0-9]";
+ lit("STRING_DOUBLE") => "\"(?s:[^\"\\\\]|\\\\.)*\"";
+ error("STRING_DOUBLE_UNTERMINATED") => "\"(?s:[^\"\\\\]|\\\\.)*";
+ lit("STRING_SINGLE") => "'(?s:[^'\\\\]|\\\\.)*'";
+ error("STRING_SINGLE_UNTERMINATED") => "'(?s:[^'\\\\]|\\\\.)*";
+ lit("STRING_DOUBLE_VERBATIM") => "@\"(?:[^\"]|\"\")*\"";
+ error("STRING_DOUBLE_VERBATIM_UNTERMINATED") => "@\"(?:[^\"]|\"\")*";
+ lit("STRING_SINGLE_VERBATIM") => "@'(?:[^']|'')*'";
+ error("STRING_SINGLE_VERBATIM_UNTERMINATED") => "@'(?:[^']|'')*";
+ error("STRING_VERBATIM_MISSING_QUOTES") => "@[^\"'\\s]\\S+";
+ lit("STRING_BLOCK") => r"\|\|\|", "crate::string_block::lex_str_block_test";
+ error("STRING_BLOCK_UNEXPECTED_END", lexer = true);
+ error("STRING_BLOCK_MISSING_NEW_LINE", lexer = true);
+ error("STRING_BLOCK_MISSING_TERMINATION", lexer = true);
+ error("STRING_BLOCK_MISSING_INDENT", lexer = true);
+ lit("IDENT") => r"[_a-zA-Z][_a-zA-Z0-9]*";
+ lit("WHITESPACE") => r"[ \t\n\r]+";
+ lit("SINGLE_LINE_SLASH_COMMENT") => r"//[^\r\n]*(\r\n|\n)?";
+ lit("SINGLE_LINE_HASH_COMMENT") => r"#[^\r\n]*(\r\n|\n)?";
+ lit("MULTI_LINE_COMMENT") => r"/\*([^*]|\*[^/])*\*/";
+ error("COMMENT_TOO_SHORT") => r"/\*/";
+ error("COMMENT_UNTERMINATED") => r"/\*([^*]|\*[^/])+";
+ ];
+ kinds
+}
xtask/src/sourcegen/mod.rsdiffbeforeafterboth--- /dev/null
+++ b/xtask/src/sourcegen/mod.rs
@@ -0,0 +1,541 @@
+use std::path::PathBuf;
+
+use anyhow::Result;
+use ast::{lower, AstSrc};
+use itertools::Itertools;
+use kinds::{KindsSrc, TokenKind};
+use proc_macro2::{Punct, Spacing, TokenStream};
+use quote::{format_ident, quote};
+use ungrammar::Grammar;
+use util::{ensure_file_contents, reformat, to_pascal_case, to_upper_snake_case};
+
+mod ast;
+mod kinds;
+mod util;
+
+enum SpecialName {
+ Literal,
+ Meta,
+ Error,
+}
+fn classify_special(name: &str) -> Option<(SpecialName, &str)> {
+ let name = name.strip_suffix('!')?;
+ Some(if let Some(name) = name.strip_prefix("LIT_") {
+ (SpecialName::Literal, name)
+ } else if let Some(name) = name.strip_prefix("META_") {
+ (SpecialName::Meta, name)
+ } else if let Some(name) = name.strip_prefix("ERROR_") {
+ (SpecialName::Error, name)
+ } else {
+ return None;
+ })
+}
+
+pub fn generate_ungrammar() -> Result<()> {
+ let grammar: Grammar = include_str!(concat!(
+ env!("CARGO_MANIFEST_DIR"),
+ "/../crates/jrsonnet-rowan-parser/jsonnet.ungram"
+ ))
+ .parse()?;
+
+ let mut kinds = kinds::jsonnet_kinds();
+ let ast = lower(&kinds, &grammar);
+
+ for token in grammar.tokens() {
+ let token = &grammar[token];
+ let token = &token.name.clone();
+ if !kinds.is_token(token) {
+ if let Some((special, name)) = classify_special(token) {
+ match special {
+ SpecialName::Literal => panic!("literal is not defined: {name}"),
+ SpecialName::Meta => {
+ eprintln!("implicit meta: {}", name);
+ kinds.define_token(TokenKind::Meta {
+ grammar_name: token.to_owned(),
+ name: format!("META_{}", name),
+ })
+ }
+ SpecialName::Error => {
+ eprintln!("implicit error: {}", name);
+ kinds.define_token(TokenKind::Error {
+ grammar_name: token.to_owned(),
+ name: format!("ERROR_{}", name),
+ regex: None,
+ priority: None,
+ is_lexer_error: true,
+ })
+ }
+ };
+ continue;
+ };
+ let name = to_upper_snake_case(token);
+ eprintln!("implicit kw: {}", token);
+ kinds.define_token(TokenKind::Keyword {
+ code: token.to_owned(),
+ name: format!("{name}_KW"),
+ });
+ }
+ }
+ for node in &ast.nodes {
+ let name = to_upper_snake_case(&node.name);
+ kinds.define_node(&name);
+ }
+ for enum_ in &ast.enums {
+ let name = to_upper_snake_case(&enum_.name);
+ kinds.define_node(&name);
+ }
+ for token_enum in &ast.token_enums {
+ let name = to_upper_snake_case(&token_enum.name);
+ kinds.define_node(&name);
+ }
+
+ let syntax_kinds = generate_syntax_kinds(&kinds, &ast)?;
+
+ let nodes = generate_nodes(&kinds, &ast)?;
+ ensure_file_contents(
+ &PathBuf::from(concat!(
+ env!("CARGO_MANIFEST_DIR"),
+ "/../crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs",
+ )),
+ &syntax_kinds,
+ )?;
+ ensure_file_contents(
+ &PathBuf::from(concat!(
+ env!("CARGO_MANIFEST_DIR"),
+ "/../crates/jrsonnet-rowan-parser/src/generated/nodes.rs",
+ )),
+ &nodes,
+ )?;
+ Ok(())
+}
+
+fn generate_syntax_kinds(kinds: &KindsSrc, grammar: &AstSrc) -> Result<String> {
+ let t_macros = kinds.tokens().filter_map(TokenKind::expand_t_macros);
+ let token_kinds = kinds.tokens().map(TokenKind::expand_kind);
+
+ let keywords = kinds
+ .tokens()
+ .filter(|k| matches!(k, TokenKind::Keyword { .. }))
+ .map(TokenKind::name)
+ .map(|n| format_ident!("{n}"));
+
+ let nodes = kinds
+ .nodes
+ .iter()
+ .map(|name| format_ident!("{}", name))
+ .collect::<Vec<_>>();
+
+ let enums = grammar
+ .enums
+ .iter()
+ .map(|e| format_ident!("{}", to_upper_snake_case(&e.name)))
+ .chain(
+ grammar
+ .token_enums
+ .iter()
+ .map(|e| format_ident!("{}", to_upper_snake_case(&e.name))),
+ );
+
+ let ast = quote! {
+ #![allow(bad_style, missing_docs, unreachable_pub, clippy::manual_non_exhaustive, clippy::match_like_matches_macro)]
+ use logos::Logos;
+
+ /// The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`.
+ #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Logos)]
+ #[repr(u16)]
+ pub enum SyntaxKind {
+ #[doc(hidden)]
+ TOMBSTONE,
+ #[doc(hidden)]
+ EOF,
+ #(#token_kinds,)*
+ /// Also acts as __LAST_TOKEN
+ #[error]
+ LEXING_ERROR,
+ #(#nodes,)*
+ #[doc(hidden)]
+ __LAST,
+ }
+ use self::SyntaxKind::*;
+
+ impl SyntaxKind {
+ pub fn is_keyword(self) -> bool {
+ match self {
+ #(#keywords)|* => true,
+ _ => false,
+ }
+ }
+ pub fn is_enum(self) -> bool {
+ match self {
+ #(#enums)|* => true,
+ _ => false,
+ }
+ }
+
+ pub fn from_raw(r: u16) -> Self {
+ assert!(r < Self::__LAST as u16);
+ unsafe { std::mem::transmute(r) }
+ }
+ pub fn into_raw(self) -> u16 {
+ self as u16
+ }
+ }
+
+ #[macro_export]
+ macro_rules! T {#(#t_macros);*}
+ pub use T;
+ };
+
+ reformat(&ast.to_string())
+}
+
+fn generate_nodes(kinds: &KindsSrc, grammar: &AstSrc) -> Result<String> {
+ let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
+ .nodes
+ .iter()
+ .map(|node| {
+ let name = format_ident!("{}", node.name);
+ let kind = format_ident!("{}", to_upper_snake_case(&node.name));
+ let traits = node.traits.iter().map(|trait_name| {
+ let trait_name = format_ident!("{}", trait_name);
+ quote!(impl ast::#trait_name for #name {})
+ });
+
+ let methods = node.fields.iter().map(|field| {
+ let method_name = field.method_name(kinds);
+ let ty = field.ty();
+
+ if field.is_many() {
+ quote! {
+ pub fn #method_name(&self) -> AstChildren<#ty> {
+ support::children(&self.syntax)
+ }
+ }
+ } else if let Some(token_kind) = field.token_kind(kinds) {
+ quote! {
+ pub fn #method_name(&self) -> Option<#ty> {
+ support::token(&self.syntax, #token_kind)
+ }
+ }
+ } else if field.is_token_enum(grammar) {
+ quote! {
+ pub fn #method_name(&self) -> Option<#ty> {
+ support::token_child(&self.syntax)
+ }
+ }
+ } else {
+ quote! {
+ pub fn #method_name(&self) -> Option<#ty> {
+ support::child(&self.syntax)
+ }
+ }
+ }
+ });
+ (
+ quote! {
+ #[pretty_doc_comment_placeholder_workaround]
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+ pub struct #name {
+ pub(crate) syntax: SyntaxNode,
+ }
+
+ #(#traits)*
+
+ impl #name {
+ #(#methods)*
+ }
+ },
+ quote! {
+ impl AstNode for #name {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ kind == #kind
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+ }
+ },
+ )
+ })
+ .unzip();
+
+ let (enum_defs, enum_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
+ .enums
+ .iter()
+ .map(|en| {
+ let variants: Vec<_> = en
+ .variants
+ .iter()
+ .map(|var| format_ident!("{}", var))
+ .collect();
+ let name = format_ident!("{}", en.name);
+ let kinds: Vec<_> = variants
+ .iter()
+ .map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string())))
+ .collect();
+ let traits = en.traits.iter().map(|trait_name| {
+ let trait_name = format_ident!("{}", trait_name);
+ quote!(impl ast::#trait_name for #name {})
+ });
+
+ let ast_node = quote! {
+ impl AstNode for #name {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ #(#kinds)|* => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ #(
+ #kinds => #name::#variants(#variants { syntax }),
+ )*
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ #(
+ #name::#variants(it) => &it.syntax,
+ )*
+ }
+ }
+ }
+ };
+
+ (
+ quote! {
+ #[pretty_doc_comment_placeholder_workaround]
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+ pub enum #name {
+ #(#variants(#variants),)*
+ }
+
+ #(#traits)*
+ },
+ quote! {
+ #(
+ impl From<#variants> for #name {
+ fn from(node: #variants) -> #name {
+ #name::#variants(node)
+ }
+ }
+ )*
+ #ast_node
+ },
+ )
+ })
+ .unzip();
+
+ let (token_enum_defs, token_enum_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
+ .token_enums
+ .iter()
+ .map(|en| {
+ let variants: Vec<_> = en
+ .variants
+ .iter()
+ .map(|token| {
+ format_ident!(
+ "{}",
+ to_pascal_case(kinds.token(token).expect("token exists").name())
+ )
+ })
+ .collect();
+ let name = format_ident!("{}", en.name);
+ let kind_name = format_ident!("{}Kind", en.name);
+ let kinds: Vec<_> = variants
+ .iter()
+ .map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string())))
+ .collect();
+
+ let ast_node = quote! {
+ impl AstToken for #name {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ #kind_name::can_cast(kind)
+ }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ let kind = #kind_name::cast(syntax.kind())?;
+ Some(#name { syntax, kind })
+ }
+ fn syntax(&self) -> &SyntaxToken {
+ &self.syntax
+ }
+ }
+
+ impl #kind_name {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ #(#kinds)|* => true,
+ _ => false,
+ }
+ }
+ pub fn cast(kind: SyntaxKind) -> Option<Self> {
+ let res = match kind {
+ #(#kinds => Self::#variants,)*
+ _ => return None,
+ };
+ Some(res)
+ }
+ }
+ };
+
+ (
+ quote! {
+ #[pretty_doc_comment_placeholder_workaround]
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+ pub struct #name { syntax: SyntaxToken, kind: #kind_name }
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ pub enum #kind_name {
+ #(#variants,)*
+ }
+ },
+ quote! {
+ #ast_node
+
+ impl #name {
+ pub fn kind(&self) -> #kind_name {
+ self.kind
+ }
+ }
+
+ impl std::fmt::Display for #name {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+ }
+ },
+ )
+ })
+ .unzip();
+
+ let (any_node_defs, any_node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
+ .nodes
+ .iter()
+ .flat_map(|node| node.traits.iter().map(move |t| (t, node)))
+ .into_group_map()
+ .into_iter()
+ .sorted_by_key(|(k, _)| *k)
+ .map(|(trait_name, nodes)| {
+ let name = format_ident!("Any{}", trait_name);
+ let trait_name = format_ident!("{}", trait_name);
+ let kinds: Vec<_> = nodes
+ .iter()
+ .map(|name| format_ident!("{}", to_upper_snake_case(&name.name.to_string())))
+ .collect();
+
+ (
+ quote! {
+ #[pretty_doc_comment_placeholder_workaround]
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+ pub struct #name {
+ pub(crate) syntax: SyntaxNode,
+ }
+ impl ast::#trait_name for #name {}
+ },
+ quote! {
+ impl #name {
+ #[inline]
+ pub fn new<T: ast::#trait_name>(node: T) -> #name {
+ #name {
+ syntax: node.syntax().clone()
+ }
+ }
+ }
+ impl AstNode for #name {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ #(#kinds)|* => true,
+ _ => false,
+ }
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ Self::can_cast(syntax.kind()).then(|| #name { syntax })
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ &self.syntax
+ }
+ }
+ },
+ )
+ })
+ .unzip();
+
+ let enum_names = grammar.enums.iter().map(|it| &it.name);
+ let node_names = grammar.nodes.iter().map(|it| &it.name);
+
+ let display_impls = enum_names
+ .chain(node_names.clone())
+ .map(|it| format_ident!("{}", it))
+ .map(|name| {
+ quote! {
+ impl std::fmt::Display for #name {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+ }
+ }
+ });
+
+ let ast = quote! {
+ #![allow(non_snake_case, clippy::match_like_matches_macro)]
+
+ use crate::{
+ SyntaxNode, SyntaxToken, SyntaxKind::{self, *},
+ ast::{AstNode, AstToken, AstChildren, support},
+ T,
+ };
+
+ #(#node_defs)*
+ #(#enum_defs)*
+ #(#token_enum_defs)*
+ #(#any_node_defs)*
+ #(#node_boilerplate_impls)*
+ #(#enum_boilerplate_impls)*
+ #(#token_enum_boilerplate_impls)*
+ #(#any_node_boilerplate_impls)*
+ #(#display_impls)*
+ };
+
+ let ast = ast.to_string().replace("T ! [", "T![");
+
+ let mut res = String::with_capacity(ast.len() * 2);
+
+ let mut docs = grammar
+ .nodes
+ .iter()
+ .map(|it| &it.doc)
+ .chain(grammar.enums.iter().map(|it| &it.doc));
+
+ for chunk in ast.split("# [pretty_doc_comment_placeholder_workaround] ") {
+ res.push_str(chunk);
+ if let Some(doc) = docs.next() {
+ write_doc_comment(doc, &mut res);
+ }
+ }
+
+ let res = reformat(&res)?;
+ Ok(res.replace("#[derive", "\n#[derive"))
+}
+
+fn write_doc_comment(contents: &[String], dest: &mut String) {
+ use std::fmt::Write;
+ for line in contents {
+ writeln!(dest, "///{}", line).unwrap();
+ }
+}
+
+pub fn escape_token_macro(token: &str) -> TokenStream {
+ if "{}[]()$".contains(token) {
+ let c = token.chars().next().unwrap();
+ quote! { #c }
+ } else if token.contains('$') {
+ quote! { #token }
+ } else {
+ let cs = token.chars().map(|c| Punct::new(c, Spacing::Joint));
+ quote! { #(#cs)* }
+ }
+}
xtask/src/sourcegen/util.rsdiffbeforeafterboth--- /dev/null
+++ b/xtask/src/sourcegen/util.rs
@@ -0,0 +1,87 @@
+use std::{fs, path::Path};
+
+use anyhow::Result;
+use xshell::{cmd, Shell};
+
+/// Checks that the `file` has the specified `contents`. If that is not the
+/// case, updates the file and then fails the test.
+pub fn ensure_file_contents(file: &Path, contents: &str) -> Result<()> {
+ if let Ok(old_contents) = fs::read_to_string(file) {
+ if normalize_newlines(&old_contents) == normalize_newlines(contents) {
+ // File is already up to date.
+ return Ok(());
+ }
+ }
+
+ eprintln!("{} was not up-to-date, updating", file.display());
+ if let Some(parent) = file.parent() {
+ let _ = fs::create_dir_all(parent);
+ }
+ fs::write(file, contents).unwrap();
+ Ok(())
+}
+
+// Eww, someone configured git to use crlf?
+fn normalize_newlines(s: &str) -> String {
+ s.replace("\r\n", "\n")
+}
+
+pub(crate) fn pluralize(s: &str) -> String {
+ format!("{}s", s)
+}
+
+pub fn to_upper_snake_case(s: &str) -> String {
+ let mut buf = String::with_capacity(s.len());
+ let mut prev = false;
+ for c in s.chars() {
+ if c.is_ascii_uppercase() && prev {
+ buf.push('_')
+ }
+ prev = true;
+
+ buf.push(c.to_ascii_uppercase());
+ }
+ buf
+}
+pub fn to_lower_snake_case(s: &str) -> String {
+ let mut buf = String::with_capacity(s.len());
+ let mut prev = false;
+ for c in s.chars() {
+ if c.is_ascii_uppercase() && prev {
+ buf.push('_')
+ }
+ prev = true;
+
+ buf.push(c.to_ascii_lowercase());
+ }
+ buf
+}
+
+pub fn to_pascal_case(s: &str) -> String {
+ let mut buf = String::with_capacity(s.len());
+ let mut prev_is_underscore = true;
+ for c in s.chars() {
+ if c == '_' {
+ prev_is_underscore = true;
+ } else if prev_is_underscore {
+ buf.push(c.to_ascii_uppercase());
+ prev_is_underscore = false;
+ } else {
+ buf.push(c.to_ascii_lowercase());
+ }
+ }
+ buf
+}
+
+pub fn reformat(text: &str) -> Result<String> {
+ // let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
+ // rustfmt()?;
+ let sh = Shell::new()?;
+ let stdout = cmd!(sh, "rustfmt").stdin(text).read()?;
+ Ok(format!(
+ "{}\n\n{}\n",
+ "//! This is a generated file, please do not edit manually. Changes can be
+//! made in codegeneration that lives in `xtask` top-level dir.",
+ stdout
+ ))
+}