git.delta.rocks / jrsonnet / refs/commits / 4ebc2d766e69

difftreelog

Merge pull request #132 from CertainLach/lsp

Yaroslav Bolyukin2023-09-22parents: #609e5ef #d1754bf.patch.diff
in: master
Introduce experimental rowan parser and pretty-printer

74 files changed

added.cargo/config.tomldiffbeforeafterboth
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,2 @@
+[alias]
+xtask = "run --package xtask --bin xtask --"
modifiedCargo.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"
modifiedCargo.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" }
addedcmds/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"
addedcmds/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)
+	}
+}
addedcmds/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
+}
addedcmds/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);
+	}
+}
addedcmds/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: '',
+  },
+}
addedcmds/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: '',
+          },
+        }"
+	)))
+}
addedcmds/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"
addedcmds/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()
+}
modifiedcmds/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" }
modifiedcmds/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>,
modifiedcrates/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
addedcrates/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"
addedcrates/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!'
addedcrates/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)
+	}
+}
addedcrates/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);
+		}
+	}
+}
addedcrates/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;
addedcrates/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)
+	}
+}
addedcrates/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;
addedcrates/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>;
addedcrates/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()
+}
addedcrates/jrsonnet-rowan-parser/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/lib.rs
@@ -0,0 +1,71 @@
+#![deny(unused_must_use)]
+
+use event::Sink;
+use generated::nodes::{SourceFile, Trivia};
+use lex::lex;
+use parser::{LocatedSyntaxError, Parser};
+pub use rowan;
+
+mod ast;
+mod event;
+mod generated;
+mod language;
+mod lex;
+mod marker;
+mod parser;
+mod precedence;
+mod string_block;
+mod tests;
+mod token_set;
+
+pub use ast::{AstChildren, AstNode, AstToken};
+pub use generated::{nodes, syntax_kinds::SyntaxKind};
+pub use language::*;
+pub use token_set::SyntaxKindSet;
+
+use self::{
+	ast::support,
+	generated::nodes::{Expr, ExprBinary, ExprObjExtend},
+};
+
+pub fn parse(input: &str) -> (SourceFile, Vec<LocatedSyntaxError>) {
+	let lexemes = lex(input);
+	let kinds = lexemes
+		.iter()
+		.map(|l| l.kind)
+		.filter(|k| !Trivia::can_cast(*k))
+		.collect();
+	let parser = Parser::new(kinds);
+	let events = parser.parse();
+	let sink = Sink::new(events, &lexemes);
+
+	let parse = sink.finish();
+	(
+		SourceFile {
+			syntax: parse.syntax(),
+		},
+		parse.errors,
+	)
+}
+impl ExprBinary {
+	pub fn lhs_work(&self) -> Option<Expr> {
+		support::child(self.syntax())
+	}
+	pub fn rhs_work(&self) -> Option<Expr> {
+		let mut children = support::children(self.syntax());
+		// skip lhs
+		children.next()?;
+		children.next()
+	}
+}
+impl ExprObjExtend {
+	pub fn lhs_work(&self) -> Option<Expr> {
+		support::child(self.syntax())
+	}
+	pub fn rhs_work(&self) -> Option<Expr> {
+		let mut children = support::children(self.syntax());
+		// skip lhs
+		children.next()?;
+		children.next()
+	}
+}
addedcrates/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(),
+			}),
+		)
+	}
+}
addedcrates/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())
+	}
+}
addedcrates/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),
+		}
+	}
+}
addedcrates/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"
addedcrates/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"
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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"
+
addedcrates/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
+   `----
+
addedcrates/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
+   `----
addedcrates/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"
addedcrates/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
+   `----
addedcrates/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"
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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"
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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"
addedcrates/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"
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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
+   `----
addedcrates/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"
addedcrates/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"
addedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__stdlib.snapdiffbeforeafterboth
after · crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__stdlib.snap
1---2source: crates/jrsonnet-rowan-parser/src/tests.rs3expression: "{\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    '<': '&lt;',\n    '>': '&gt;',\n    '&': '&amp;',\n    '\"': '&quot;',\n    \"'\": '&apos;',\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"4---5SOURCE_FILE@0..78356  EXPR@0..78347    EXPR_OBJECT@0..78348      OBJ_BODY_MEMBER_LIST@0..78349        L_BRACE@0..1 "{"10        WHITESPACE@1..4 "\n  "11        MEMBER_BIND_STMT@4..2012          OBJ_LOCAL@4..2013            LOCAL_KW@4..9 "local"14            WHITESPACE@9..10 " "15            BIND_DESTRUCT@10..2016              DESTRUCT_FULL@10..1317                NAME@10..1318                  IDENT@10..13 "std"19              WHITESPACE@13..14 " "20              ASSIGN@14..15 "="21              WHITESPACE@15..16 " "22              EXPR@16..2023                EXPR_LITERAL@16..2024                  SELF_KW@16..20 "self"25        COMMA@20..21 ","26        WHITESPACE@21..24 "\n  "27        MEMBER_BIND_STMT@24..4128          OBJ_LOCAL@24..4129            LOCAL_KW@24..29 "local"30            WHITESPACE@29..30 " "31            BIND_DESTRUCT@30..4132              DESTRUCT_FULL@30..3233                NAME@30..3234                  IDENT@30..32 "id"35              WHITESPACE@32..33 " "36              ASSIGN@33..34 "="37              WHITESPACE@34..35 " "38              EXPR@35..4139                EXPR_VAR@35..3840                  NAME@35..3841                    IDENT@35..38 "std"42                SUFFIX_INDEX@38..4143                  DOT@38..39 "."44                  NAME@39..4145                    IDENT@39..41 "id"46        COMMA@41..42 ","47        WHITESPACE@42..46 "\n\n  "48        MEMBER_FIELD_NORMAL@46..22449          FIELD_NAME_FIXED@46..5450            NAME@46..5451              IDENT@46..54 "thisFile"52          COLONCOLON@54..56 "::"53          WHITESPACE@56..57 " "54          EXPR@57..22455            EXPR_ERROR@57..22456              ERROR_KW@57..62 "error"57              WHITESPACE@62..63 " "58              EXPR@63..22459                EXPR_STRING@63..22460                  STRING_SINGLE@63..224 "'std.thisFile is depr ..."61        COMMA@224..225 ","62        WHITESPACE@225..229 "\n\n  "63        MEMBER_FIELD_METHOD@229..37264          FIELD_NAME_FIXED@229..24065            NAME@229..24066              IDENT@229..240 "lstripChars"67          PARAMS_DESC@240..25268            L_PAREN@240..241 "("69            PARAM@241..24470              DESTRUCT_FULL@241..24471                NAME@241..24472                  IDENT@241..244 "str"73            COMMA@244..245 ","74            WHITESPACE@245..246 " "75            PARAM@246..25176              DESTRUCT_FULL@246..25177                NAME@246..25178                  IDENT@246..251 "chars"79            R_PAREN@251..252 ")"80          COLONCOLON@252..254 "::"81          WHITESPACE@254..259 "\n    "82          EXPR@259..37283            EXPR_IF_THEN_ELSE@259..37284              IF_KW@259..261 "if"85              WHITESPACE@261..262 " "86              EXPR@262..31087                EXPR_BINARY@262..31088                  EXPR@262..28189                    EXPR_BINARY@262..28190                      EXPR@262..26591                        EXPR_VAR@262..26592                          NAME@262..26593                            IDENT@262..265 "std"94                      SUFFIX_INDEX@265..27295                        DOT@265..266 "."96                        NAME@266..27297                          IDENT@266..272 "length"98                      SUFFIX_APPLY@272..27799                        ARGS_DESC@272..277100                          L_PAREN@272..273 "("101                          ARG@273..276102                            EXPR@273..276103                              EXPR_VAR@273..276104                                NAME@273..276105                                  IDENT@273..276 "str"106                          R_PAREN@276..277 ")"107                      WHITESPACE@277..278 " "108                      GT@278..279 ">"109                      WHITESPACE@279..280 " "110                      EXPR@280..281111                        EXPR_NUMBER@280..281112                          FLOAT@280..281 "0"113                  WHITESPACE@281..282 " "114                  AND@282..284 "&&"115                  WHITESPACE@284..285 " "116                  EXPR@285..310117                    EXPR_VAR@285..288118                      NAME@285..288119                        IDENT@285..288 "std"120                    SUFFIX_INDEX@288..295121                      DOT@288..289 "."122                      NAME@289..295123                        IDENT@289..295 "member"124                    SUFFIX_APPLY@295..310125                      ARGS_DESC@295..310126                        L_PAREN@295..296 "("127                        ARG@296..301128                          EXPR@296..301129                            EXPR_VAR@296..301130                              NAME@296..301131                                IDENT@296..301 "chars"132                        COMMA@301..302 ","133                        WHITESPACE@302..303 " "134                        ARG@303..309135                          EXPR@303..309136                            EXPR_VAR@303..306137                              NAME@303..306138                                IDENT@303..306 "str"139                            SUFFIX_INDEX_EXPR@306..309140                              L_BRACK@306..307 "["141                              EXPR@307..308142                                EXPR_NUMBER@307..308143                                  FLOAT@307..308 "0"144                              R_BRACK@308..309 "]"145                        R_PAREN@309..310 ")"146              WHITESPACE@310..311 " "147              THEN_KW@311..315 "then"148              WHITESPACE@315..322 "\n      "149              TRUE_EXPR@322..353150                EXPR@322..353151                  EXPR_VAR@322..325152                    NAME@322..325153                      IDENT@322..325 "std"154                  SUFFIX_INDEX@325..337155                    DOT@325..326 "."156                    NAME@326..337157                      IDENT@326..337 "lstripChars"158                  SUFFIX_APPLY@337..353159                    ARGS_DESC@337..353160                      L_PAREN@337..338 "("161                      ARG@338..345162                        EXPR@338..345163                          EXPR_VAR@338..341164                            NAME@338..341165                              IDENT@338..341 "str"166                          SUFFIX_SLICE@341..345167                            SLICE_DESC@341..345168                              L_BRACK@341..342 "["169                              EXPR@342..343170                                EXPR_NUMBER@342..343171                                  FLOAT@342..343 "1"172                              COLON@343..344 ":"173                              R_BRACK@344..345 "]"174                      COMMA@345..346 ","175                      WHITESPACE@346..347 " "176                      ARG@347..352177                        EXPR@347..352178                          EXPR_VAR@347..352179                            NAME@347..352180                              IDENT@347..352 "chars"181                      R_PAREN@352..353 ")"182              WHITESPACE@353..358 "\n    "183              ELSE_KW@358..362 "else"184              WHITESPACE@362..369 "\n      "185              FALSE_EXPR@369..372186                EXPR@369..372187                  EXPR_VAR@369..372188                    NAME@369..372189                      IDENT@369..372 "str"190        COMMA@372..373 ","191        WHITESPACE@373..377 "\n\n  "192        MEMBER_FIELD_METHOD@377..553193          FIELD_NAME_FIXED@377..388194            NAME@377..388195              IDENT@377..388 "rstripChars"196          PARAMS_DESC@388..400197            L_PAREN@388..389 "("198            PARAM@389..392199              DESTRUCT_FULL@389..392200                NAME@389..392201                  IDENT@389..392 "str"202            COMMA@392..393 ","203            WHITESPACE@393..394 " "204            PARAM@394..399205              DESTRUCT_FULL@394..399206                NAME@394..399207                  IDENT@394..399 "chars"208            R_PAREN@399..400 ")"209          COLONCOLON@400..402 "::"210          WHITESPACE@402..407 "\n    "211          EXPR@407..553212            STMT_LOCAL@407..435213              LOCAL_KW@407..412 "local"214              WHITESPACE@412..413 " "215              BIND_DESTRUCT@413..434216                DESTRUCT_FULL@413..416217                  NAME@413..416218                    IDENT@413..416 "len"219                WHITESPACE@416..417 " "220                ASSIGN@417..418 "="221                WHITESPACE@418..419 " "222                EXPR@419..434223                  EXPR_VAR@419..422224                    NAME@419..422225                      IDENT@419..422 "std"226                  SUFFIX_INDEX@422..429227                    DOT@422..423 "."228                    NAME@423..429229                      IDENT@423..429 "length"230                  SUFFIX_APPLY@429..434231                    ARGS_DESC@429..434232                      L_PAREN@429..430 "("233                      ARG@430..433234                        EXPR@430..433235                          EXPR_VAR@430..433236                            NAME@430..433237                              IDENT@430..433 "str"238                      R_PAREN@433..434 ")"239              SEMI@434..435 ";"240            WHITESPACE@435..440 "\n    "241            EXPR_IF_THEN_ELSE@440..553242              IF_KW@440..442 "if"243              WHITESPACE@442..443 " "244              EXPR@443..485245                EXPR_BINARY@443..485246                  EXPR@443..450247                    EXPR_BINARY@443..450248                      EXPR@443..446249                        EXPR_VAR@443..446250                          NAME@443..446251                            IDENT@443..446 "len"252                      WHITESPACE@446..447 " "253                      GT@447..448 ">"254                      WHITESPACE@448..449 " "255                      EXPR@449..450256                        EXPR_NUMBER@449..450257                          FLOAT@449..450 "0"258                  WHITESPACE@450..451 " "259                  AND@451..453 "&&"260                  WHITESPACE@453..454 " "261                  EXPR@454..485262                    EXPR_VAR@454..457263                      NAME@454..457264                        IDENT@454..457 "std"265                    SUFFIX_INDEX@457..464266                      DOT@457..458 "."267                      NAME@458..464268                        IDENT@458..464 "member"269                    SUFFIX_APPLY@464..485270                      ARGS_DESC@464..485271                        L_PAREN@464..465 "("272                        ARG@465..470273                          EXPR@465..470274                            EXPR_VAR@465..470275                              NAME@465..470276                                IDENT@465..470 "chars"277                        COMMA@470..471 ","278                        WHITESPACE@471..472 " "279                        ARG@472..484280                          EXPR@472..484281                            EXPR_VAR@472..475282                              NAME@472..475283                                IDENT@472..475 "str"284                            SUFFIX_INDEX_EXPR@475..484285                              L_BRACK@475..476 "["286                              EXPR@476..483287                                EXPR_BINARY@476..483288                                  EXPR@476..479289                                    EXPR_VAR@476..479290                                      NAME@476..479291                                        IDENT@476..479 "len"292                                  WHITESPACE@479..480 " "293                                  MINUS@480..481 "-"294                                  WHITESPACE@481..482 " "295                                  EXPR@482..483296                                    EXPR_NUMBER@482..483297                                      FLOAT@482..483 "1"298                              R_BRACK@483..484 "]"299                        R_PAREN@484..485 ")"300              WHITESPACE@485..486 " "301              THEN_KW@486..490 "then"302              WHITESPACE@490..497 "\n      "303              TRUE_EXPR@497..534304                EXPR@497..534305                  EXPR_VAR@497..500306                    NAME@497..500307                      IDENT@497..500 "std"308                  SUFFIX_INDEX@500..512309                    DOT@500..501 "."310                    NAME@501..512311                      IDENT@501..512 "rstripChars"312                  SUFFIX_APPLY@512..534313                    ARGS_DESC@512..534314                      L_PAREN@512..513 "("315                      ARG@513..526316                        EXPR@513..526317                          EXPR_VAR@513..516318                            NAME@513..516319                              IDENT@513..516 "str"320                          SUFFIX_SLICE@516..526321                            SLICE_DESC@516..526322                              L_BRACK@516..517 "["323                              COLON@517..518 ":"324                              SLICE_DESC_END@518..525325                                EXPR@518..525326                                  EXPR_BINARY@518..525327                                    EXPR@518..521328                                      EXPR_VAR@518..521329                                        NAME@518..521330                                          IDENT@518..521 "len"331                                    WHITESPACE@521..522 " "332                                    MINUS@522..523 "-"333                                    WHITESPACE@523..524 " "334                                    EXPR@524..525335                                      EXPR_NUMBER@524..525336                                        FLOAT@524..525 "1"337                              R_BRACK@525..526 "]"338                      COMMA@526..527 ","339                      WHITESPACE@527..528 " "340                      ARG@528..533341                        EXPR@528..533342                          EXPR_VAR@528..533343                            NAME@528..533344                              IDENT@528..533 "chars"345                      R_PAREN@533..534 ")"346              WHITESPACE@534..539 "\n    "347              ELSE_KW@539..543 "else"348              WHITESPACE@543..550 "\n      "349              FALSE_EXPR@550..553350                EXPR@550..553351                  EXPR_VAR@550..553352                    NAME@550..553353                      IDENT@550..553 "str"354        COMMA@553..554 ","355        WHITESPACE@554..558 "\n\n  "356        MEMBER_FIELD_METHOD@558..638357          FIELD_NAME_FIXED@558..568358            NAME@558..568359              IDENT@558..568 "stripChars"360          PARAMS_DESC@568..580361            L_PAREN@568..569 "("362            PARAM@569..572363              DESTRUCT_FULL@569..572364                NAME@569..572365                  IDENT@569..572 "str"366            COMMA@572..573 ","367            WHITESPACE@573..574 " "368            PARAM@574..579369              DESTRUCT_FULL@574..579370                NAME@574..579371                  IDENT@574..579 "chars"372            R_PAREN@579..580 ")"373          COLONCOLON@580..582 "::"374          WHITESPACE@582..587 "\n    "375          EXPR@587..638376            EXPR_VAR@587..590377              NAME@587..590378                IDENT@587..590 "std"379            SUFFIX_INDEX@590..602380              DOT@590..591 "."381              NAME@591..602382                IDENT@591..602 "lstripChars"383            SUFFIX_APPLY@602..638384              ARGS_DESC@602..638385                L_PAREN@602..603 "("386                ARG@603..630387                  EXPR@603..630388                    EXPR_VAR@603..606389                      NAME@603..606390                        IDENT@603..606 "std"391                    SUFFIX_INDEX@606..618392                      DOT@606..607 "."393                      NAME@607..618394                        IDENT@607..618 "rstripChars"395                    SUFFIX_APPLY@618..630396                      ARGS_DESC@618..630397                        L_PAREN@618..619 "("398                        ARG@619..622399                          EXPR@619..622400                            EXPR_VAR@619..622401                              NAME@619..622402                                IDENT@619..622 "str"403                        COMMA@622..623 ","404                        WHITESPACE@623..624 " "405                        ARG@624..629406                          EXPR@624..629407                            EXPR_VAR@624..629408                              NAME@624..629409                                IDENT@624..629 "chars"410                        R_PAREN@629..630 ")"411                COMMA@630..631 ","412                WHITESPACE@631..632 " "413                ARG@632..637414                  EXPR@632..637415                    EXPR_VAR@632..637416                      NAME@632..637417                        IDENT@632..637 "chars"418                R_PAREN@637..638 ")"419        COMMA@638..639 ","420        WHITESPACE@639..643 "\n\n  "421        MEMBER_FIELD_METHOD@643..921422          FIELD_NAME_FIXED@643..654423            NAME@643..654424              IDENT@643..654 "splitLimitR"425          PARAMS_DESC@654..673426            L_PAREN@654..655 "("427            PARAM@655..658428              DESTRUCT_FULL@655..658429                NAME@655..658430                  IDENT@655..658 "str"431            COMMA@658..659 ","432            WHITESPACE@659..660 " "433            PARAM@660..661434              DESTRUCT_FULL@660..661435                NAME@660..661436                  IDENT@660..661 "c"437            COMMA@661..662 ","438            WHITESPACE@662..663 " "439            PARAM@663..672440              DESTRUCT_FULL@663..672441                NAME@663..672442                  IDENT@663..672 "maxsplits"443            R_PAREN@672..673 ")"444          COLONCOLON@673..675 "::"445          WHITESPACE@675..680 "\n    "446          EXPR@680..921447            EXPR_IF_THEN_ELSE@680..921448              IF_KW@680..682 "if"449              WHITESPACE@682..683 " "450              EXPR@683..698451                EXPR_BINARY@683..698452                  EXPR@683..692453                    EXPR_VAR@683..692454                      NAME@683..692455                        IDENT@683..692 "maxsplits"456                  WHITESPACE@692..693 " "457                  EQ@693..695 "=="458                  WHITESPACE@695..696 " "459                  EXPR@696..698460                    EXPR_UNARY@696..698461                      MINUS@696..697 "-"462                      EXPR_NUMBER@697..698463                        FLOAT@697..698 "1"464              WHITESPACE@698..699 " "465              THEN_KW@699..703 "then"466              WHITESPACE@703..710 "\n      "467              TRUE_EXPR@710..736468                EXPR@710..736469                  EXPR_VAR@710..713470                    NAME@710..713471                      IDENT@710..713 "std"472                  SUFFIX_INDEX@713..724473                    DOT@713..714 "."474                    NAME@714..724475                      IDENT@714..724 "splitLimit"476                  SUFFIX_APPLY@724..736477                    ARGS_DESC@724..736478                      L_PAREN@724..725 "("479                      ARG@725..728480                        EXPR@725..728481                          EXPR_VAR@725..728482                            NAME@725..728483                              IDENT@725..728 "str"484                      COMMA@728..729 ","485                      WHITESPACE@729..730 " "486                      ARG@730..731487                        EXPR@730..731488                          EXPR_VAR@730..731489                            NAME@730..731490                              IDENT@730..731 "c"491                      COMMA@731..732 ","492                      WHITESPACE@732..733 " "493                      ARG@733..735494                        EXPR@733..735495                          EXPR_UNARY@733..735496                            MINUS@733..734 "-"497                            EXPR_NUMBER@734..735498                              FLOAT@734..735 "1"499                      R_PAREN@735..736 ")"500              WHITESPACE@736..741 "\n    "501              ELSE_KW@741..745 "else"502              WHITESPACE@745..752 "\n      "503              FALSE_EXPR@752..921504                EXPR@752..921505                  STMT_LOCAL@752..820506                    LOCAL_KW@752..757 "local"507                    WHITESPACE@757..758 " "508                    BIND_FUNCTION@758..819509                      NAME@758..764510                        IDENT@758..764 "revStr"511                      PARAMS_DESC@764..769512                        L_PAREN@764..765 "("513                        PARAM@765..768514                          DESTRUCT_FULL@765..768515                            NAME@765..768516                              IDENT@765..768 "str"517                        R_PAREN@768..769 ")"518                      WHITESPACE@769..770 " "519                      ASSIGN@770..771 "="520                      WHITESPACE@771..772 " "521                      EXPR@772..819522                        EXPR_VAR@772..775523                          NAME@772..775524                            IDENT@772..775 "std"525                        SUFFIX_INDEX@775..780526                          DOT@775..776 "."527                          NAME@776..780528                            IDENT@776..780 "join"529                        SUFFIX_APPLY@780..819530                          ARGS_DESC@780..819531                            L_PAREN@780..781 "("532                            ARG@781..783533                              EXPR@781..783534                                EXPR_STRING@781..783535                                  STRING_SINGLE@781..783 "''"536                            COMMA@783..784 ","537                            WHITESPACE@784..785 " "538                            ARG@785..818539                              EXPR@785..818540                                EXPR_VAR@785..788541                                  NAME@785..788542                                    IDENT@785..788 "std"543                                SUFFIX_INDEX@788..796544                                  DOT@788..789 "."545                                  NAME@789..796546                                    IDENT@789..796 "reverse"547                                SUFFIX_APPLY@796..818548                                  ARGS_DESC@796..818549                                    L_PAREN@796..797 "("550                                    ARG@797..817551                                      EXPR@797..817552                                        EXPR_VAR@797..800553                                          NAME@797..800554                                            IDENT@797..800 "std"555                                        SUFFIX_INDEX@800..812556                                          DOT@800..801 "."557                                          NAME@801..812558                                            IDENT@801..812 "stringChars"559                                        SUFFIX_APPLY@812..817560                                          ARGS_DESC@812..817561                                            L_PAREN@812..813 "("562                                            ARG@813..816563                                              EXPR@813..816564                                                EXPR_VAR@813..816565                                                  NAME@813..816566                                                    IDENT@813..816 "str"567                                            R_PAREN@816..817 ")"568                                    R_PAREN@817..818 ")"569                            R_PAREN@818..819 ")"570                    SEMI@819..820 ";"571                  WHITESPACE@820..827 "\n      "572                  EXPR_VAR@827..830573                    NAME@827..830574                      IDENT@827..830 "std"575                  SUFFIX_INDEX@830..834576                    DOT@830..831 "."577                    NAME@831..834578                      IDENT@831..834 "map"579                  SUFFIX_APPLY@834..921580                    ARGS_DESC@834..921581                      L_PAREN@834..835 "("582                      ARG@835..856583                        EXPR@835..856584                          EXPR_FUNCTION@835..856585                            FUNCTION_KW@835..843 "function"586                            PARAMS_DESC@843..846587                              L_PAREN@843..844 "("588                              PARAM@844..845589                                DESTRUCT_FULL@844..845590                                  NAME@844..845591                                    IDENT@844..845 "e"592                              R_PAREN@845..846 ")"593                            WHITESPACE@846..847 " "594                            EXPR@847..856595                              EXPR_VAR@847..853596                                NAME@847..853597                                  IDENT@847..853 "revStr"598                              SUFFIX_APPLY@853..856599                                ARGS_DESC@853..856600                                  L_PAREN@853..854 "("601                                  ARG@854..855602                                    EXPR@854..855603                                      EXPR_VAR@854..855604                                        NAME@854..855605                                          IDENT@854..855 "e"606                                  R_PAREN@855..856 ")"607                      COMMA@856..857 ","608                      WHITESPACE@857..858 " "609                      ARG@858..920610                        EXPR@858..920611                          EXPR_VAR@858..861612                            NAME@858..861613                              IDENT@858..861 "std"614                          SUFFIX_INDEX@861..869615                            DOT@861..862 "."616                            NAME@862..869617                              IDENT@862..869 "reverse"618                          SUFFIX_APPLY@869..920619                            ARGS_DESC@869..920620                              L_PAREN@869..870 "("621                              ARG@870..919622                                EXPR@870..919623                                  EXPR_VAR@870..873624                                    NAME@870..873625                                      IDENT@870..873 "std"626                                  SUFFIX_INDEX@873..884627                                    DOT@873..874 "."628                                    NAME@874..884629                                      IDENT@874..884 "splitLimit"630                                  SUFFIX_APPLY@884..919631                                    ARGS_DESC@884..919632                                      L_PAREN@884..885 "("633                                      ARG@885..896634                                        EXPR@885..896635                                          EXPR_VAR@885..891636                                            NAME@885..891637                                              IDENT@885..891 "revStr"638                                          SUFFIX_APPLY@891..896639                                            ARGS_DESC@891..896640                                              L_PAREN@891..892 "("641                                              ARG@892..895642                                                EXPR@892..895643                                                  EXPR_VAR@892..895644                                                    NAME@892..895645                                                      IDENT@892..895 "str"646                                              R_PAREN@895..896 ")"647                                      COMMA@896..897 ","648                                      WHITESPACE@897..898 " "649                                      ARG@898..907650                                        EXPR@898..907651                                          EXPR_VAR@898..904652                                            NAME@898..904653                                              IDENT@898..904 "revStr"654                                          SUFFIX_APPLY@904..907655                                            ARGS_DESC@904..907656                                              L_PAREN@904..905 "("657                                              ARG@905..906658                                                EXPR@905..906659                                                  EXPR_VAR@905..906660                                                    NAME@905..906661                                                      IDENT@905..906 "c"662                                              R_PAREN@906..907 ")"663                                      COMMA@907..908 ","664                                      WHITESPACE@908..909 " "665                                      ARG@909..918666                                        EXPR@909..918667                                          EXPR_VAR@909..918668                                            NAME@909..918669                                              IDENT@909..918 "maxsplits"670                                      R_PAREN@918..919 ")"671                              R_PAREN@919..920 ")"672                      R_PAREN@920..921 ")"673        COMMA@921..922 ","674        WHITESPACE@922..926 "\n\n  "675        MEMBER_FIELD_METHOD@926..968676          FIELD_NAME_FIXED@926..931677            NAME@926..931678              IDENT@926..931 "split"679          PARAMS_DESC@931..939680            L_PAREN@931..932 "("681            PARAM@932..935682              DESTRUCT_FULL@932..935683                NAME@932..935684                  IDENT@932..935 "str"685            COMMA@935..936 ","686            WHITESPACE@936..937 " "687            PARAM@937..938688              DESTRUCT_FULL@937..938689                NAME@937..938690                  IDENT@937..938 "c"691            R_PAREN@938..939 ")"692          COLONCOLON@939..941 "::"693          WHITESPACE@941..942 " "694          EXPR@942..968695            EXPR_VAR@942..945696              NAME@942..945697                IDENT@942..945 "std"698            SUFFIX_INDEX@945..956699              DOT@945..946 "."700              NAME@946..956701                IDENT@946..956 "splitLimit"702            SUFFIX_APPLY@956..968703              ARGS_DESC@956..968704                L_PAREN@956..957 "("705                ARG@957..960706                  EXPR@957..960707                    EXPR_VAR@957..960708                      NAME@957..960709                        IDENT@957..960 "str"710                COMMA@960..961 ","711                WHITESPACE@961..962 " "712                ARG@962..963713                  EXPR@962..963714                    EXPR_VAR@962..963715                      NAME@962..963716                        IDENT@962..963 "c"717                COMMA@963..964 ","718                WHITESPACE@964..965 " "719                ARG@965..967720                  EXPR@965..967721                    EXPR_UNARY@965..967722                      MINUS@965..966 "-"723                      EXPR_NUMBER@966..967724                        FLOAT@966..967 "1"725                R_PAREN@967..968 ")"726        COMMA@968..969 ","727        WHITESPACE@969..973 "\n\n  "728        MEMBER_FIELD_METHOD@973..1331729          FIELD_NAME_FIXED@973..985730            NAME@973..985731              IDENT@973..985 "mapWithIndex"732          PARAMS_DESC@985..996733            L_PAREN@985..986 "("734            PARAM@986..990735              DESTRUCT_FULL@986..990736                NAME@986..990737                  IDENT@986..990 "func"738            COMMA@990..991 ","739            WHITESPACE@991..992 " "740            PARAM@992..995741              DESTRUCT_FULL@992..995742                NAME@992..995743                  IDENT@992..995 "arr"744            R_PAREN@995..996 ")"745          COLONCOLON@996..998 "::"746          WHITESPACE@998..1003 "\n    "747          EXPR@1003..1331748            EXPR_IF_THEN_ELSE@1003..1331749              IF_KW@1003..1005 "if"750              WHITESPACE@1005..1006 " "751              EXPR@1006..1027752                EXPR_UNARY@1006..1027753                  NOT@1006..1007 "!"754                  EXPR_VAR@1007..1010755                    NAME@1007..1010756                      IDENT@1007..1010 "std"757                  SUFFIX_INDEX@1010..1021758                    DOT@1010..1011 "."759                    NAME@1011..1021760                      IDENT@1011..1021 "isFunction"761                  SUFFIX_APPLY@1021..1027762                    ARGS_DESC@1021..1027763                      L_PAREN@1021..1022 "("764                      ARG@1022..1026765                        EXPR@1022..1026766                          EXPR_VAR@1022..1026767                            NAME@1022..1026768                              IDENT@1022..1026 "func"769                      R_PAREN@1026..1027 ")"770              WHITESPACE@1027..1028 " "771              THEN_KW@1028..1032 "then"772              WHITESPACE@1032..1039 "\n      "773              TRUE_EXPR@1039..1117774                EXPR@1039..1117775                  EXPR_ERROR@1039..1117776                    ERROR_KW@1039..1044 "error"777                    WHITESPACE@1044..1045 " "778                    EXPR@1045..1117779                      EXPR_PARENED@1045..1117780                        L_PAREN@1045..1046 "("781                        EXPR@1046..1116782                          EXPR_BINARY@1046..1116783                            EXPR@1046..1099784                              EXPR_STRING@1046..1099785                                STRING_SINGLE@1046..1099 "'std.mapWithIndex fir ..."786                            WHITESPACE@1099..1100 " "787                            PLUS@1100..1101 "+"788                            WHITESPACE@1101..1102 " "789                            EXPR@1102..1116790                              EXPR_VAR@1102..1105791                                NAME@1102..1105792                                  IDENT@1102..1105 "std"793                              SUFFIX_INDEX@1105..1110794                                DOT@1105..1106 "."795                                NAME@1106..1110796                                  IDENT@1106..1110 "type"797                              SUFFIX_APPLY@1110..1116798                                ARGS_DESC@1110..1116799                                  L_PAREN@1110..1111 "("800                                  ARG@1111..1115801                                    EXPR@1111..1115802                                      EXPR_VAR@1111..1115803                                        NAME@1111..1115804                                          IDENT@1111..1115 "func"805                                  R_PAREN@1115..1116 ")"806                        R_PAREN@1116..1117 ")"807              WHITESPACE@1117..1122 "\n    "808              ELSE_KW@1122..1126 "else"809              WHITESPACE@1126..1127 " "810              FALSE_EXPR@1127..1331811                EXPR@1127..1331812                  EXPR_IF_THEN_ELSE@1127..1331813                    IF_KW@1127..1129 "if"814                    WHITESPACE@1129..1130 " "815                    EXPR@1130..1169816                      EXPR_BINARY@1130..1169817                        EXPR@1130..1147818                          EXPR_UNARY@1130..1147819                            NOT@1130..1131 "!"820                            EXPR_VAR@1131..1134821                              NAME@1131..1134822                                IDENT@1131..1134 "std"823                            SUFFIX_INDEX@1134..1142824                              DOT@1134..1135 "."825                              NAME@1135..1142826                                IDENT@1135..1142 "isArray"827                            SUFFIX_APPLY@1142..1147828                              ARGS_DESC@1142..1147829                                L_PAREN@1142..1143 "("830                                ARG@1143..1146831                                  EXPR@1143..1146832                                    EXPR_VAR@1143..1146833                                      NAME@1143..1146834                                        IDENT@1143..1146 "arr"835                                R_PAREN@1146..1147 ")"836                        WHITESPACE@1147..1148 " "837                        AND@1148..1150 "&&"838                        WHITESPACE@1150..1151 " "839                        EXPR@1151..1169840                          EXPR_UNARY@1151..1169841                            NOT@1151..1152 "!"842                            EXPR_VAR@1152..1155843                              NAME@1152..1155844                                IDENT@1152..1155 "std"845                            SUFFIX_INDEX@1155..1164846                              DOT@1155..1156 "."847                              NAME@1156..1164848                                IDENT@1156..1164 "isString"849                            SUFFIX_APPLY@1164..1169850                              ARGS_DESC@1164..1169851                                L_PAREN@1164..1165 "("852                                ARG@1165..1168853                                  EXPR@1165..1168854                                    EXPR_VAR@1165..1168855                                      NAME@1165..1168856                                        IDENT@1165..1168 "arr"857                                R_PAREN@1168..1169 ")"858                    WHITESPACE@1169..1170 " "859                    THEN_KW@1170..1174 "then"860                    WHITESPACE@1174..1181 "\n      "861                    TRUE_EXPR@1181..1256862                      EXPR@1181..1256863                        EXPR_ERROR@1181..1256864                          ERROR_KW@1181..1186 "error"865                          WHITESPACE@1186..1187 " "866                          EXPR@1187..1256867                            EXPR_PARENED@1187..1256868                              L_PAREN@1187..1188 "("869                              EXPR@1188..1255870                                EXPR_BINARY@1188..1255871                                  EXPR@1188..1239872                                    EXPR_STRING@1188..1239873                                      STRING_SINGLE@1188..1239 "'std.mapWithIndex sec ..."874                                  WHITESPACE@1239..1240 " "875                                  PLUS@1240..1241 "+"876                                  WHITESPACE@1241..1242 " "877                                  EXPR@1242..1255878                                    EXPR_VAR@1242..1245879                                      NAME@1242..1245880                                        IDENT@1242..1245 "std"881                                    SUFFIX_INDEX@1245..1250882                                      DOT@1245..1246 "."883                                      NAME@1246..1250884                                        IDENT@1246..1250 "type"885                                    SUFFIX_APPLY@1250..1255886                                      ARGS_DESC@1250..1255887                                        L_PAREN@1250..1251 "("888                                        ARG@1251..1254889                                          EXPR@1251..1254890                                            EXPR_VAR@1251..1254891                                              NAME@1251..1254892                                                IDENT@1251..1254 "arr"893                                        R_PAREN@1254..1255 ")"894                              R_PAREN@1255..1256 ")"895                    WHITESPACE@1256..1261 "\n    "896                    ELSE_KW@1261..1265 "else"897                    WHITESPACE@1265..1272 "\n      "898                    FALSE_EXPR@1272..1331899                      EXPR@1272..1331900                        EXPR_VAR@1272..1275901                          NAME@1272..1275902                            IDENT@1272..1275 "std"903                        SUFFIX_INDEX@1275..1285904                          DOT@1275..1276 "."905                          NAME@1276..1285906                            IDENT@1276..1285 "makeArray"907                        SUFFIX_APPLY@1285..1331908                          ARGS_DESC@1285..1331909                            L_PAREN@1285..1286 "("910                            ARG@1286..1301911                              EXPR@1286..1301912                                EXPR_VAR@1286..1289913                                  NAME@1286..1289914                                    IDENT@1286..1289 "std"915                                SUFFIX_INDEX@1289..1296916                                  DOT@1289..1290 "."917                                  NAME@1290..1296918                                    IDENT@1290..1296 "length"919                                SUFFIX_APPLY@1296..1301920                                  ARGS_DESC@1296..1301921                                    L_PAREN@1296..1297 "("922                                    ARG@1297..1300923                                      EXPR@1297..1300924                                        EXPR_VAR@1297..1300925                                          NAME@1297..1300926                                            IDENT@1297..1300 "arr"927                                    R_PAREN@1300..1301 ")"928                            COMMA@1301..1302 ","929                            WHITESPACE@1302..1303 " "930                            ARG@1303..1330931                              EXPR@1303..1330932                                EXPR_FUNCTION@1303..1330933                                  FUNCTION_KW@1303..1311 "function"934                                  PARAMS_DESC@1311..1314935                                    L_PAREN@1311..1312 "("936                                    PARAM@1312..1313937                                      DESTRUCT_FULL@1312..1313938                                        NAME@1312..1313939                                          IDENT@1312..1313 "i"940                                    R_PAREN@1313..1314 ")"941                                  WHITESPACE@1314..1315 " "942                                  EXPR@1315..1330943                                    EXPR_VAR@1315..1319944                                      NAME@1315..1319945                                        IDENT@1315..1319 "func"946                                    SUFFIX_APPLY@1319..1330947                                      ARGS_DESC@1319..1330948                                        L_PAREN@1319..1320 "("949                                        ARG@1320..1321950                                          EXPR@1320..1321951                                            EXPR_VAR@1320..1321952                                              NAME@1320..1321953                                                IDENT@1320..1321 "i"954                                        COMMA@1321..1322 ","955                                        WHITESPACE@1322..1323 " "956                                        ARG@1323..1329957                                          EXPR@1323..1329958                                            EXPR_VAR@1323..1326959                                              NAME@1323..1326960                                                IDENT@1323..1326 "arr"961                                            SUFFIX_INDEX_EXPR@1326..1329962                                              L_BRACK@1326..1327 "["963                                              EXPR@1327..1328964                                                EXPR_VAR@1327..1328965                                                  NAME@1327..1328966                                                    IDENT@1327..1328 "i"967                                              R_BRACK@1328..1329 "]"968                                        R_PAREN@1329..1330 ")"969                            R_PAREN@1330..1331 ")"970        COMMA@1331..1332 ","971        WHITESPACE@1332..1336 "\n\n  "972        MEMBER_FIELD_METHOD@1336..1664973          FIELD_NAME_FIXED@1336..1346974            NAME@1336..1346975              IDENT@1336..1346 "mapWithKey"976          PARAMS_DESC@1346..1357977            L_PAREN@1346..1347 "("978            PARAM@1347..1351979              DESTRUCT_FULL@1347..1351980                NAME@1347..1351981                  IDENT@1347..1351 "func"982            COMMA@1351..1352 ","983            WHITESPACE@1352..1353 " "984            PARAM@1353..1356985              DESTRUCT_FULL@1353..1356986                NAME@1353..1356987                  IDENT@1353..1356 "obj"988            R_PAREN@1356..1357 ")"989          COLONCOLON@1357..1359 "::"990          WHITESPACE@1359..1364 "\n    "991          EXPR@1364..1664992            EXPR_IF_THEN_ELSE@1364..1664993              IF_KW@1364..1366 "if"994              WHITESPACE@1366..1367 " "995              EXPR@1367..1388996                EXPR_UNARY@1367..1388997                  NOT@1367..1368 "!"998                  EXPR_VAR@1368..1371999                    NAME@1368..13711000                      IDENT@1368..1371 "std"1001                  SUFFIX_INDEX@1371..13821002                    DOT@1371..1372 "."1003                    NAME@1372..13821004                      IDENT@1372..1382 "isFunction"1005                  SUFFIX_APPLY@1382..13881006                    ARGS_DESC@1382..13881007                      L_PAREN@1382..1383 "("1008                      ARG@1383..13871009                        EXPR@1383..13871010                          EXPR_VAR@1383..13871011                            NAME@1383..13871012                              IDENT@1383..1387 "func"1013                      R_PAREN@1387..1388 ")"1014              WHITESPACE@1388..1389 " "1015              THEN_KW@1389..1393 "then"1016              WHITESPACE@1393..1400 "\n      "1017              TRUE_EXPR@1400..14761018                EXPR@1400..14761019                  EXPR_ERROR@1400..14761020                    ERROR_KW@1400..1405 "error"1021                    WHITESPACE@1405..1406 " "1022                    EXPR@1406..14761023                      EXPR_PARENED@1406..14761024                        L_PAREN@1406..1407 "("1025                        EXPR@1407..14751026                          EXPR_BINARY@1407..14751027                            EXPR@1407..14581028                              EXPR_STRING@1407..14581029                                STRING_SINGLE@1407..1458 "'std.mapWithKey first ..."1030                            WHITESPACE@1458..1459 " "1031                            PLUS@1459..1460 "+"1032                            WHITESPACE@1460..1461 " "1033                            EXPR@1461..14751034                              EXPR_VAR@1461..14641035                                NAME@1461..14641036                                  IDENT@1461..1464 "std"1037                              SUFFIX_INDEX@1464..14691038                                DOT@1464..1465 "."1039                                NAME@1465..14691040                                  IDENT@1465..1469 "type"1041                              SUFFIX_APPLY@1469..14751042                                ARGS_DESC@1469..14751043                                  L_PAREN@1469..1470 "("1044                                  ARG@1470..14741045                                    EXPR@1470..14741046                                      EXPR_VAR@1470..14741047                                        NAME@1470..14741048                                          IDENT@1470..1474 "func"1049                                  R_PAREN@1474..1475 ")"1050                        R_PAREN@1475..1476 ")"1051              WHITESPACE@1476..1481 "\n    "1052              ELSE_KW@1481..1485 "else"1053              WHITESPACE@1485..1486 " "1054              FALSE_EXPR@1486..16641055                EXPR@1486..16641056                  EXPR_IF_THEN_ELSE@1486..16641057                    IF_KW@1486..1488 "if"1058                    WHITESPACE@1488..1489 " "1059                    EXPR@1489..15071060                      EXPR_UNARY@1489..15071061                        NOT@1489..1490 "!"1062                        EXPR_VAR@1490..14931063                          NAME@1490..14931064                            IDENT@1490..1493 "std"1065                        SUFFIX_INDEX@1493..15021066                          DOT@1493..1494 "."1067                          NAME@1494..15021068                            IDENT@1494..1502 "isObject"1069                        SUFFIX_APPLY@1502..15071070                          ARGS_DESC@1502..15071071                            L_PAREN@1502..1503 "("1072                            ARG@1503..15061073                              EXPR@1503..15061074                                EXPR_VAR@1503..15061075                                  NAME@1503..15061076                                    IDENT@1503..1506 "obj"1077                            R_PAREN@1506..1507 ")"1078                    WHITESPACE@1507..1508 " "1079                    THEN_KW@1508..1512 "then"1080                    WHITESPACE@1512..1519 "\n      "1081                    TRUE_EXPR@1519..15931082                      EXPR@1519..15931083                        EXPR_ERROR@1519..15931084                          ERROR_KW@1519..1524 "error"1085                          WHITESPACE@1524..1525 " "1086                          EXPR@1525..15931087                            EXPR_PARENED@1525..15931088                              L_PAREN@1525..1526 "("1089                              EXPR@1526..15921090                                EXPR_BINARY@1526..15921091                                  EXPR@1526..15761092                                    EXPR_STRING@1526..15761093                                      STRING_SINGLE@1526..1576 "'std.mapWithKey secon ..."1094                                  WHITESPACE@1576..1577 " "1095                                  PLUS@1577..1578 "+"1096                                  WHITESPACE@1578..1579 " "1097                                  EXPR@1579..15921098                                    EXPR_VAR@1579..15821099                                      NAME@1579..15821100                                        IDENT@1579..1582 "std"1101                                    SUFFIX_INDEX@1582..15871102                                      DOT@1582..1583 "."1103                                      NAME@1583..15871104                                        IDENT@1583..1587 "type"1105                                    SUFFIX_APPLY@1587..15921106                                      ARGS_DESC@1587..15921107                                        L_PAREN@1587..1588 "("1108                                        ARG@1588..15911109                                          EXPR@1588..15911110                                            EXPR_VAR@1588..15911111                                              NAME@1588..15911112                                                IDENT@1588..1591 "obj"1113                                        R_PAREN@1591..1592 ")"1114                              R_PAREN@1592..1593 ")"1115                    WHITESPACE@1593..1598 "\n    "1116                    ELSE_KW@1598..1602 "else"1117                    WHITESPACE@1602..1609 "\n      "1118                    FALSE_EXPR@1609..16641119                      EXPR@1609..16641120                        EXPR_OBJECT@1609..16641121                          OBJ_BODY_COMP@1609..16641122                            L_BRACE@1609..1610 "{"1123                            WHITESPACE@1610..1611 " "1124                            MEMBER_FIELD_NORMAL@1611..16311125                              FIELD_NAME_DYNAMIC@1611..16141126                                L_BRACK@1611..1612 "["1127                                EXPR@1612..16131128                                  EXPR_VAR@1612..16131129                                    NAME@1612..16131130                                      IDENT@1612..1613 "k"1131                                R_BRACK@1613..1614 "]"1132                              COLON@1614..1615 ":"1133                              WHITESPACE@1615..1616 " "1134                              EXPR@1616..16311135                                EXPR_VAR@1616..16201136                                  NAME@1616..16201137                                    IDENT@1616..1620 "func"1138                                SUFFIX_APPLY@1620..16311139                                  ARGS_DESC@1620..16311140                                    L_PAREN@1620..1621 "("1141                                    ARG@1621..16221142                                      EXPR@1621..16221143                                        EXPR_VAR@1621..16221144                                          NAME@1621..16221145                                            IDENT@1621..1622 "k"1146                                    COMMA@1622..1623 ","1147                                    WHITESPACE@1623..1624 " "1148                                    ARG@1624..16301149                                      EXPR@1624..16301150                                        EXPR_VAR@1624..16271151                                          NAME@1624..16271152                                            IDENT@1624..1627 "obj"1153                                        SUFFIX_INDEX_EXPR@1627..16301154                                          L_BRACK@1627..1628 "["1155                                          EXPR@1628..16291156                                            EXPR_VAR@1628..16291157                                              NAME@1628..16291158                                                IDENT@1628..1629 "k"1159                                          R_BRACK@1629..1630 "]"1160                                    R_PAREN@1630..1631 ")"1161                            WHITESPACE@1631..1632 " "1162                            FOR_SPEC@1632..16621163                              FOR_KW@1632..1635 "for"1164                              WHITESPACE@1635..1636 " "1165                              DESTRUCT_FULL@1636..16371166                                NAME@1636..16371167                                  IDENT@1636..1637 "k"1168                              WHITESPACE@1637..1638 " "1169                              IN_KW@1638..1640 "in"1170                              WHITESPACE@1640..1641 " "1171                              EXPR@1641..16621172                                EXPR_VAR@1641..16441173                                  NAME@1641..16441174                                    IDENT@1641..1644 "std"1175                                SUFFIX_INDEX@1644..16571176                                  DOT@1644..1645 "."1177                                  NAME@1645..16571178                                    IDENT@1645..1657 "objectFields"1179                                SUFFIX_APPLY@1657..16621180                                  ARGS_DESC@1657..16621181                                    L_PAREN@1657..1658 "("1182                                    ARG@1658..16611183                                      EXPR@1658..16611184                                        EXPR_VAR@1658..16611185                                          NAME@1658..16611186                                            IDENT@1658..1661 "obj"1187                                    R_PAREN@1661..1662 ")"1188                            WHITESPACE@1662..1663 " "1189                            R_BRACE@1663..1664 "}"1190        COMMA@1664..1665 ","1191        WHITESPACE@1665..1669 "\n\n  "1192        MEMBER_FIELD_METHOD@1669..17121193          FIELD_NAME_FIXED@1669..16741194            NAME@1669..16741195              IDENT@1669..1674 "lines"1196          PARAMS_DESC@1674..16791197            L_PAREN@1674..1675 "("1198            PARAM@1675..16781199              DESTRUCT_FULL@1675..16781200                NAME@1675..16781201                  IDENT@1675..1678 "arr"1202            R_PAREN@1678..1679 ")"1203          COLONCOLON@1679..1681 "::"1204          WHITESPACE@1681..1686 "\n    "1205          EXPR@1686..17121206            EXPR_VAR@1686..16891207              NAME@1686..16891208                IDENT@1686..1689 "std"1209            SUFFIX_INDEX@1689..16941210              DOT@1689..1690 "."1211              NAME@1690..16941212                IDENT@1690..1694 "join"1213            SUFFIX_APPLY@1694..17121214              ARGS_DESC@1694..17121215                L_PAREN@1694..1695 "("1216                ARG@1695..16991217                  EXPR@1695..16991218                    EXPR_STRING@1695..16991219                      STRING_SINGLE@1695..1699 "'\\n'"1220                COMMA@1699..1700 ","1221                WHITESPACE@1700..1701 " "1222                ARG@1701..17111223                  EXPR@1701..17111224                    EXPR_BINARY@1701..17111225                      EXPR@1701..17041226                        EXPR_VAR@1701..17041227                          NAME@1701..17041228                            IDENT@1701..1704 "arr"1229                      WHITESPACE@1704..1705 " "1230                      PLUS@1705..1706 "+"1231                      WHITESPACE@1706..1707 " "1232                      EXPR@1707..17111233                        EXPR_ARRAY@1707..17111234                          L_BRACK@1707..1708 "["1235                          EXPR@1708..17101236                            EXPR_STRING@1708..17101237                              STRING_SINGLE@1708..1710 "''"1238                          R_BRACK@1710..1711 "]"1239                R_PAREN@1711..1712 ")"1240        COMMA@1712..1713 ","1241        WHITESPACE@1713..1717 "\n\n  "1242        MEMBER_FIELD_METHOD@1717..19291243          FIELD_NAME_FIXED@1717..17251244            NAME@1717..17251245              IDENT@1717..1725 "deepJoin"1246          PARAMS_DESC@1725..17301247            L_PAREN@1725..1726 "("1248            PARAM@1726..17291249              DESTRUCT_FULL@1726..17291250                NAME@1726..17291251                  IDENT@1726..1729 "arr"1252            R_PAREN@1729..1730 ")"1253          COLONCOLON@1730..1732 "::"1254          WHITESPACE@1732..1737 "\n    "1255          EXPR@1737..19291256            EXPR_IF_THEN_ELSE@1737..19291257              IF_KW@1737..1739 "if"1258              WHITESPACE@1739..1740 " "1259              EXPR@1740..17571260                EXPR_VAR@1740..17431261                  NAME@1740..17431262                    IDENT@1740..1743 "std"1263                SUFFIX_INDEX@1743..17521264                  DOT@1743..1744 "."1265                  NAME@1744..17521266                    IDENT@1744..1752 "isString"1267                SUFFIX_APPLY@1752..17571268                  ARGS_DESC@1752..17571269                    L_PAREN@1752..1753 "("1270                    ARG@1753..17561271                      EXPR@1753..17561272                        EXPR_VAR@1753..17561273                          NAME@1753..17561274                            IDENT@1753..1756 "arr"1275                    R_PAREN@1756..1757 ")"1276              WHITESPACE@1757..1758 " "1277              THEN_KW@1758..1762 "then"1278              WHITESPACE@1762..1769 "\n      "1279              TRUE_EXPR@1769..17721280                EXPR@1769..17721281                  EXPR_VAR@1769..17721282                    NAME@1769..17721283                      IDENT@1769..1772 "arr"1284              WHITESPACE@1772..1777 "\n    "1285              ELSE_KW@1777..1781 "else"1286              WHITESPACE@1781..1782 " "1287              FALSE_EXPR@1782..19291288                EXPR@1782..19291289                  EXPR_IF_THEN_ELSE@1782..19291290                    IF_KW@1782..1784 "if"1291                    WHITESPACE@1784..1785 " "1292                    EXPR@1785..18011293                      EXPR_VAR@1785..17881294                        NAME@1785..17881295                          IDENT@1785..1788 "std"1296                      SUFFIX_INDEX@1788..17961297                        DOT@1788..1789 "."1298                        NAME@1789..17961299                          IDENT@1789..1796 "isArray"1300                      SUFFIX_APPLY@1796..18011301                        ARGS_DESC@1796..18011302                          L_PAREN@1796..1797 "("1303                          ARG@1797..18001304                            EXPR@1797..18001305                              EXPR_VAR@1797..18001306                                NAME@1797..18001307                                  IDENT@1797..1800 "arr"1308                          R_PAREN@1800..1801 ")"1309                    WHITESPACE@1801..1802 " "1310                    THEN_KW@1802..1806 "then"1311                    WHITESPACE@1806..1813 "\n      "1312                    TRUE_EXPR@1813..18571313                      EXPR@1813..18571314                        EXPR_VAR@1813..18161315                          NAME@1813..18161316                            IDENT@1813..1816 "std"1317                        SUFFIX_INDEX@1816..18211318                          DOT@1816..1817 "."1319                          NAME@1817..18211320                            IDENT@1817..1821 "join"1321                        SUFFIX_APPLY@1821..18571322                          ARGS_DESC@1821..18571323                            L_PAREN@1821..1822 "("1324                            ARG@1822..18241325                              EXPR@1822..18241326                                EXPR_STRING@1822..18241327                                  STRING_SINGLE@1822..1824 "''"1328                            COMMA@1824..1825 ","1329                            WHITESPACE@1825..1826 " "1330                            ARG@1826..18561331                              EXPR@1826..18561332                                EXPR_ARRAY_COMP@1826..18561333                                  L_BRACK@1826..1827 "["1334                                  EXPR@1827..18421335                                    EXPR_VAR@1827..18301336                                      NAME@1827..18301337                                        IDENT@1827..1830 "std"1338                                    SUFFIX_INDEX@1830..18391339                                      DOT@1830..1831 "."1340                                      NAME@1831..18391341                                        IDENT@1831..1839 "deepJoin"1342                                    SUFFIX_APPLY@1839..18421343                                      ARGS_DESC@1839..18421344                                        L_PAREN@1839..1840 "("1345                                        ARG@1840..18411346                                          EXPR@1840..18411347                                            EXPR_VAR@1840..18411348                                              NAME@1840..18411349                                                IDENT@1840..1841 "x"1350                                        R_PAREN@1841..1842 ")"1351                                  WHITESPACE@1842..1843 " "1352                                  FOR_SPEC@1843..18551353                                    FOR_KW@1843..1846 "for"1354                                    WHITESPACE@1846..1847 " "1355                                    DESTRUCT_FULL@1847..18481356                                      NAME@1847..18481357                                        IDENT@1847..1848 "x"1358                                    WHITESPACE@1848..1849 " "1359                                    IN_KW@1849..1851 "in"1360                                    WHITESPACE@1851..1852 " "1361                                    EXPR@1852..18551362                                      EXPR_VAR@1852..18551363                                        NAME@1852..18551364                                          IDENT@1852..1855 "arr"1365                                  R_BRACK@1855..1856 "]"1366                            R_PAREN@1856..1857 ")"1367                    WHITESPACE@1857..1862 "\n    "1368                    ELSE_KW@1862..1866 "else"1369                    WHITESPACE@1866..1873 "\n      "1370                    FALSE_EXPR@1873..19291371                      EXPR@1873..19291372                        EXPR_ERROR@1873..19291373                          ERROR_KW@1873..1878 "error"1374                          WHITESPACE@1878..1879 " "1375                          EXPR@1879..19291376                            EXPR_BINARY@1879..19291377                              EXPR@1879..19131378                                EXPR_STRING@1879..19131379                                  STRING_SINGLE@1879..1913 "'Expected string or a ..."1380                              WHITESPACE@1913..1914 " "1381                              MODULO@1914..1915 "%"1382                              WHITESPACE@1915..1916 " "1383                              EXPR@1916..19291384                                EXPR_VAR@1916..19191385                                  NAME@1916..19191386                                    IDENT@1916..1919 "std"1387                                SUFFIX_INDEX@1919..19241388                                  DOT@1919..1920 "."1389                                  NAME@1920..19241390                                    IDENT@1920..1924 "type"1391                                SUFFIX_APPLY@1924..19291392                                  ARGS_DESC@1924..19291393                                    L_PAREN@1924..1925 "("1394                                    ARG@1925..19281395                                      EXPR@1925..19281396                                        EXPR_VAR@1925..19281397                                          NAME@1925..19281398                                            IDENT@1925..1928 "arr"1399                                    R_PAREN@1928..1929 ")"1400        COMMA@1929..1930 ","1401        WHITESPACE@1930..1934 "\n\n  "1402        MEMBER_FIELD_METHOD@1934..20421403          FIELD_NAME_FIXED@1934..19451404            NAME@1934..19451405              IDENT@1934..1945 "assertEqual"1406          PARAMS_DESC@1945..19511407            L_PAREN@1945..1946 "("1408            PARAM@1946..19471409              DESTRUCT_FULL@1946..19471410                NAME@1946..19471411                  IDENT@1946..1947 "a"1412            COMMA@1947..1948 ","1413            WHITESPACE@1948..1949 " "1414            PARAM@1949..19501415              DESTRUCT_FULL@1949..19501416                NAME@1949..19501417                  IDENT@1949..1950 "b"1418            R_PAREN@1950..1951 ")"1419          COLONCOLON@1951..1953 "::"1420          WHITESPACE@1953..1958 "\n    "1421          EXPR@1958..20421422            EXPR_IF_THEN_ELSE@1958..20421423              IF_KW@1958..1960 "if"1424              WHITESPACE@1960..1961 " "1425              EXPR@1961..19671426                EXPR_BINARY@1961..19671427                  EXPR@1961..19621428                    EXPR_VAR@1961..19621429                      NAME@1961..19621430                        IDENT@1961..1962 "a"1431                  WHITESPACE@1962..1963 " "1432                  EQ@1963..1965 "=="1433                  WHITESPACE@1965..1966 " "1434                  EXPR@1966..19671435                    EXPR_VAR@1966..19671436                      NAME@1966..19671437                        IDENT@1966..1967 "b"1438              WHITESPACE@1967..1968 " "1439              THEN_KW@1968..1972 "then"1440              WHITESPACE@1972..1979 "\n      "1441              TRUE_EXPR@1979..19831442                EXPR@1979..19831443                  EXPR_LITERAL@1979..19831444                    TRUE_KW@1979..1983 "true"1445              WHITESPACE@1983..1988 "\n    "1446              ELSE_KW@1988..1992 "else"1447              WHITESPACE@1992..1999 "\n      "1448              FALSE_EXPR@1999..20421449                EXPR@1999..20421450                  EXPR_ERROR@1999..20421451                    ERROR_KW@1999..2004 "error"1452                    WHITESPACE@2004..2005 " "1453                    EXPR@2005..20421454                      EXPR_BINARY@2005..20421455                        EXPR@2005..20381456                          EXPR_BINARY@2005..20381457                            EXPR@2005..20291458                              EXPR_BINARY@2005..20291459                                EXPR@2005..20251460                                  EXPR_STRING@2005..20251461                                    STRING_SINGLE@2005..2025 "'Assertion failed. '"1462                                WHITESPACE@2025..2026 " "1463                                PLUS@2026..2027 "+"1464                                WHITESPACE@2027..2028 " "1465                                EXPR@2028..20291466                                  EXPR_VAR@2028..20291467                                    NAME@2028..20291468                                      IDENT@2028..2029 "a"1469                            WHITESPACE@2029..2030 " "1470                            PLUS@2030..2031 "+"1471                            WHITESPACE@2031..2032 " "1472                            EXPR@2032..20381473                              EXPR_STRING@2032..20381474                                STRING_SINGLE@2032..2038 "' != '"1475                        WHITESPACE@2038..2039 " "1476                        PLUS@2039..2040 "+"1477                        WHITESPACE@2040..2041 " "1478                        EXPR@2041..20421479                          EXPR_VAR@2041..20421480                            NAME@2041..20421481                              IDENT@2041..2042 "b"1482        COMMA@2042..2043 ","1483        WHITESPACE@2043..2047 "\n\n  "1484        MEMBER_FIELD_METHOD@2047..21491485          FIELD_NAME_FIXED@2047..20521486            NAME@2047..20521487              IDENT@2047..2052 "clamp"1488          PARAMS_DESC@2052..20711489            L_PAREN@2052..2053 "("1490            PARAM@2053..20541491              DESTRUCT_FULL@2053..20541492                NAME@2053..20541493                  IDENT@2053..2054 "x"1494            COMMA@2054..2055 ","1495            WHITESPACE@2055..2056 " "1496            PARAM@2056..20621497              DESTRUCT_FULL@2056..20621498                NAME@2056..20621499                  IDENT@2056..2062 "minVal"1500            COMMA@2062..2063 ","1501            WHITESPACE@2063..2064 " "1502            PARAM@2064..20701503              DESTRUCT_FULL@2064..20701504                NAME@2064..20701505                  IDENT@2064..2070 "maxVal"1506            R_PAREN@2070..2071 ")"1507          COLONCOLON@2071..2073 "::"1508          WHITESPACE@2073..2078 "\n    "1509          EXPR@2078..21491510            EXPR_IF_THEN_ELSE@2078..21491511              IF_KW@2078..2080 "if"1512              WHITESPACE@2080..2081 " "1513              EXPR@2081..20911514                EXPR_BINARY@2081..20911515                  EXPR@2081..20821516                    EXPR_VAR@2081..20821517                      NAME@2081..20821518                        IDENT@2081..2082 "x"1519                  WHITESPACE@2082..2083 " "1520                  LT@2083..2084 "<"1521                  WHITESPACE@2084..2085 " "1522                  EXPR@2085..20911523                    EXPR_VAR@2085..20911524                      NAME@2085..20911525                        IDENT@2085..2091 "minVal"1526              WHITESPACE@2091..2092 " "1527              THEN_KW@2092..2096 "then"1528              WHITESPACE@2096..2097 " "1529              TRUE_EXPR@2097..21031530                EXPR@2097..21031531                  EXPR_VAR@2097..21031532                    NAME@2097..21031533                      IDENT@2097..2103 "minVal"1534              WHITESPACE@2103..2108 "\n    "1535              ELSE_KW@2108..2112 "else"1536              WHITESPACE@2112..2113 " "1537              FALSE_EXPR@2113..21491538                EXPR@2113..21491539                  EXPR_IF_THEN_ELSE@2113..21491540                    IF_KW@2113..2115 "if"1541                    WHITESPACE@2115..2116 " "1542                    EXPR@2116..21261543                      EXPR_BINARY@2116..21261544                        EXPR@2116..21171545                          EXPR_VAR@2116..21171546                            NAME@2116..21171547                              IDENT@2116..2117 "x"1548                        WHITESPACE@2117..2118 " "1549                        GT@2118..2119 ">"1550                        WHITESPACE@2119..2120 " "1551                        EXPR@2120..21261552                          EXPR_VAR@2120..21261553                            NAME@2120..21261554                              IDENT@2120..2126 "maxVal"1555                    WHITESPACE@2126..2127 " "1556                    THEN_KW@2127..2131 "then"1557                    WHITESPACE@2131..2132 " "1558                    TRUE_EXPR@2132..21381559                      EXPR@2132..21381560                        EXPR_VAR@2132..21381561                          NAME@2132..21381562                            IDENT@2132..2138 "maxVal"1563                    WHITESPACE@2138..2143 "\n    "1564                    ELSE_KW@2143..2147 "else"1565                    WHITESPACE@2147..2148 " "1566                    FALSE_EXPR@2148..21491567                      EXPR@2148..21491568                        EXPR_VAR@2148..21491569                          NAME@2148..21491570                            IDENT@2148..2149 "x"1571        COMMA@2149..2150 ","1572        WHITESPACE@2150..2154 "\n\n  "1573        MEMBER_FIELD_METHOD@2154..28401574          FIELD_NAME_FIXED@2154..21651575            NAME@2154..21651576              IDENT@2154..2165 "manifestIni"1577          PARAMS_DESC@2165..21701578            L_PAREN@2165..2166 "("1579            PARAM@2166..21691580              DESTRUCT_FULL@2166..21691581                NAME@2166..21691582                  IDENT@2166..2169 "ini"1583            R_PAREN@2169..2170 ")"1584          COLONCOLON@2170..2172 "::"1585          WHITESPACE@2172..2177 "\n    "1586          EXPR@2177..28401587            STMT_LOCAL@2177..24811588              LOCAL_KW@2177..2182 "local"1589              WHITESPACE@2182..2183 " "1590              BIND_FUNCTION@2183..24801591                NAME@2183..21931592                  IDENT@2183..2193 "body_lines"1593                PARAMS_DESC@2193..21991594                  L_PAREN@2193..2194 "("1595                  PARAM@2194..21981596                    DESTRUCT_FULL@2194..21981597                      NAME@2194..21981598                        IDENT@2194..2198 "body"1599                  R_PAREN@2198..2199 ")"1600                WHITESPACE@2199..2200 " "1601                ASSIGN@2200..2201 "="1602                WHITESPACE@2201..2208 "\n      "1603                EXPR@2208..24801604                  EXPR_VAR@2208..22111605                    NAME@2208..22111606                      IDENT@2208..2211 "std"1607                  SUFFIX_INDEX@2211..22161608                    DOT@2211..2212 "."1609                    NAME@2212..22161610                      IDENT@2212..2216 "join"1611                  SUFFIX_APPLY@2216..24801612                    ARGS_DESC@2216..24801613                      L_PAREN@2216..2217 "("1614                      ARG@2217..22191615                        EXPR@2217..22191616                          EXPR_ARRAY@2217..22191617                            L_BRACK@2217..2218 "["1618                            R_BRACK@2218..2219 "]"1619                      COMMA@2219..2220 ","1620                      WHITESPACE@2220..2221 " "1621                      ARG@2221..24791622                        EXPR@2221..24791623                          EXPR_ARRAY_COMP@2221..24791624                            L_BRACK@2221..2222 "["1625                            WHITESPACE@2222..2231 "\n        "1626                            EXPR@2231..24301627                              STMT_LOCAL@2231..22631628                                LOCAL_KW@2231..2236 "local"1629                                WHITESPACE@2236..2237 " "1630                                BIND_DESTRUCT@2237..22621631                                  DESTRUCT_FULL@2237..22521632                                    NAME@2237..22521633                                      IDENT@2237..2252 "value_or_values"1634                                  WHITESPACE@2252..2253 " "1635                                  ASSIGN@2253..2254 "="1636                                  WHITESPACE@2254..2255 " "1637                                  EXPR@2255..22621638                                    EXPR_VAR@2255..22591639                                      NAME@2255..22591640                                        IDENT@2255..2259 "body"1641                                    SUFFIX_INDEX_EXPR@2259..22621642                                      L_BRACK@2259..2260 "["1643                                      EXPR@2260..22611644                                        EXPR_VAR@2260..22611645                                          NAME@2260..22611646                                            IDENT@2260..2261 "k"1647                                      R_BRACK@2261..2262 "]"1648                                SEMI@2262..2263 ";"1649                              WHITESPACE@2263..2272 "\n        "1650                              EXPR_IF_THEN_ELSE@2272..24301651                                IF_KW@2272..2274 "if"1652                                WHITESPACE@2274..2275 " "1653                                EXPR@2275..23031654                                  EXPR_VAR@2275..22781655                                    NAME@2275..22781656                                      IDENT@2275..2278 "std"1657                                  SUFFIX_INDEX@2278..22861658                                    DOT@2278..2279 "."1659                                    NAME@2279..22861660                                      IDENT@2279..2286 "isArray"1661                                  SUFFIX_APPLY@2286..23031662                                    ARGS_DESC@2286..23031663                                      L_PAREN@2286..2287 "("1664                                      ARG@2287..23021665                                        EXPR@2287..23021666                                          EXPR_VAR@2287..23021667                                            NAME@2287..23021668                                              IDENT@2287..2302 "value_or_values"1669                                      R_PAREN@2302..2303 ")"1670                                WHITESPACE@2303..2304 " "1671                                THEN_KW@2304..2308 "then"1672                                WHITESPACE@2308..2319 "\n          "1673                                TRUE_EXPR@2319..23721674                                  EXPR@2319..23721675                                    EXPR_ARRAY_COMP@2319..23721676                                      L_BRACK@2319..2320 "["1677                                      EXPR@2320..23421678                                        EXPR_BINARY@2320..23421679                                          EXPR@2320..23291680                                            EXPR_STRING@2320..23291681                                              STRING_SINGLE@2320..2329 "'%s = %s'"1682                                          WHITESPACE@2329..2330 " "1683                                          MODULO@2330..2331 "%"1684                                          WHITESPACE@2331..2332 " "1685                                          EXPR@2332..23421686                                            EXPR_ARRAY@2332..23421687                                              L_BRACK@2332..2333 "["1688                                              EXPR@2333..23341689                                                EXPR_VAR@2333..23341690                                                  NAME@2333..23341691                                                    IDENT@2333..2334 "k"1692                                              COMMA@2334..2335 ","1693                                              WHITESPACE@2335..2336 " "1694                                              EXPR@2336..23411695                                                EXPR_VAR@2336..23411696                                                  NAME@2336..23411697                                                    IDENT@2336..2341 "value"1698                                              R_BRACK@2341..2342 "]"1699                                      WHITESPACE@2342..2343 " "1700                                      FOR_SPEC@2343..23711701                                        FOR_KW@2343..2346 "for"1702                                        WHITESPACE@2346..2347 " "1703                                        DESTRUCT_FULL@2347..23521704                                          NAME@2347..23521705                                            IDENT@2347..2352 "value"1706                                        WHITESPACE@2352..2353 " "1707                                        IN_KW@2353..2355 "in"1708                                        WHITESPACE@2355..2356 " "1709                                        EXPR@2356..23711710                                          EXPR_VAR@2356..23711711                                            NAME@2356..23711712                                              IDENT@2356..2371 "value_or_values"1713                                      R_BRACK@2371..2372 "]"1714                                WHITESPACE@2372..2381 "\n        "1715                                ELSE_KW@2381..2385 "else"1716                                WHITESPACE@2385..2396 "\n          "1717                                FALSE_EXPR@2396..24301718                                  EXPR@2396..24301719                                    EXPR_ARRAY@2396..24301720                                      L_BRACK@2396..2397 "["1721                                      EXPR@2397..24291722                                        EXPR_BINARY@2397..24291723                                          EXPR@2397..24061724                                            EXPR_STRING@2397..24061725                                              STRING_SINGLE@2397..2406 "'%s = %s'"1726                                          WHITESPACE@2406..2407 " "1727                                          MODULO@2407..2408 "%"1728                                          WHITESPACE@2408..2409 " "1729                                          EXPR@2409..24291730                                            EXPR_ARRAY@2409..24291731                                              L_BRACK@2409..2410 "["1732                                              EXPR@2410..24111733                                                EXPR_VAR@2410..24111734                                                  NAME@2410..24111735                                                    IDENT@2410..2411 "k"1736                                              COMMA@2411..2412 ","1737                                              WHITESPACE@2412..2413 " "1738                                              EXPR@2413..24281739                                                EXPR_VAR@2413..24281740                                                  NAME@2413..24281741                                                    IDENT@2413..2428 "value_or_values"1742                                              R_BRACK@2428..2429 "]"1743                                      R_BRACK@2429..2430 "]"1744                            WHITESPACE@2430..2440 "\n\n        "1745                            FOR_SPEC@2440..24711746                              FOR_KW@2440..2443 "for"1747                              WHITESPACE@2443..2444 " "1748                              DESTRUCT_FULL@2444..24451749                                NAME@2444..24451750                                  IDENT@2444..2445 "k"1751                              WHITESPACE@2445..2446 " "1752                              IN_KW@2446..2448 "in"1753                              WHITESPACE@2448..2449 " "1754                              EXPR@2449..24711755                                EXPR_VAR@2449..24521756                                  NAME@2449..24521757                                    IDENT@2449..2452 "std"1758                                SUFFIX_INDEX@2452..24651759                                  DOT@2452..2453 "."1760                                  NAME@2453..24651761                                    IDENT@2453..2465 "objectFields"1762                                SUFFIX_APPLY@2465..24711763                                  ARGS_DESC@2465..24711764                                    L_PAREN@2465..2466 "("1765                                    ARG@2466..24701766                                      EXPR@2466..24701767                                        EXPR_VAR@2466..24701768                                          NAME@2466..24701769                                            IDENT@2466..2470 "body"1770                                    R_PAREN@2470..2471 ")"1771                            WHITESPACE@2471..2478 "\n      "1772                            R_BRACK@2478..2479 "]"1773                      R_PAREN@2479..2480 ")"1774              SEMI@2480..2481 ";"1775            WHITESPACE@2481..2487 "\n\n    "1776            STMT_LOCAL@2487..27691777              LOCAL_KW@2487..2492 "local"1778              WHITESPACE@2492..2493 " "1779              BIND_FUNCTION@2493..25611780                NAME@2493..25061781                  IDENT@2493..2506 "section_lines"1782                PARAMS_DESC@2506..25201783                  L_PAREN@2506..2507 "("1784                  PARAM@2507..25121785                    DESTRUCT_FULL@2507..25121786                      NAME@2507..25121787                        IDENT@2507..2512 "sname"1788                  COMMA@2512..2513 ","1789                  WHITESPACE@2513..2514 " "1790                  PARAM@2514..25191791                    DESTRUCT_FULL@2514..25191792                      NAME@2514..25191793                        IDENT@2514..2519 "sbody"1794                  R_PAREN@2519..2520 ")"1795                WHITESPACE@2520..2521 " "1796                ASSIGN@2521..2522 "="1797                WHITESPACE@2522..2523 " "1798                EXPR@2523..25611799                  EXPR_BINARY@2523..25611800                    EXPR@2523..25411801                      EXPR_ARRAY@2523..25411802                        L_BRACK@2523..2524 "["1803                        EXPR@2524..25401804                          EXPR_BINARY@2524..25401805                            EXPR@2524..25301806                              EXPR_STRING@2524..25301807                                STRING_SINGLE@2524..2530 "'[%s]'"1808                            WHITESPACE@2530..2531 " "1809                            MODULO@2531..2532 "%"1810                            WHITESPACE@2532..2533 " "1811                            EXPR@2533..25401812                              EXPR_ARRAY@2533..25401813                                L_BRACK@2533..2534 "["1814                                EXPR@2534..25391815                                  EXPR_VAR@2534..25391816                                    NAME@2534..25391817                                      IDENT@2534..2539 "sname"1818                                R_BRACK@2539..2540 "]"1819                        R_BRACK@2540..2541 "]"1820                    WHITESPACE@2541..2542 " "1821                    PLUS@2542..2543 "+"1822                    WHITESPACE@2543..2544 " "1823                    EXPR@2544..25611824                      EXPR_VAR@2544..25541825                        NAME@2544..25541826                          IDENT@2544..2554 "body_lines"1827                      SUFFIX_APPLY@2554..25611828                        ARGS_DESC@2554..25611829                          L_PAREN@2554..2555 "("1830                          ARG@2555..25601831                            EXPR@2555..25601832                              EXPR_VAR@2555..25601833                                NAME@2555..25601834                                  IDENT@2555..2560 "sbody"1835                          R_PAREN@2560..2561 ")"1836              COMMA@2561..2562 ","1837              WHITESPACE@2562..2573 "\n          "1838              BIND_DESTRUCT@2573..26481839                DESTRUCT_FULL@2573..25821840                  NAME@2573..25821841                    IDENT@2573..2582 "main_body"1842                WHITESPACE@2582..2583 " "1843                ASSIGN@2583..2584 "="1844                WHITESPACE@2584..2585 " "1845                EXPR@2585..26481846                  EXPR_IF_THEN_ELSE@2585..26481847                    IF_KW@2585..2587 "if"1848                    WHITESPACE@2587..2588 " "1849                    EXPR@2588..26141850                      EXPR_VAR@2588..25911851                        NAME@2588..25911852                          IDENT@2588..2591 "std"1853                      SUFFIX_INDEX@2591..26011854                        DOT@2591..2592 "."1855                        NAME@2592..26011856                          IDENT@2592..2601 "objectHas"1857                      SUFFIX_APPLY@2601..26141858                        ARGS_DESC@2601..26141859                          L_PAREN@2601..2602 "("1860                          ARG@2602..26051861                            EXPR@2602..26051862                              EXPR_VAR@2602..26051863                                NAME@2602..26051864                                  IDENT@2602..2605 "ini"1865                          COMMA@2605..2606 ","1866                          WHITESPACE@2606..2607 " "1867                          ARG@2607..26131868                            EXPR@2607..26131869                              EXPR_STRING@2607..26131870                                STRING_SINGLE@2607..2613 "'main'"1871                          R_PAREN@2613..2614 ")"1872                    WHITESPACE@2614..2615 " "1873                    THEN_KW@2615..2619 "then"1874                    WHITESPACE@2619..2620 " "1875                    TRUE_EXPR@2620..26401876                      EXPR@2620..26401877                        EXPR_VAR@2620..26301878                          NAME@2620..26301879                            IDENT@2620..2630 "body_lines"1880                        SUFFIX_APPLY@2630..26401881                          ARGS_DESC@2630..26401882                            L_PAREN@2630..2631 "("1883                            ARG@2631..26391884                              EXPR@2631..26391885                                EXPR_VAR@2631..26341886                                  NAME@2631..26341887                                    IDENT@2631..2634 "ini"1888                                SUFFIX_INDEX@2634..26391889                                  DOT@2634..2635 "."1890                                  NAME@2635..26391891                                    IDENT@2635..2639 "main"1892                            R_PAREN@2639..2640 ")"1893                    WHITESPACE@2640..2641 " "1894                    ELSE_KW@2641..2645 "else"1895                    WHITESPACE@2645..2646 " "1896                    FALSE_EXPR@2646..26481897                      EXPR@2646..26481898                        EXPR_ARRAY@2646..26481899                          L_BRACK@2646..2647 "["1900                          R_BRACK@2647..2648 "]"1901              COMMA@2648..2649 ","1902              WHITESPACE@2649..2660 "\n          "1903              BIND_DESTRUCT@2660..27681904                DESTRUCT_FULL@2660..26721905                  NAME@2660..26721906                    IDENT@2660..2672 "all_sections"1907                WHITESPACE@2672..2673 " "1908                ASSIGN@2673..2674 "="1909                WHITESPACE@2674..2675 " "1910                EXPR@2675..27681911                  EXPR_ARRAY_COMP@2675..27681912                    L_BRACK@2675..2676 "["1913                    WHITESPACE@2676..2683 "\n      "1914                    EXPR@2683..27161915                      EXPR_VAR@2683..26961916                        NAME@2683..26961917                          IDENT@2683..2696 "section_lines"1918                      SUFFIX_APPLY@2696..27161919                        ARGS_DESC@2696..27161920                          L_PAREN@2696..2697 "("1921                          ARG@2697..26981922                            EXPR@2697..26981923                              EXPR_VAR@2697..26981924                                NAME@2697..26981925                                  IDENT@2697..2698 "k"1926                          COMMA@2698..2699 ","1927                          WHITESPACE@2699..2700 " "1928                          ARG@2700..27151929                            EXPR@2700..27151930                              EXPR_VAR@2700..27031931                                NAME@2700..27031932                                  IDENT@2700..2703 "ini"1933                              SUFFIX_INDEX@2703..27121934                                DOT@2703..2704 "."1935                                NAME@2704..27121936                                  IDENT@2704..2712 "sections"1937                              SUFFIX_INDEX_EXPR@2712..27151938                                L_BRACK@2712..2713 "["1939                                EXPR@2713..27141940                                  EXPR_VAR@2713..27141941                                    NAME@2713..27141942                                      IDENT@2713..2714 "k"1943                                R_BRACK@2714..2715 "]"1944                          R_PAREN@2715..2716 ")"1945                    WHITESPACE@2716..2723 "\n      "1946                    FOR_SPEC@2723..27621947                      FOR_KW@2723..2726 "for"1948                      WHITESPACE@2726..2727 " "1949                      DESTRUCT_FULL@2727..27281950                        NAME@2727..27281951                          IDENT@2727..2728 "k"1952                      WHITESPACE@2728..2729 " "1953                      IN_KW@2729..2731 "in"1954                      WHITESPACE@2731..2732 " "1955                      EXPR@2732..27621956                        EXPR_VAR@2732..27351957                          NAME@2732..27351958                            IDENT@2732..2735 "std"1959                        SUFFIX_INDEX@2735..27481960                          DOT@2735..2736 "."1961                          NAME@2736..27481962                            IDENT@2736..2748 "objectFields"1963                        SUFFIX_APPLY@2748..27621964                          ARGS_DESC@2748..27621965                            L_PAREN@2748..2749 "("1966                            ARG@2749..27611967                              EXPR@2749..27611968                                EXPR_VAR@2749..27521969                                  NAME@2749..27521970                                    IDENT@2749..2752 "ini"1971                                SUFFIX_INDEX@2752..27611972                                  DOT@2752..2753 "."1973                                  NAME@2753..27611974                                    IDENT@2753..2761 "sections"1975                            R_PAREN@2761..2762 ")"1976                    WHITESPACE@2762..2767 "\n    "1977                    R_BRACK@2767..2768 "]"1978              SEMI@2768..2769 ";"1979            WHITESPACE@2769..2774 "\n    "1980            EXPR_VAR@2774..27771981              NAME@2774..27771982                IDENT@2774..2777 "std"1983            SUFFIX_INDEX@2777..27821984              DOT@2777..2778 "."1985              NAME@2778..27821986                IDENT@2778..2782 "join"1987            SUFFIX_APPLY@2782..28401988              ARGS_DESC@2782..28401989                L_PAREN@2782..2783 "("1990                ARG@2783..27871991                  EXPR@2783..27871992                    EXPR_STRING@2783..27871993                      STRING_SINGLE@2783..2787 "'\\n'"1994                COMMA@2787..2788 ","1995                WHITESPACE@2788..2789 " "1996                ARG@2789..28391997                  EXPR@2789..28391998                    EXPR_BINARY@2789..28391999                      EXPR@2789..28322000                        EXPR_BINARY@2789..28322001                          EXPR@2789..27982002                            EXPR_VAR@2789..27982003                              NAME@2789..27982004                                IDENT@2789..2798 "main_body"2005                          WHITESPACE@2798..2799 " "2006                          PLUS@2799..2800 "+"2007                          WHITESPACE@2800..2801 " "2008                          EXPR@2801..28322009                            EXPR_VAR@2801..28042010                              NAME@2801..28042011                                IDENT@2801..2804 "std"2012                            SUFFIX_INDEX@2804..28182013                              DOT@2804..2805 "."2014                              NAME@2805..28182015                                IDENT@2805..2818 "flattenArrays"2016                            SUFFIX_APPLY@2818..28322017                              ARGS_DESC@2818..28322018                                L_PAREN@2818..2819 "("2019                                ARG@2819..28312020                                  EXPR@2819..28312021                                    EXPR_VAR@2819..28312022                                      NAME@2819..28312023                                        IDENT@2819..2831 "all_sections"2024                                R_PAREN@2831..2832 ")"2025                      WHITESPACE@2832..2833 " "2026                      PLUS@2833..2834 "+"2027                      WHITESPACE@2834..2835 " "2028                      EXPR@2835..28392029                        EXPR_ARRAY@2835..28392030                          L_BRACK@2835..2836 "["2031                          EXPR@2836..28382032                            EXPR_STRING@2836..28382033                              STRING_SINGLE@2836..2838 "''"2034                          R_BRACK@2838..2839 "]"2035                R_PAREN@2839..2840 ")"2036        COMMA@2840..2841 ","2037        WHITESPACE@2841..2845 "\n\n  "2038        MEMBER_FIELD_METHOD@2845..28982039          FIELD_NAME_FIXED@2845..28572040            NAME@2845..28572041              IDENT@2845..2857 "manifestToml"2042          PARAMS_DESC@2857..28642043            L_PAREN@2857..2858 "("2044            PARAM@2858..28632045              DESTRUCT_FULL@2858..28632046                NAME@2858..28632047                  IDENT@2858..2863 "value"2048            R_PAREN@2863..2864 ")"2049          COLONCOLON@2864..2866 "::"2050          WHITESPACE@2866..2867 " "2051          EXPR@2867..28982052            EXPR_VAR@2867..28702053              NAME@2867..28702054                IDENT@2867..2870 "std"2055            SUFFIX_INDEX@2870..28852056              DOT@2870..2871 "."2057              NAME@2871..28852058                IDENT@2871..2885 "manifestTomlEx"2059            SUFFIX_APPLY@2885..28982060              ARGS_DESC@2885..28982061                L_PAREN@2885..2886 "("2062                ARG@2886..28912063                  EXPR@2886..28912064                    EXPR_VAR@2886..28912065                      NAME@2886..28912066                        IDENT@2886..2891 "value"2067                COMMA@2891..2892 ","2068                WHITESPACE@2892..2893 " "2069                ARG@2893..28972070                  EXPR@2893..28972071                    EXPR_STRING@2893..28972072                      STRING_SINGLE@2893..2897 "'  '"2073                R_PAREN@2897..2898 ")"2074        COMMA@2898..2899 ","2075        WHITESPACE@2899..2903 "\n\n  "2076        MEMBER_FIELD_METHOD@2903..29582077          FIELD_NAME_FIXED@2903..29212078            NAME@2903..29212079              IDENT@2903..2921 "escapeStringPython"2080          PARAMS_DESC@2921..29262081            L_PAREN@2921..2922 "("2082            PARAM@2922..29252083              DESTRUCT_FULL@2922..29252084                NAME@2922..29252085                  IDENT@2922..2925 "str"2086            R_PAREN@2925..2926 ")"2087          COLONCOLON@2926..2928 "::"2088          WHITESPACE@2928..2933 "\n    "2089          EXPR@2933..29582090            EXPR_VAR@2933..29362091              NAME@2933..29362092                IDENT@2933..2936 "std"2093            SUFFIX_INDEX@2936..29532094              DOT@2936..2937 "."2095              NAME@2937..29532096                IDENT@2937..2953 "escapeStringJson"2097            SUFFIX_APPLY@2953..29582098              ARGS_DESC@2953..29582099                L_PAREN@2953..2954 "("2100                ARG@2954..29572101                  EXPR@2954..29572102                    EXPR_VAR@2954..29572103                      NAME@2954..29572104                        IDENT@2954..2957 "str"2105                R_PAREN@2957..2958 ")"2106        COMMA@2958..2959 ","2107        WHITESPACE@2959..2963 "\n\n  "2108        MEMBER_FIELD_METHOD@2963..31802109          FIELD_NAME_FIXED@2963..29792110            NAME@2963..29792111              IDENT@2963..2979 "escapeStringBash"2112          PARAMS_DESC@2979..29852113            L_PAREN@2979..2980 "("2114            PARAM@2980..29842115              DESTRUCT_FULL@2980..29842116                NAME@2980..29842117                  IDENT@2980..2984 "str_"2118            R_PAREN@2984..2985 ")"2119          COLONCOLON@2985..2987 "::"2120          WHITESPACE@2987..2992 "\n    "2121          EXPR@2992..31802122            STMT_LOCAL@2992..30232123              LOCAL_KW@2992..2997 "local"2124              WHITESPACE@2997..2998 " "2125              BIND_DESTRUCT@2998..30222126                DESTRUCT_FULL@2998..30012127                  NAME@2998..30012128                    IDENT@2998..3001 "str"2129                WHITESPACE@3001..3002 " "2130                ASSIGN@3002..3003 "="2131                WHITESPACE@3003..3004 " "2132                EXPR@3004..30222133                  EXPR_VAR@3004..30072134                    NAME@3004..30072135                      IDENT@3004..3007 "std"2136                  SUFFIX_INDEX@3007..30162137                    DOT@3007..3008 "."2138                    NAME@3008..30162139                      IDENT@3008..3016 "toString"2140                  SUFFIX_APPLY@3016..30222141                    ARGS_DESC@3016..30222142                      L_PAREN@3016..3017 "("2143                      ARG@3017..30212144                        EXPR@3017..30212145                          EXPR_VAR@3017..30212146                            NAME@3017..30212147                              IDENT@3017..3021 "str_"2148                      R_PAREN@3021..3022 ")"2149              SEMI@3022..3023 ";"2150            WHITESPACE@3023..3028 "\n    "2151            STMT_LOCAL@3028..31102152              LOCAL_KW@3028..3033 "local"2153              WHITESPACE@3033..3034 " "2154              BIND_FUNCTION@3034..31092155                NAME@3034..30392156                  IDENT@3034..3039 "trans"2157                PARAMS_DESC@3039..30432158                  L_PAREN@3039..3040 "("2159                  PARAM@3040..30422160                    DESTRUCT_FULL@3040..30422161                      NAME@3040..30422162                        IDENT@3040..3042 "ch"2163                  R_PAREN@3042..3043 ")"2164                WHITESPACE@3043..3044 " "2165                ASSIGN@3044..3045 "="2166                WHITESPACE@3045..3052 "\n      "2167                EXPR@3052..31092168                  EXPR_IF_THEN_ELSE@3052..31092169                    IF_KW@3052..3054 "if"2170                    WHITESPACE@3054..3055 " "2171                    EXPR@3055..30642172                      EXPR_BINARY@3055..30642173                        EXPR@3055..30572174                          EXPR_VAR@3055..30572175                            NAME@3055..30572176                              IDENT@3055..3057 "ch"2177                        WHITESPACE@3057..3058 " "2178                        EQ@3058..3060 "=="2179                        WHITESPACE@3060..3061 " "2180                        EXPR@3061..30642181                          EXPR_STRING@3061..30642182                            STRING_DOUBLE@3061..3064 "\"'\""2183                    WHITESPACE@3064..3065 " "2184                    THEN_KW@3065..3069 "then"2185                    WHITESPACE@3069..3078 "\n        "2186                    TRUE_EXPR@3078..30872187                      EXPR@3078..30872188                        EXPR_STRING@3078..30872189                          STRING_DOUBLE@3078..3087 "\"'\\\"'\\\"'\""2190                    WHITESPACE@3087..3094 "\n      "2191                    ELSE_KW@3094..3098 "else"2192                    WHITESPACE@3098..3107 "\n        "2193                    FALSE_EXPR@3107..31092194                      EXPR@3107..31092195                        EXPR_VAR@3107..31092196                          NAME@3107..31092197                            IDENT@3107..3109 "ch"2198              SEMI@3109..3110 ";"2199            WHITESPACE@3110..3115 "\n    "2200            EXPR_BINARY@3115..31802201              EXPR@3115..31212202                EXPR_STRING@3115..31212203                  STRING_DOUBLE@3115..3121 "\"'%s'\""2204              WHITESPACE@3121..3122 " "2205              MODULO@3122..3123 "%"2206              WHITESPACE@3123..3124 " "2207              EXPR@3124..31802208                EXPR_VAR@3124..31272209                  NAME@3124..31272210                    IDENT@3124..3127 "std"2211                SUFFIX_INDEX@3127..31322212                  DOT@3127..3128 "."2213                  NAME@3128..31322214                    IDENT@3128..3132 "join"2215                SUFFIX_APPLY@3132..31802216                  ARGS_DESC@3132..31802217                    L_PAREN@3132..3133 "("2218                    ARG@3133..31352219                      EXPR@3133..31352220                        EXPR_STRING@3133..31352221                          STRING_SINGLE@3133..3135 "''"2222                    COMMA@3135..3136 ","2223                    WHITESPACE@3136..3137 " "2224                    ARG@3137..31792225                      EXPR@3137..31792226                        EXPR_ARRAY_COMP@3137..31792227                          L_BRACK@3137..3138 "["2228                          EXPR@3138..31472229                            EXPR_VAR@3138..31432230                              NAME@3138..31432231                                IDENT@3138..3143 "trans"2232                            SUFFIX_APPLY@3143..31472233                              ARGS_DESC@3143..31472234                                L_PAREN@3143..3144 "("2235                                ARG@3144..31462236                                  EXPR@3144..31462237                                    EXPR_VAR@3144..31462238                                      NAME@3144..31462239                                        IDENT@3144..3146 "ch"2240                                R_PAREN@3146..3147 ")"2241                          WHITESPACE@3147..3148 " "2242                          FOR_SPEC@3148..31782243                            FOR_KW@3148..3151 "for"2244                            WHITESPACE@3151..3152 " "2245                            DESTRUCT_FULL@3152..31542246                              NAME@3152..31542247                                IDENT@3152..3154 "ch"2248                            WHITESPACE@3154..3155 " "2249                            IN_KW@3155..3157 "in"2250                            WHITESPACE@3157..3158 " "2251                            EXPR@3158..31782252                              EXPR_VAR@3158..31612253                                NAME@3158..31612254                                  IDENT@3158..3161 "std"2255                              SUFFIX_INDEX@3161..31732256                                DOT@3161..3162 "."2257                                NAME@3162..31732258                                  IDENT@3162..3173 "stringChars"2259                              SUFFIX_APPLY@3173..31782260                                ARGS_DESC@3173..31782261                                  L_PAREN@3173..3174 "("2262                                  ARG@3174..31772263                                    EXPR@3174..31772264                                      EXPR_VAR@3174..31772265                                        NAME@3174..31772266                                          IDENT@3174..3177 "str"2267                                  R_PAREN@3177..3178 ")"2268                          R_BRACK@3178..3179 "]"2269                    R_PAREN@3179..3180 ")"2270        COMMA@3180..3181 ","2271        WHITESPACE@3181..3185 "\n\n  "2272        MEMBER_FIELD_METHOD@3185..33992273          FIELD_NAME_FIXED@3185..32042274            NAME@3185..32042275              IDENT@3185..3204 "escapeStringDollars"2276          PARAMS_DESC@3204..32102277            L_PAREN@3204..3205 "("2278            PARAM@3205..32092279              DESTRUCT_FULL@3205..32092280                NAME@3205..32092281                  IDENT@3205..3209 "str_"2282            R_PAREN@3209..3210 ")"2283          COLONCOLON@3210..3212 "::"2284          WHITESPACE@3212..3217 "\n    "2285          EXPR@3217..33992286            STMT_LOCAL@3217..32482287              LOCAL_KW@3217..3222 "local"2288              WHITESPACE@3222..3223 " "2289              BIND_DESTRUCT@3223..32472290                DESTRUCT_FULL@3223..32262291                  NAME@3223..32262292                    IDENT@3223..3226 "str"2293                WHITESPACE@3226..3227 " "2294                ASSIGN@3227..3228 "="2295                WHITESPACE@3228..3229 " "2296                EXPR@3229..32472297                  EXPR_VAR@3229..32322298                    NAME@3229..32322299                      IDENT@3229..3232 "std"2300                  SUFFIX_INDEX@3232..32412301                    DOT@3232..3233 "."2302                    NAME@3233..32412303                      IDENT@3233..3241 "toString"2304                  SUFFIX_APPLY@3241..32472305                    ARGS_DESC@3241..32472306                      L_PAREN@3241..3242 "("2307                      ARG@3242..32462308                        EXPR@3242..32462309                          EXPR_VAR@3242..32462310                            NAME@3242..32462311                              IDENT@3242..3246 "str_"2312                      R_PAREN@3246..3247 ")"2313              SEMI@3247..3248 ";"2314            WHITESPACE@3248..3253 "\n    "2315            STMT_LOCAL@3253..33302316              LOCAL_KW@3253..3258 "local"2317              WHITESPACE@3258..3259 " "2318              BIND_FUNCTION@3259..33292319                NAME@3259..32642320                  IDENT@3259..3264 "trans"2321                PARAMS_DESC@3264..32682322                  L_PAREN@3264..3265 "("2323                  PARAM@3265..32672324                    DESTRUCT_FULL@3265..32672325                      NAME@3265..32672326                        IDENT@3265..3267 "ch"2327                  R_PAREN@3267..3268 ")"2328                WHITESPACE@3268..3269 " "2329                ASSIGN@3269..3270 "="2330                WHITESPACE@3270..3277 "\n      "2331                EXPR@3277..33292332                  EXPR_IF_THEN_ELSE@3277..33292333                    IF_KW@3277..3279 "if"2334                    WHITESPACE@3279..3280 " "2335                    EXPR@3280..32892336                      EXPR_BINARY@3280..32892337                        EXPR@3280..32822338                          EXPR_VAR@3280..32822339                            NAME@3280..32822340                              IDENT@3280..3282 "ch"2341                        WHITESPACE@3282..3283 " "2342                        EQ@3283..3285 "=="2343                        WHITESPACE@3285..3286 " "2344                        EXPR@3286..32892345                          EXPR_STRING@3286..32892346                            STRING_SINGLE@3286..3289 "'$'"2347                    WHITESPACE@3289..3290 " "2348                    THEN_KW@3290..3294 "then"2349                    WHITESPACE@3294..3303 "\n        "2350                    TRUE_EXPR@3303..33072351                      EXPR@3303..33072352                        EXPR_STRING@3303..33072353                          STRING_SINGLE@3303..3307 "'$$'"2354                    WHITESPACE@3307..3314 "\n      "2355                    ELSE_KW@3314..3318 "else"2356                    WHITESPACE@3318..3327 "\n        "2357                    FALSE_EXPR@3327..33292358                      EXPR@3327..33292359                        EXPR_VAR@3327..33292360                          NAME@3327..33292361                            IDENT@3327..3329 "ch"2362              SEMI@3329..3330 ";"2363            WHITESPACE@3330..3335 "\n    "2364            EXPR_VAR@3335..33382365              NAME@3335..33382366                IDENT@3335..3338 "std"2367            SUFFIX_INDEX@3338..33442368              DOT@3338..3339 "."2369              NAME@3339..33442370                IDENT@3339..3344 "foldl"2371            SUFFIX_APPLY@3344..33992372              ARGS_DESC@3344..33992373                L_PAREN@3344..3345 "("2374                ARG@3345..33722375                  EXPR@3345..33722376                    EXPR_FUNCTION@3345..33722377                      FUNCTION_KW@3345..3353 "function"2378                      PARAMS_DESC@3353..33592379                        L_PAREN@3353..3354 "("2380                        PARAM@3354..33552381                          DESTRUCT_FULL@3354..33552382                            NAME@3354..33552383                              IDENT@3354..3355 "a"2384                        COMMA@3355..3356 ","2385                        WHITESPACE@3356..3357 " "2386                        PARAM@3357..33582387                          DESTRUCT_FULL@3357..33582388                            NAME@3357..33582389                              IDENT@3357..3358 "b"2390                        R_PAREN@3358..3359 ")"2391                      WHITESPACE@3359..3360 " "2392                      EXPR@3360..33722393                        EXPR_BINARY@3360..33722394                          EXPR@3360..33612395                            EXPR_VAR@3360..33612396                              NAME@3360..33612397                                IDENT@3360..3361 "a"2398                          WHITESPACE@3361..3362 " "2399                          PLUS@3362..3363 "+"2400                          WHITESPACE@3363..3364 " "2401                          EXPR@3364..33722402                            EXPR_VAR@3364..33692403                              NAME@3364..33692404                                IDENT@3364..3369 "trans"2405                            SUFFIX_APPLY@3369..33722406                              ARGS_DESC@3369..33722407                                L_PAREN@3369..3370 "("2408                                ARG@3370..33712409                                  EXPR@3370..33712410                                    EXPR_VAR@3370..33712411                                      NAME@3370..33712412                                        IDENT@3370..3371 "b"2413                                R_PAREN@3371..3372 ")"2414                COMMA@3372..3373 ","2415                WHITESPACE@3373..3374 " "2416                ARG@3374..33942417                  EXPR@3374..33942418                    EXPR_VAR@3374..33772419                      NAME@3374..33772420                        IDENT@3374..3377 "std"2421                    SUFFIX_INDEX@3377..33892422                      DOT@3377..3378 "."2423                      NAME@3378..33892424                        IDENT@3378..3389 "stringChars"2425                    SUFFIX_APPLY@3389..33942426                      ARGS_DESC@3389..33942427                        L_PAREN@3389..3390 "("2428                        ARG@3390..33932429                          EXPR@3390..33932430                            EXPR_VAR@3390..33932431                              NAME@3390..33932432                                IDENT@3390..3393 "str"2433                        R_PAREN@3393..3394 ")"2434                COMMA@3394..3395 ","2435                WHITESPACE@3395..3396 " "2436                ARG@3396..33982437                  EXPR@3396..33982438                    EXPR_STRING@3396..33982439                      STRING_SINGLE@3396..3398 "''"2440                R_PAREN@3398..3399 ")"2441        COMMA@3399..3400 ","2442        WHITESPACE@3400..3404 "\n\n  "2443        MEMBER_BIND_STMT@3404..35192444          OBJ_LOCAL@3404..35192445            LOCAL_KW@3404..3409 "local"2446            WHITESPACE@3409..3410 " "2447            BIND_DESTRUCT@3410..35192448              DESTRUCT_FULL@3410..34212449                NAME@3410..34212450                  IDENT@3410..3421 "xml_escapes"2451              WHITESPACE@3421..3422 " "2452              ASSIGN@3422..3423 "="2453              WHITESPACE@3423..3424 " "2454              EXPR@3424..35192455                EXPR_OBJECT@3424..35192456                  OBJ_BODY_MEMBER_LIST@3424..35192457                    L_BRACE@3424..3425 "{"2458                    WHITESPACE@3425..3430 "\n    "2459                    MEMBER_FIELD_NORMAL@3430..34412460                      FIELD_NAME_FIXED@3430..34332461                        STRING_SINGLE@3430..3433 "'<'"2462                      COLON@3433..3434 ":"2463                      WHITESPACE@3434..3435 " "2464                      EXPR@3435..34412465                        EXPR_STRING@3435..34412466                          STRING_SINGLE@3435..3441 "'&lt;'"2467                    COMMA@3441..3442 ","2468                    WHITESPACE@3442..3447 "\n    "2469                    MEMBER_FIELD_NORMAL@3447..34582470                      FIELD_NAME_FIXED@3447..34502471                        STRING_SINGLE@3447..3450 "'>'"2472                      COLON@3450..3451 ":"2473                      WHITESPACE@3451..3452 " "2474                      EXPR@3452..34582475                        EXPR_STRING@3452..34582476                          STRING_SINGLE@3452..3458 "'&gt;'"2477                    COMMA@3458..3459 ","2478                    WHITESPACE@3459..3464 "\n    "2479                    MEMBER_FIELD_NORMAL@3464..34762480                      FIELD_NAME_FIXED@3464..34672481                        STRING_SINGLE@3464..3467 "'&'"2482                      COLON@3467..3468 ":"2483                      WHITESPACE@3468..3469 " "2484                      EXPR@3469..34762485                        EXPR_STRING@3469..34762486                          STRING_SINGLE@3469..3476 "'&amp;'"2487                    COMMA@3476..3477 ","2488                    WHITESPACE@3477..3482 "\n    "2489                    MEMBER_FIELD_NORMAL@3482..34952490                      FIELD_NAME_FIXED@3482..34852491                        STRING_SINGLE@3482..3485 "'\"'"2492                      COLON@3485..3486 ":"2493                      WHITESPACE@3486..3487 " "2494                      EXPR@3487..34952495                        EXPR_STRING@3487..34952496                          STRING_SINGLE@3487..3495 "'&quot;'"2497                    COMMA@3495..3496 ","2498                    WHITESPACE@3496..3501 "\n    "2499                    MEMBER_FIELD_NORMAL@3501..35142500                      FIELD_NAME_FIXED@3501..35042501                        STRING_DOUBLE@3501..3504 "\"'\""2502                      COLON@3504..3505 ":"2503                      WHITESPACE@3505..3506 " "2504                      EXPR@3506..35142505                        EXPR_STRING@3506..35142506                          STRING_SINGLE@3506..3514 "'&apos;'"2507                    COMMA@3514..3515 ","2508                    WHITESPACE@3515..3518 "\n  "2509                    R_BRACE@3518..3519 "}"2510        COMMA@3519..3520 ","2511        WHITESPACE@3520..3524 "\n\n  "2512        MEMBER_FIELD_METHOD@3524..36632513          FIELD_NAME_FIXED@3524..35392514            NAME@3524..35392515              IDENT@3524..3539 "escapeStringXML"2516          PARAMS_DESC@3539..35452517            L_PAREN@3539..3540 "("2518            PARAM@3540..35442519              DESTRUCT_FULL@3540..35442520                NAME@3540..35442521                  IDENT@3540..3544 "str_"2522            R_PAREN@3544..3545 ")"2523          COLONCOLON@3545..3547 "::"2524          WHITESPACE@3547..3552 "\n    "2525          EXPR@3552..36632526            STMT_LOCAL@3552..35832527              LOCAL_KW@3552..3557 "local"2528              WHITESPACE@3557..3558 " "2529              BIND_DESTRUCT@3558..35822530                DESTRUCT_FULL@3558..35612531                  NAME@3558..35612532                    IDENT@3558..3561 "str"2533                WHITESPACE@3561..3562 " "2534                ASSIGN@3562..3563 "="2535                WHITESPACE@3563..3564 " "2536                EXPR@3564..35822537                  EXPR_VAR@3564..35672538                    NAME@3564..35672539                      IDENT@3564..3567 "std"2540                  SUFFIX_INDEX@3567..35762541                    DOT@3567..3568 "."2542                    NAME@3568..35762543                      IDENT@3568..3576 "toString"2544                  SUFFIX_APPLY@3576..35822545                    ARGS_DESC@3576..35822546                      L_PAREN@3576..3577 "("2547                      ARG@3577..35812548                        EXPR@3577..35812549                          EXPR_VAR@3577..35812550                            NAME@3577..35812551                              IDENT@3577..3581 "str_"2552                      R_PAREN@3581..3582 ")"2553              SEMI@3582..3583 ";"2554            WHITESPACE@3583..3588 "\n    "2555            EXPR_VAR@3588..35912556              NAME@3588..35912557                IDENT@3588..3591 "std"2558            SUFFIX_INDEX@3591..35962559              DOT@3591..3592 "."2560              NAME@3592..35962561                IDENT@3592..3596 "join"2562            SUFFIX_APPLY@3596..36632563              ARGS_DESC@3596..36632564                L_PAREN@3596..3597 "("2565                ARG@3597..35992566                  EXPR@3597..35992567                    EXPR_STRING@3597..35992568                      STRING_SINGLE@3597..3599 "''"2569                COMMA@3599..3600 ","2570                WHITESPACE@3600..3601 " "2571                ARG@3601..36622572                  EXPR@3601..36622573                    EXPR_ARRAY_COMP@3601..36622574                      L_BRACK@3601..3602 "["2575                      EXPR@3602..36302576                        EXPR_VAR@3602..36052577                          NAME@3602..36052578                            IDENT@3602..3605 "std"2579                        SUFFIX_INDEX@3605..36092580                          DOT@3605..3606 "."2581                          NAME@3606..36092582                            IDENT@3606..3609 "get"2583                        SUFFIX_APPLY@3609..36302584                          ARGS_DESC@3609..36302585                            L_PAREN@3609..3610 "("2586                            ARG@3610..36212587                              EXPR@3610..36212588                                EXPR_VAR@3610..36212589                                  NAME@3610..36212590                                    IDENT@3610..3621 "xml_escapes"2591                            COMMA@3621..3622 ","2592                            WHITESPACE@3622..3623 " "2593                            ARG@3623..36252594                              EXPR@3623..36252595                                EXPR_VAR@3623..36252596                                  NAME@3623..36252597                                    IDENT@3623..3625 "ch"2598                            COMMA@3625..3626 ","2599                            WHITESPACE@3626..3627 " "2600                            ARG@3627..36292601                              EXPR@3627..36292602                                EXPR_VAR@3627..36292603                                  NAME@3627..36292604                                    IDENT@3627..3629 "ch"2605                            R_PAREN@3629..3630 ")"2606                      WHITESPACE@3630..3631 " "2607                      FOR_SPEC@3631..36612608                        FOR_KW@3631..3634 "for"2609                        WHITESPACE@3634..3635 " "2610                        DESTRUCT_FULL@3635..36372611                          NAME@3635..36372612                            IDENT@3635..3637 "ch"2613                        WHITESPACE@3637..3638 " "2614                        IN_KW@3638..3640 "in"2615                        WHITESPACE@3640..3641 " "2616                        EXPR@3641..36612617                          EXPR_VAR@3641..36442618                            NAME@3641..36442619                              IDENT@3641..3644 "std"2620                          SUFFIX_INDEX@3644..36562621                            DOT@3644..3645 "."2622                            NAME@3645..36562623                              IDENT@3645..3656 "stringChars"2624                          SUFFIX_APPLY@3656..36612625                            ARGS_DESC@3656..36612626                              L_PAREN@3656..3657 "("2627                              ARG@3657..36602628                                EXPR@3657..36602629                                  EXPR_VAR@3657..36602630                                    NAME@3657..36602631                                      IDENT@3657..3660 "str"2632                              R_PAREN@3660..3661 ")"2633                      R_BRACK@3661..3662 "]"2634                R_PAREN@3662..3663 ")"2635        COMMA@3663..3664 ","2636        WHITESPACE@3664..3668 "\n\n  "2637        MEMBER_FIELD_METHOD@3668..37342638          FIELD_NAME_FIXED@3668..36802639            NAME@3668..36802640              IDENT@3668..3680 "manifestJson"2641          PARAMS_DESC@3680..36872642            L_PAREN@3680..3681 "("2643            PARAM@3681..36862644              DESTRUCT_FULL@3681..36862645                NAME@3681..36862646                  IDENT@3681..3686 "value"2647            R_PAREN@3686..3687 ")"2648          COLONCOLON@3687..3689 "::"2649          WHITESPACE@3689..3690 " "2650          EXPR@3690..37342651            EXPR_VAR@3690..36932652              NAME@3690..36932653                IDENT@3690..3693 "std"2654            SUFFIX_INDEX@3693..37082655              DOT@3693..3694 "."2656              NAME@3694..37082657                IDENT@3694..3708 "manifestJsonEx"2658            SUFFIX_APPLY@3708..37342659              ARGS_DESC@3708..37342660                L_PAREN@3708..3709 "("2661                ARG@3709..37142662                  EXPR@3709..37142663                    EXPR_VAR@3709..37142664                      NAME@3709..37142665                        IDENT@3709..3714 "value"2666                COMMA@3714..3715 ","2667                WHITESPACE@3715..3716 " "2668                ARG@3716..37222669                  EXPR@3716..37222670                    EXPR_STRING@3716..37222671                      STRING_SINGLE@3716..3722 "'    '"2672                R_PAREN@3722..3723 ")"2673                WHITESPACE@3723..3724 " "2674                TAILSTRICT_KW@3724..3734 "tailstrict"2675        COMMA@3734..3735 ","2676        WHITESPACE@3735..3739 "\n\n  "2677        MEMBER_FIELD_METHOD@3739..38072678          FIELD_NAME_FIXED@3739..37592679            NAME@3739..37592680              IDENT@3739..3759 "manifestJsonMinified"2681          PARAMS_DESC@3759..37662682            L_PAREN@3759..3760 "("2683            PARAM@3760..37652684              DESTRUCT_FULL@3760..37652685                NAME@3760..37652686                  IDENT@3760..3765 "value"2687            R_PAREN@3765..3766 ")"2688          COLONCOLON@3766..3768 "::"2689          WHITESPACE@3768..3769 " "2690          EXPR@3769..38072691            EXPR_VAR@3769..37722692              NAME@3769..37722693                IDENT@3769..3772 "std"2694            SUFFIX_INDEX@3772..37872695              DOT@3772..3773 "."2696              NAME@3773..37872697                IDENT@3773..3787 "manifestJsonEx"2698            SUFFIX_APPLY@3787..38072699              ARGS_DESC@3787..38072700                L_PAREN@3787..3788 "("2701                ARG@3788..37932702                  EXPR@3788..37932703                    EXPR_VAR@3788..37932704                      NAME@3788..37932705                        IDENT@3788..3793 "value"2706                COMMA@3793..3794 ","2707                WHITESPACE@3794..3795 " "2708                ARG@3795..37972709                  EXPR@3795..37972710                    EXPR_STRING@3795..37972711                      STRING_SINGLE@3795..3797 "''"2712                COMMA@3797..3798 ","2713                WHITESPACE@3798..3799 " "2714                ARG@3799..38012715                  EXPR@3799..38012716                    EXPR_STRING@3799..38012717                      STRING_SINGLE@3799..3801 "''"2718                COMMA@3801..3802 ","2719                WHITESPACE@3802..3803 " "2720                ARG@3803..38062721                  EXPR@3803..38062722                    EXPR_STRING@3803..38062723                      STRING_SINGLE@3803..3806 "':'"2724                R_PAREN@3806..3807 ")"2725        COMMA@3807..3808 ","2726        WHITESPACE@3808..3812 "\n\n  "2727        MEMBER_FIELD_METHOD@3812..41972728          FIELD_NAME_FIXED@3812..38302729            NAME@3812..38302730              IDENT@3812..3830 "manifestYamlStream"2731          PARAMS_DESC@3830..39052732            L_PAREN@3830..3831 "("2733            PARAM@3831..38362734              DESTRUCT_FULL@3831..38362735                NAME@3831..38362736                  IDENT@3831..3836 "value"2737            COMMA@3836..3837 ","2738            WHITESPACE@3837..3838 " "2739            PARAM@3838..38662740              DESTRUCT_FULL@3838..38602741                NAME@3838..38602742                  IDENT@3838..3860 "indent_array_in_object"2743              ASSIGN@3860..3861 "="2744              EXPR@3861..38662745                EXPR_LITERAL@3861..38662746                  FALSE_KW@3861..3866 "false"2747            COMMA@3866..3867 ","2748            WHITESPACE@3867..3868 " "2749            PARAM@3868..38872750              DESTRUCT_FULL@3868..38822751                NAME@3868..38822752                  IDENT@3868..3882 "c_document_end"2753              ASSIGN@3882..3883 "="2754              EXPR@3883..38872755                EXPR_LITERAL@3883..38872756                  TRUE_KW@3883..3887 "true"2757            COMMA@3887..3888 ","2758            WHITESPACE@3888..3889 " "2759            PARAM@3889..39042760              DESTRUCT_FULL@3889..38992761                NAME@3889..38992762                  IDENT@3889..3899 "quote_keys"2763              ASSIGN@3899..3900 "="2764              EXPR@3900..39042765                EXPR_LITERAL@3900..39042766                  TRUE_KW@3900..3904 "true"2767            R_PAREN@3904..3905 ")"2768          COLONCOLON@3905..3907 "::"2769          WHITESPACE@3907..3912 "\n    "2770          EXPR@3912..41972771            EXPR_IF_THEN_ELSE@3912..41972772              IF_KW@3912..3914 "if"2773              WHITESPACE@3914..3915 " "2774              EXPR@3915..39342775                EXPR_UNARY@3915..39342776                  NOT@3915..3916 "!"2777                  EXPR_VAR@3916..39192778                    NAME@3916..39192779                      IDENT@3916..3919 "std"2780                  SUFFIX_INDEX@3919..39272781                    DOT@3919..3920 "."2782                    NAME@3920..39272783                      IDENT@3920..3927 "isArray"2784                  SUFFIX_APPLY@3927..39342785                    ARGS_DESC@3927..39342786                      L_PAREN@3927..3928 "("2787                      ARG@3928..39332788                        EXPR@3928..39332789                          EXPR_VAR@3928..39332790                            NAME@3928..39332791                              IDENT@3928..3933 "value"2792                      R_PAREN@3933..3934 ")"2793              WHITESPACE@3934..3935 " "2794              THEN_KW@3935..3939 "then"2795              WHITESPACE@3939..3946 "\n      "2796              TRUE_EXPR@3946..40142797                EXPR@3946..40142798                  EXPR_ERROR@3946..40142799                    ERROR_KW@3946..3951 "error"2800                    WHITESPACE@3951..3952 " "2801                    EXPR@3952..40142802                      EXPR_BINARY@3952..40142803                        EXPR@3952..39962804                          EXPR_STRING@3952..39962805                            STRING_SINGLE@3952..3996 "'manifestYamlStream o ..."2806                        WHITESPACE@3996..3997 " "2807                        PLUS@3997..3998 "+"2808                        WHITESPACE@3998..3999 " "2809                        EXPR@3999..40142810                          EXPR_VAR@3999..40022811                            NAME@3999..40022812                              IDENT@3999..4002 "std"2813                          SUFFIX_INDEX@4002..40072814                            DOT@4002..4003 "."2815                            NAME@4003..40072816                              IDENT@4003..4007 "type"2817                          SUFFIX_APPLY@4007..40142818                            ARGS_DESC@4007..40142819                              L_PAREN@4007..4008 "("2820                              ARG@4008..40132821                                EXPR@4008..40132822                                  EXPR_VAR@4008..40132823                                    NAME@4008..40132824                                      IDENT@4008..4013 "value"2825                              R_PAREN@4013..4014 ")"2826              WHITESPACE@4014..4019 "\n    "2827              ELSE_KW@4019..4023 "else"2828              WHITESPACE@4023..4030 "\n      "2829              FALSE_EXPR@4030..41972830                EXPR@4030..41972831                  EXPR_BINARY@4030..41972832                    EXPR@4030..41522833                      EXPR_BINARY@4030..41522834                        EXPR@4030..40372835                          EXPR_STRING@4030..40372836                            STRING_SINGLE@4030..4037 "'---\\n'"2837                        WHITESPACE@4037..4038 " "2838                        PLUS@4038..4039 "+"2839                        WHITESPACE@4039..4040 " "2840                        EXPR@4040..41522841                          EXPR_VAR@4040..40432842                            NAME@4040..40432843                              IDENT@4040..4043 "std"2844                          SUFFIX_INDEX@4043..40482845                            DOT@4043..4044 "."2846                            NAME@4044..40482847                              IDENT@4044..4048 "join"2848                          SUFFIX_APPLY@4048..41522849                            ARGS_DESC@4048..41522850                              L_PAREN@4048..4049 "("2851                              WHITESPACE@4049..4058 "\n        "2852                              ARG@4058..40672853                                EXPR@4058..40672854                                  EXPR_STRING@4058..40672855                                    STRING_SINGLE@4058..4067 "'\\n---\\n'"2856                              COMMA@4067..4068 ","2857                              WHITESPACE@4068..4069 " "2858                              ARG@4069..41442859                                EXPR@4069..41442860                                  EXPR_ARRAY_COMP@4069..41442861                                    L_BRACK@4069..4070 "["2862                                    EXPR@4070..41282863                                      EXPR_VAR@4070..40732864                                        NAME@4070..40732865                                          IDENT@4070..4073 "std"2866                                      SUFFIX_INDEX@4073..40892867                                        DOT@4073..4074 "."2868                                        NAME@4074..40892869                                          IDENT@4074..4089 "manifestYamlDoc"2870                                      SUFFIX_APPLY@4089..41282871                                        ARGS_DESC@4089..41282872                                          L_PAREN@4089..4090 "("2873                                          ARG@4090..40912874                                            EXPR@4090..40912875                                              EXPR_VAR@4090..40912876                                                NAME@4090..40912877                                                  IDENT@4090..4091 "e"2878                                          COMMA@4091..4092 ","2879                                          WHITESPACE@4092..4093 " "2880                                          ARG@4093..41152881                                            EXPR@4093..41152882                                              EXPR_VAR@4093..41152883                                                NAME@4093..41152884                                                  IDENT@4093..4115 "indent_array_in_object"2885                                          COMMA@4115..4116 ","2886                                          WHITESPACE@4116..4117 " "2887                                          ARG@4117..41272888                                            EXPR@4117..41272889                                              EXPR_VAR@4117..41272890                                                NAME@4117..41272891                                                  IDENT@4117..4127 "quote_keys"2892                                          R_PAREN@4127..4128 ")"2893                                    WHITESPACE@4128..4129 " "2894                                    FOR_SPEC@4129..41432895                                      FOR_KW@4129..4132 "for"2896                                      WHITESPACE@4132..4133 " "2897                                      DESTRUCT_FULL@4133..41342898                                        NAME@4133..41342899                                          IDENT@4133..4134 "e"2900                                      WHITESPACE@4134..4135 " "2901                                      IN_KW@4135..4137 "in"2902                                      WHITESPACE@4137..4138 " "2903                                      EXPR@4138..41432904                                        EXPR_VAR@4138..41432905                                          NAME@4138..41432906                                            IDENT@4138..4143 "value"2907                                    R_BRACK@4143..4144 "]"2908                              WHITESPACE@4144..4151 "\n      "2909                              R_PAREN@4151..4152 ")"2910                    WHITESPACE@4152..4153 " "2911                    PLUS@4153..4154 "+"2912                    WHITESPACE@4154..4155 " "2913                    EXPR@4155..41972914                      EXPR_IF_THEN_ELSE@4155..41972915                        IF_KW@4155..4157 "if"2916                        WHITESPACE@4157..4158 " "2917                        EXPR@4158..41722918                          EXPR_VAR@4158..41722919                            NAME@4158..41722920                              IDENT@4158..4172 "c_document_end"2921                        WHITESPACE@4172..4173 " "2922                        THEN_KW@4173..4177 "then"2923                        WHITESPACE@4177..4178 " "2924                        TRUE_EXPR@4178..41872925                          EXPR@4178..41872926                            EXPR_STRING@4178..41872927                              STRING_SINGLE@4178..4187 "'\\n...\\n'"2928                        WHITESPACE@4187..4188 " "2929                        ELSE_KW@4188..4192 "else"2930                        WHITESPACE@4192..4193 " "2931                        FALSE_EXPR@4193..41972932                          EXPR@4193..41972933                            EXPR_STRING@4193..41972934                              STRING_SINGLE@4193..4197 "'\\n'"2935        COMMA@4197..4198 ","2936        WHITESPACE@4198..4202 "\n\n  "2937        MEMBER_FIELD_METHOD@4202..48582938          FIELD_NAME_FIXED@4202..42162939            NAME@4202..42162940              IDENT@4202..4216 "manifestPython"2941          PARAMS_DESC@4216..42192942            L_PAREN@4216..4217 "("2943            PARAM@4217..42182944              DESTRUCT_FULL@4217..42182945                NAME@4217..42182946                  IDENT@4217..4218 "v"2947            R_PAREN@4218..4219 ")"2948          COLONCOLON@4219..4221 "::"2949          WHITESPACE@4221..4226 "\n    "2950          EXPR@4226..48582951            EXPR_IF_THEN_ELSE@4226..48582952              IF_KW@4226..4228 "if"2953              WHITESPACE@4228..4229 " "2954              EXPR@4229..42442955                EXPR_VAR@4229..42322956                  NAME@4229..42322957                    IDENT@4229..4232 "std"2958                SUFFIX_INDEX@4232..42412959                  DOT@4232..4233 "."2960                  NAME@4233..42412961                    IDENT@4233..4241 "isObject"2962                SUFFIX_APPLY@4241..42442963                  ARGS_DESC@4241..42442964                    L_PAREN@4241..4242 "("2965                    ARG@4242..42432966                      EXPR@4242..42432967                        EXPR_VAR@4242..42432968                          NAME@4242..42432969                            IDENT@4242..4243 "v"2970                    R_PAREN@4243..4244 ")"2971              WHITESPACE@4244..4245 " "2972              THEN_KW@4245..4249 "then"2973              WHITESPACE@4249..4256 "\n      "2974              TRUE_EXPR@4256..44312975                EXPR@4256..44312976                  STMT_LOCAL@4256..43912977                    LOCAL_KW@4256..4261 "local"2978                    WHITESPACE@4261..4262 " "2979                    BIND_DESTRUCT@4262..43902980                      DESTRUCT_FULL@4262..42682981                        NAME@4262..42682982                          IDENT@4262..4268 "fields"2983                      WHITESPACE@4268..4269 " "2984                      ASSIGN@4269..4270 "="2985                      WHITESPACE@4270..4271 " "2986                      EXPR@4271..43902987                        EXPR_ARRAY_COMP@4271..43902988                          L_BRACK@4271..4272 "["2989                          WHITESPACE@4272..4281 "\n        "2990                          EXPR@4281..43452991                            EXPR_BINARY@4281..43452992                              EXPR@4281..42892993                                EXPR_STRING@4281..42892994                                  STRING_SINGLE@4281..4289 "'%s: %s'"2995                              WHITESPACE@4289..4290 " "2996                              MODULO@4290..4291 "%"2997                              WHITESPACE@4291..4292 " "2998                              EXPR@4292..43452999                                EXPR_ARRAY@4292..43453000                                  L_BRACK@4292..4293 "["3001                                  EXPR@4293..43183002                                    EXPR_VAR@4293..42963003                                      NAME@4293..42963004                                        IDENT@4293..4296 "std"3005                                    SUFFIX_INDEX@4296..43153006                                      DOT@4296..4297 "."3007                                      NAME@4297..43153008                                        IDENT@4297..4315 "escapeStringPython"3009                                    SUFFIX_APPLY@4315..43183010                                      ARGS_DESC@4315..43183011                                        L_PAREN@4315..4316 "("3012                                        ARG@4316..43173013                                          EXPR@4316..43173014                                            EXPR_VAR@4316..43173015                                              NAME@4316..43173016                                                IDENT@4316..4317 "k"3017                                        R_PAREN@4317..4318 ")"3018                                  COMMA@4318..4319 ","3019                                  WHITESPACE@4319..4320 " "3020                                  EXPR@4320..43443021                                    EXPR_VAR@4320..43233022                                      NAME@4320..43233023                                        IDENT@4320..4323 "std"3024                                    SUFFIX_INDEX@4323..43383025                                      DOT@4323..4324 "."3026                                      NAME@4324..43383027                                        IDENT@4324..4338 "manifestPython"3028                                    SUFFIX_APPLY@4338..43443029                                      ARGS_DESC@4338..43443030                                        L_PAREN@4338..4339 "("3031                                        ARG@4339..43433032                                          EXPR@4339..43433033                                            EXPR_VAR@4339..43403034                                              NAME@4339..43403035                                                IDENT@4339..4340 "v"3036                                            SUFFIX_INDEX_EXPR@4340..43433037                                              L_BRACK@4340..4341 "["3038                                              EXPR@4341..43423039                                                EXPR_VAR@4341..43423040                                                  NAME@4341..43423041                                                    IDENT@4341..4342 "k"3042                                              R_BRACK@4342..4343 "]"3043                                        R_PAREN@4343..4344 ")"3044                                  R_BRACK@4344..4345 "]"3045                          WHITESPACE@4345..4354 "\n        "3046                          FOR_SPEC@4354..43823047                            FOR_KW@4354..4357 "for"3048                            WHITESPACE@4357..4358 " "3049                            DESTRUCT_FULL@4358..43593050                              NAME@4358..43593051                                IDENT@4358..4359 "k"3052                            WHITESPACE@4359..4360 " "3053                            IN_KW@4360..4362 "in"3054                            WHITESPACE@4362..4363 " "3055                            EXPR@4363..43823056                              EXPR_VAR@4363..43663057                                NAME@4363..43663058                                  IDENT@4363..4366 "std"3059                              SUFFIX_INDEX@4366..43793060                                DOT@4366..4367 "."3061                                NAME@4367..43793062                                  IDENT@4367..4379 "objectFields"3063                              SUFFIX_APPLY@4379..43823064                                ARGS_DESC@4379..43823065                                  L_PAREN@4379..4380 "("3066                                  ARG@4380..43813067                                    EXPR@4380..43813068                                      EXPR_VAR@4380..43813069                                        NAME@4380..43813070                                          IDENT@4380..4381 "v"3071                                  R_PAREN@4381..4382 ")"3072                          WHITESPACE@4382..4389 "\n      "3073                          R_BRACK@4389..4390 "]"3074                    SEMI@4390..4391 ";"3075                  WHITESPACE@4391..4398 "\n      "3076                  EXPR_BINARY@4398..44313077                    EXPR@4398..44043078                      EXPR_STRING@4398..44043079                        STRING_SINGLE@4398..4404 "'{%s}'"3080                    WHITESPACE@4404..4405 " "3081                    MODULO@4405..4406 "%"3082                    WHITESPACE@4406..4407 " "3083                    EXPR@4407..44313084                      EXPR_ARRAY@4407..44313085                        L_BRACK@4407..4408 "["3086                        EXPR@4408..44303087                          EXPR_VAR@4408..44113088                            NAME@4408..44113089                              IDENT@4408..4411 "std"3090                          SUFFIX_INDEX@4411..44163091                            DOT@4411..4412 "."3092                            NAME@4412..44163093                              IDENT@4412..4416 "join"3094                          SUFFIX_APPLY@4416..44303095                            ARGS_DESC@4416..44303096                              L_PAREN@4416..4417 "("3097                              ARG@4417..44213098                                EXPR@4417..44213099                                  EXPR_STRING@4417..44213100                                    STRING_SINGLE@4417..4421 "', '"3101                              COMMA@4421..4422 ","3102                              WHITESPACE@4422..4423 " "3103                              ARG@4423..44293104                                EXPR@4423..44293105                                  EXPR_VAR@4423..44293106                                    NAME@4423..44293107                                      IDENT@4423..4429 "fields"3108                              R_PAREN@4429..4430 ")"3109                        R_BRACK@4430..4431 "]"3110              WHITESPACE@4431..4436 "\n    "3111              ELSE_KW@4436..4440 "else"3112              WHITESPACE@4440..4441 " "3113              FALSE_EXPR@4441..48583114                EXPR@4441..48583115                  EXPR_IF_THEN_ELSE@4441..48583116                    IF_KW@4441..4443 "if"3117                    WHITESPACE@4443..4444 " "3118                    EXPR@4444..44583119                      EXPR_VAR@4444..44473120                        NAME@4444..44473121                          IDENT@4444..4447 "std"3122                      SUFFIX_INDEX@4447..44553123                        DOT@4447..4448 "."3124                        NAME@4448..44553125                          IDENT@4448..4455 "isArray"3126                      SUFFIX_APPLY@4455..44583127                        ARGS_DESC@4455..44583128                          L_PAREN@4455..4456 "("3129                          ARG@4456..44573130                            EXPR@4456..44573131                              EXPR_VAR@4456..44573132                                NAME@4456..44573133                                  IDENT@4456..4457 "v"3134                          R_PAREN@4457..4458 ")"3135                    WHITESPACE@4458..4459 " "3136                    THEN_KW@4459..4463 "then"3137                    WHITESPACE@4463..4470 "\n      "3138                    TRUE_EXPR@4470..45333139                      EXPR@4470..45333140                        EXPR_BINARY@4470..45333141                          EXPR@4470..44763142                            EXPR_STRING@4470..44763143                              STRING_SINGLE@4470..4476 "'[%s]'"3144                          WHITESPACE@4476..4477 " "3145                          MODULO@4477..4478 "%"3146                          WHITESPACE@4478..4479 " "3147                          EXPR@4479..45333148                            EXPR_ARRAY@4479..45333149                              L_BRACK@4479..4480 "["3150                              EXPR@4480..45323151                                EXPR_VAR@4480..44833152                                  NAME@4480..44833153                                    IDENT@4480..4483 "std"3154                                SUFFIX_INDEX@4483..44883155                                  DOT@4483..4484 "."3156                                  NAME@4484..44883157                                    IDENT@4484..4488 "join"3158                                SUFFIX_APPLY@4488..45323159                                  ARGS_DESC@4488..45323160                                    L_PAREN@4488..4489 "("3161                                    ARG@4489..44933162                                      EXPR@4489..44933163                                        EXPR_STRING@4489..44933164                                          STRING_SINGLE@4489..4493 "', '"3165                                    COMMA@4493..4494 ","3166                                    WHITESPACE@4494..4495 " "3167                                    ARG@4495..45313168                                      EXPR@4495..45313169                                        EXPR_ARRAY_COMP@4495..45313170                                          L_BRACK@4495..4496 "["3171                                          EXPR@4496..45183172                                            EXPR_VAR@4496..44993173                                              NAME@4496..44993174                                                IDENT@4496..4499 "std"3175                                            SUFFIX_INDEX@4499..45143176                                              DOT@4499..4500 "."3177                                              NAME@4500..45143178                                                IDENT@4500..4514 "manifestPython"3179                                            SUFFIX_APPLY@4514..45183180                                              ARGS_DESC@4514..45183181                                                L_PAREN@4514..4515 "("3182                                                ARG@4515..45173183                                                  EXPR@4515..45173184                                                    EXPR_VAR@4515..45173185                                                      NAME@4515..45173186                                                        IDENT@4515..4517 "v2"3187                                                R_PAREN@4517..4518 ")"3188                                          WHITESPACE@4518..4519 " "3189                                          FOR_SPEC@4519..45303190                                            FOR_KW@4519..4522 "for"3191                                            WHITESPACE@4522..4523 " "3192                                            DESTRUCT_FULL@4523..45253193                                              NAME@4523..45253194                                                IDENT@4523..4525 "v2"3195                                            WHITESPACE@4525..4526 " "3196                                            IN_KW@4526..4528 "in"3197                                            WHITESPACE@4528..4529 " "3198                                            EXPR@4529..45303199                                              EXPR_VAR@4529..45303200                                                NAME@4529..45303201                                                  IDENT@4529..4530 "v"3202                                          R_BRACK@4530..4531 "]"3203                                    R_PAREN@4531..4532 ")"3204                              R_BRACK@4532..4533 "]"3205                    WHITESPACE@4533..4538 "\n    "3206                    ELSE_KW@4538..4542 "else"3207                    WHITESPACE@4542..4543 " "3208                    FALSE_EXPR@4543..48583209                      EXPR@4543..48583210                        EXPR_IF_THEN_ELSE@4543..48583211                          IF_KW@4543..4545 "if"3212                          WHITESPACE@4545..4546 " "3213                          EXPR@4546..45613214                            EXPR_VAR@4546..45493215                              NAME@4546..45493216                                IDENT@4546..4549 "std"3217                            SUFFIX_INDEX@4549..45583218                              DOT@4549..4550 "."3219                              NAME@4550..45583220                                IDENT@4550..4558 "isString"3221                            SUFFIX_APPLY@4558..45613222                              ARGS_DESC@4558..45613223                                L_PAREN@4558..4559 "("3224                                ARG@4559..45603225                                  EXPR@4559..45603226                                    EXPR_VAR@4559..45603227                                      NAME@4559..45603228                                        IDENT@4559..4560 "v"3229                                R_PAREN@4560..4561 ")"3230                          WHITESPACE@4561..4562 " "3231                          THEN_KW@4562..4566 "then"3232                          WHITESPACE@4566..4573 "\n      "3233                          TRUE_EXPR@4573..46073234                            EXPR@4573..46073235                              EXPR_BINARY@4573..46073236                                EXPR@4573..45773237                                  EXPR_STRING@4573..45773238                                    STRING_SINGLE@4573..4577 "'%s'"3239                                WHITESPACE@4577..4578 " "3240                                MODULO@4578..4579 "%"3241                                WHITESPACE@4579..4580 " "3242                                EXPR@4580..46073243                                  EXPR_ARRAY@4580..46073244                                    L_BRACK@4580..4581 "["3245                                    EXPR@4581..46063246                                      EXPR_VAR@4581..45843247                                        NAME@4581..45843248                                          IDENT@4581..4584 "std"3249                                      SUFFIX_INDEX@4584..46033250                                        DOT@4584..4585 "."3251                                        NAME@4585..46033252                                          IDENT@4585..4603 "escapeStringPython"3253                                      SUFFIX_APPLY@4603..46063254                                        ARGS_DESC@4603..46063255                                          L_PAREN@4603..4604 "("3256                                          ARG@4604..46053257                                            EXPR@4604..46053258                                              EXPR_VAR@4604..46053259                                                NAME@4604..46053260                                                  IDENT@4604..4605 "v"3261                                          R_PAREN@4605..4606 ")"3262                                    R_BRACK@4606..4607 "]"3263                          WHITESPACE@4607..4612 "\n    "3264                          ELSE_KW@4612..4616 "else"3265                          WHITESPACE@4616..4617 " "3266                          FALSE_EXPR@4617..48583267                            EXPR@4617..48583268                              EXPR_IF_THEN_ELSE@4617..48583269                                IF_KW@4617..4619 "if"3270                                WHITESPACE@4619..4620 " "3271                                EXPR@4620..46373272                                  EXPR_VAR@4620..46233273                                    NAME@4620..46233274                                      IDENT@4620..4623 "std"3275                                  SUFFIX_INDEX@4623..46343276                                    DOT@4623..4624 "."3277                                    NAME@4624..46343278                                      IDENT@4624..4634 "isFunction"3279                                  SUFFIX_APPLY@4634..46373280                                    ARGS_DESC@4634..46373281                                      L_PAREN@4634..4635 "("3282                                      ARG@4635..46363283                                        EXPR@4635..46363284                                          EXPR_VAR@4635..46363285                                            NAME@4635..46363286                                              IDENT@4635..4636 "v"3287                                      R_PAREN@4636..4637 ")"3288                                WHITESPACE@4637..4638 " "3289                                THEN_KW@4638..4642 "then"3290                                WHITESPACE@4642..4649 "\n      "3291                                TRUE_EXPR@4649..46813292                                  EXPR@4649..46813293                                    EXPR_ERROR@4649..46813294                                      ERROR_KW@4649..4654 "error"3295                                      WHITESPACE@4654..4655 " "3296                                      EXPR@4655..46813297                                        EXPR_STRING@4655..46813298                                          STRING_SINGLE@4655..4681 "'cannot manifest func ..."3299                                WHITESPACE@4681..4686 "\n    "3300                                ELSE_KW@4686..4690 "else"3301                                WHITESPACE@4690..4691 " "3302                                FALSE_EXPR@4691..48583303                                  EXPR@4691..48583304                                    EXPR_IF_THEN_ELSE@4691..48583305                                      IF_KW@4691..4693 "if"3306                                      WHITESPACE@4693..4694 " "3307                                      EXPR@4694..47093308                                        EXPR_VAR@4694..46973309                                          NAME@4694..46973310                                            IDENT@4694..4697 "std"3311                                        SUFFIX_INDEX@4697..47063312                                          DOT@4697..4698 "."3313                                          NAME@4698..47063314                                            IDENT@4698..4706 "isNumber"3315                                        SUFFIX_APPLY@4706..47093316                                          ARGS_DESC@4706..47093317                                            L_PAREN@4706..4707 "("3318                                            ARG@4707..47083319                                              EXPR@4707..47083320                                                EXPR_VAR@4707..47083321                                                  NAME@4707..47083322                                                    IDENT@4707..4708 "v"3323                                            R_PAREN@4708..4709 ")"3324                                      WHITESPACE@4709..4710 " "3325                                      THEN_KW@4710..4714 "then"3326                                      WHITESPACE@4714..4721 "\n      "3327                                      TRUE_EXPR@4721..47363328                                        EXPR@4721..47363329                                          EXPR_VAR@4721..47243330                                            NAME@4721..47243331                                              IDENT@4721..4724 "std"3332                                          SUFFIX_INDEX@4724..47333333                                            DOT@4724..4725 "."3334                                            NAME@4725..47333335                                              IDENT@4725..4733 "toString"3336                                          SUFFIX_APPLY@4733..47363337                                            ARGS_DESC@4733..47363338                                              L_PAREN@4733..4734 "("3339                                              ARG@4734..47353340                                                EXPR@4734..47353341                                                  EXPR_VAR@4734..47353342                                                    NAME@4734..47353343                                                      IDENT@4734..4735 "v"3344                                              R_PAREN@4735..4736 ")"3345                                      WHITESPACE@4736..4741 "\n    "3346                                      ELSE_KW@4741..4745 "else"3347                                      WHITESPACE@4745..4746 " "3348                                      FALSE_EXPR@4746..48583349                                        EXPR@4746..48583350                                          EXPR_IF_THEN_ELSE@4746..48583351                                            IF_KW@4746..4748 "if"3352                                            WHITESPACE@4748..4749 " "3353                                            EXPR@4749..47583354                                              EXPR_BINARY@4749..47583355                                                EXPR@4749..47503356                                                  EXPR_VAR@4749..47503357                                                    NAME@4749..47503358                                                      IDENT@4749..4750 "v"3359                                                WHITESPACE@4750..4751 " "3360                                                EQ@4751..4753 "=="3361                                                WHITESPACE@4753..4754 " "3362                                                EXPR@4754..47583363                                                  EXPR_LITERAL@4754..47583364                                                    TRUE_KW@4754..4758 "true"3365                                            WHITESPACE@4758..4759 " "3366                                            THEN_KW@4759..4763 "then"3367                                            WHITESPACE@4763..4770 "\n      "3368                                            TRUE_EXPR@4770..47763369                                              EXPR@4770..47763370                                                EXPR_STRING@4770..47763371                                                  STRING_SINGLE@4770..4776 "'True'"3372                                            WHITESPACE@4776..4781 "\n    "3373                                            ELSE_KW@4781..4785 "else"3374                                            WHITESPACE@4785..4786 " "3375                                            FALSE_EXPR@4786..48583376                                              EXPR@4786..48583377                                                EXPR_IF_THEN_ELSE@4786..48583378                                                  IF_KW@4786..4788 "if"3379                                                  WHITESPACE@4788..4789 " "3380                                                  EXPR@4789..47993381                                                    EXPR_BINARY@4789..47993382                                                      EXPR@4789..47903383                                                        EXPR_VAR@4789..47903384                                                          NAME@4789..47903385                                                            IDENT@4789..4790 "v"3386                                                      WHITESPACE@4790..4791 " "3387                                                      EQ@4791..4793 "=="3388                                                      WHITESPACE@4793..4794 " "3389                                                      EXPR@4794..47993390                                                        EXPR_LITERAL@4794..47993391                                                          FALSE_KW@4794..4799 "false"3392                                                  WHITESPACE@4799..4800 " "3393                                                  THEN_KW@4800..4804 "then"3394                                                  WHITESPACE@4804..4811 "\n      "3395                                                  TRUE_EXPR@4811..48183396                                                    EXPR@4811..48183397                                                      EXPR_STRING@4811..48183398                                                        STRING_SINGLE@4811..4818 "'False'"3399                                                  WHITESPACE@4818..4823 "\n    "3400                                                  ELSE_KW@4823..4827 "else"3401                                                  WHITESPACE@4827..4828 " "3402                                                  FALSE_EXPR@4828..48583403                                                    EXPR@4828..48583404                                                      EXPR_IF_THEN_ELSE@4828..48583405                                                        IF_KW@4828..4830 "if"3406                                                        WHITESPACE@4830..4831 " "3407                                                        EXPR@4831..48403408                                                          EXPR_BINARY@4831..48403409                                                            EXPR@4831..48323410                                                              EXPR_VAR@4831..48323411                                                                NAME@4831..48323412                                                                  IDENT@4831..4832 "v"3413                                                            WHITESPACE@4832..4833 " "3414                                                            EQ@4833..4835 "=="3415                                                            WHITESPACE@4835..4836 " "3416                                                            EXPR@4836..48403417                                                              EXPR_LITERAL@4836..48403418                                                                NULL_KW@4836..4840 "null"3419                                                        WHITESPACE@4840..4841 " "3420                                                        THEN_KW@4841..4845 "then"3421                                                        WHITESPACE@4845..4852 "\n      "3422                                                        TRUE_EXPR@4852..48583423                                                          EXPR@4852..48583424                                                            EXPR_STRING@4852..48583425                                                              STRING_SINGLE@4852..4858 "'None'"3426        COMMA@4858..4859 ","3427        WHITESPACE@4859..4863 "\n\n  "3428        MEMBER_FIELD_METHOD@4863..50183429          FIELD_NAME_FIXED@4863..48813430            NAME@4863..48813431              IDENT@4863..4881 "manifestPythonVars"3432          PARAMS_DESC@4881..48873433            L_PAREN@4881..4882 "("3434            PARAM@4882..48863435              DESTRUCT_FULL@4882..48863436                NAME@4882..48863437                  IDENT@4882..4886 "conf"3438            R_PAREN@4886..4887 ")"3439          COLONCOLON@4887..4889 "::"3440          WHITESPACE@4889..4894 "\n    "3441          EXPR@4894..50183442            STMT_LOCAL@4894..49863443              LOCAL_KW@4894..4899 "local"3444              WHITESPACE@4899..4900 " "3445              BIND_DESTRUCT@4900..49853446                DESTRUCT_FULL@4900..49043447                  NAME@4900..49043448                    IDENT@4900..4904 "vars"3449                WHITESPACE@4904..4905 " "3450                ASSIGN@4905..4906 "="3451                WHITESPACE@4906..4907 " "3452                EXPR@4907..49853453                  EXPR_ARRAY_COMP@4907..49853454                    L_BRACK@4907..4908 "["3455                    EXPR@4908..49523456                      EXPR_BINARY@4908..49523457                        EXPR@4908..49173458                          EXPR_STRING@4908..49173459                            STRING_SINGLE@4908..4917 "'%s = %s'"3460                        WHITESPACE@4917..4918 " "3461                        MODULO@4918..4919 "%"3462                        WHITESPACE@4919..4920 " "3463                        EXPR@4920..49523464                          EXPR_ARRAY@4920..49523465                            L_BRACK@4920..4921 "["3466                            EXPR@4921..49223467                              EXPR_VAR@4921..49223468                                NAME@4921..49223469                                  IDENT@4921..4922 "k"3470                            COMMA@4922..4923 ","3471                            WHITESPACE@4923..4924 " "3472                            EXPR@4924..49513473                              EXPR_VAR@4924..49273474                                NAME@4924..49273475                                  IDENT@4924..4927 "std"3476                              SUFFIX_INDEX@4927..49423477                                DOT@4927..4928 "."3478                                NAME@4928..49423479                                  IDENT@4928..4942 "manifestPython"3480                              SUFFIX_APPLY@4942..49513481                                ARGS_DESC@4942..49513482                                  L_PAREN@4942..4943 "("3483                                  ARG@4943..49503484                                    EXPR@4943..49503485                                      EXPR_VAR@4943..49473486                                        NAME@4943..49473487                                          IDENT@4943..4947 "conf"3488                                      SUFFIX_INDEX_EXPR@4947..49503489                                        L_BRACK@4947..4948 "["3490                                        EXPR@4948..49493491                                          EXPR_VAR@4948..49493492                                            NAME@4948..49493493                                              IDENT@4948..4949 "k"3494                                        R_BRACK@4949..4950 "]"3495                                  R_PAREN@4950..4951 ")"3496                            R_BRACK@4951..4952 "]"3497                    WHITESPACE@4952..4953 " "3498                    FOR_SPEC@4953..49843499                      FOR_KW@4953..4956 "for"3500                      WHITESPACE@4956..4957 " "3501                      DESTRUCT_FULL@4957..49583502                        NAME@4957..49583503                          IDENT@4957..4958 "k"3504                      WHITESPACE@4958..4959 " "3505                      IN_KW@4959..4961 "in"3506                      WHITESPACE@4961..4962 " "3507                      EXPR@4962..49843508                        EXPR_VAR@4962..49653509                          NAME@4962..49653510                            IDENT@4962..4965 "std"3511                        SUFFIX_INDEX@4965..49783512                          DOT@4965..4966 "."3513                          NAME@4966..49783514                            IDENT@4966..4978 "objectFields"3515                        SUFFIX_APPLY@4978..49843516                          ARGS_DESC@4978..49843517                            L_PAREN@4978..4979 "("3518                            ARG@4979..49833519                              EXPR@4979..49833520                                EXPR_VAR@4979..49833521                                  NAME@4979..49833522                                    IDENT@4979..4983 "conf"3523                            R_PAREN@4983..4984 ")"3524                    R_BRACK@4984..4985 "]"3525              SEMI@4985..4986 ";"3526            WHITESPACE@4986..4991 "\n    "3527            EXPR_VAR@4991..49943528              NAME@4991..49943529                IDENT@4991..4994 "std"3530            SUFFIX_INDEX@4994..49993531              DOT@4994..4995 "."3532              NAME@4995..49993533                IDENT@4995..4999 "join"3534            SUFFIX_APPLY@4999..50183535              ARGS_DESC@4999..50183536                L_PAREN@4999..5000 "("3537                ARG@5000..50043538                  EXPR@5000..50043539                    EXPR_STRING@5000..50043540                      STRING_SINGLE@5000..5004 "'\\n'"3541                COMMA@5004..5005 ","3542                WHITESPACE@5005..5006 " "3543                ARG@5006..50173544                  EXPR@5006..50173545                    EXPR_BINARY@5006..50173546                      EXPR@5006..50103547                        EXPR_VAR@5006..50103548                          NAME@5006..50103549                            IDENT@5006..5010 "vars"3550                      WHITESPACE@5010..5011 " "3551                      PLUS@5011..5012 "+"3552                      WHITESPACE@5012..5013 " "3553                      EXPR@5013..50173554                        EXPR_ARRAY@5013..50173555                          L_BRACK@5013..5014 "["3556                          EXPR@5014..50163557                            EXPR_STRING@5014..50163558                              STRING_SINGLE@5014..5016 "''"3559                          R_BRACK@5016..5017 "]"3560                R_PAREN@5017..5018 ")"3561        COMMA@5018..5019 ","3562        WHITESPACE@5019..5023 "\n\n  "3563        MEMBER_FIELD_METHOD@5023..56903564          FIELD_NAME_FIXED@5023..50403565            NAME@5023..50403566              IDENT@5023..5040 "manifestXmlJsonml"3567          PARAMS_DESC@5040..50473568            L_PAREN@5040..5041 "("3569            PARAM@5041..50463570              DESTRUCT_FULL@5041..50463571                NAME@5041..50463572                  IDENT@5041..5046 "value"3573            R_PAREN@5046..5047 ")"3574          COLONCOLON@5047..5049 "::"3575          WHITESPACE@5049..5054 "\n    "3576          EXPR@5054..56903577            EXPR_IF_THEN_ELSE@5054..56903578              IF_KW@5054..5056 "if"3579              WHITESPACE@5056..5057 " "3580              EXPR@5057..50763581                EXPR_UNARY@5057..50763582                  NOT@5057..5058 "!"3583                  EXPR_VAR@5058..50613584                    NAME@5058..50613585                      IDENT@5058..5061 "std"3586                  SUFFIX_INDEX@5061..50693587                    DOT@5061..5062 "."3588                    NAME@5062..50693589                      IDENT@5062..5069 "isArray"3590                  SUFFIX_APPLY@5069..50763591                    ARGS_DESC@5069..50763592                      L_PAREN@5069..5070 "("3593                      ARG@5070..50753594                        EXPR@5070..50753595                          EXPR_VAR@5070..50753596                            NAME@5070..50753597                              IDENT@5070..5075 "value"3598                      R_PAREN@5075..5076 ")"3599              WHITESPACE@5076..5077 " "3600              THEN_KW@5077..5081 "then"3601              WHITESPACE@5081..5088 "\n      "3602              TRUE_EXPR@5088..51563603                EXPR@5088..51563604                  EXPR_ERROR@5088..51563605                    ERROR_KW@5088..5093 "error"3606                    WHITESPACE@5093..5094 " "3607                    EXPR@5094..51563608                      EXPR_BINARY@5094..51563609                        EXPR@5094..51383610                          EXPR_STRING@5094..51383611                            STRING_SINGLE@5094..5138 "'Expected a JSONML va ..."3612                        WHITESPACE@5138..5139 " "3613                        MODULO@5139..5140 "%"3614                        WHITESPACE@5140..5141 " "3615                        EXPR@5141..51563616                          EXPR_VAR@5141..51443617                            NAME@5141..51443618                              IDENT@5141..5144 "std"3619                          SUFFIX_INDEX@5144..51493620                            DOT@5144..5145 "."3621                            NAME@5145..51493622                              IDENT@5145..5149 "type"3623                          SUFFIX_APPLY@5149..51563624                            ARGS_DESC@5149..51563625                              L_PAREN@5149..5150 "("3626                              ARG@5150..51553627                                EXPR@5150..51553628                                  EXPR_VAR@5150..51553629                                    NAME@5150..51553630                                      IDENT@5150..5155 "value"3631                              R_PAREN@5155..5156 ")"3632              WHITESPACE@5156..5161 "\n    "3633              ELSE_KW@5161..5165 "else"3634              WHITESPACE@5165..5172 "\n      "3635              FALSE_EXPR@5172..56903636                EXPR@5172..56903637                  STMT_LOCAL@5172..56723638                    LOCAL_KW@5172..5177 "local"3639                    WHITESPACE@5177..5178 " "3640                    BIND_FUNCTION@5178..56713641                      NAME@5178..51813642                        IDENT@5178..5181 "aux"3643                      PARAMS_DESC@5181..51843644                        L_PAREN@5181..5182 "("3645                        PARAM@5182..51833646                          DESTRUCT_FULL@5182..51833647                            NAME@5182..51833648                              IDENT@5182..5183 "v"3649                        R_PAREN@5183..5184 ")"3650                      WHITESPACE@5184..5185 " "3651                      ASSIGN@5185..5186 "="3652                      WHITESPACE@5186..5195 "\n        "3653                      EXPR@5195..56713654                        EXPR_IF_THEN_ELSE@5195..56713655                          IF_KW@5195..5197 "if"3656                          WHITESPACE@5197..5198 " "3657                          EXPR@5198..52133658                            EXPR_VAR@5198..52013659                              NAME@5198..52013660                                IDENT@5198..5201 "std"3661                            SUFFIX_INDEX@5201..52103662                              DOT@5201..5202 "."3663                              NAME@5202..52103664                                IDENT@5202..5210 "isString"3665                            SUFFIX_APPLY@5210..52133666                              ARGS_DESC@5210..52133667                                L_PAREN@5210..5211 "("3668                                ARG@5211..52123669                                  EXPR@5211..52123670                                    EXPR_VAR@5211..52123671                                      NAME@5211..52123672                                        IDENT@5211..5212 "v"3673                                R_PAREN@5212..5213 ")"3674                          WHITESPACE@5213..5214 " "3675                          THEN_KW@5214..5218 "then"3676                          WHITESPACE@5218..5229 "\n          "3677                          TRUE_EXPR@5229..52303678                            EXPR@5229..52303679                              EXPR_VAR@5229..52303680                                NAME@5229..52303681                                  IDENT@5229..5230 "v"3682                          WHITESPACE@5230..5239 "\n        "3683                          ELSE_KW@5239..5243 "else"3684                          WHITESPACE@5243..5254 "\n          "3685                          FALSE_EXPR@5254..56713686                            EXPR@5254..56713687                              STMT_LOCAL@5254..52713688                                LOCAL_KW@5254..5259 "local"3689                                WHITESPACE@5259..5260 " "3690                                BIND_DESTRUCT@5260..52703691                                  DESTRUCT_FULL@5260..52633692                                    NAME@5260..52633693                                      IDENT@5260..5263 "tag"3694                                  WHITESPACE@5263..5264 " "3695                                  ASSIGN@5264..5265 "="3696                                  WHITESPACE@5265..5266 " "3697                                  EXPR@5266..52703698                                    EXPR_VAR@5266..52673699                                      NAME@5266..52673700                                        IDENT@5266..5267 "v"3701                                    SUFFIX_INDEX_EXPR@5267..52703702                                      L_BRACK@5267..5268 "["3703                                      EXPR@5268..52693704                                        EXPR_NUMBER@5268..52693705                                          FLOAT@5268..5269 "0"3706                                      R_BRACK@5269..5270 "]"3707                                SEMI@5270..5271 ";"3708                              WHITESPACE@5271..5282 "\n          "3709                              STMT_LOCAL@5282..53403710                                LOCAL_KW@5282..5287 "local"3711                                WHITESPACE@5287..5288 " "3712                                BIND_DESTRUCT@5288..53393713                                  DESTRUCT_FULL@5288..52973714                                    NAME@5288..52973715                                      IDENT@5288..5297 "has_attrs"3716                                  WHITESPACE@5297..5298 " "3717                                  ASSIGN@5298..5299 "="3718                                  WHITESPACE@5299..5300 " "3719                                  EXPR@5300..53393720                                    EXPR_BINARY@5300..53393721                                      EXPR@5300..53173722                                        EXPR_BINARY@5300..53173723                                          EXPR@5300..53033724                                            EXPR_VAR@5300..53033725                                              NAME@5300..53033726                                                IDENT@5300..5303 "std"3727                                          SUFFIX_INDEX@5303..53103728                                            DOT@5303..5304 "."3729                                            NAME@5304..53103730                                              IDENT@5304..5310 "length"3731                                          SUFFIX_APPLY@5310..53133732                                            ARGS_DESC@5310..53133733                                              L_PAREN@5310..5311 "("3734                                              ARG@5311..53123735                                                EXPR@5311..53123736                                                  EXPR_VAR@5311..53123737                                                    NAME@5311..53123738                                                      IDENT@5311..5312 "v"3739                                              R_PAREN@5312..5313 ")"3740                                          WHITESPACE@5313..5314 " "3741                                          GT@5314..5315 ">"3742                                          WHITESPACE@5315..5316 " "3743                                          EXPR@5316..53173744                                            EXPR_NUMBER@5316..53173745                                              FLOAT@5316..5317 "1"3746                                      WHITESPACE@5317..5318 " "3747                                      AND@5318..5320 "&&"3748                                      WHITESPACE@5320..5321 " "3749                                      EXPR@5321..53393750                                        EXPR_VAR@5321..53243751                                          NAME@5321..53243752                                            IDENT@5321..5324 "std"3753                                        SUFFIX_INDEX@5324..53333754                                          DOT@5324..5325 "."3755                                          NAME@5325..53333756                                            IDENT@5325..5333 "isObject"3757                                        SUFFIX_APPLY@5333..53393758                                          ARGS_DESC@5333..53393759                                            L_PAREN@5333..5334 "("3760                                            ARG@5334..53383761                                              EXPR@5334..53383762                                                EXPR_VAR@5334..53353763                                                  NAME@5334..53353764                                                    IDENT@5334..5335 "v"3765                                                SUFFIX_INDEX_EXPR@5335..53383766                                                  L_BRACK@5335..5336 "["3767                                                  EXPR@5336..53373768                                                    EXPR_NUMBER@5336..53373769                                                      FLOAT@5336..5337 "1"3770                                                  R_BRACK@5337..5338 "]"3771                                            R_PAREN@5338..5339 ")"3772                                SEMI@5339..5340 ";"3773                              WHITESPACE@5340..5351 "\n          "3774                              STMT_LOCAL@5351..53963775                                LOCAL_KW@5351..5356 "local"3776                                WHITESPACE@5356..5357 " "3777                                BIND_DESTRUCT@5357..53953778                                  DESTRUCT_FULL@5357..53623779                                    NAME@5357..53623780                                      IDENT@5357..5362 "attrs"3781                                  WHITESPACE@5362..5363 " "3782                                  ASSIGN@5363..5364 "="3783                                  WHITESPACE@5364..5365 " "3784                                  EXPR@5365..53953785                                    EXPR_IF_THEN_ELSE@5365..53953786                                      IF_KW@5365..5367 "if"3787                                      WHITESPACE@5367..5368 " "3788                                      EXPR@5368..53773789                                        EXPR_VAR@5368..53773790                                          NAME@5368..53773791                                            IDENT@5368..5377 "has_attrs"3792                                      WHITESPACE@5377..5378 " "3793                                      THEN_KW@5378..5382 "then"3794                                      WHITESPACE@5382..5383 " "3795                                      TRUE_EXPR@5383..53873796                                        EXPR@5383..53873797                                          EXPR_VAR@5383..53843798                                            NAME@5383..53843799                                              IDENT@5383..5384 "v"3800                                          SUFFIX_INDEX_EXPR@5384..53873801                                            L_BRACK@5384..5385 "["3802                                            EXPR@5385..53863803                                              EXPR_NUMBER@5385..53863804                                                FLOAT@5385..5386 "1"3805                                            R_BRACK@5386..5387 "]"3806                                      WHITESPACE@5387..5388 " "3807                                      ELSE_KW@5388..5392 "else"3808                                      WHITESPACE@5392..5393 " "3809                                      FALSE_EXPR@5393..53953810                                        EXPR@5393..53953811                                          EXPR_OBJECT@5393..53953812                                            OBJ_BODY_MEMBER_LIST@5393..53953813                                              L_BRACE@5393..5394 "{"3814                                              R_BRACE@5394..5395 "}"3815                                SEMI@5395..5396 ";"3816                              WHITESPACE@5396..5407 "\n          "3817                              STMT_LOCAL@5407..54593818                                LOCAL_KW@5407..5412 "local"3819                                WHITESPACE@5412..5413 " "3820                                BIND_DESTRUCT@5413..54583821                                  DESTRUCT_FULL@5413..54213822                                    NAME@5413..54213823                                      IDENT@5413..5421 "children"3824                                  WHITESPACE@5421..5422 " "3825                                  ASSIGN@5422..5423 "="3826                                  WHITESPACE@5423..5424 " "3827                                  EXPR@5424..54583828                                    EXPR_IF_THEN_ELSE@5424..54583829                                      IF_KW@5424..5426 "if"3830                                      WHITESPACE@5426..5427 " "3831                                      EXPR@5427..54363832                                        EXPR_VAR@5427..54363833                                          NAME@5427..54363834                                            IDENT@5427..5436 "has_attrs"3835                                      WHITESPACE@5436..5437 " "3836                                      THEN_KW@5437..5441 "then"3837                                      WHITESPACE@5441..5442 " "3838                                      TRUE_EXPR@5442..54473839                                        EXPR@5442..54473840                                          EXPR_VAR@5442..54433841                                            NAME@5442..54433842                                              IDENT@5442..5443 "v"3843                                          SUFFIX_SLICE@5443..54473844                                            SLICE_DESC@5443..54473845                                              L_BRACK@5443..5444 "["3846                                              EXPR@5444..54453847                                                EXPR_NUMBER@5444..54453848                                                  FLOAT@5444..5445 "2"3849                                              COLON@5445..5446 ":"3850                                              R_BRACK@5446..5447 "]"3851                                      WHITESPACE@5447..5448 " "3852                                      ELSE_KW@5448..5452 "else"3853                                      WHITESPACE@5452..5453 " "3854                                      FALSE_EXPR@5453..54583855                                        EXPR@5453..54583856                                          EXPR_VAR@5453..54543857                                            NAME@5453..54543858                                              IDENT@5453..5454 "v"3859                                          SUFFIX_SLICE@5454..54583860                                            SLICE_DESC@5454..54583861                                              L_BRACK@5454..5455 "["3862                                              EXPR@5455..54563863                                                EXPR_NUMBER@5455..54563864                                                  FLOAT@5455..5456 "1"3865                                              COLON@5456..5457 ":"3866                                              R_BRACK@5457..5458 "]"3867                                SEMI@5458..5459 ";"3868                              WHITESPACE@5459..5470 "\n          "3869                              STMT_LOCAL@5470..55763870                                LOCAL_KW@5470..5475 "local"3871                                WHITESPACE@5475..5476 " "3872                                BIND_DESTRUCT@5476..55753873                                  DESTRUCT_FULL@5476..54853874                                    NAME@5476..54853875                                      IDENT@5476..5485 "attrs_str"3876                                  WHITESPACE@5485..5486 " "3877                                  ASSIGN@5486..5487 "="3878                                  WHITESPACE@5487..5500 "\n            "3879                                  EXPR@5500..55753880                                    EXPR_VAR@5500..55033881                                      NAME@5500..55033882                                        IDENT@5500..5503 "std"3883                                    SUFFIX_INDEX@5503..55083884                                      DOT@5503..5504 "."3885                                      NAME@5504..55083886                                        IDENT@5504..5508 "join"3887                                    SUFFIX_APPLY@5508..55753888                                      ARGS_DESC@5508..55753889                                        L_PAREN@5508..5509 "("3890                                        ARG@5509..55113891                                          EXPR@5509..55113892                                            EXPR_STRING@5509..55113893                                              STRING_SINGLE@5509..5511 "''"3894                                        COMMA@5511..5512 ","3895                                        WHITESPACE@5512..5513 " "3896                                        ARG@5513..55743897                                          EXPR@5513..55743898                                            EXPR_ARRAY_COMP@5513..55743899                                              L_BRACK@5513..5514 "["3900                                              EXPR@5514..55403901                                                EXPR_BINARY@5514..55403902                                                  EXPR@5514..55243903                                                    EXPR_STRING@5514..55243904                                                      STRING_SINGLE@5514..5524 "' %s=\"%s\"'"3905                                                  WHITESPACE@5524..5525 " "3906                                                  MODULO@5525..5526 "%"3907                                                  WHITESPACE@5526..5527 " "3908                                                  EXPR@5527..55403909                                                    EXPR_ARRAY@5527..55403910                                                      L_BRACK@5527..5528 "["3911                                                      EXPR@5528..55293912                                                        EXPR_VAR@5528..55293913                                                          NAME@5528..55293914                                                            IDENT@5528..5529 "k"3915                                                      COMMA@5529..5530 ","3916                                                      WHITESPACE@5530..5531 " "3917                                                      EXPR@5531..55393918                                                        EXPR_VAR@5531..55363919                                                          NAME@5531..55363920                                                            IDENT@5531..5536 "attrs"3921                                                        SUFFIX_INDEX_EXPR@5536..55393922                                                          L_BRACK@5536..5537 "["3923                                                          EXPR@5537..55383924                                                            EXPR_VAR@5537..55383925                                                              NAME@5537..55383926                                                                IDENT@5537..5538 "k"3927                                                          R_BRACK@5538..5539 "]"3928                                                      R_BRACK@5539..5540 "]"3929                                              WHITESPACE@5540..5541 " "3930                                              FOR_SPEC@5541..55733931                                                FOR_KW@5541..5544 "for"3932                                                WHITESPACE@5544..5545 " "3933                                                DESTRUCT_FULL@5545..55463934                                                  NAME@5545..55463935                                                    IDENT@5545..5546 "k"3936                                                WHITESPACE@5546..5547 " "3937                                                IN_KW@5547..5549 "in"3938                                                WHITESPACE@5549..5550 " "3939                                                EXPR@5550..55733940                                                  EXPR_VAR@5550..55533941                                                    NAME@5550..55533942                                                      IDENT@5550..5553 "std"3943                                                  SUFFIX_INDEX@5553..55663944                                                    DOT@5553..5554 "."3945                                                    NAME@5554..55663946                                                      IDENT@5554..5566 "objectFields"3947                                                  SUFFIX_APPLY@5566..55733948                                                    ARGS_DESC@5566..55733949                                                      L_PAREN@5566..5567 "("3950                                                      ARG@5567..55723951                                                        EXPR@5567..55723952                                                          EXPR_VAR@5567..55723953                                                            NAME@5567..55723954                                                              IDENT@5567..5572 "attrs"3955                                                      R_PAREN@5572..5573 ")"3956                                              R_BRACK@5573..5574 "]"3957                                        R_PAREN@5574..5575 ")"3958                                SEMI@5575..5576 ";"3959                              WHITESPACE@5576..5587 "\n          "3960                              EXPR_VAR@5587..55903961                                NAME@5587..55903962                                  IDENT@5587..5590 "std"3963                              SUFFIX_INDEX@5590..55993964                                DOT@5590..5591 "."3965                                NAME@5591..55993966                                  IDENT@5591..5599 "deepJoin"3967                              SUFFIX_APPLY@5599..56713968                                ARGS_DESC@5599..56713969                                  L_PAREN@5599..5600 "("3970                                  ARG@5600..56703971                                    EXPR@5600..56703972                                      EXPR_ARRAY@5600..56703973                                        L_BRACK@5600..5601 "["3974                                        EXPR@5601..56043975                                          EXPR_STRING@5601..56043976                                            STRING_SINGLE@5601..5604 "'<'"3977                                        COMMA@5604..5605 ","3978                                        WHITESPACE@5605..5606 " "3979                                        EXPR@5606..56093980                                          EXPR_VAR@5606..56093981                                            NAME@5606..56093982                                              IDENT@5606..5609 "tag"3983                                        COMMA@5609..5610 ","3984                                        WHITESPACE@5610..5611 " "3985                                        EXPR@5611..56203986                                          EXPR_VAR@5611..56203987                                            NAME@5611..56203988                                              IDENT@5611..5620 "attrs_str"3989                                        COMMA@5620..5621 ","3990                                        WHITESPACE@5621..5622 " "3991                                        EXPR@5622..56253992                                          EXPR_STRING@5622..56253993                                            STRING_SINGLE@5622..5625 "'>'"3994                                        COMMA@5625..5626 ","3995                                        WHITESPACE@5626..5627 " "3996                                        EXPR@5627..56533997                                          EXPR_ARRAY_COMP@5627..56533998                                            L_BRACK@5627..5628 "["3999                                            EXPR@5628..56344000                                              EXPR_VAR@5628..56314001                                                NAME@5628..56314002                                                  IDENT@5628..5631 "aux"4003                                              SUFFIX_APPLY@5631..56344004                                                ARGS_DESC@5631..56344005                                                  L_PAREN@5631..5632 "("4006                                                  ARG@5632..56334007                                                    EXPR@5632..56334008                                                      EXPR_VAR@5632..56334009                                                        NAME@5632..56334010                                                          IDENT@5632..5633 "x"4011                                                  R_PAREN@5633..5634 ")"4012                                            WHITESPACE@5634..5635 " "4013                                            FOR_SPEC@5635..56524014                                              FOR_KW@5635..5638 "for"4015                                              WHITESPACE@5638..5639 " "4016                                              DESTRUCT_FULL@5639..56404017                                                NAME@5639..56404018                                                  IDENT@5639..5640 "x"4019                                              WHITESPACE@5640..5641 " "4020                                              IN_KW@5641..5643 "in"4021                                              WHITESPACE@5643..5644 " "4022                                              EXPR@5644..56524023                                                EXPR_VAR@5644..56524024                                                  NAME@5644..56524025                                                    IDENT@5644..5652 "children"4026                                            R_BRACK@5652..5653 "]"4027                                        COMMA@5653..5654 ","4028                                        WHITESPACE@5654..5655 " "4029                                        EXPR@5655..56594030                                          EXPR_STRING@5655..56594031                                            STRING_SINGLE@5655..5659 "'</'"4032                                        COMMA@5659..5660 ","4033                                        WHITESPACE@5660..5661 " "4034                                        EXPR@5661..56644035                                          EXPR_VAR@5661..56644036                                            NAME@5661..56644037                                              IDENT@5661..5664 "tag"4038                                        COMMA@5664..5665 ","4039                                        WHITESPACE@5665..5666 " "4040                                        EXPR@5666..56694041                                          EXPR_STRING@5666..56694042                                            STRING_SINGLE@5666..5669 "'>'"4043                                        R_BRACK@5669..5670 "]"4044                                  R_PAREN@5670..5671 ")"4045                    SEMI@5671..5672 ";"4046                  WHITESPACE@5672..5680 "\n\n      "4047                  EXPR_VAR@5680..56834048                    NAME@5680..56834049                      IDENT@5680..5683 "aux"4050                  SUFFIX_APPLY@5683..56904051                    ARGS_DESC@5683..56904052                      L_PAREN@5683..5684 "("4053                      ARG@5684..56894054                        EXPR@5684..56894055                          EXPR_VAR@5684..56894056                            NAME@5684..56894057                              IDENT@5684..5689 "value"4058                      R_PAREN@5689..5690 ")"4059        COMMA@5690..5691 ","4060        WHITESPACE@5691..5695 "\n\n  "4061        MEMBER_FIELD_METHOD@5695..64834062          FIELD_NAME_FIXED@5695..57054063            NAME@5695..57054064              IDENT@5695..5705 "mergePatch"4065          PARAMS_DESC@5705..57204066            L_PAREN@5705..5706 "("4067            PARAM@5706..57124068              DESTRUCT_FULL@5706..57124069                NAME@5706..57124070                  IDENT@5706..5712 "target"4071            COMMA@5712..5713 ","4072            WHITESPACE@5713..5714 " "4073            PARAM@5714..57194074              DESTRUCT_FULL@5714..57194075                NAME@5714..57194076                  IDENT@5714..5719 "patch"4077            R_PAREN@5719..5720 ")"4078          COLONCOLON@5720..5722 "::"4079          WHITESPACE@5722..5727 "\n    "4080          EXPR@5727..64834081            EXPR_IF_THEN_ELSE@5727..64834082              IF_KW@5727..5729 "if"4083              WHITESPACE@5729..5730 " "4084              EXPR@5730..57494085                EXPR_VAR@5730..57334086                  NAME@5730..57334087                    IDENT@5730..5733 "std"4088                SUFFIX_INDEX@5733..57424089                  DOT@5733..5734 "."4090                  NAME@5734..57424091                    IDENT@5734..5742 "isObject"4092                SUFFIX_APPLY@5742..57494093                  ARGS_DESC@5742..57494094                    L_PAREN@5742..5743 "("4095                    ARG@5743..57484096                      EXPR@5743..57484097                        EXPR_VAR@5743..57484098                          NAME@5743..57484099                            IDENT@5743..5748 "patch"4100                    R_PAREN@5748..5749 ")"4101              WHITESPACE@5749..5750 " "4102              THEN_KW@5750..5754 "then"4103              WHITESPACE@5754..5761 "\n      "4104              TRUE_EXPR@5761..64624105                EXPR@5761..64624106                  STMT_LOCAL@5761..58354107                    LOCAL_KW@5761..5766 "local"4108                    WHITESPACE@5766..5767 " "4109                    BIND_DESTRUCT@5767..58344110                      DESTRUCT_FULL@5767..57804111                        NAME@5767..57804112                          IDENT@5767..5780 "target_object"4113                      WHITESPACE@5780..5781 " "4114                      ASSIGN@5781..5782 "="4115                      WHITESPACE@5782..5791 "\n        "4116                      EXPR@5791..58344117                        EXPR_IF_THEN_ELSE@5791..58344118                          IF_KW@5791..5793 "if"4119                          WHITESPACE@5793..5794 " "4120                          EXPR@5794..58144121                            EXPR_VAR@5794..57974122                              NAME@5794..57974123                                IDENT@5794..5797 "std"4124                            SUFFIX_INDEX@5797..58064125                              DOT@5797..5798 "."4126                              NAME@5798..58064127                                IDENT@5798..5806 "isObject"4128                            SUFFIX_APPLY@5806..58144129                              ARGS_DESC@5806..58144130                                L_PAREN@5806..5807 "("4131                                ARG@5807..58134132                                  EXPR@5807..58134133                                    EXPR_VAR@5807..58134134                                      NAME@5807..58134135                                        IDENT@5807..5813 "target"4136                                R_PAREN@5813..5814 ")"4137                          WHITESPACE@5814..5815 " "4138                          THEN_KW@5815..5819 "then"4139                          WHITESPACE@5819..5820 " "4140                          TRUE_EXPR@5820..58264141                            EXPR@5820..58264142                              EXPR_VAR@5820..58264143                                NAME@5820..58264144                                  IDENT@5820..5826 "target"4145                          WHITESPACE@5826..5827 " "4146                          ELSE_KW@5827..5831 "else"4147                          WHITESPACE@5831..5832 " "4148                          FALSE_EXPR@5832..58344149                            EXPR@5832..58344150                              EXPR_OBJECT@5832..58344151                                OBJ_BODY_MEMBER_LIST@5832..58344152                                  L_BRACE@5832..5833 "{"4153                                  R_BRACE@5833..5834 "}"4154                    SEMI@5834..5835 ";"4155                  WHITESPACE@5835..5843 "\n\n      "4156                  STMT_LOCAL@5843..59494157                    LOCAL_KW@5843..5848 "local"4158                    WHITESPACE@5848..5849 " "4159                    BIND_DESTRUCT@5849..59484160                      DESTRUCT_FULL@5849..58624161                        NAME@5849..58624162                          IDENT@5849..5862 "target_fields"4163                      WHITESPACE@5862..5863 " "4164                      ASSIGN@5863..5864 "="4165                      WHITESPACE@5864..5873 "\n        "4166                      EXPR@5873..59484167                        EXPR_IF_THEN_ELSE@5873..59484168                          IF_KW@5873..5875 "if"4169                          WHITESPACE@5875..5876 " "4170                          EXPR@5876..59034171                            EXPR_VAR@5876..58794172                              NAME@5876..58794173                                IDENT@5876..5879 "std"4174                            SUFFIX_INDEX@5879..58884175                              DOT@5879..5880 "."4176                              NAME@5880..58884177                                IDENT@5880..5888 "isObject"4178                            SUFFIX_APPLY@5888..59034179                              ARGS_DESC@5888..59034180                                L_PAREN@5888..5889 "("4181                                ARG@5889..59024182                                  EXPR@5889..59024183                                    EXPR_VAR@5889..59024184                                      NAME@5889..59024185                                        IDENT@5889..5902 "target_object"4186                                R_PAREN@5902..5903 ")"4187                          WHITESPACE@5903..5904 " "4188                          THEN_KW@5904..5908 "then"4189                          WHITESPACE@5908..5909 " "4190                          TRUE_EXPR@5909..59404191                            EXPR@5909..59404192                              EXPR_VAR@5909..59124193                                NAME@5909..59124194                                  IDENT@5909..5912 "std"4195                              SUFFIX_INDEX@5912..59254196                                DOT@5912..5913 "."4197                                NAME@5913..59254198                                  IDENT@5913..5925 "objectFields"4199                              SUFFIX_APPLY@5925..59404200                                ARGS_DESC@5925..59404201                                  L_PAREN@5925..5926 "("4202                                  ARG@5926..59394203                                    EXPR@5926..59394204                                      EXPR_VAR@5926..59394205                                        NAME@5926..59394206                                          IDENT@5926..5939 "target_object"4207                                  R_PAREN@5939..5940 ")"4208                          WHITESPACE@5940..5941 " "4209                          ELSE_KW@5941..5945 "else"4210                          WHITESPACE@5945..5946 " "4211                          FALSE_EXPR@5946..59484212                            EXPR@5946..59484213                              EXPR_ARRAY@5946..59484214                                L_BRACK@5946..5947 "["4215                                R_BRACK@5947..5948 "]"4216                    SEMI@5948..5949 ";"4217                  WHITESPACE@5949..5957 "\n\n      "4218                  STMT_LOCAL@5957..60344219                    LOCAL_KW@5957..5962 "local"4220                    WHITESPACE@5962..5963 " "4221                    BIND_DESTRUCT@5963..60334222                      DESTRUCT_FULL@5963..59744223                        NAME@5963..59744224                          IDENT@5963..5974 "null_fields"4225                      WHITESPACE@5974..5975 " "4226                      ASSIGN@5975..5976 "="4227                      WHITESPACE@5976..5977 " "4228                      EXPR@5977..60334229                        EXPR_ARRAY_COMP@5977..60334230                          L_BRACK@5977..5978 "["4231                          EXPR@5978..59794232                            EXPR_VAR@5978..59794233                              NAME@5978..59794234                                IDENT@5978..5979 "k"4235                          WHITESPACE@5979..5980 " "4236                          FOR_SPEC@5980..60124237                            FOR_KW@5980..5983 "for"4238                            WHITESPACE@5983..5984 " "4239                            DESTRUCT_FULL@5984..59854240                              NAME@5984..59854241                                IDENT@5984..5985 "k"4242                            WHITESPACE@5985..5986 " "4243                            IN_KW@5986..5988 "in"4244                            WHITESPACE@5988..5989 " "4245                            EXPR@5989..60124246                              EXPR_VAR@5989..59924247                                NAME@5989..59924248                                  IDENT@5989..5992 "std"4249                              SUFFIX_INDEX@5992..60054250                                DOT@5992..5993 "."4251                                NAME@5993..60054252                                  IDENT@5993..6005 "objectFields"4253                              SUFFIX_APPLY@6005..60124254                                ARGS_DESC@6005..60124255                                  L_PAREN@6005..6006 "("4256                                  ARG@6006..60114257                                    EXPR@6006..60114258                                      EXPR_VAR@6006..60114259                                        NAME@6006..60114260                                          IDENT@6006..6011 "patch"4261                                  R_PAREN@6011..6012 ")"4262                          WHITESPACE@6012..6013 " "4263                          IF_SPEC@6013..60324264                            IF_KW@6013..6015 "if"4265                            WHITESPACE@6015..6016 " "4266                            EXPR@6016..60324267                              EXPR_BINARY@6016..60324268                                EXPR@6016..60214269                                  EXPR_VAR@6016..60214270                                    NAME@6016..60214271                                      IDENT@6016..6021 "patch"4272                                SUFFIX_INDEX_EXPR@6021..60244273                                  L_BRACK@6021..6022 "["4274                                  EXPR@6022..60234275                                    EXPR_VAR@6022..60234276                                      NAME@6022..60234277                                        IDENT@6022..6023 "k"4278                                  R_BRACK@6023..6024 "]"4279                                WHITESPACE@6024..6025 " "4280                                EQ@6025..6027 "=="4281                                WHITESPACE@6027..6028 " "4282                                EXPR@6028..60324283                                  EXPR_LITERAL@6028..60324284                                    NULL_KW@6028..6032 "null"4285                          R_BRACK@6032..6033 "]"4286                    SEMI@6033..6034 ";"4287                  WHITESPACE@6034..6041 "\n      "4288                  STMT_LOCAL@6041..61144289                    LOCAL_KW@6041..6046 "local"4290                    WHITESPACE@6046..6047 " "4291                    BIND_DESTRUCT@6047..61134292                      DESTRUCT_FULL@6047..60584293                        NAME@6047..60584294                          IDENT@6047..6058 "both_fields"4295                      WHITESPACE@6058..6059 " "4296                      ASSIGN@6059..6060 "="4297                      WHITESPACE@6060..6061 " "4298                      EXPR@6061..61134299                        EXPR_VAR@6061..60644300                          NAME@6061..60644301                            IDENT@6061..6064 "std"4302                        SUFFIX_INDEX@6064..60734303                          DOT@6064..6065 "."4304                          NAME@6065..60734305                            IDENT@6065..6073 "setUnion"4306                        SUFFIX_APPLY@6073..61134307                          ARGS_DESC@6073..61134308                            L_PAREN@6073..6074 "("4309                            ARG@6074..60874310                              EXPR@6074..60874311                                EXPR_VAR@6074..60874312                                  NAME@6074..60874313                                    IDENT@6074..6087 "target_fields"4314                            COMMA@6087..6088 ","4315                            WHITESPACE@6088..6089 " "4316                            ARG@6089..61124317                              EXPR@6089..61124318                                EXPR_VAR@6089..60924319                                  NAME@6089..60924320                                    IDENT@6089..6092 "std"4321                                SUFFIX_INDEX@6092..61054322                                  DOT@6092..6093 "."4323                                  NAME@6093..61054324                                    IDENT@6093..6105 "objectFields"4325                                SUFFIX_APPLY@6105..61124326                                  ARGS_DESC@6105..61124327                                    L_PAREN@6105..6106 "("4328                                    ARG@6106..61114329                                      EXPR@6106..61114330                                        EXPR_VAR@6106..61114331                                          NAME@6106..61114332                                            IDENT@6106..6111 "patch"4333                                    R_PAREN@6111..6112 ")"4334                            R_PAREN@6112..6113 ")"4335                    SEMI@6113..6114 ";"4336                  WHITESPACE@6114..6122 "\n\n      "4337                  EXPR_OBJECT@6122..64624338                    OBJ_BODY_COMP@6122..64624339                      L_BRACE@6122..6123 "{"4340                      WHITESPACE@6123..6132 "\n        "4341                      MEMBER_FIELD_NORMAL@6132..63994342                        FIELD_NAME_DYNAMIC@6132..61354343                          L_BRACK@6132..6133 "["4344                          EXPR@6133..61344345                            EXPR_VAR@6133..61344346                              NAME@6133..61344347                                IDENT@6133..6134 "k"4348                          R_BRACK@6134..6135 "]"4349                        COLON@6135..6136 ":"4350                        WHITESPACE@6136..6147 "\n          "4351                        EXPR@6147..63994352                          EXPR_IF_THEN_ELSE@6147..63994353                            IF_KW@6147..6149 "if"4354                            WHITESPACE@6149..6150 " "4355                            EXPR@6150..61744356                              EXPR_UNARY@6150..61744357                                NOT@6150..6151 "!"4358                                EXPR_VAR@6151..61544359                                  NAME@6151..61544360                                    IDENT@6151..6154 "std"4361                                SUFFIX_INDEX@6154..61644362                                  DOT@6154..6155 "."4363                                  NAME@6155..61644364                                    IDENT@6155..6164 "objectHas"4365                                SUFFIX_APPLY@6164..61744366                                  ARGS_DESC@6164..61744367                                    L_PAREN@6164..6165 "("4368                                    ARG@6165..61704369                                      EXPR@6165..61704370                                        EXPR_VAR@6165..61704371                                          NAME@6165..61704372                                            IDENT@6165..6170 "patch"4373                                    COMMA@6170..6171 ","4374                                    WHITESPACE@6171..6172 " "4375                                    ARG@6172..61734376                                      EXPR@6172..61734377                                        EXPR_VAR@6172..61734378                                          NAME@6172..61734379                                            IDENT@6172..6173 "k"4380                                    R_PAREN@6173..6174 ")"4381                            WHITESPACE@6174..6175 " "4382                            THEN_KW@6175..6179 "then"4383                            WHITESPACE@6179..6192 "\n            "4384                            TRUE_EXPR@6192..62084385                              EXPR@6192..62084386                                EXPR_VAR@6192..62054387                                  NAME@6192..62054388                                    IDENT@6192..6205 "target_object"4389                                SUFFIX_INDEX_EXPR@6205..62084390                                  L_BRACK@6205..6206 "["4391                                  EXPR@6206..62074392                                    EXPR_VAR@6206..62074393                                      NAME@6206..62074394                                        IDENT@6206..6207 "k"4395                                  R_BRACK@6207..6208 "]"4396                            WHITESPACE@6208..6219 "\n          "4397                            ELSE_KW@6219..6223 "else"4398                            WHITESPACE@6223..6224 " "4399                            FALSE_EXPR@6224..63994400                              EXPR@6224..63994401                                EXPR_IF_THEN_ELSE@6224..63994402                                  IF_KW@6224..6226 "if"4403                                  WHITESPACE@6226..6227 " "4404                                  EXPR@6227..62594405                                    EXPR_UNARY@6227..62594406                                      NOT@6227..6228 "!"4407                                      EXPR_VAR@6228..62314408                                        NAME@6228..62314409                                          IDENT@6228..6231 "std"4410                                      SUFFIX_INDEX@6231..62414411                                        DOT@6231..6232 "."4412                                        NAME@6232..62414413                                          IDENT@6232..6241 "objectHas"4414                                      SUFFIX_APPLY@6241..62594415                                        ARGS_DESC@6241..62594416                                          L_PAREN@6241..6242 "("4417                                          ARG@6242..62554418                                            EXPR@6242..62554419                                              EXPR_VAR@6242..62554420                                                NAME@6242..62554421                                                  IDENT@6242..6255 "target_object"4422                                          COMMA@6255..6256 ","4423                                          WHITESPACE@6256..6257 " "4424                                          ARG@6257..62584425                                            EXPR@6257..62584426                                              EXPR_VAR@6257..62584427                                                NAME@6257..62584428                                                  IDENT@6257..6258 "k"4429                                          R_PAREN@6258..6259 ")"4430                                  WHITESPACE@6259..6260 " "4431                                  THEN_KW@6260..6264 "then"4432                                  WHITESPACE@6264..6277 "\n            "4433                                  TRUE_EXPR@6277..63184434                                    EXPR@6277..63184435                                      EXPR_VAR@6277..62804436                                        NAME@6277..62804437                                          IDENT@6277..6280 "std"4438                                      SUFFIX_INDEX@6280..62914439                                        DOT@6280..6281 "."4440                                        NAME@6281..62914441                                          IDENT@6281..6291 "mergePatch"4442                                      SUFFIX_APPLY@6291..63184443                                        ARGS_DESC@6291..63184444                                          L_PAREN@6291..6292 "("4445                                          ARG@6292..62964446                                            EXPR@6292..62964447                                              EXPR_LITERAL@6292..62964448                                                NULL_KW@6292..6296 "null"4449                                          COMMA@6296..6297 ","4450                                          WHITESPACE@6297..6298 " "4451                                          ARG@6298..63064452                                            EXPR@6298..63064453                                              EXPR_VAR@6298..63034454                                                NAME@6298..63034455                                                  IDENT@6298..6303 "patch"4456                                              SUFFIX_INDEX_EXPR@6303..63064457                                                L_BRACK@6303..6304 "["4458                                                EXPR@6304..63054459                                                  EXPR_VAR@6304..63054460                                                    NAME@6304..63054461                                                      IDENT@6304..6305 "k"4462                                                R_BRACK@6305..6306 "]"4463                                          R_PAREN@6306..6307 ")"4464                                          WHITESPACE@6307..6308 " "4465                                          TAILSTRICT_KW@6308..6318 "tailstrict"4466                                  WHITESPACE@6318..6329 "\n          "4467                                  ELSE_KW@6329..6333 "else"4468                                  WHITESPACE@6333..6346 "\n            "4469                                  FALSE_EXPR@6346..63994470                                    EXPR@6346..63994471                                      EXPR_VAR@6346..63494472                                        NAME@6346..63494473                                          IDENT@6346..6349 "std"4474                                      SUFFIX_INDEX@6349..63604475                                        DOT@6349..6350 "."4476                                        NAME@6350..63604477                                          IDENT@6350..6360 "mergePatch"4478                                      SUFFIX_APPLY@6360..63994479                                        ARGS_DESC@6360..63994480                                          L_PAREN@6360..6361 "("4481                                          ARG@6361..63774482                                            EXPR@6361..63774483                                              EXPR_VAR@6361..63744484                                                NAME@6361..63744485                                                  IDENT@6361..6374 "target_object"4486                                              SUFFIX_INDEX_EXPR@6374..63774487                                                L_BRACK@6374..6375 "["4488                                                EXPR@6375..63764489                                                  EXPR_VAR@6375..63764490                                                    NAME@6375..63764491                                                      IDENT@6375..6376 "k"4492                                                R_BRACK@6376..6377 "]"4493                                          COMMA@6377..6378 ","4494                                          WHITESPACE@6378..6379 " "4495                                          ARG@6379..63874496                                            EXPR@6379..63874497                                              EXPR_VAR@6379..63844498                                                NAME@6379..63844499                                                  IDENT@6379..6384 "patch"4500                                              SUFFIX_INDEX_EXPR@6384..63874501                                                L_BRACK@6384..6385 "["4502                                                EXPR@6385..63864503                                                  EXPR_VAR@6385..63864504                                                    NAME@6385..63864505                                                      IDENT@6385..6386 "k"4506                                                R_BRACK@6386..6387 "]"4507                                          R_PAREN@6387..6388 ")"4508                                          WHITESPACE@6388..6389 " "4509                                          TAILSTRICT_KW@6389..6399 "tailstrict"4510                      WHITESPACE@6399..6408 "\n        "4511                      FOR_SPEC@6408..64544512                        FOR_KW@6408..6411 "for"4513                        WHITESPACE@6411..6412 " "4514                        DESTRUCT_FULL@6412..64134515                          NAME@6412..64134516                            IDENT@6412..6413 "k"4517                        WHITESPACE@6413..6414 " "4518                        IN_KW@6414..6416 "in"4519                        WHITESPACE@6416..6417 " "4520                        EXPR@6417..64544521                          EXPR_VAR@6417..64204522                            NAME@6417..64204523                              IDENT@6417..6420 "std"4524                          SUFFIX_INDEX@6420..64284525                            DOT@6420..6421 "."4526                            NAME@6421..64284527                              IDENT@6421..6428 "setDiff"4528                          SUFFIX_APPLY@6428..64544529                            ARGS_DESC@6428..64544530                              L_PAREN@6428..6429 "("4531                              ARG@6429..64404532                                EXPR@6429..64404533                                  EXPR_VAR@6429..64404534                                    NAME@6429..64404535                                      IDENT@6429..6440 "both_fields"4536                              COMMA@6440..6441 ","4537                              WHITESPACE@6441..6442 " "4538                              ARG@6442..64534539                                EXPR@6442..64534540                                  EXPR_VAR@6442..64534541                                    NAME@6442..64534542                                      IDENT@6442..6453 "null_fields"4543                              R_PAREN@6453..6454 ")"4544                      WHITESPACE@6454..6461 "\n      "4545                      R_BRACE@6461..6462 "}"4546              WHITESPACE@6462..6467 "\n    "4547              ELSE_KW@6467..6471 "else"4548              WHITESPACE@6471..6478 "\n      "4549              FALSE_EXPR@6478..64834550                EXPR@6478..64834551                  EXPR_VAR@6478..64834552                    NAME@6478..64834553                      IDENT@6478..6483 "patch"4554        COMMA@6483..6484 ","4555        WHITESPACE@6484..6488 "\n\n  "4556        MEMBER_FIELD_METHOD@6488..65944557          FIELD_NAME_FIXED@6488..64914558            NAME@6488..64914559              IDENT@6488..6491 "get"4560          PARAMS_DESC@6491..65284561            L_PAREN@6491..6492 "("4562            PARAM@6492..64934563              DESTRUCT_FULL@6492..64934564                NAME@6492..64934565                  IDENT@6492..6493 "o"4566            COMMA@6493..6494 ","4567            WHITESPACE@6494..6495 " "4568            PARAM@6495..64964569              DESTRUCT_FULL@6495..64964570                NAME@6495..64964571                  IDENT@6495..6496 "f"4572            COMMA@6496..6497 ","4573            WHITESPACE@6497..6498 " "4574            PARAM@6498..65104575              DESTRUCT_FULL@6498..65054576                NAME@6498..65054577                  IDENT@6498..6505 "default"4578              ASSIGN@6505..6506 "="4579              EXPR@6506..65104580                EXPR_LITERAL@6506..65104581                  NULL_KW@6506..6510 "null"4582            COMMA@6510..6511 ","4583            WHITESPACE@6511..6512 " "4584            PARAM@6512..65274585              DESTRUCT_FULL@6512..65224586                NAME@6512..65224587                  IDENT@6512..6522 "inc_hidden"4588              ASSIGN@6522..6523 "="4589              EXPR@6523..65274590                EXPR_LITERAL@6523..65274591                  TRUE_KW@6523..6527 "true"4592            R_PAREN@6527..6528 ")"4593          COLONCOLON@6528..6530 "::"4594          WHITESPACE@6530..6535 "\n    "4595          EXPR@6535..65944596            EXPR_IF_THEN_ELSE@6535..65944597              IF_KW@6535..6537 "if"4598              WHITESPACE@6537..6538 " "4599              EXPR@6538..65714600                EXPR_VAR@6538..65414601                  NAME@6538..65414602                    IDENT@6538..6541 "std"4603                SUFFIX_INDEX@6541..65534604                  DOT@6541..6542 "."4605                  NAME@6542..65534606                    IDENT@6542..6553 "objectHasEx"4607                SUFFIX_APPLY@6553..65714608                  ARGS_DESC@6553..65714609                    L_PAREN@6553..6554 "("4610                    ARG@6554..65554611                      EXPR@6554..65554612                        EXPR_VAR@6554..65554613                          NAME@6554..65554614                            IDENT@6554..6555 "o"4615                    COMMA@6555..6556 ","4616                    WHITESPACE@6556..6557 " "4617                    ARG@6557..65584618                      EXPR@6557..65584619                        EXPR_VAR@6557..65584620                          NAME@6557..65584621                            IDENT@6557..6558 "f"4622                    COMMA@6558..6559 ","4623                    WHITESPACE@6559..6560 " "4624                    ARG@6560..65704625                      EXPR@6560..65704626                        EXPR_VAR@6560..65704627                          NAME@6560..65704628                            IDENT@6560..6570 "inc_hidden"4629                    R_PAREN@6570..6571 ")"4630              WHITESPACE@6571..6572 " "4631              THEN_KW@6572..6576 "then"4632              WHITESPACE@6576..6577 " "4633              TRUE_EXPR@6577..65814634                EXPR@6577..65814635                  EXPR_VAR@6577..65784636                    NAME@6577..65784637                      IDENT@6577..6578 "o"4638                  SUFFIX_INDEX_EXPR@6578..65814639                    L_BRACK@6578..6579 "["4640                    EXPR@6579..65804641                      EXPR_VAR@6579..65804642                        NAME@6579..65804643                          IDENT@6579..6580 "f"4644                    R_BRACK@6580..6581 "]"4645              WHITESPACE@6581..6582 " "4646              ELSE_KW@6582..6586 "else"4647              WHITESPACE@6586..6587 " "4648              FALSE_EXPR@6587..65944649                EXPR@6587..65944650                  EXPR_VAR@6587..65944651                    NAME@6587..65944652                      IDENT@6587..6594 "default"4653        COMMA@6594..6595 ","4654        WHITESPACE@6595..6599 "\n\n  "4655        MEMBER_FIELD_METHOD@6599..67334656          FIELD_NAME_FIXED@6599..66104657            NAME@6599..66104658              IDENT@6599..6610 "resolvePath"4659          PARAMS_DESC@6610..66164660            L_PAREN@6610..6611 "("4661            PARAM@6611..66124662              DESTRUCT_FULL@6611..66124663                NAME@6611..66124664                  IDENT@6611..6612 "f"4665            COMMA@6612..6613 ","4666            WHITESPACE@6613..6614 " "4667            PARAM@6614..66154668              DESTRUCT_FULL@6614..66154669                NAME@6614..66154670                  IDENT@6614..6615 "r"4671            R_PAREN@6615..6616 ")"4672          COLONCOLON@6616..6618 "::"4673          WHITESPACE@6618..6623 "\n    "4674          EXPR@6623..67334675            STMT_LOCAL@6623..66534676              LOCAL_KW@6623..6628 "local"4677              WHITESPACE@6628..6629 " "4678              BIND_DESTRUCT@6629..66524679                DESTRUCT_FULL@6629..66324680                  NAME@6629..66324681                    IDENT@6629..6632 "arr"4682                WHITESPACE@6632..6633 " "4683                ASSIGN@6633..6634 "="4684                WHITESPACE@6634..6635 " "4685                EXPR@6635..66524686                  EXPR_VAR@6635..66384687                    NAME@6635..66384688                      IDENT@6635..6638 "std"4689                  SUFFIX_INDEX@6638..66444690                    DOT@6638..6639 "."4691                    NAME@6639..66444692                      IDENT@6639..6644 "split"4693                  SUFFIX_APPLY@6644..66524694                    ARGS_DESC@6644..66524695                      L_PAREN@6644..6645 "("4696                      ARG@6645..66464697                        EXPR@6645..66464698                          EXPR_VAR@6645..66464699                            NAME@6645..66464700                              IDENT@6645..6646 "f"4701                      COMMA@6646..6647 ","4702                      WHITESPACE@6647..6648 " "4703                      ARG@6648..66514704                        EXPR@6648..66514705                          EXPR_STRING@6648..66514706                            STRING_SINGLE@6648..6651 "'/'"4707                      R_PAREN@6651..6652 ")"4708              SEMI@6652..6653 ";"4709            WHITESPACE@6653..6658 "\n    "4710            EXPR_VAR@6658..66614711              NAME@6658..66614712                IDENT@6658..6661 "std"4713            SUFFIX_INDEX@6661..66664714              DOT@6661..6662 "."4715              NAME@6662..66664716                IDENT@6662..6666 "join"4717            SUFFIX_APPLY@6666..67334718              ARGS_DESC@6666..67334719                L_PAREN@6666..6667 "("4720                ARG@6667..66704721                  EXPR@6667..66704722                    EXPR_STRING@6667..66704723                      STRING_SINGLE@6667..6670 "'/'"4724                COMMA@6670..6671 ","4725                WHITESPACE@6671..6672 " "4726                ARG@6672..67324727                  EXPR@6672..67324728                    EXPR_BINARY@6672..67324729                      EXPR@6672..66754730                        EXPR_VAR@6672..66754731                          NAME@6672..66754732                            IDENT@6672..6675 "std"4733                      SUFFIX_INDEX@6675..66854734                        DOT@6675..6676 "."4735                        NAME@6676..66854736                          IDENT@6676..6685 "makeArray"4737                      SUFFIX_APPLY@6685..67264738                        ARGS_DESC@6685..67264739                          L_PAREN@6685..6686 "("4740                          ARG@6686..67054741                            EXPR@6686..67054742                              EXPR_BINARY@6686..67054743                                EXPR@6686..66894744                                  EXPR_VAR@6686..66894745                                    NAME@6686..66894746                                      IDENT@6686..6689 "std"4747                                SUFFIX_INDEX@6689..66964748                                  DOT@6689..6690 "."4749                                  NAME@6690..66964750                                    IDENT@6690..6696 "length"4751                                SUFFIX_APPLY@6696..67014752                                  ARGS_DESC@6696..67014753                                    L_PAREN@6696..6697 "("4754                                    ARG@6697..67004755                                      EXPR@6697..67004756                                        EXPR_VAR@6697..67004757                                          NAME@6697..67004758                                            IDENT@6697..6700 "arr"4759                                    R_PAREN@6700..6701 ")"4760                                WHITESPACE@6701..6702 " "4761                                MINUS@6702..6703 "-"4762                                WHITESPACE@6703..6704 " "4763                                EXPR@6704..67054764                                  EXPR_NUMBER@6704..67054765                                    FLOAT@6704..6705 "1"4766                          COMMA@6705..6706 ","4767                          WHITESPACE@6706..6707 " "4768                          ARG@6707..67254769                            EXPR@6707..67254770                              EXPR_FUNCTION@6707..67254771                                FUNCTION_KW@6707..6715 "function"4772                                PARAMS_DESC@6715..67184773                                  L_PAREN@6715..6716 "("4774                                  PARAM@6716..67174775                                    DESTRUCT_FULL@6716..67174776                                      NAME@6716..67174777                                        IDENT@6716..6717 "i"4778                                  R_PAREN@6717..6718 ")"4779                                WHITESPACE@6718..6719 " "4780                                EXPR@6719..67254781                                  EXPR_VAR@6719..67224782                                    NAME@6719..67224783                                      IDENT@6719..6722 "arr"4784                                  SUFFIX_INDEX_EXPR@6722..67254785                                    L_BRACK@6722..6723 "["4786                                    EXPR@6723..67244787                                      EXPR_VAR@6723..67244788                                        NAME@6723..67244789                                          IDENT@6723..6724 "i"4790                                    R_BRACK@6724..6725 "]"4791                          R_PAREN@6725..6726 ")"4792                      WHITESPACE@6726..6727 " "4793                      PLUS@6727..6728 "+"4794                      WHITESPACE@6728..6729 " "4795                      EXPR@6729..67324796                        EXPR_ARRAY@6729..67324797                          L_BRACK@6729..6730 "["4798                          EXPR@6730..67314799                            EXPR_VAR@6730..67314800                              NAME@6730..67314801                                IDENT@6730..6731 "r"4802                          R_BRACK@6731..6732 "]"4803                R_PAREN@6732..6733 ")"4804        COMMA@6733..6734 ","4805        WHITESPACE@6734..6738 "\n\n  "4806        MEMBER_FIELD_METHOD@6738..71914807          FIELD_NAME_FIXED@6738..67434808            NAME@6738..67434809              IDENT@6738..6743 "prune"4810          PARAMS_DESC@6743..67464811            L_PAREN@6743..6744 "("4812            PARAM@6744..67454813              DESTRUCT_FULL@6744..67454814                NAME@6744..67454815                  IDENT@6744..6745 "a"4816            R_PAREN@6745..6746 ")"4817          COLONCOLON@6746..6748 "::"4818          WHITESPACE@6748..6753 "\n    "4819          EXPR@6753..71914820            STMT_LOCAL@6753..69574821              LOCAL_KW@6753..6758 "local"4822              WHITESPACE@6758..6759 " "4823              BIND_FUNCTION@6759..69564824                NAME@6759..67684825                  IDENT@6759..6768 "isContent"4826                PARAMS_DESC@6768..67714827                  L_PAREN@6768..6769 "("4828                  PARAM@6769..67704829                    DESTRUCT_FULL@6769..67704830                      NAME@6769..67704831                        IDENT@6769..6770 "b"4832                  R_PAREN@6770..6771 ")"4833                WHITESPACE@6771..6772 " "4834                ASSIGN@6772..6773 "="4835                WHITESPACE@6773..6780 "\n      "4836                EXPR@6780..69564837                  EXPR_IF_THEN_ELSE@6780..69564838                    IF_KW@6780..6782 "if"4839                    WHITESPACE@6782..6783 " "4840                    EXPR@6783..67924841                      EXPR_BINARY@6783..67924842                        EXPR@6783..67844843                          EXPR_VAR@6783..67844844                            NAME@6783..67844845                              IDENT@6783..6784 "b"4846                        WHITESPACE@6784..6785 " "4847                        EQ@6785..6787 "=="4848                        WHITESPACE@6787..6788 " "4849                        EXPR@6788..67924850                          EXPR_LITERAL@6788..67924851                            NULL_KW@6788..6792 "null"4852                    WHITESPACE@6792..6793 " "4853                    THEN_KW@6793..6797 "then"4854                    WHITESPACE@6797..6806 "\n        "4855                    TRUE_EXPR@6806..68114856                      EXPR@6806..68114857                        EXPR_LITERAL@6806..68114858                          FALSE_KW@6806..6811 "false"4859                    WHITESPACE@6811..6818 "\n      "4860                    ELSE_KW@6818..6822 "else"4861                    WHITESPACE@6822..6823 " "4862                    FALSE_EXPR@6823..69564863                      EXPR@6823..69564864                        EXPR_IF_THEN_ELSE@6823..69564865                          IF_KW@6823..6825 "if"4866                          WHITESPACE@6825..6826 " "4867                          EXPR@6826..68404868                            EXPR_VAR@6826..68294869                              NAME@6826..68294870                                IDENT@6826..6829 "std"4871                            SUFFIX_INDEX@6829..68374872                              DOT@6829..6830 "."4873                              NAME@6830..68374874                                IDENT@6830..6837 "isArray"4875                            SUFFIX_APPLY@6837..68404876                              ARGS_DESC@6837..68404877                                L_PAREN@6837..6838 "("4878                                ARG@6838..68394879                                  EXPR@6838..68394880                                    EXPR_VAR@6838..68394881                                      NAME@6838..68394882                                        IDENT@6838..6839 "b"4883                                R_PAREN@6839..6840 ")"4884                          WHITESPACE@6840..6841 " "4885                          THEN_KW@6841..6845 "then"4886                          WHITESPACE@6845..6854 "\n        "4887                          TRUE_EXPR@6854..68714888                            EXPR@6854..68714889                              EXPR_BINARY@6854..68714890                                EXPR@6854..68574891                                  EXPR_VAR@6854..68574892                                    NAME@6854..68574893                                      IDENT@6854..6857 "std"4894                                SUFFIX_INDEX@6857..68644895                                  DOT@6857..6858 "."4896                                  NAME@6858..68644897                                    IDENT@6858..6864 "length"4898                                SUFFIX_APPLY@6864..68674899                                  ARGS_DESC@6864..68674900                                    L_PAREN@6864..6865 "("4901                                    ARG@6865..68664902                                      EXPR@6865..68664903                                        EXPR_VAR@6865..68664904                                          NAME@6865..68664905                                            IDENT@6865..6866 "b"4906                                    R_PAREN@6866..6867 ")"4907                                WHITESPACE@6867..6868 " "4908                                GT@6868..6869 ">"4909                                WHITESPACE@6869..6870 " "4910                                EXPR@6870..68714911                                  EXPR_NUMBER@6870..68714912                                    FLOAT@6870..6871 "0"4913                          WHITESPACE@6871..6878 "\n      "4914                          ELSE_KW@6878..6882 "else"4915                          WHITESPACE@6882..6883 " "4916                          FALSE_EXPR@6883..69564917                            EXPR@6883..69564918                              EXPR_IF_THEN_ELSE@6883..69564919                                IF_KW@6883..6885 "if"4920                                WHITESPACE@6885..6886 " "4921                                EXPR@6886..69014922                                  EXPR_VAR@6886..68894923                                    NAME@6886..68894924                                      IDENT@6886..6889 "std"4925                                  SUFFIX_INDEX@6889..68984926                                    DOT@6889..6890 "."4927                                    NAME@6890..68984928                                      IDENT@6890..6898 "isObject"4929                                  SUFFIX_APPLY@6898..69014930                                    ARGS_DESC@6898..69014931                                      L_PAREN@6898..6899 "("4932                                      ARG@6899..69004933                                        EXPR@6899..69004934                                          EXPR_VAR@6899..69004935                                            NAME@6899..69004936                                              IDENT@6899..6900 "b"4937                                      R_PAREN@6900..6901 ")"4938                                WHITESPACE@6901..6902 " "4939                                THEN_KW@6902..6906 "then"4940                                WHITESPACE@6906..6915 "\n        "4941                                TRUE_EXPR@6915..69324942                                  EXPR@6915..69324943                                    EXPR_BINARY@6915..69324944                                      EXPR@6915..69184945                                        EXPR_VAR@6915..69184946                                          NAME@6915..69184947                                            IDENT@6915..6918 "std"4948                                      SUFFIX_INDEX@6918..69254949                                        DOT@6918..6919 "."4950                                        NAME@6919..69254951                                          IDENT@6919..6925 "length"4952                                      SUFFIX_APPLY@6925..69284953                                        ARGS_DESC@6925..69284954                                          L_PAREN@6925..6926 "("4955                                          ARG@6926..69274956                                            EXPR@6926..69274957                                              EXPR_VAR@6926..69274958                                                NAME@6926..69274959                                                  IDENT@6926..6927 "b"4960                                          R_PAREN@6927..6928 ")"4961                                      WHITESPACE@6928..6929 " "4962                                      GT@6929..6930 ">"4963                                      WHITESPACE@6930..6931 " "4964                                      EXPR@6931..69324965                                        EXPR_NUMBER@6931..69324966                                          FLOAT@6931..6932 "0"4967                                WHITESPACE@6932..6939 "\n      "4968                                ELSE_KW@6939..6943 "else"4969                                WHITESPACE@6943..6952 "\n        "4970                                FALSE_EXPR@6952..69564971                                  EXPR@6952..69564972                                    EXPR_LITERAL@6952..69564973                                      TRUE_KW@6952..6956 "true"4974              SEMI@6956..6957 ";"4975            WHITESPACE@6957..6962 "\n    "4976            EXPR_IF_THEN_ELSE@6962..71914977              IF_KW@6962..6964 "if"4978              WHITESPACE@6964..6965 " "4979              EXPR@6965..69794980                EXPR_VAR@6965..69684981                  NAME@6965..69684982                    IDENT@6965..6968 "std"4983                SUFFIX_INDEX@6968..69764984                  DOT@6968..6969 "."4985                  NAME@6969..69764986                    IDENT@6969..6976 "isArray"4987                SUFFIX_APPLY@6976..69794988                  ARGS_DESC@6976..69794989                    L_PAREN@6976..6977 "("4990                    ARG@6977..69784991                      EXPR@6977..69784992                        EXPR_VAR@6977..69784993                          NAME@6977..69784994                            IDENT@6977..6978 "a"4995                    R_PAREN@6978..6979 ")"4996              WHITESPACE@6979..6980 " "4997              THEN_KW@6980..6984 "then"4998              WHITESPACE@6984..6991 "\n      "4999              TRUE_EXPR@6991..70415000                EXPR@6991..70415001                  EXPR_ARRAY_COMP@6991..70415002                    L_BRACK@6991..6992 "["5003                    EXPR@6992..70045004                      EXPR_VAR@6992..69955005                        NAME@6992..69955006                          IDENT@6992..6995 "std"5007                      SUFFIX_INDEX@6995..70015008                        DOT@6995..6996 "."5009                        NAME@6996..70015010                          IDENT@6996..7001 "prune"5011                      SUFFIX_APPLY@7001..70045012                        ARGS_DESC@7001..70045013                          L_PAREN@7001..7002 "("5014                          ARG@7002..70035015                            EXPR@7002..70035016                              EXPR_VAR@7002..70035017                                NAME@7002..70035018                                  IDENT@7002..7003 "x"5019                          R_PAREN@7003..7004 ")"5020                    WHITESPACE@7004..7005 " "5021                    FOR_SPEC@7005..70155022                      FOR_KW@7005..7008 "for"5023                      WHITESPACE@7008..7009 " "5024                      DESTRUCT_FULL@7009..70105025                        NAME@7009..70105026                          IDENT@7009..7010 "x"5027                      WHITESPACE@7010..7011 " "5028                      IN_KW@7011..7013 "in"5029                      WHITESPACE@7013..7014 " "5030                      EXPR@7014..70155031                        EXPR_VAR@7014..70155032                          NAME@7014..70155033                            IDENT@7014..7015 "a"5034                    WHITESPACE@7015..7016 " "5035                    IF_SPEC@7016..70405036                      IF_KW@7016..7018 "if"5037                      WHITESPACE@7018..7019 " "5038                      EXPR@7019..70405039                        EXPR_VAR@7019..70285040                          NAME@7019..70285041                            IDENT@7019..7028 "isContent"5042                        SUFFIX_APPLY@7028..70405043                          ARGS_DESC@7028..70405044                            L_PAREN@7028..7029 "("5045                            ARG@7029..70395046                              EXPR@7029..70395047                                EXPR_LITERAL@7029..70305048                                  DOLLAR@7029..7030 "$"5049                                SUFFIX_INDEX@7030..70365050                                  DOT@7030..7031 "."5051                                  NAME@7031..70365052                                    IDENT@7031..7036 "prune"5053                                SUFFIX_APPLY@7036..70395054                                  ARGS_DESC@7036..70395055                                    L_PAREN@7036..7037 "("5056                                    ARG@7037..70385057                                      EXPR@7037..70385058                                        EXPR_VAR@7037..70385059                                          NAME@7037..70385060                                            IDENT@7037..7038 "x"5061                                    R_PAREN@7038..7039 ")"5062                            R_PAREN@7039..7040 ")"5063                    R_BRACK@7040..7041 "]"5064              WHITESPACE@7041..7046 "\n    "5065              ELSE_KW@7046..7050 "else"5066              WHITESPACE@7050..7051 " "5067              FALSE_EXPR@7051..71915068                EXPR@7051..71915069                  EXPR_IF_THEN_ELSE@7051..71915070                    IF_KW@7051..7053 "if"5071                    WHITESPACE@7053..7054 " "5072                    EXPR@7054..70695073                      EXPR_VAR@7054..70575074                        NAME@7054..70575075                          IDENT@7054..7057 "std"5076                      SUFFIX_INDEX@7057..70665077                        DOT@7057..7058 "."5078                        NAME@7058..70665079                          IDENT@7058..7066 "isObject"5080                      SUFFIX_APPLY@7066..70695081                        ARGS_DESC@7066..70695082                          L_PAREN@7066..7067 "("5083                          ARG@7067..70685084                            EXPR@7067..70685085                              EXPR_VAR@7067..70685086                                NAME@7067..70685087                                  IDENT@7067..7068 "a"5088                          R_PAREN@7068..7069 ")"5089                    WHITESPACE@7069..7070 " "5090                    THEN_KW@7070..7074 "then"5091                    WHITESPACE@7074..7075 " "5092                    TRUE_EXPR@7075..71785093                      EXPR@7075..71785094                        EXPR_OBJECT@7075..71785095                          OBJ_BODY_COMP@7075..71785096                            L_BRACE@7075..7076 "{"5097                            WHITESPACE@7076..7083 "\n      "5098                            MEMBER_FIELD_NORMAL@7083..71015099                              FIELD_NAME_DYNAMIC@7083..70865100                                L_BRACK@7083..7084 "["5101                                EXPR@7084..70855102                                  EXPR_VAR@7084..70855103                                    NAME@7084..70855104                                      IDENT@7084..7085 "x"5105                                R_BRACK@7085..7086 "]"5106                              COLON@7086..7087 ":"5107                              WHITESPACE@7087..7088 " "5108                              EXPR@7088..71015109                                EXPR_LITERAL@7088..70895110                                  DOLLAR@7088..7089 "$"5111                                SUFFIX_INDEX@7089..70955112                                  DOT@7089..7090 "."5113                                  NAME@7090..70955114                                    IDENT@7090..7095 "prune"5115                                SUFFIX_APPLY@7095..71015116                                  ARGS_DESC@7095..71015117                                    L_PAREN@7095..7096 "("5118                                    ARG@7096..71005119                                      EXPR@7096..71005120                                        EXPR_VAR@7096..70975121                                          NAME@7096..70975122                                            IDENT@7096..7097 "a"5123                                        SUFFIX_INDEX_EXPR@7097..71005124                                          L_BRACK@7097..7098 "["5125                                          EXPR@7098..70995126                                            EXPR_VAR@7098..70995127                                              NAME@7098..70995128                                                IDENT@7098..7099 "x"5129                                          R_BRACK@7099..7100 "]"5130                                    R_PAREN@7100..7101 ")"5131                            WHITESPACE@7101..7108 "\n      "5132                            FOR_SPEC@7108..71365133                              FOR_KW@7108..7111 "for"5134                              WHITESPACE@7111..7112 " "5135                              DESTRUCT_FULL@7112..71135136                                NAME@7112..71135137                                  IDENT@7112..7113 "x"5138                              WHITESPACE@7113..7114 " "5139                              IN_KW@7114..7116 "in"5140                              WHITESPACE@7116..7117 " "5141                              EXPR@7117..71365142                                EXPR_VAR@7117..71205143                                  NAME@7117..71205144                                    IDENT@7117..7120 "std"5145                                SUFFIX_INDEX@7120..71335146                                  DOT@7120..7121 "."5147                                  NAME@7121..71335148                                    IDENT@7121..7133 "objectFields"5149                                SUFFIX_APPLY@7133..71365150                                  ARGS_DESC@7133..71365151                                    L_PAREN@7133..7134 "("5152                                    ARG@7134..71355153                                      EXPR@7134..71355154                                        EXPR_VAR@7134..71355155                                          NAME@7134..71355156                                            IDENT@7134..7135 "a"5157                                    R_PAREN@7135..7136 ")"5158                            WHITESPACE@7136..7143 "\n      "5159                            IF_SPEC@7143..71725160                              IF_KW@7143..7145 "if"5161                              WHITESPACE@7145..7146 " "5162                              EXPR@7146..71725163                                EXPR_VAR@7146..71555164                                  NAME@7146..71555165                                    IDENT@7146..7155 "isContent"5166                                SUFFIX_APPLY@7155..71725167                                  ARGS_DESC@7155..71725168                                    L_PAREN@7155..7156 "("5169                                    ARG@7156..71715170                                      EXPR@7156..71715171                                        EXPR_VAR@7156..71595172                                          NAME@7156..71595173                                            IDENT@7156..7159 "std"5174                                        SUFFIX_INDEX@7159..71655175                                          DOT@7159..7160 "."5176                                          NAME@7160..71655177                                            IDENT@7160..7165 "prune"5178                                        SUFFIX_APPLY@7165..71715179                                          ARGS_DESC@7165..71715180                                            L_PAREN@7165..7166 "("5181                                            ARG@7166..71705182                                              EXPR@7166..71705183                                                EXPR_VAR@7166..71675184                                                  NAME@7166..71675185                                                    IDENT@7166..7167 "a"5186                                                SUFFIX_INDEX_EXPR@7167..71705187                                                  L_BRACK@7167..7168 "["5188                                                  EXPR@7168..71695189                                                    EXPR_VAR@7168..71695190                                                      NAME@7168..71695191                                                        IDENT@7168..7169 "x"5192                                                  R_BRACK@7169..7170 "]"5193                                            R_PAREN@7170..7171 ")"5194                                    R_PAREN@7171..7172 ")"5195                            WHITESPACE@7172..7177 "\n    "5196                            R_BRACE@7177..7178 "}"5197                    WHITESPACE@7178..7179 " "5198                    ELSE_KW@7179..7183 "else"5199                    WHITESPACE@7183..7190 "\n      "5200                    FALSE_EXPR@7190..71915201                      EXPR@7190..71915202                        EXPR_VAR@7190..71915203                          NAME@7190..71915204                            IDENT@7190..7191 "a"5205        COMMA@7191..7192 ","5206        WHITESPACE@7192..7196 "\n\n  "5207        MEMBER_FIELD_METHOD@7196..74115208          FIELD_NAME_FIXED@7196..72005209            NAME@7196..72005210              IDENT@7196..7200 "find"5211          PARAMS_DESC@7200..72125212            L_PAREN@7200..7201 "("5213            PARAM@7201..72065214              DESTRUCT_FULL@7201..72065215                NAME@7201..72065216                  IDENT@7201..7206 "value"5217            COMMA@7206..7207 ","5218            WHITESPACE@7207..7208 " "5219            PARAM@7208..72115220              DESTRUCT_FULL@7208..72115221                NAME@7208..72115222                  IDENT@7208..7211 "arr"5223            R_PAREN@7211..7212 ")"5224          COLONCOLON@7212..7214 "::"5225          WHITESPACE@7214..7219 "\n    "5226          EXPR@7219..74115227            EXPR_IF_THEN_ELSE@7219..74115228              IF_KW@7219..7221 "if"5229              WHITESPACE@7221..7222 " "5230              EXPR@7222..72395231                EXPR_UNARY@7222..72395232                  NOT@7222..7223 "!"5233                  EXPR_VAR@7223..72265234                    NAME@7223..72265235                      IDENT@7223..7226 "std"5236                  SUFFIX_INDEX@7226..72345237                    DOT@7226..7227 "."5238                    NAME@7227..72345239                      IDENT@7227..7234 "isArray"5240                  SUFFIX_APPLY@7234..72395241                    ARGS_DESC@7234..72395242                      L_PAREN@7234..7235 "("5243                      ARG@7235..72385244                        EXPR@7235..72385245                          EXPR_VAR@7235..72385246                            NAME@7235..72385247                              IDENT@7235..7238 "arr"5248                      R_PAREN@7238..7239 ")"5249              WHITESPACE@7239..7240 " "5250              THEN_KW@7240..7244 "then"5251              WHITESPACE@7244..7251 "\n      "5252              TRUE_EXPR@7251..73215253                EXPR@7251..73215254                  EXPR_ERROR@7251..73215255                    ERROR_KW@7251..7256 "error"5256                    WHITESPACE@7256..7257 " "5257                    EXPR@7257..73215258                      EXPR_BINARY@7257..73215259                        EXPR@7257..73055260                          EXPR_STRING@7257..73055261                            STRING_SINGLE@7257..7305 "'find second paramete ..."5262                        WHITESPACE@7305..7306 " "5263                        PLUS@7306..7307 "+"5264                        WHITESPACE@7307..7308 " "5265                        EXPR@7308..73215266                          EXPR_VAR@7308..73115267                            NAME@7308..73115268                              IDENT@7308..7311 "std"5269                          SUFFIX_INDEX@7311..73165270                            DOT@7311..7312 "."5271                            NAME@7312..73165272                              IDENT@7312..7316 "type"5273                          SUFFIX_APPLY@7316..73215274                            ARGS_DESC@7316..73215275                              L_PAREN@7316..7317 "("5276                              ARG@7317..73205277                                EXPR@7317..73205278                                  EXPR_VAR@7317..73205279                                    NAME@7317..73205280                                      IDENT@7317..7320 "arr"5281                              R_PAREN@7320..7321 ")"5282              WHITESPACE@7321..7326 "\n    "5283              ELSE_KW@7326..7330 "else"5284              WHITESPACE@7330..7337 "\n      "5285              FALSE_EXPR@7337..74115286                EXPR@7337..74115287                  EXPR_VAR@7337..73405288                    NAME@7337..73405289                      IDENT@7337..7340 "std"5290                  SUFFIX_INDEX@7340..73475291                    DOT@7340..7341 "."5292                    NAME@7341..73475293                      IDENT@7341..7347 "filter"5294                  SUFFIX_APPLY@7347..74115295                    ARGS_DESC@7347..74115296                      L_PAREN@7347..7348 "("5297                      ARG@7348..73755298                        EXPR@7348..73755299                          EXPR_FUNCTION@7348..73755300                            FUNCTION_KW@7348..7356 "function"5301                            PARAMS_DESC@7356..73595302                              L_PAREN@7356..7357 "("5303                              PARAM@7357..73585304                                DESTRUCT_FULL@7357..73585305                                  NAME@7357..73585306                                    IDENT@7357..7358 "i"5307                              R_PAREN@7358..7359 ")"5308                            WHITESPACE@7359..7360 " "5309                            EXPR@7360..73755310                              EXPR_BINARY@7360..73755311                                EXPR@7360..73635312                                  EXPR_VAR@7360..73635313                                    NAME@7360..73635314                                      IDENT@7360..7363 "arr"5315                                SUFFIX_INDEX_EXPR@7363..73665316                                  L_BRACK@7363..7364 "["5317                                  EXPR@7364..73655318                                    EXPR_VAR@7364..73655319                                      NAME@7364..73655320                                        IDENT@7364..7365 "i"5321                                  R_BRACK@7365..7366 "]"5322                                WHITESPACE@7366..7367 " "5323                                EQ@7367..7369 "=="5324                                WHITESPACE@7369..7370 " "5325                                EXPR@7370..73755326                                  EXPR_VAR@7370..73755327                                    NAME@7370..73755328                                      IDENT@7370..7375 "value"5329                      COMMA@7375..7376 ","5330                      WHITESPACE@7376..7377 " "5331                      ARG@7377..74105332                        EXPR@7377..74105333                          EXPR_VAR@7377..73805334                            NAME@7377..73805335                              IDENT@7377..7380 "std"5336                          SUFFIX_INDEX@7380..73865337                            DOT@7380..7381 "."5338                            NAME@7381..73865339                              IDENT@7381..7386 "range"5340                          SUFFIX_APPLY@7386..74105341                            ARGS_DESC@7386..74105342                              L_PAREN@7386..7387 "("5343                              ARG@7387..73885344                                EXPR@7387..73885345                                  EXPR_NUMBER@7387..73885346                                    FLOAT@7387..7388 "0"5347                              COMMA@7388..7389 ","5348                              WHITESPACE@7389..7390 " "5349                              ARG@7390..74095350                                EXPR@7390..74095351                                  EXPR_BINARY@7390..74095352                                    EXPR@7390..73935353                                      EXPR_VAR@7390..73935354                                        NAME@7390..73935355                                          IDENT@7390..7393 "std"5356                                    SUFFIX_INDEX@7393..74005357                                      DOT@7393..7394 "."5358                                      NAME@7394..74005359                                        IDENT@7394..7400 "length"5360                                    SUFFIX_APPLY@7400..74055361                                      ARGS_DESC@7400..74055362                                        L_PAREN@7400..7401 "("5363                                        ARG@7401..74045364                                          EXPR@7401..74045365                                            EXPR_VAR@7401..74045366                                              NAME@7401..74045367                                                IDENT@7401..7404 "arr"5368                                        R_PAREN@7404..7405 ")"5369                                    WHITESPACE@7405..7406 " "5370                                    MINUS@7406..7407 "-"5371                                    WHITESPACE@7407..7408 " "5372                                    EXPR@7408..74095373                                      EXPR_NUMBER@7408..74095374                                        FLOAT@7408..7409 "1"5375                              R_PAREN@7409..7410 ")"5376                      R_PAREN@7410..7411 ")"5377        COMMA@7411..7412 ","5378        WHITESPACE@7412..7416 "\n\n  "5379        SINGLE_LINE_SLASH_COMMENT@7416..7426 "// Compat\n"5380        WHITESPACE@7426..7428 "  "5381        MEMBER_FIELD_METHOD@7428..75385382          FIELD_NAME_FIXED@7428..74435383            NAME@7428..74435384              IDENT@7428..7443 "__compare_array"5385          PARAMS_DESC@7443..74555386            L_PAREN@7443..7444 "("5387            PARAM@7444..74485388              DESTRUCT_FULL@7444..74485389                NAME@7444..74485390                  IDENT@7444..7448 "arr1"5391            COMMA@7448..7449 ","5392            WHITESPACE@7449..7450 " "5393            PARAM@7450..74545394              DESTRUCT_FULL@7450..74545395                NAME@7450..74545396                  IDENT@7450..7454 "arr2"5397            R_PAREN@7454..7455 ")"5398          COLONCOLON@7455..7457 "::"5399          WHITESPACE@7457..7462 "\n    "5400          EXPR@7462..75385401            STMT_ASSERT@7462..75085402              ASSERTION@7462..75075403                ASSERT_KW@7462..7468 "assert"5404                WHITESPACE@7468..7469 " "5405                EXPR@7469..75075406                  EXPR_BINARY@7469..75075407                    EXPR@7469..74725408                      EXPR_VAR@7469..74725409                        NAME@7469..74725410                          IDENT@7469..7472 "std"5411                    SUFFIX_INDEX@7472..74805412                      DOT@7472..7473 "."5413                      NAME@7473..74805414                        IDENT@7473..7480 "isArray"5415                    SUFFIX_APPLY@7480..74865416                      ARGS_DESC@7480..74865417                        L_PAREN@7480..7481 "("5418                        ARG@7481..74855419                          EXPR@7481..74855420                            EXPR_VAR@7481..74855421                              NAME@7481..74855422                                IDENT@7481..7485 "arr1"5423                        R_PAREN@7485..7486 ")"5424                    WHITESPACE@7486..7487 " "5425                    AND@7487..7489 "&&"5426                    WHITESPACE@7489..7490 " "5427                    EXPR@7490..75075428                      EXPR_VAR@7490..74935429                        NAME@7490..74935430                          IDENT@7490..7493 "std"5431                      SUFFIX_INDEX@7493..75015432                        DOT@7493..7494 "."5433                        NAME@7494..75015434                          IDENT@7494..7501 "isArray"5435                      SUFFIX_APPLY@7501..75075436                        ARGS_DESC@7501..75075437                          L_PAREN@7501..7502 "("5438                          ARG@7502..75065439                            EXPR@7502..75065440                              EXPR_VAR@7502..75065441                                NAME@7502..75065442                                  IDENT@7502..7506 "arr2"5443                          R_PAREN@7506..7507 ")"5444              SEMI@7507..7508 ";"5445            WHITESPACE@7508..7513 "\n    "5446            EXPR_VAR@7513..75165447              NAME@7513..75165448                IDENT@7513..7516 "std"5449            SUFFIX_INDEX@7516..75265450              DOT@7516..7517 "."5451              NAME@7517..75265452                IDENT@7517..7526 "__compare"5453            SUFFIX_APPLY@7526..75385454              ARGS_DESC@7526..75385455                L_PAREN@7526..7527 "("5456                ARG@7527..75315457                  EXPR@7527..75315458                    EXPR_VAR@7527..75315459                      NAME@7527..75315460                        IDENT@7527..7531 "arr1"5461                COMMA@7531..7532 ","5462                WHITESPACE@7532..7533 " "5463                ARG@7533..75375464                  EXPR@7533..75375465                    EXPR_VAR@7533..75375466                      NAME@7533..75375467                        IDENT@7533..7537 "arr2"5468                R_PAREN@7537..7538 ")"5469        COMMA@7538..7539 ","5470        WHITESPACE@7539..7542 "\n  "5471        MEMBER_FIELD_METHOD@7542..76065472          FIELD_NAME_FIXED@7542..75545473            NAME@7542..75545474              IDENT@7542..7554 "__array_less"5475          PARAMS_DESC@7554..75665476            L_PAREN@7554..7555 "("5477            PARAM@7555..75595478              DESTRUCT_FULL@7555..75595479                NAME@7555..75595480                  IDENT@7555..7559 "arr1"5481            COMMA@7559..7560 ","5482            WHITESPACE@7560..7561 " "5483            PARAM@7561..75655484              DESTRUCT_FULL@7561..75655485                NAME@7561..75655486                  IDENT@7561..7565 "arr2"5487            R_PAREN@7565..7566 ")"5488          COLONCOLON@7566..7568 "::"5489          WHITESPACE@7568..7569 " "5490          EXPR@7569..76065491            EXPR_BINARY@7569..76065492              EXPR@7569..75725493                EXPR_VAR@7569..75725494                  NAME@7569..75725495                    IDENT@7569..7572 "std"5496              SUFFIX_INDEX@7572..75885497                DOT@7572..7573 "."5498                NAME@7573..75885499                  IDENT@7573..7588 "__compare_array"5500              SUFFIX_APPLY@7588..76005501                ARGS_DESC@7588..76005502                  L_PAREN@7588..7589 "("5503                  ARG@7589..75935504                    EXPR@7589..75935505                      EXPR_VAR@7589..75935506                        NAME@7589..75935507                          IDENT@7589..7593 "arr1"5508                  COMMA@7593..7594 ","5509                  WHITESPACE@7594..7595 " "5510                  ARG@7595..75995511                    EXPR@7595..75995512                      EXPR_VAR@7595..75995513                        NAME@7595..75995514                          IDENT@7595..7599 "arr2"5515                  R_PAREN@7599..7600 ")"5516              WHITESPACE@7600..7601 " "5517              EQ@7601..7603 "=="5518              WHITESPACE@7603..7604 " "5519              EXPR@7604..76065520                EXPR_UNARY@7604..76065521                  MINUS@7604..7605 "-"5522                  EXPR_NUMBER@7605..76065523                    FLOAT@7605..7606 "1"5524        COMMA@7606..7607 ","5525        WHITESPACE@7607..7610 "\n  "5526        MEMBER_FIELD_METHOD@7610..76765527          FIELD_NAME_FIXED@7610..76255528            NAME@7610..76255529              IDENT@7610..7625 "__array_greater"5530          PARAMS_DESC@7625..76375531            L_PAREN@7625..7626 "("5532            PARAM@7626..76305533              DESTRUCT_FULL@7626..76305534                NAME@7626..76305535                  IDENT@7626..7630 "arr1"5536            COMMA@7630..7631 ","5537            WHITESPACE@7631..7632 " "5538            PARAM@7632..76365539              DESTRUCT_FULL@7632..76365540                NAME@7632..76365541                  IDENT@7632..7636 "arr2"5542            R_PAREN@7636..7637 ")"5543          COLONCOLON@7637..7639 "::"5544          WHITESPACE@7639..7640 " "5545          EXPR@7640..76765546            EXPR_BINARY@7640..76765547              EXPR@7640..76435548                EXPR_VAR@7640..76435549                  NAME@7640..76435550                    IDENT@7640..7643 "std"5551              SUFFIX_INDEX@7643..76595552                DOT@7643..7644 "."5553                NAME@7644..76595554                  IDENT@7644..7659 "__compare_array"5555              SUFFIX_APPLY@7659..76715556                ARGS_DESC@7659..76715557                  L_PAREN@7659..7660 "("5558                  ARG@7660..76645559                    EXPR@7660..76645560                      EXPR_VAR@7660..76645561                        NAME@7660..76645562                          IDENT@7660..7664 "arr1"5563                  COMMA@7664..7665 ","5564                  WHITESPACE@7665..7666 " "5565                  ARG@7666..76705566                    EXPR@7666..76705567                      EXPR_VAR@7666..76705568                        NAME@7666..76705569                          IDENT@7666..7670 "arr2"5570                  R_PAREN@7670..7671 ")"5571              WHITESPACE@7671..7672 " "5572              EQ@7672..7674 "=="5573              WHITESPACE@7674..7675 " "5574              EXPR@7675..76765575                EXPR_NUMBER@7675..76765576                  FLOAT@7675..7676 "1"5577        COMMA@7676..7677 ","5578        WHITESPACE@7677..7680 "\n  "5579        MEMBER_FIELD_METHOD@7680..77525580          FIELD_NAME_FIXED@7680..77015581            NAME@7680..77015582              IDENT@7680..7701 "__array_less_or_equal"5583          PARAMS_DESC@7701..77135584            L_PAREN@7701..7702 "("5585            PARAM@7702..77065586              DESTRUCT_FULL@7702..77065587                NAME@7702..77065588                  IDENT@7702..7706 "arr1"5589            COMMA@7706..7707 ","5590            WHITESPACE@7707..7708 " "5591            PARAM@7708..77125592              DESTRUCT_FULL@7708..77125593                NAME@7708..77125594                  IDENT@7708..7712 "arr2"5595            R_PAREN@7712..7713 ")"5596          COLONCOLON@7713..7715 "::"5597          WHITESPACE@7715..7716 " "5598          EXPR@7716..77525599            EXPR_BINARY@7716..77525600              EXPR@7716..77195601                EXPR_VAR@7716..77195602                  NAME@7716..77195603                    IDENT@7716..7719 "std"5604              SUFFIX_INDEX@7719..77355605                DOT@7719..7720 "."5606                NAME@7720..77355607                  IDENT@7720..7735 "__compare_array"5608              SUFFIX_APPLY@7735..77475609                ARGS_DESC@7735..77475610                  L_PAREN@7735..7736 "("5611                  ARG@7736..77405612                    EXPR@7736..77405613                      EXPR_VAR@7736..77405614                        NAME@7736..77405615                          IDENT@7736..7740 "arr1"5616                  COMMA@7740..7741 ","5617                  WHITESPACE@7741..7742 " "5618                  ARG@7742..77465619                    EXPR@7742..77465620                      EXPR_VAR@7742..77465621                        NAME@7742..77465622                          IDENT@7742..7746 "arr2"5623                  R_PAREN@7746..7747 ")"5624              WHITESPACE@7747..7748 " "5625              LE@7748..7750 "<="5626              WHITESPACE@7750..7751 " "5627              EXPR@7751..77525628                EXPR_NUMBER@7751..77525629                  FLOAT@7751..7752 "0"5630        COMMA@7752..7753 ","5631        WHITESPACE@7753..7756 "\n  "5632        MEMBER_FIELD_METHOD@7756..78315633          FIELD_NAME_FIXED@7756..77805634            NAME@7756..77805635              IDENT@7756..7780 "__array_greater_or_equal"5636          PARAMS_DESC@7780..77925637            L_PAREN@7780..7781 "("5638            PARAM@7781..77855639              DESTRUCT_FULL@7781..77855640                NAME@7781..77855641                  IDENT@7781..7785 "arr1"5642            COMMA@7785..7786 ","5643            WHITESPACE@7786..7787 " "5644            PARAM@7787..77915645              DESTRUCT_FULL@7787..77915646                NAME@7787..77915647                  IDENT@7787..7791 "arr2"5648            R_PAREN@7791..7792 ")"5649          COLONCOLON@7792..7794 "::"5650          WHITESPACE@7794..7795 " "5651          EXPR@7795..78315652            EXPR_BINARY@7795..78315653              EXPR@7795..77985654                EXPR_VAR@7795..77985655                  NAME@7795..77985656                    IDENT@7795..7798 "std"5657              SUFFIX_INDEX@7798..78145658                DOT@7798..7799 "."5659                NAME@7799..78145660                  IDENT@7799..7814 "__compare_array"5661              SUFFIX_APPLY@7814..78265662                ARGS_DESC@7814..78265663                  L_PAREN@7814..7815 "("5664                  ARG@7815..78195665                    EXPR@7815..78195666                      EXPR_VAR@7815..78195667                        NAME@7815..78195668                          IDENT@7815..7819 "arr1"5669                  COMMA@7819..7820 ","5670                  WHITESPACE@7820..7821 " "5671                  ARG@7821..78255672                    EXPR@7821..78255673                      EXPR_VAR@7821..78255674                        NAME@7821..78255675                          IDENT@7821..7825 "arr2"5676                  R_PAREN@7825..7826 ")"5677              WHITESPACE@7826..7827 " "5678              GE@7827..7829 ">="5679              WHITESPACE@7829..7830 " "5680              EXPR@7830..78315681                EXPR_NUMBER@7830..78315682                  FLOAT@7830..7831 "0"5683        COMMA@7831..7832 ","5684        WHITESPACE@7832..7833 "\n"5685        R_BRACE@7833..7834 "}"5686  WHITESPACE@7834..7835 "\n"
addedcrates/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"
addedcrates/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"
addedcrates/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"
addedcrates/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"
addedcrates/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
+   `----
addedcrates/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 | }
+   `----
addedcrates/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(())
+}
addedcrates/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);
+}
addedcrates/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
+}
modifiedcrates/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()
modifiedflake.nixdiffbeforeafterboth
--- a/flake.nix
+++ b/flake.nix
@@ -131,6 +131,8 @@
             cargo-edit
             cargo-asm
             cargo-outdated
+            cargo-watch
+            cargo-insta
             lld
             hyperfine
             graphviz
addedxtask/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"
addedxtask/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()
+}
addedxtask/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();
+	}
+}
addedxtask/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
+}
addedxtask/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)* }
+	}
+}
addedxtask/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
+	))
+}