--- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --package xtask --bin xtask --" --- 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" --- 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" } --- /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" --- /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>; + +/// 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( + node: SyntaxNode, + start: Option<&SyntaxElement>, + end: Option<&SyntaxElement>, + trailing: Option, +) -> (Vec>, 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( + items: impl Iterator, + loose: bool, + mut trailing: Option, +) -> (Vec>, EndingComments) { + let mut out = Vec::new(); + let mut current_child = None::>; + 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 { + /// 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) + } +} --- /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::>(); + 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 +} --- /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: {{ + $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

Printable for Option

+where + P: Printable, +{ + fn print(&self) -> PrintItems { + if let Some(v) = self { + v.print() + } else { + p!(new: string( + format!( + "/*missing {}*/", + type_name::

().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: { + 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: 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: 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: 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::( + 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::( + 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 { + let (children, end_comments) = children_between::( + 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: 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::( + 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::( + 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::( + 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: { + 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: { + 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 { + 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); + } +} --- /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: '', + }, +} --- /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: '', + }, + }" + ))) +} --- /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" --- /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::(&req) { + reply(Response::new_ok(id, >::new())); + } else if let Some((id, params)) = cast::(&req) { + let pos = params + .text_document_position_params + .text_document + .uri + .path(); + let buf = PathBuf::from_str(pos).unwrap(); + // let pos = es + // .map_from_source_location( + // &buf, + // params.text_document_position_params.position.line as usize + 1, + // params.text_document_position_params.position.character as usize + 1, + // ) + // .unwrap(); + // let el = ExprLocation(buf.clone().into(), pos as usize, pos as usize); + // let es2 = es.clone(); + // reply(Response::new_ok( + // id, + // Some(Hover { + // range: None, + // contents: HoverContents::Markup(MarkupContent { + // kind: MarkupKind::Markdown, + // value: es + // .run_in_state_with_breakpoint(el, move || { + // es2.reset_evaluation_state(&buf); + // es2.import_file(&PathBuf::new(), &buf)? + // .to_string() + // .map(|_| ()) + // }) + // .unwrap() + // .unwrap_or_else(|| Val::Null) + // .value_type() + // .to_string(), + // }), + // }), + // )); + } else { + reply(Response::new_err( + req.id, + ErrorCode::MethodNotFound as i32, + format!("unrecognized request {}", req.method), + )) + } + /* + if let Some((id, params)) = cast::(&req) { + let links = handle_links(&files, params).unwrap_or_default(); + reply(Response::new_ok(id, links)); + } else if let Some((id, params)) = cast::(&req) { + if let Some(loc) = handle_goto(&files, params) { + reply(Response::new_ok(id, loc)) + } else { + reply(Response::new_ok(id, ())) + } + } else if let Some((id, params)) = cast::(&req) { + match handle_hover(&files, params) { + Some((range, markdown)) => { + reply(Response::new_ok( + id, + Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: markdown, + }), + range, + }, + )); + } + None => { + reply(Response::new_ok(id, ())); + } + } + } else if let Some((id, params)) = cast::(&req) { + let completions = handle_completion(&files, params.text_document_position) + .unwrap_or_default(); + reply(Response::new_ok(id, completions)); + } else + */ + } + 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(req: &Request) -> Option<(RequestId, R::Params)> +where + R: lsp_types::request::Request, + R::Params: serde::de::DeserializeOwned, +{ + req.clone().extract(R::METHOD).ok() +} --- 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" } --- 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, /// 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, --- 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 --- /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" --- /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!' --- /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 + 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 + where + Self: Sized; + + fn syntax(&self) -> &SyntaxToken; + + fn text(&self) -> &str { + self.syntax().text() + } +} + +#[derive(Debug, Clone)] +pub struct AstChildren { + inner: SyntaxNodeChildren, + ph: PhantomData, +} + +impl AstChildren { + fn new(parent: &SyntaxNode) -> Self { + AstChildren { + inner: parent.children(), + ph: PhantomData, + } + } +} + +impl Iterator for AstChildren { + type Item = N; + fn next(&mut self) -> Option { + self.inner.find_map(N::cast) + } +} + +pub mod support { + use super::{AstChildren, AstNode, AstToken, SyntaxKind, SyntaxNode, SyntaxToken}; + + pub fn child(parent: &SyntaxNode) -> Option { + parent.children().find_map(N::cast) + } + pub fn token_child(parent: &SyntaxNode) -> Option { + parent.children_with_tokens().find_map(|n| match n { + rowan::NodeOrToken::Node(_) => None, + rowan::NodeOrToken::Token(t) => N::cast(t), + }) + } + + pub fn children(parent: &SyntaxNode) -> AstChildren { + AstChildren::new(parent) + } + + pub fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { + parent + .children_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == kind) + } +} --- /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, + }, + /// 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, + error: Option>, + }, + /// 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, + pub errors: Vec, +} + +impl<'i> Sink<'i> { + pub(super) fn new(events: Vec, lexemes: &'i [Lexeme<'i>]) -> Self { + Self { + builder: GreenNodeBuilder::new(), + lexemes, + offset: 0, + events, + errors: vec![], + } + } + + 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); + } + } +} --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/generated/mod.rs @@ -0,0 +1,2 @@ +pub mod nodes; +pub mod syntax_kinds; --- /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 { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Expr { + pub(crate) syntax: SyntaxNode, +} +impl Expr { + pub fn stmts(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn expr_base(&self) -> Option { + support::child(&self.syntax) + } + pub fn suffixs(&self) -> AstChildren { + 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 { + support::token(&self.syntax, T![?]) + } + pub fn dot_token(&self) -> Option { + support::token(&self.syntax, T![.]) + } + pub fn index(&self) -> Option { + 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 { + 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 { + support::token(&self.syntax, T![?]) + } + pub fn dot_token(&self) -> Option { + support::token(&self.syntax, T![.]) + } + pub fn l_brack_token(&self) -> Option { + support::token(&self.syntax, T!['[']) + } + pub fn index(&self) -> Option { + support::child(&self.syntax) + } + pub fn r_brack_token(&self) -> Option { + 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 { + 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 { + support::token(&self.syntax, T!['[']) + } + pub fn from(&self) -> Option { + support::child(&self.syntax) + } + pub fn colon_token(&self) -> Option { + support::token(&self.syntax, T![:]) + } + pub fn end(&self) -> Option { + support::child(&self.syntax) + } + pub fn step(&self) -> Option { + support::child(&self.syntax) + } + pub fn r_brack_token(&self) -> Option { + 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 { + support::child(&self.syntax) + } + pub fn tailstrict_kw_token(&self) -> Option { + 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 { + support::token(&self.syntax, T!['(']) + } + pub fn args(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn r_paren_token(&self) -> Option { + 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 { + support::token(&self.syntax, T![local]) + } + pub fn binds(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn semi_token(&self) -> Option { + 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 { + support::child(&self.syntax) + } + pub fn semi_token(&self) -> Option { + 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 { + support::token(&self.syntax, T![assert]) + } + pub fn condition(&self) -> Option { + support::child(&self.syntax) + } + pub fn colon_token(&self) -> Option { + support::token(&self.syntax, T![:]) + } + pub fn message(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ExprBinary { + pub(crate) syntax: SyntaxNode, +} +impl ExprBinary { + pub fn lhs(&self) -> Option { + support::child(&self.syntax) + } + pub fn binary_operator(&self) -> Option { + support::token_child(&self.syntax) + } + pub fn rhs(&self) -> Option { + 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 { + support::token_child(&self.syntax) + } + pub fn rhs(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ExprObjExtend { + pub(crate) syntax: SyntaxNode, +} +impl ExprObjExtend { + pub fn expr(&self) -> Option { + 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 { + support::token(&self.syntax, T!['(']) + } + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } + pub fn r_paren_token(&self) -> Option { + 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 { + 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 { + 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 { + 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 { + support::token(&self.syntax, T!['[']) + } + pub fn exprs(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn r_brack_token(&self) -> Option { + 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 { + 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 { + support::token(&self.syntax, T!['[']) + } + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } + pub fn comma_token(&self) -> Option { + support::token(&self.syntax, T![,]) + } + pub fn comp_specs(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn r_brack_token(&self) -> Option { + 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 { + support::token_child(&self.syntax) + } + pub fn text(&self) -> Option { + 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 { + 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 { + support::token(&self.syntax, T![if]) + } + pub fn cond(&self) -> Option { + support::child(&self.syntax) + } + pub fn then_kw_token(&self) -> Option { + support::token(&self.syntax, T![then]) + } + pub fn then(&self) -> Option { + support::child(&self.syntax) + } + pub fn else_kw_token(&self) -> Option { + support::token(&self.syntax, T![else]) + } + pub fn else_(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TrueExpr { + pub(crate) syntax: SyntaxNode, +} +impl TrueExpr { + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FalseExpr { + pub(crate) syntax: SyntaxNode, +} +impl FalseExpr { + pub fn expr(&self) -> Option { + 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 { + support::token(&self.syntax, T![function]) + } + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, T!['(']) + } + pub fn params_desc(&self) -> Option { + support::child(&self.syntax) + } + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, T![')']) + } + pub fn expr(&self) -> Option { + 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 { + support::token(&self.syntax, T!['(']) + } + pub fn params(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn r_paren_token(&self) -> Option { + 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 { + support::token(&self.syntax, T![error]) + } + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SliceDescEnd { + pub(crate) syntax: SyntaxNode, +} +impl SliceDescEnd { + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SliceDescStep { + pub(crate) syntax: SyntaxNode, +} +impl SliceDescStep { + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Arg { + pub(crate) syntax: SyntaxNode, +} +impl Arg { + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + pub fn assign_token(&self) -> Option { + support::token(&self.syntax, T![=]) + } + pub fn expr(&self) -> Option { + 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 { + support::token(&self.syntax, T!['{']) + } + pub fn member_comps(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn comp_specs(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn r_brace_token(&self) -> Option { + 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 { + support::token(&self.syntax, T!['{']) + } + pub fn members(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn r_brace_token(&self) -> Option { + 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 { + 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 { + support::token(&self.syntax, T![local]) + } + pub fn bind(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MemberAssertStmt { + pub(crate) syntax: SyntaxNode, +} +impl MemberAssertStmt { + pub fn assertion(&self) -> Option { + 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 { + support::child(&self.syntax) + } + pub fn plus_token(&self) -> Option { + support::token(&self.syntax, T![+]) + } + pub fn visibility(&self) -> Option { + support::token_child(&self.syntax) + } + pub fn expr(&self) -> Option { + 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 { + support::child(&self.syntax) + } + pub fn params_desc(&self) -> Option { + support::child(&self.syntax) + } + pub fn visibility(&self) -> Option { + support::token_child(&self.syntax) + } + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FieldNameFixed { + pub(crate) syntax: SyntaxNode, +} +impl FieldNameFixed { + pub fn id(&self) -> Option { + support::child(&self.syntax) + } + pub fn text(&self) -> Option { + 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 { + support::token(&self.syntax, T!['[']) + } + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } + pub fn r_brack_token(&self) -> Option { + 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 { + support::token(&self.syntax, T![for]) + } + pub fn bind(&self) -> Option { + support::child(&self.syntax) + } + pub fn in_kw_token(&self) -> Option { + support::token(&self.syntax, T![in]) + } + pub fn expr(&self) -> Option { + 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 { + support::token(&self.syntax, T![if]) + } + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BindDestruct { + pub(crate) syntax: SyntaxNode, +} +impl BindDestruct { + pub fn into(&self) -> Option { + support::child(&self.syntax) + } + pub fn assign_token(&self) -> Option { + support::token(&self.syntax, T![=]) + } + pub fn value(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BindFunction { + pub(crate) syntax: SyntaxNode, +} +impl BindFunction { + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + pub fn params(&self) -> Option { + support::child(&self.syntax) + } + pub fn assign_token(&self) -> Option { + support::token(&self.syntax, T![=]) + } + pub fn value(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Param { + pub(crate) syntax: SyntaxNode, +} +impl Param { + pub fn destruct(&self) -> Option { + support::child(&self.syntax) + } + pub fn assign_token(&self) -> Option { + support::token(&self.syntax, T![=]) + } + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DestructFull { + pub(crate) syntax: SyntaxNode, +} +impl DestructFull { + pub fn name(&self) -> Option { + 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 { + 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 { + support::token(&self.syntax, T!['[']) + } + pub fn destruct_array_parts(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn r_brack_token(&self) -> Option { + 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 { + support::token(&self.syntax, T!['{']) + } + pub fn destruct_object_fields(&self) -> AstChildren { + support::children(&self.syntax) + } + pub fn destruct_rest(&self) -> Option { + support::child(&self.syntax) + } + pub fn comma_token(&self) -> Option { + support::token(&self.syntax, T![,]) + } + pub fn r_brace_token(&self) -> Option { + 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 { + support::child(&self.syntax) + } + pub fn colon_token(&self) -> Option { + support::token(&self.syntax, T![:]) + } + pub fn destruct(&self) -> Option { + support::child(&self.syntax) + } + pub fn assign_token(&self) -> Option { + support::token(&self.syntax, T![=]) + } + pub fn expr(&self) -> Option { + 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 { + support::token(&self.syntax, T![...]) + } + pub fn into(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DestructArrayElement { + pub(crate) syntax: SyntaxNode, +} +impl DestructArrayElement { + pub fn destruct(&self) -> Option { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl From for Suffix { + fn from(node: SuffixIndex) -> Suffix { + Suffix::SuffixIndex(node) + } +} +impl From for Suffix { + fn from(node: SuffixIndexExpr) -> Suffix { + Suffix::SuffixIndexExpr(node) + } +} +impl From for Suffix { + fn from(node: SuffixSlice) -> Suffix { + Suffix::SuffixSlice(node) + } +} +impl From 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 { + 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 for Bind { + fn from(node: BindDestruct) -> Bind { + Bind::BindDestruct(node) + } +} +impl From 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 { + 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 for Stmt { + fn from(node: StmtLocal) -> Stmt { + Stmt::StmtLocal(node) + } +} +impl From 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 { + 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 for ObjBody { + fn from(node: ObjBodyComp) -> ObjBody { + ObjBody::ObjBodyComp(node) + } +} +impl From 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 { + 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 for CompSpec { + fn from(node: ForSpec) -> CompSpec { + CompSpec::ForSpec(node) + } +} +impl From 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 { + 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 for ExprBase { + fn from(node: ExprBinary) -> ExprBase { + ExprBase::ExprBinary(node) + } +} +impl From for ExprBase { + fn from(node: ExprUnary) -> ExprBase { + ExprBase::ExprUnary(node) + } +} +impl From for ExprBase { + fn from(node: ExprObjExtend) -> ExprBase { + ExprBase::ExprObjExtend(node) + } +} +impl From for ExprBase { + fn from(node: ExprParened) -> ExprBase { + ExprBase::ExprParened(node) + } +} +impl From for ExprBase { + fn from(node: ExprString) -> ExprBase { + ExprBase::ExprString(node) + } +} +impl From for ExprBase { + fn from(node: ExprNumber) -> ExprBase { + ExprBase::ExprNumber(node) + } +} +impl From for ExprBase { + fn from(node: ExprLiteral) -> ExprBase { + ExprBase::ExprLiteral(node) + } +} +impl From for ExprBase { + fn from(node: ExprArray) -> ExprBase { + ExprBase::ExprArray(node) + } +} +impl From for ExprBase { + fn from(node: ExprObject) -> ExprBase { + ExprBase::ExprObject(node) + } +} +impl From for ExprBase { + fn from(node: ExprArrayComp) -> ExprBase { + ExprBase::ExprArrayComp(node) + } +} +impl From for ExprBase { + fn from(node: ExprImport) -> ExprBase { + ExprBase::ExprImport(node) + } +} +impl From for ExprBase { + fn from(node: ExprVar) -> ExprBase { + ExprBase::ExprVar(node) + } +} +impl From for ExprBase { + fn from(node: ExprIfThenElse) -> ExprBase { + ExprBase::ExprIfThenElse(node) + } +} +impl From for ExprBase { + fn from(node: ExprFunction) -> ExprBase { + ExprBase::ExprFunction(node) + } +} +impl From 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 { + 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 for MemberComp { + fn from(node: MemberBindStmt) -> MemberComp { + MemberComp::MemberBindStmt(node) + } +} +impl From for MemberComp { + fn from(node: MemberFieldNormal) -> MemberComp { + MemberComp::MemberFieldNormal(node) + } +} +impl From 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 { + 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 for Member { + fn from(node: MemberBindStmt) -> Member { + Member::MemberBindStmt(node) + } +} +impl From for Member { + fn from(node: MemberAssertStmt) -> Member { + Member::MemberAssertStmt(node) + } +} +impl From for Member { + fn from(node: MemberFieldNormal) -> Member { + Member::MemberFieldNormal(node) + } +} +impl From 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 { + 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 for FieldName { + fn from(node: FieldNameFixed) -> FieldName { + FieldName::FieldNameFixed(node) + } +} +impl From 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 { + 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 for Destruct { + fn from(node: DestructFull) -> Destruct { + Destruct::DestructFull(node) + } +} +impl From for Destruct { + fn from(node: DestructSkip) -> Destruct { + Destruct::DestructSkip(node) + } +} +impl From for Destruct { + fn from(node: DestructArray) -> Destruct { + Destruct::DestructArray(node) + } +} +impl From 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 { + 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 for DestructArrayPart { + fn from(node: DestructArrayElement) -> DestructArrayPart { + DestructArrayPart::DestructArrayElement(node) + } +} +impl From 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) + } +} --- /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; --- /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; +pub type SyntaxToken = rowan::SyntaxToken; +pub type SyntaxElement = rowan::SyntaxElement; +pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren; +pub type SyntaxElementChildren = rowan::SyntaxElementChildren; +pub type PreorderWithTokens = rowan::api::PreorderWithTokens; --- /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 { + 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::::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> { + Lexer::new(input).collect() +} --- /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) { + 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 { + support::child(self.syntax()) + } + pub fn rhs_work(&self) -> Option { + let mut children = support::children(self.syntax()); + // skip lhs + children.next()?; + children.next() + } +} +impl ExprObjExtend { + pub fn lhs_work(&self) -> Option { + support::child(self.syntax()) + } + pub fn rhs_work(&self) -> Option { + let mut children = support::children(self.syntax()); + // skip lhs + children.next()?; + children.next() + } +} --- /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, + ) -> 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) -> 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, + ) -> 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) -> CompletedMarker { + self.wrap_raw( + p, + SyntaxKind::ERROR_CUSTOM, + Some(SyntaxError::Custom { + error: msg.as_ref().to_owned(), + }), + ) + } +} --- /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, +} + +pub struct Parser { + // TODO: remove all trivia before feeding to parser? + kinds: Vec, + pub offset: usize, + pub events: Vec, + pub entered: u32, + pub hints: Vec<(u32, TextRange, String)>, + pub last_error_token: usize, + expected_syntax_tracking_state: Rc>, + steps: Cell, +} + +#[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 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) -> 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 { + 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>, +} + +impl ExpectedSyntaxGuard { + fn new(expected_syntax_tracking_state: Rc>) -> Self { + Self { + expected_syntax_tracking_state, + } + } +} + +impl Drop for ExpectedSyntaxGuard { + fn drop(&mut self) { + self.expected_syntax_tracking_state + .set(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 { + 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 { + 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 { + 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()) + } +} --- /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), + } + } +} --- /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" --- /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" --- /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 + `---- --- /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 + `---- --- /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 + `---- --- /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" + --- /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 + `---- + --- /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 + `---- --- /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" --- /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 + `---- --- /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" --- /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 + `---- --- /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 + `---- --- /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 + `---- --- /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" --- /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 + `---- --- /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 + `---- --- /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 + `---- --- /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 + `---- --- /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 + `---- --- /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 + `---- --- /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" --- /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" --- /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 + `---- --- /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 + `---- --- /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 + `---- --- /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 + `---- --- /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" --- /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" --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__stdlib.snap @@ -0,0 +1,5686 @@ +--- +source: crates/jrsonnet-rowan-parser/src/tests.rs +expression: "{\n local std = self,\n local id = std.id,\n\n thisFile:: error 'std.thisFile is deprecated, to enable its support in jrsonnet - recompile it with \"legacy-this-file\" support.\\nThis will slow down stdlib caching a bit, though',\n\n lstripChars(str, chars)::\n if std.length(str) > 0 && std.member(chars, str[0]) then\n std.lstripChars(str[1:], chars)\n else\n str,\n\n rstripChars(str, chars)::\n local len = std.length(str);\n if len > 0 && std.member(chars, str[len - 1]) then\n std.rstripChars(str[:len - 1], chars)\n else\n str,\n\n stripChars(str, chars)::\n std.lstripChars(std.rstripChars(str, chars), chars),\n\n splitLimitR(str, c, maxsplits)::\n if maxsplits == -1 then\n std.splitLimit(str, c, -1)\n else\n local revStr(str) = std.join('', std.reverse(std.stringChars(str)));\n std.map(function(e) revStr(e), std.reverse(std.splitLimit(revStr(str), revStr(c), maxsplits))),\n\n split(str, c):: std.splitLimit(str, c, -1),\n\n mapWithIndex(func, arr)::\n if !std.isFunction(func) then\n error ('std.mapWithIndex first param must be function, got ' + std.type(func))\n else if !std.isArray(arr) && !std.isString(arr) then\n error ('std.mapWithIndex second param must be array, got ' + std.type(arr))\n else\n std.makeArray(std.length(arr), function(i) func(i, arr[i])),\n\n mapWithKey(func, obj)::\n if !std.isFunction(func) then\n error ('std.mapWithKey first param must be function, got ' + std.type(func))\n else if !std.isObject(obj) then\n error ('std.mapWithKey second param must be object, got ' + std.type(obj))\n else\n { [k]: func(k, obj[k]) for k in std.objectFields(obj) },\n\n lines(arr)::\n std.join('\\n', arr + ['']),\n\n deepJoin(arr)::\n if std.isString(arr) then\n arr\n else if std.isArray(arr) then\n std.join('', [std.deepJoin(x) for x in arr])\n else\n error 'Expected string or array, got %s' % std.type(arr),\n\n assertEqual(a, b)::\n if a == b then\n true\n else\n error 'Assertion failed. ' + a + ' != ' + b,\n\n clamp(x, minVal, maxVal)::\n if x < minVal then minVal\n else if x > maxVal then maxVal\n else x,\n\n manifestIni(ini)::\n local body_lines(body) =\n std.join([], [\n local value_or_values = body[k];\n if std.isArray(value_or_values) then\n ['%s = %s' % [k, value] for value in value_or_values]\n else\n ['%s = %s' % [k, value_or_values]]\n\n for k in std.objectFields(body)\n ]);\n\n local section_lines(sname, sbody) = ['[%s]' % [sname]] + body_lines(sbody),\n main_body = if std.objectHas(ini, 'main') then body_lines(ini.main) else [],\n all_sections = [\n section_lines(k, ini.sections[k])\n for k in std.objectFields(ini.sections)\n ];\n std.join('\\n', main_body + std.flattenArrays(all_sections) + ['']),\n\n manifestToml(value):: std.manifestTomlEx(value, ' '),\n\n escapeStringPython(str)::\n std.escapeStringJson(str),\n\n escapeStringBash(str_)::\n local str = std.toString(str_);\n local trans(ch) =\n if ch == \"'\" then\n \"'\\\"'\\\"'\"\n else\n ch;\n \"'%s'\" % std.join('', [trans(ch) for ch in std.stringChars(str)]),\n\n escapeStringDollars(str_)::\n local str = std.toString(str_);\n local trans(ch) =\n if ch == '$' then\n '$$'\n else\n ch;\n std.foldl(function(a, b) a + trans(b), std.stringChars(str), ''),\n\n local xml_escapes = {\n '<': '<',\n '>': '>',\n '&': '&',\n '\"': '"',\n \"'\": ''',\n },\n\n escapeStringXML(str_)::\n local str = std.toString(str_);\n std.join('', [std.get(xml_escapes, ch, ch) for ch in std.stringChars(str)]),\n\n manifestJson(value):: std.manifestJsonEx(value, ' ') tailstrict,\n\n manifestJsonMinified(value):: std.manifestJsonEx(value, '', '', ':'),\n\n manifestYamlStream(value, indent_array_in_object=false, c_document_end=true, quote_keys=true)::\n if !std.isArray(value) then\n error 'manifestYamlStream only takes arrays, got ' + std.type(value)\n else\n '---\\n' + std.join(\n '\\n---\\n', [std.manifestYamlDoc(e, indent_array_in_object, quote_keys) for e in value]\n ) + if c_document_end then '\\n...\\n' else '\\n',\n\n manifestPython(v)::\n if std.isObject(v) then\n local fields = [\n '%s: %s' % [std.escapeStringPython(k), std.manifestPython(v[k])]\n for k in std.objectFields(v)\n ];\n '{%s}' % [std.join(', ', fields)]\n else if std.isArray(v) then\n '[%s]' % [std.join(', ', [std.manifestPython(v2) for v2 in v])]\n else if std.isString(v) then\n '%s' % [std.escapeStringPython(v)]\n else if std.isFunction(v) then\n error 'cannot manifest function'\n else if std.isNumber(v) then\n std.toString(v)\n else if v == true then\n 'True'\n else if v == false then\n 'False'\n else if v == null then\n 'None',\n\n manifestPythonVars(conf)::\n local vars = ['%s = %s' % [k, std.manifestPython(conf[k])] for k in std.objectFields(conf)];\n std.join('\\n', vars + ['']),\n\n manifestXmlJsonml(value)::\n if !std.isArray(value) then\n error 'Expected a JSONML value (an array), got %s' % std.type(value)\n else\n local aux(v) =\n if std.isString(v) then\n v\n else\n local tag = v[0];\n local has_attrs = std.length(v) > 1 && std.isObject(v[1]);\n local attrs = if has_attrs then v[1] else {};\n local children = if has_attrs then v[2:] else v[1:];\n local attrs_str =\n std.join('', [' %s=\"%s\"' % [k, attrs[k]] for k in std.objectFields(attrs)]);\n std.deepJoin(['<', tag, attrs_str, '>', [aux(x) for x in children], '']);\n\n aux(value),\n\n mergePatch(target, patch)::\n if std.isObject(patch) then\n local target_object =\n if std.isObject(target) then target else {};\n\n local target_fields =\n if std.isObject(target_object) then std.objectFields(target_object) else [];\n\n local null_fields = [k for k in std.objectFields(patch) if patch[k] == null];\n local both_fields = std.setUnion(target_fields, std.objectFields(patch));\n\n {\n [k]:\n if !std.objectHas(patch, k) then\n target_object[k]\n else if !std.objectHas(target_object, k) then\n std.mergePatch(null, patch[k]) tailstrict\n else\n std.mergePatch(target_object[k], patch[k]) tailstrict\n for k in std.setDiff(both_fields, null_fields)\n }\n else\n patch,\n\n get(o, f, default=null, inc_hidden=true)::\n if std.objectHasEx(o, f, inc_hidden) then o[f] else default,\n\n resolvePath(f, r)::\n local arr = std.split(f, '/');\n std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),\n\n prune(a)::\n local isContent(b) =\n if b == null then\n false\n else if std.isArray(b) then\n std.length(b) > 0\n else if std.isObject(b) then\n std.length(b) > 0\n else\n true;\n if std.isArray(a) then\n [std.prune(x) for x in a if isContent($.prune(x))]\n else if std.isObject(a) then {\n [x]: $.prune(a[x])\n for x in std.objectFields(a)\n if isContent(std.prune(a[x]))\n } else\n a,\n\n find(value, arr)::\n if !std.isArray(arr) then\n error 'find second parameter should be an array, got ' + std.type(arr)\n else\n std.filter(function(i) arr[i] == value, std.range(0, std.length(arr) - 1)),\n\n // Compat\n __compare_array(arr1, arr2)::\n assert std.isArray(arr1) && std.isArray(arr2);\n std.__compare(arr1, arr2),\n __array_less(arr1, arr2):: std.__compare_array(arr1, arr2) == -1,\n __array_greater(arr1, arr2):: std.__compare_array(arr1, arr2) == 1,\n __array_less_or_equal(arr1, arr2):: std.__compare_array(arr1, arr2) <= 0,\n __array_greater_or_equal(arr1, arr2):: std.__compare_array(arr1, arr2) >= 0,\n}\n" +--- +SOURCE_FILE@0..7835 + EXPR@0..7834 + EXPR_OBJECT@0..7834 + OBJ_BODY_MEMBER_LIST@0..7834 + L_BRACE@0..1 "{" + WHITESPACE@1..4 "\n " + MEMBER_BIND_STMT@4..20 + OBJ_LOCAL@4..20 + LOCAL_KW@4..9 "local" + WHITESPACE@9..10 " " + BIND_DESTRUCT@10..20 + DESTRUCT_FULL@10..13 + NAME@10..13 + IDENT@10..13 "std" + WHITESPACE@13..14 " " + ASSIGN@14..15 "=" + WHITESPACE@15..16 " " + EXPR@16..20 + EXPR_LITERAL@16..20 + SELF_KW@16..20 "self" + COMMA@20..21 "," + WHITESPACE@21..24 "\n " + MEMBER_BIND_STMT@24..41 + OBJ_LOCAL@24..41 + LOCAL_KW@24..29 "local" + WHITESPACE@29..30 " " + BIND_DESTRUCT@30..41 + DESTRUCT_FULL@30..32 + NAME@30..32 + IDENT@30..32 "id" + WHITESPACE@32..33 " " + ASSIGN@33..34 "=" + WHITESPACE@34..35 " " + EXPR@35..41 + EXPR_VAR@35..38 + NAME@35..38 + IDENT@35..38 "std" + SUFFIX_INDEX@38..41 + DOT@38..39 "." + NAME@39..41 + IDENT@39..41 "id" + COMMA@41..42 "," + WHITESPACE@42..46 "\n\n " + MEMBER_FIELD_NORMAL@46..224 + FIELD_NAME_FIXED@46..54 + NAME@46..54 + IDENT@46..54 "thisFile" + COLONCOLON@54..56 "::" + WHITESPACE@56..57 " " + EXPR@57..224 + EXPR_ERROR@57..224 + ERROR_KW@57..62 "error" + WHITESPACE@62..63 " " + EXPR@63..224 + EXPR_STRING@63..224 + STRING_SINGLE@63..224 "'std.thisFile is depr ..." + COMMA@224..225 "," + WHITESPACE@225..229 "\n\n " + MEMBER_FIELD_METHOD@229..372 + FIELD_NAME_FIXED@229..240 + NAME@229..240 + IDENT@229..240 "lstripChars" + PARAMS_DESC@240..252 + L_PAREN@240..241 "(" + PARAM@241..244 + DESTRUCT_FULL@241..244 + NAME@241..244 + IDENT@241..244 "str" + COMMA@244..245 "," + WHITESPACE@245..246 " " + PARAM@246..251 + DESTRUCT_FULL@246..251 + NAME@246..251 + IDENT@246..251 "chars" + R_PAREN@251..252 ")" + COLONCOLON@252..254 "::" + WHITESPACE@254..259 "\n " + EXPR@259..372 + EXPR_IF_THEN_ELSE@259..372 + IF_KW@259..261 "if" + WHITESPACE@261..262 " " + EXPR@262..310 + EXPR_BINARY@262..310 + EXPR@262..281 + EXPR_BINARY@262..281 + EXPR@262..265 + EXPR_VAR@262..265 + NAME@262..265 + IDENT@262..265 "std" + SUFFIX_INDEX@265..272 + DOT@265..266 "." + NAME@266..272 + IDENT@266..272 "length" + SUFFIX_APPLY@272..277 + ARGS_DESC@272..277 + L_PAREN@272..273 "(" + ARG@273..276 + EXPR@273..276 + EXPR_VAR@273..276 + NAME@273..276 + IDENT@273..276 "str" + R_PAREN@276..277 ")" + WHITESPACE@277..278 " " + GT@278..279 ">" + WHITESPACE@279..280 " " + EXPR@280..281 + EXPR_NUMBER@280..281 + FLOAT@280..281 "0" + WHITESPACE@281..282 " " + AND@282..284 "&&" + WHITESPACE@284..285 " " + EXPR@285..310 + EXPR_VAR@285..288 + NAME@285..288 + IDENT@285..288 "std" + SUFFIX_INDEX@288..295 + DOT@288..289 "." + NAME@289..295 + IDENT@289..295 "member" + SUFFIX_APPLY@295..310 + ARGS_DESC@295..310 + L_PAREN@295..296 "(" + ARG@296..301 + EXPR@296..301 + EXPR_VAR@296..301 + NAME@296..301 + IDENT@296..301 "chars" + COMMA@301..302 "," + WHITESPACE@302..303 " " + ARG@303..309 + EXPR@303..309 + EXPR_VAR@303..306 + NAME@303..306 + IDENT@303..306 "str" + SUFFIX_INDEX_EXPR@306..309 + L_BRACK@306..307 "[" + EXPR@307..308 + EXPR_NUMBER@307..308 + FLOAT@307..308 "0" + R_BRACK@308..309 "]" + R_PAREN@309..310 ")" + WHITESPACE@310..311 " " + THEN_KW@311..315 "then" + WHITESPACE@315..322 "\n " + TRUE_EXPR@322..353 + EXPR@322..353 + EXPR_VAR@322..325 + NAME@322..325 + IDENT@322..325 "std" + SUFFIX_INDEX@325..337 + DOT@325..326 "." + NAME@326..337 + IDENT@326..337 "lstripChars" + SUFFIX_APPLY@337..353 + ARGS_DESC@337..353 + L_PAREN@337..338 "(" + ARG@338..345 + EXPR@338..345 + EXPR_VAR@338..341 + NAME@338..341 + IDENT@338..341 "str" + SUFFIX_SLICE@341..345 + SLICE_DESC@341..345 + L_BRACK@341..342 "[" + EXPR@342..343 + EXPR_NUMBER@342..343 + FLOAT@342..343 "1" + COLON@343..344 ":" + R_BRACK@344..345 "]" + COMMA@345..346 "," + WHITESPACE@346..347 " " + ARG@347..352 + EXPR@347..352 + EXPR_VAR@347..352 + NAME@347..352 + IDENT@347..352 "chars" + R_PAREN@352..353 ")" + WHITESPACE@353..358 "\n " + ELSE_KW@358..362 "else" + WHITESPACE@362..369 "\n " + FALSE_EXPR@369..372 + EXPR@369..372 + EXPR_VAR@369..372 + NAME@369..372 + IDENT@369..372 "str" + COMMA@372..373 "," + WHITESPACE@373..377 "\n\n " + MEMBER_FIELD_METHOD@377..553 + FIELD_NAME_FIXED@377..388 + NAME@377..388 + IDENT@377..388 "rstripChars" + PARAMS_DESC@388..400 + L_PAREN@388..389 "(" + PARAM@389..392 + DESTRUCT_FULL@389..392 + NAME@389..392 + IDENT@389..392 "str" + COMMA@392..393 "," + WHITESPACE@393..394 " " + PARAM@394..399 + DESTRUCT_FULL@394..399 + NAME@394..399 + IDENT@394..399 "chars" + R_PAREN@399..400 ")" + COLONCOLON@400..402 "::" + WHITESPACE@402..407 "\n " + EXPR@407..553 + STMT_LOCAL@407..435 + LOCAL_KW@407..412 "local" + WHITESPACE@412..413 " " + BIND_DESTRUCT@413..434 + DESTRUCT_FULL@413..416 + NAME@413..416 + IDENT@413..416 "len" + WHITESPACE@416..417 " " + ASSIGN@417..418 "=" + WHITESPACE@418..419 " " + EXPR@419..434 + EXPR_VAR@419..422 + NAME@419..422 + IDENT@419..422 "std" + SUFFIX_INDEX@422..429 + DOT@422..423 "." + NAME@423..429 + IDENT@423..429 "length" + SUFFIX_APPLY@429..434 + ARGS_DESC@429..434 + L_PAREN@429..430 "(" + ARG@430..433 + EXPR@430..433 + EXPR_VAR@430..433 + NAME@430..433 + IDENT@430..433 "str" + R_PAREN@433..434 ")" + SEMI@434..435 ";" + WHITESPACE@435..440 "\n " + EXPR_IF_THEN_ELSE@440..553 + IF_KW@440..442 "if" + WHITESPACE@442..443 " " + EXPR@443..485 + EXPR_BINARY@443..485 + EXPR@443..450 + EXPR_BINARY@443..450 + EXPR@443..446 + EXPR_VAR@443..446 + NAME@443..446 + IDENT@443..446 "len" + WHITESPACE@446..447 " " + GT@447..448 ">" + WHITESPACE@448..449 " " + EXPR@449..450 + EXPR_NUMBER@449..450 + FLOAT@449..450 "0" + WHITESPACE@450..451 " " + AND@451..453 "&&" + WHITESPACE@453..454 " " + EXPR@454..485 + EXPR_VAR@454..457 + NAME@454..457 + IDENT@454..457 "std" + SUFFIX_INDEX@457..464 + DOT@457..458 "." + NAME@458..464 + IDENT@458..464 "member" + SUFFIX_APPLY@464..485 + ARGS_DESC@464..485 + L_PAREN@464..465 "(" + ARG@465..470 + EXPR@465..470 + EXPR_VAR@465..470 + NAME@465..470 + IDENT@465..470 "chars" + COMMA@470..471 "," + WHITESPACE@471..472 " " + ARG@472..484 + EXPR@472..484 + EXPR_VAR@472..475 + NAME@472..475 + IDENT@472..475 "str" + SUFFIX_INDEX_EXPR@475..484 + L_BRACK@475..476 "[" + EXPR@476..483 + EXPR_BINARY@476..483 + EXPR@476..479 + EXPR_VAR@476..479 + NAME@476..479 + IDENT@476..479 "len" + WHITESPACE@479..480 " " + MINUS@480..481 "-" + WHITESPACE@481..482 " " + EXPR@482..483 + EXPR_NUMBER@482..483 + FLOAT@482..483 "1" + R_BRACK@483..484 "]" + R_PAREN@484..485 ")" + WHITESPACE@485..486 " " + THEN_KW@486..490 "then" + WHITESPACE@490..497 "\n " + TRUE_EXPR@497..534 + EXPR@497..534 + EXPR_VAR@497..500 + NAME@497..500 + IDENT@497..500 "std" + SUFFIX_INDEX@500..512 + DOT@500..501 "." + NAME@501..512 + IDENT@501..512 "rstripChars" + SUFFIX_APPLY@512..534 + ARGS_DESC@512..534 + L_PAREN@512..513 "(" + ARG@513..526 + EXPR@513..526 + EXPR_VAR@513..516 + NAME@513..516 + IDENT@513..516 "str" + SUFFIX_SLICE@516..526 + SLICE_DESC@516..526 + L_BRACK@516..517 "[" + COLON@517..518 ":" + SLICE_DESC_END@518..525 + EXPR@518..525 + EXPR_BINARY@518..525 + EXPR@518..521 + EXPR_VAR@518..521 + NAME@518..521 + IDENT@518..521 "len" + WHITESPACE@521..522 " " + MINUS@522..523 "-" + WHITESPACE@523..524 " " + EXPR@524..525 + EXPR_NUMBER@524..525 + FLOAT@524..525 "1" + R_BRACK@525..526 "]" + COMMA@526..527 "," + WHITESPACE@527..528 " " + ARG@528..533 + EXPR@528..533 + EXPR_VAR@528..533 + NAME@528..533 + IDENT@528..533 "chars" + R_PAREN@533..534 ")" + WHITESPACE@534..539 "\n " + ELSE_KW@539..543 "else" + WHITESPACE@543..550 "\n " + FALSE_EXPR@550..553 + EXPR@550..553 + EXPR_VAR@550..553 + NAME@550..553 + IDENT@550..553 "str" + COMMA@553..554 "," + WHITESPACE@554..558 "\n\n " + MEMBER_FIELD_METHOD@558..638 + FIELD_NAME_FIXED@558..568 + NAME@558..568 + IDENT@558..568 "stripChars" + PARAMS_DESC@568..580 + L_PAREN@568..569 "(" + PARAM@569..572 + DESTRUCT_FULL@569..572 + NAME@569..572 + IDENT@569..572 "str" + COMMA@572..573 "," + WHITESPACE@573..574 " " + PARAM@574..579 + DESTRUCT_FULL@574..579 + NAME@574..579 + IDENT@574..579 "chars" + R_PAREN@579..580 ")" + COLONCOLON@580..582 "::" + WHITESPACE@582..587 "\n " + EXPR@587..638 + EXPR_VAR@587..590 + NAME@587..590 + IDENT@587..590 "std" + SUFFIX_INDEX@590..602 + DOT@590..591 "." + NAME@591..602 + IDENT@591..602 "lstripChars" + SUFFIX_APPLY@602..638 + ARGS_DESC@602..638 + L_PAREN@602..603 "(" + ARG@603..630 + EXPR@603..630 + EXPR_VAR@603..606 + NAME@603..606 + IDENT@603..606 "std" + SUFFIX_INDEX@606..618 + DOT@606..607 "." + NAME@607..618 + IDENT@607..618 "rstripChars" + SUFFIX_APPLY@618..630 + ARGS_DESC@618..630 + L_PAREN@618..619 "(" + ARG@619..622 + EXPR@619..622 + EXPR_VAR@619..622 + NAME@619..622 + IDENT@619..622 "str" + COMMA@622..623 "," + WHITESPACE@623..624 " " + ARG@624..629 + EXPR@624..629 + EXPR_VAR@624..629 + NAME@624..629 + IDENT@624..629 "chars" + R_PAREN@629..630 ")" + COMMA@630..631 "," + WHITESPACE@631..632 " " + ARG@632..637 + EXPR@632..637 + EXPR_VAR@632..637 + NAME@632..637 + IDENT@632..637 "chars" + R_PAREN@637..638 ")" + COMMA@638..639 "," + WHITESPACE@639..643 "\n\n " + MEMBER_FIELD_METHOD@643..921 + FIELD_NAME_FIXED@643..654 + NAME@643..654 + IDENT@643..654 "splitLimitR" + PARAMS_DESC@654..673 + L_PAREN@654..655 "(" + PARAM@655..658 + DESTRUCT_FULL@655..658 + NAME@655..658 + IDENT@655..658 "str" + COMMA@658..659 "," + WHITESPACE@659..660 " " + PARAM@660..661 + DESTRUCT_FULL@660..661 + NAME@660..661 + IDENT@660..661 "c" + COMMA@661..662 "," + WHITESPACE@662..663 " " + PARAM@663..672 + DESTRUCT_FULL@663..672 + NAME@663..672 + IDENT@663..672 "maxsplits" + R_PAREN@672..673 ")" + COLONCOLON@673..675 "::" + WHITESPACE@675..680 "\n " + EXPR@680..921 + EXPR_IF_THEN_ELSE@680..921 + IF_KW@680..682 "if" + WHITESPACE@682..683 " " + EXPR@683..698 + EXPR_BINARY@683..698 + EXPR@683..692 + EXPR_VAR@683..692 + NAME@683..692 + IDENT@683..692 "maxsplits" + WHITESPACE@692..693 " " + EQ@693..695 "==" + WHITESPACE@695..696 " " + EXPR@696..698 + EXPR_UNARY@696..698 + MINUS@696..697 "-" + EXPR_NUMBER@697..698 + FLOAT@697..698 "1" + WHITESPACE@698..699 " " + THEN_KW@699..703 "then" + WHITESPACE@703..710 "\n " + TRUE_EXPR@710..736 + EXPR@710..736 + EXPR_VAR@710..713 + NAME@710..713 + IDENT@710..713 "std" + SUFFIX_INDEX@713..724 + DOT@713..714 "." + NAME@714..724 + IDENT@714..724 "splitLimit" + SUFFIX_APPLY@724..736 + ARGS_DESC@724..736 + L_PAREN@724..725 "(" + ARG@725..728 + EXPR@725..728 + EXPR_VAR@725..728 + NAME@725..728 + IDENT@725..728 "str" + COMMA@728..729 "," + WHITESPACE@729..730 " " + ARG@730..731 + EXPR@730..731 + EXPR_VAR@730..731 + NAME@730..731 + IDENT@730..731 "c" + COMMA@731..732 "," + WHITESPACE@732..733 " " + ARG@733..735 + EXPR@733..735 + EXPR_UNARY@733..735 + MINUS@733..734 "-" + EXPR_NUMBER@734..735 + FLOAT@734..735 "1" + R_PAREN@735..736 ")" + WHITESPACE@736..741 "\n " + ELSE_KW@741..745 "else" + WHITESPACE@745..752 "\n " + FALSE_EXPR@752..921 + EXPR@752..921 + STMT_LOCAL@752..820 + LOCAL_KW@752..757 "local" + WHITESPACE@757..758 " " + BIND_FUNCTION@758..819 + NAME@758..764 + IDENT@758..764 "revStr" + PARAMS_DESC@764..769 + L_PAREN@764..765 "(" + PARAM@765..768 + DESTRUCT_FULL@765..768 + NAME@765..768 + IDENT@765..768 "str" + R_PAREN@768..769 ")" + WHITESPACE@769..770 " " + ASSIGN@770..771 "=" + WHITESPACE@771..772 " " + EXPR@772..819 + EXPR_VAR@772..775 + NAME@772..775 + IDENT@772..775 "std" + SUFFIX_INDEX@775..780 + DOT@775..776 "." + NAME@776..780 + IDENT@776..780 "join" + SUFFIX_APPLY@780..819 + ARGS_DESC@780..819 + L_PAREN@780..781 "(" + ARG@781..783 + EXPR@781..783 + EXPR_STRING@781..783 + STRING_SINGLE@781..783 "''" + COMMA@783..784 "," + WHITESPACE@784..785 " " + ARG@785..818 + EXPR@785..818 + EXPR_VAR@785..788 + NAME@785..788 + IDENT@785..788 "std" + SUFFIX_INDEX@788..796 + DOT@788..789 "." + NAME@789..796 + IDENT@789..796 "reverse" + SUFFIX_APPLY@796..818 + ARGS_DESC@796..818 + L_PAREN@796..797 "(" + ARG@797..817 + EXPR@797..817 + EXPR_VAR@797..800 + NAME@797..800 + IDENT@797..800 "std" + SUFFIX_INDEX@800..812 + DOT@800..801 "." + NAME@801..812 + IDENT@801..812 "stringChars" + SUFFIX_APPLY@812..817 + ARGS_DESC@812..817 + L_PAREN@812..813 "(" + ARG@813..816 + EXPR@813..816 + EXPR_VAR@813..816 + NAME@813..816 + IDENT@813..816 "str" + R_PAREN@816..817 ")" + R_PAREN@817..818 ")" + R_PAREN@818..819 ")" + SEMI@819..820 ";" + WHITESPACE@820..827 "\n " + EXPR_VAR@827..830 + NAME@827..830 + IDENT@827..830 "std" + SUFFIX_INDEX@830..834 + DOT@830..831 "." + NAME@831..834 + IDENT@831..834 "map" + SUFFIX_APPLY@834..921 + ARGS_DESC@834..921 + L_PAREN@834..835 "(" + ARG@835..856 + EXPR@835..856 + EXPR_FUNCTION@835..856 + FUNCTION_KW@835..843 "function" + PARAMS_DESC@843..846 + L_PAREN@843..844 "(" + PARAM@844..845 + DESTRUCT_FULL@844..845 + NAME@844..845 + IDENT@844..845 "e" + R_PAREN@845..846 ")" + WHITESPACE@846..847 " " + EXPR@847..856 + EXPR_VAR@847..853 + NAME@847..853 + IDENT@847..853 "revStr" + SUFFIX_APPLY@853..856 + ARGS_DESC@853..856 + L_PAREN@853..854 "(" + ARG@854..855 + EXPR@854..855 + EXPR_VAR@854..855 + NAME@854..855 + IDENT@854..855 "e" + R_PAREN@855..856 ")" + COMMA@856..857 "," + WHITESPACE@857..858 " " + ARG@858..920 + EXPR@858..920 + EXPR_VAR@858..861 + NAME@858..861 + IDENT@858..861 "std" + SUFFIX_INDEX@861..869 + DOT@861..862 "." + NAME@862..869 + IDENT@862..869 "reverse" + SUFFIX_APPLY@869..920 + ARGS_DESC@869..920 + L_PAREN@869..870 "(" + ARG@870..919 + EXPR@870..919 + EXPR_VAR@870..873 + NAME@870..873 + IDENT@870..873 "std" + SUFFIX_INDEX@873..884 + DOT@873..874 "." + NAME@874..884 + IDENT@874..884 "splitLimit" + SUFFIX_APPLY@884..919 + ARGS_DESC@884..919 + L_PAREN@884..885 "(" + ARG@885..896 + EXPR@885..896 + EXPR_VAR@885..891 + NAME@885..891 + IDENT@885..891 "revStr" + SUFFIX_APPLY@891..896 + ARGS_DESC@891..896 + L_PAREN@891..892 "(" + ARG@892..895 + EXPR@892..895 + EXPR_VAR@892..895 + NAME@892..895 + IDENT@892..895 "str" + R_PAREN@895..896 ")" + COMMA@896..897 "," + WHITESPACE@897..898 " " + ARG@898..907 + EXPR@898..907 + EXPR_VAR@898..904 + NAME@898..904 + IDENT@898..904 "revStr" + SUFFIX_APPLY@904..907 + ARGS_DESC@904..907 + L_PAREN@904..905 "(" + ARG@905..906 + EXPR@905..906 + EXPR_VAR@905..906 + NAME@905..906 + IDENT@905..906 "c" + R_PAREN@906..907 ")" + COMMA@907..908 "," + WHITESPACE@908..909 " " + ARG@909..918 + EXPR@909..918 + EXPR_VAR@909..918 + NAME@909..918 + IDENT@909..918 "maxsplits" + R_PAREN@918..919 ")" + R_PAREN@919..920 ")" + R_PAREN@920..921 ")" + COMMA@921..922 "," + WHITESPACE@922..926 "\n\n " + MEMBER_FIELD_METHOD@926..968 + FIELD_NAME_FIXED@926..931 + NAME@926..931 + IDENT@926..931 "split" + PARAMS_DESC@931..939 + L_PAREN@931..932 "(" + PARAM@932..935 + DESTRUCT_FULL@932..935 + NAME@932..935 + IDENT@932..935 "str" + COMMA@935..936 "," + WHITESPACE@936..937 " " + PARAM@937..938 + DESTRUCT_FULL@937..938 + NAME@937..938 + IDENT@937..938 "c" + R_PAREN@938..939 ")" + COLONCOLON@939..941 "::" + WHITESPACE@941..942 " " + EXPR@942..968 + EXPR_VAR@942..945 + NAME@942..945 + IDENT@942..945 "std" + SUFFIX_INDEX@945..956 + DOT@945..946 "." + NAME@946..956 + IDENT@946..956 "splitLimit" + SUFFIX_APPLY@956..968 + ARGS_DESC@956..968 + L_PAREN@956..957 "(" + ARG@957..960 + EXPR@957..960 + EXPR_VAR@957..960 + NAME@957..960 + IDENT@957..960 "str" + COMMA@960..961 "," + WHITESPACE@961..962 " " + ARG@962..963 + EXPR@962..963 + EXPR_VAR@962..963 + NAME@962..963 + IDENT@962..963 "c" + COMMA@963..964 "," + WHITESPACE@964..965 " " + ARG@965..967 + EXPR@965..967 + EXPR_UNARY@965..967 + MINUS@965..966 "-" + EXPR_NUMBER@966..967 + FLOAT@966..967 "1" + R_PAREN@967..968 ")" + COMMA@968..969 "," + WHITESPACE@969..973 "\n\n " + MEMBER_FIELD_METHOD@973..1331 + FIELD_NAME_FIXED@973..985 + NAME@973..985 + IDENT@973..985 "mapWithIndex" + PARAMS_DESC@985..996 + L_PAREN@985..986 "(" + PARAM@986..990 + DESTRUCT_FULL@986..990 + NAME@986..990 + IDENT@986..990 "func" + COMMA@990..991 "," + WHITESPACE@991..992 " " + PARAM@992..995 + DESTRUCT_FULL@992..995 + NAME@992..995 + IDENT@992..995 "arr" + R_PAREN@995..996 ")" + COLONCOLON@996..998 "::" + WHITESPACE@998..1003 "\n " + EXPR@1003..1331 + EXPR_IF_THEN_ELSE@1003..1331 + IF_KW@1003..1005 "if" + WHITESPACE@1005..1006 " " + EXPR@1006..1027 + EXPR_UNARY@1006..1027 + NOT@1006..1007 "!" + EXPR_VAR@1007..1010 + NAME@1007..1010 + IDENT@1007..1010 "std" + SUFFIX_INDEX@1010..1021 + DOT@1010..1011 "." + NAME@1011..1021 + IDENT@1011..1021 "isFunction" + SUFFIX_APPLY@1021..1027 + ARGS_DESC@1021..1027 + L_PAREN@1021..1022 "(" + ARG@1022..1026 + EXPR@1022..1026 + EXPR_VAR@1022..1026 + NAME@1022..1026 + IDENT@1022..1026 "func" + R_PAREN@1026..1027 ")" + WHITESPACE@1027..1028 " " + THEN_KW@1028..1032 "then" + WHITESPACE@1032..1039 "\n " + TRUE_EXPR@1039..1117 + EXPR@1039..1117 + EXPR_ERROR@1039..1117 + ERROR_KW@1039..1044 "error" + WHITESPACE@1044..1045 " " + EXPR@1045..1117 + EXPR_PARENED@1045..1117 + L_PAREN@1045..1046 "(" + EXPR@1046..1116 + EXPR_BINARY@1046..1116 + EXPR@1046..1099 + EXPR_STRING@1046..1099 + STRING_SINGLE@1046..1099 "'std.mapWithIndex fir ..." + WHITESPACE@1099..1100 " " + PLUS@1100..1101 "+" + WHITESPACE@1101..1102 " " + EXPR@1102..1116 + EXPR_VAR@1102..1105 + NAME@1102..1105 + IDENT@1102..1105 "std" + SUFFIX_INDEX@1105..1110 + DOT@1105..1106 "." + NAME@1106..1110 + IDENT@1106..1110 "type" + SUFFIX_APPLY@1110..1116 + ARGS_DESC@1110..1116 + L_PAREN@1110..1111 "(" + ARG@1111..1115 + EXPR@1111..1115 + EXPR_VAR@1111..1115 + NAME@1111..1115 + IDENT@1111..1115 "func" + R_PAREN@1115..1116 ")" + R_PAREN@1116..1117 ")" + WHITESPACE@1117..1122 "\n " + ELSE_KW@1122..1126 "else" + WHITESPACE@1126..1127 " " + FALSE_EXPR@1127..1331 + EXPR@1127..1331 + EXPR_IF_THEN_ELSE@1127..1331 + IF_KW@1127..1129 "if" + WHITESPACE@1129..1130 " " + EXPR@1130..1169 + EXPR_BINARY@1130..1169 + EXPR@1130..1147 + EXPR_UNARY@1130..1147 + NOT@1130..1131 "!" + EXPR_VAR@1131..1134 + NAME@1131..1134 + IDENT@1131..1134 "std" + SUFFIX_INDEX@1134..1142 + DOT@1134..1135 "." + NAME@1135..1142 + IDENT@1135..1142 "isArray" + SUFFIX_APPLY@1142..1147 + ARGS_DESC@1142..1147 + L_PAREN@1142..1143 "(" + ARG@1143..1146 + EXPR@1143..1146 + EXPR_VAR@1143..1146 + NAME@1143..1146 + IDENT@1143..1146 "arr" + R_PAREN@1146..1147 ")" + WHITESPACE@1147..1148 " " + AND@1148..1150 "&&" + WHITESPACE@1150..1151 " " + EXPR@1151..1169 + EXPR_UNARY@1151..1169 + NOT@1151..1152 "!" + EXPR_VAR@1152..1155 + NAME@1152..1155 + IDENT@1152..1155 "std" + SUFFIX_INDEX@1155..1164 + DOT@1155..1156 "." + NAME@1156..1164 + IDENT@1156..1164 "isString" + SUFFIX_APPLY@1164..1169 + ARGS_DESC@1164..1169 + L_PAREN@1164..1165 "(" + ARG@1165..1168 + EXPR@1165..1168 + EXPR_VAR@1165..1168 + NAME@1165..1168 + IDENT@1165..1168 "arr" + R_PAREN@1168..1169 ")" + WHITESPACE@1169..1170 " " + THEN_KW@1170..1174 "then" + WHITESPACE@1174..1181 "\n " + TRUE_EXPR@1181..1256 + EXPR@1181..1256 + EXPR_ERROR@1181..1256 + ERROR_KW@1181..1186 "error" + WHITESPACE@1186..1187 " " + EXPR@1187..1256 + EXPR_PARENED@1187..1256 + L_PAREN@1187..1188 "(" + EXPR@1188..1255 + EXPR_BINARY@1188..1255 + EXPR@1188..1239 + EXPR_STRING@1188..1239 + STRING_SINGLE@1188..1239 "'std.mapWithIndex sec ..." + WHITESPACE@1239..1240 " " + PLUS@1240..1241 "+" + WHITESPACE@1241..1242 " " + EXPR@1242..1255 + EXPR_VAR@1242..1245 + NAME@1242..1245 + IDENT@1242..1245 "std" + SUFFIX_INDEX@1245..1250 + DOT@1245..1246 "." + NAME@1246..1250 + IDENT@1246..1250 "type" + SUFFIX_APPLY@1250..1255 + ARGS_DESC@1250..1255 + L_PAREN@1250..1251 "(" + ARG@1251..1254 + EXPR@1251..1254 + EXPR_VAR@1251..1254 + NAME@1251..1254 + IDENT@1251..1254 "arr" + R_PAREN@1254..1255 ")" + R_PAREN@1255..1256 ")" + WHITESPACE@1256..1261 "\n " + ELSE_KW@1261..1265 "else" + WHITESPACE@1265..1272 "\n " + FALSE_EXPR@1272..1331 + EXPR@1272..1331 + EXPR_VAR@1272..1275 + NAME@1272..1275 + IDENT@1272..1275 "std" + SUFFIX_INDEX@1275..1285 + DOT@1275..1276 "." + NAME@1276..1285 + IDENT@1276..1285 "makeArray" + SUFFIX_APPLY@1285..1331 + ARGS_DESC@1285..1331 + L_PAREN@1285..1286 "(" + ARG@1286..1301 + EXPR@1286..1301 + EXPR_VAR@1286..1289 + NAME@1286..1289 + IDENT@1286..1289 "std" + SUFFIX_INDEX@1289..1296 + DOT@1289..1290 "." + NAME@1290..1296 + IDENT@1290..1296 "length" + SUFFIX_APPLY@1296..1301 + ARGS_DESC@1296..1301 + L_PAREN@1296..1297 "(" + ARG@1297..1300 + EXPR@1297..1300 + EXPR_VAR@1297..1300 + NAME@1297..1300 + IDENT@1297..1300 "arr" + R_PAREN@1300..1301 ")" + COMMA@1301..1302 "," + WHITESPACE@1302..1303 " " + ARG@1303..1330 + EXPR@1303..1330 + EXPR_FUNCTION@1303..1330 + FUNCTION_KW@1303..1311 "function" + PARAMS_DESC@1311..1314 + L_PAREN@1311..1312 "(" + PARAM@1312..1313 + DESTRUCT_FULL@1312..1313 + NAME@1312..1313 + IDENT@1312..1313 "i" + R_PAREN@1313..1314 ")" + WHITESPACE@1314..1315 " " + EXPR@1315..1330 + EXPR_VAR@1315..1319 + NAME@1315..1319 + IDENT@1315..1319 "func" + SUFFIX_APPLY@1319..1330 + ARGS_DESC@1319..1330 + L_PAREN@1319..1320 "(" + ARG@1320..1321 + EXPR@1320..1321 + EXPR_VAR@1320..1321 + NAME@1320..1321 + IDENT@1320..1321 "i" + COMMA@1321..1322 "," + WHITESPACE@1322..1323 " " + ARG@1323..1329 + EXPR@1323..1329 + EXPR_VAR@1323..1326 + NAME@1323..1326 + IDENT@1323..1326 "arr" + SUFFIX_INDEX_EXPR@1326..1329 + L_BRACK@1326..1327 "[" + EXPR@1327..1328 + EXPR_VAR@1327..1328 + NAME@1327..1328 + IDENT@1327..1328 "i" + R_BRACK@1328..1329 "]" + R_PAREN@1329..1330 ")" + R_PAREN@1330..1331 ")" + COMMA@1331..1332 "," + WHITESPACE@1332..1336 "\n\n " + MEMBER_FIELD_METHOD@1336..1664 + FIELD_NAME_FIXED@1336..1346 + NAME@1336..1346 + IDENT@1336..1346 "mapWithKey" + PARAMS_DESC@1346..1357 + L_PAREN@1346..1347 "(" + PARAM@1347..1351 + DESTRUCT_FULL@1347..1351 + NAME@1347..1351 + IDENT@1347..1351 "func" + COMMA@1351..1352 "," + WHITESPACE@1352..1353 " " + PARAM@1353..1356 + DESTRUCT_FULL@1353..1356 + NAME@1353..1356 + IDENT@1353..1356 "obj" + R_PAREN@1356..1357 ")" + COLONCOLON@1357..1359 "::" + WHITESPACE@1359..1364 "\n " + EXPR@1364..1664 + EXPR_IF_THEN_ELSE@1364..1664 + IF_KW@1364..1366 "if" + WHITESPACE@1366..1367 " " + EXPR@1367..1388 + EXPR_UNARY@1367..1388 + NOT@1367..1368 "!" + EXPR_VAR@1368..1371 + NAME@1368..1371 + IDENT@1368..1371 "std" + SUFFIX_INDEX@1371..1382 + DOT@1371..1372 "." + NAME@1372..1382 + IDENT@1372..1382 "isFunction" + SUFFIX_APPLY@1382..1388 + ARGS_DESC@1382..1388 + L_PAREN@1382..1383 "(" + ARG@1383..1387 + EXPR@1383..1387 + EXPR_VAR@1383..1387 + NAME@1383..1387 + IDENT@1383..1387 "func" + R_PAREN@1387..1388 ")" + WHITESPACE@1388..1389 " " + THEN_KW@1389..1393 "then" + WHITESPACE@1393..1400 "\n " + TRUE_EXPR@1400..1476 + EXPR@1400..1476 + EXPR_ERROR@1400..1476 + ERROR_KW@1400..1405 "error" + WHITESPACE@1405..1406 " " + EXPR@1406..1476 + EXPR_PARENED@1406..1476 + L_PAREN@1406..1407 "(" + EXPR@1407..1475 + EXPR_BINARY@1407..1475 + EXPR@1407..1458 + EXPR_STRING@1407..1458 + STRING_SINGLE@1407..1458 "'std.mapWithKey first ..." + WHITESPACE@1458..1459 " " + PLUS@1459..1460 "+" + WHITESPACE@1460..1461 " " + EXPR@1461..1475 + EXPR_VAR@1461..1464 + NAME@1461..1464 + IDENT@1461..1464 "std" + SUFFIX_INDEX@1464..1469 + DOT@1464..1465 "." + NAME@1465..1469 + IDENT@1465..1469 "type" + SUFFIX_APPLY@1469..1475 + ARGS_DESC@1469..1475 + L_PAREN@1469..1470 "(" + ARG@1470..1474 + EXPR@1470..1474 + EXPR_VAR@1470..1474 + NAME@1470..1474 + IDENT@1470..1474 "func" + R_PAREN@1474..1475 ")" + R_PAREN@1475..1476 ")" + WHITESPACE@1476..1481 "\n " + ELSE_KW@1481..1485 "else" + WHITESPACE@1485..1486 " " + FALSE_EXPR@1486..1664 + EXPR@1486..1664 + EXPR_IF_THEN_ELSE@1486..1664 + IF_KW@1486..1488 "if" + WHITESPACE@1488..1489 " " + EXPR@1489..1507 + EXPR_UNARY@1489..1507 + NOT@1489..1490 "!" + EXPR_VAR@1490..1493 + NAME@1490..1493 + IDENT@1490..1493 "std" + SUFFIX_INDEX@1493..1502 + DOT@1493..1494 "." + NAME@1494..1502 + IDENT@1494..1502 "isObject" + SUFFIX_APPLY@1502..1507 + ARGS_DESC@1502..1507 + L_PAREN@1502..1503 "(" + ARG@1503..1506 + EXPR@1503..1506 + EXPR_VAR@1503..1506 + NAME@1503..1506 + IDENT@1503..1506 "obj" + R_PAREN@1506..1507 ")" + WHITESPACE@1507..1508 " " + THEN_KW@1508..1512 "then" + WHITESPACE@1512..1519 "\n " + TRUE_EXPR@1519..1593 + EXPR@1519..1593 + EXPR_ERROR@1519..1593 + ERROR_KW@1519..1524 "error" + WHITESPACE@1524..1525 " " + EXPR@1525..1593 + EXPR_PARENED@1525..1593 + L_PAREN@1525..1526 "(" + EXPR@1526..1592 + EXPR_BINARY@1526..1592 + EXPR@1526..1576 + EXPR_STRING@1526..1576 + STRING_SINGLE@1526..1576 "'std.mapWithKey secon ..." + WHITESPACE@1576..1577 " " + PLUS@1577..1578 "+" + WHITESPACE@1578..1579 " " + EXPR@1579..1592 + EXPR_VAR@1579..1582 + NAME@1579..1582 + IDENT@1579..1582 "std" + SUFFIX_INDEX@1582..1587 + DOT@1582..1583 "." + NAME@1583..1587 + IDENT@1583..1587 "type" + SUFFIX_APPLY@1587..1592 + ARGS_DESC@1587..1592 + L_PAREN@1587..1588 "(" + ARG@1588..1591 + EXPR@1588..1591 + EXPR_VAR@1588..1591 + NAME@1588..1591 + IDENT@1588..1591 "obj" + R_PAREN@1591..1592 ")" + R_PAREN@1592..1593 ")" + WHITESPACE@1593..1598 "\n " + ELSE_KW@1598..1602 "else" + WHITESPACE@1602..1609 "\n " + FALSE_EXPR@1609..1664 + EXPR@1609..1664 + EXPR_OBJECT@1609..1664 + OBJ_BODY_COMP@1609..1664 + L_BRACE@1609..1610 "{" + WHITESPACE@1610..1611 " " + MEMBER_FIELD_NORMAL@1611..1631 + FIELD_NAME_DYNAMIC@1611..1614 + L_BRACK@1611..1612 "[" + EXPR@1612..1613 + EXPR_VAR@1612..1613 + NAME@1612..1613 + IDENT@1612..1613 "k" + R_BRACK@1613..1614 "]" + COLON@1614..1615 ":" + WHITESPACE@1615..1616 " " + EXPR@1616..1631 + EXPR_VAR@1616..1620 + NAME@1616..1620 + IDENT@1616..1620 "func" + SUFFIX_APPLY@1620..1631 + ARGS_DESC@1620..1631 + L_PAREN@1620..1621 "(" + ARG@1621..1622 + EXPR@1621..1622 + EXPR_VAR@1621..1622 + NAME@1621..1622 + IDENT@1621..1622 "k" + COMMA@1622..1623 "," + WHITESPACE@1623..1624 " " + ARG@1624..1630 + EXPR@1624..1630 + EXPR_VAR@1624..1627 + NAME@1624..1627 + IDENT@1624..1627 "obj" + SUFFIX_INDEX_EXPR@1627..1630 + L_BRACK@1627..1628 "[" + EXPR@1628..1629 + EXPR_VAR@1628..1629 + NAME@1628..1629 + IDENT@1628..1629 "k" + R_BRACK@1629..1630 "]" + R_PAREN@1630..1631 ")" + WHITESPACE@1631..1632 " " + FOR_SPEC@1632..1662 + FOR_KW@1632..1635 "for" + WHITESPACE@1635..1636 " " + DESTRUCT_FULL@1636..1637 + NAME@1636..1637 + IDENT@1636..1637 "k" + WHITESPACE@1637..1638 " " + IN_KW@1638..1640 "in" + WHITESPACE@1640..1641 " " + EXPR@1641..1662 + EXPR_VAR@1641..1644 + NAME@1641..1644 + IDENT@1641..1644 "std" + SUFFIX_INDEX@1644..1657 + DOT@1644..1645 "." + NAME@1645..1657 + IDENT@1645..1657 "objectFields" + SUFFIX_APPLY@1657..1662 + ARGS_DESC@1657..1662 + L_PAREN@1657..1658 "(" + ARG@1658..1661 + EXPR@1658..1661 + EXPR_VAR@1658..1661 + NAME@1658..1661 + IDENT@1658..1661 "obj" + R_PAREN@1661..1662 ")" + WHITESPACE@1662..1663 " " + R_BRACE@1663..1664 "}" + COMMA@1664..1665 "," + WHITESPACE@1665..1669 "\n\n " + MEMBER_FIELD_METHOD@1669..1712 + FIELD_NAME_FIXED@1669..1674 + NAME@1669..1674 + IDENT@1669..1674 "lines" + PARAMS_DESC@1674..1679 + L_PAREN@1674..1675 "(" + PARAM@1675..1678 + DESTRUCT_FULL@1675..1678 + NAME@1675..1678 + IDENT@1675..1678 "arr" + R_PAREN@1678..1679 ")" + COLONCOLON@1679..1681 "::" + WHITESPACE@1681..1686 "\n " + EXPR@1686..1712 + EXPR_VAR@1686..1689 + NAME@1686..1689 + IDENT@1686..1689 "std" + SUFFIX_INDEX@1689..1694 + DOT@1689..1690 "." + NAME@1690..1694 + IDENT@1690..1694 "join" + SUFFIX_APPLY@1694..1712 + ARGS_DESC@1694..1712 + L_PAREN@1694..1695 "(" + ARG@1695..1699 + EXPR@1695..1699 + EXPR_STRING@1695..1699 + STRING_SINGLE@1695..1699 "'\\n'" + COMMA@1699..1700 "," + WHITESPACE@1700..1701 " " + ARG@1701..1711 + EXPR@1701..1711 + EXPR_BINARY@1701..1711 + EXPR@1701..1704 + EXPR_VAR@1701..1704 + NAME@1701..1704 + IDENT@1701..1704 "arr" + WHITESPACE@1704..1705 " " + PLUS@1705..1706 "+" + WHITESPACE@1706..1707 " " + EXPR@1707..1711 + EXPR_ARRAY@1707..1711 + L_BRACK@1707..1708 "[" + EXPR@1708..1710 + EXPR_STRING@1708..1710 + STRING_SINGLE@1708..1710 "''" + R_BRACK@1710..1711 "]" + R_PAREN@1711..1712 ")" + COMMA@1712..1713 "," + WHITESPACE@1713..1717 "\n\n " + MEMBER_FIELD_METHOD@1717..1929 + FIELD_NAME_FIXED@1717..1725 + NAME@1717..1725 + IDENT@1717..1725 "deepJoin" + PARAMS_DESC@1725..1730 + L_PAREN@1725..1726 "(" + PARAM@1726..1729 + DESTRUCT_FULL@1726..1729 + NAME@1726..1729 + IDENT@1726..1729 "arr" + R_PAREN@1729..1730 ")" + COLONCOLON@1730..1732 "::" + WHITESPACE@1732..1737 "\n " + EXPR@1737..1929 + EXPR_IF_THEN_ELSE@1737..1929 + IF_KW@1737..1739 "if" + WHITESPACE@1739..1740 " " + EXPR@1740..1757 + EXPR_VAR@1740..1743 + NAME@1740..1743 + IDENT@1740..1743 "std" + SUFFIX_INDEX@1743..1752 + DOT@1743..1744 "." + NAME@1744..1752 + IDENT@1744..1752 "isString" + SUFFIX_APPLY@1752..1757 + ARGS_DESC@1752..1757 + L_PAREN@1752..1753 "(" + ARG@1753..1756 + EXPR@1753..1756 + EXPR_VAR@1753..1756 + NAME@1753..1756 + IDENT@1753..1756 "arr" + R_PAREN@1756..1757 ")" + WHITESPACE@1757..1758 " " + THEN_KW@1758..1762 "then" + WHITESPACE@1762..1769 "\n " + TRUE_EXPR@1769..1772 + EXPR@1769..1772 + EXPR_VAR@1769..1772 + NAME@1769..1772 + IDENT@1769..1772 "arr" + WHITESPACE@1772..1777 "\n " + ELSE_KW@1777..1781 "else" + WHITESPACE@1781..1782 " " + FALSE_EXPR@1782..1929 + EXPR@1782..1929 + EXPR_IF_THEN_ELSE@1782..1929 + IF_KW@1782..1784 "if" + WHITESPACE@1784..1785 " " + EXPR@1785..1801 + EXPR_VAR@1785..1788 + NAME@1785..1788 + IDENT@1785..1788 "std" + SUFFIX_INDEX@1788..1796 + DOT@1788..1789 "." + NAME@1789..1796 + IDENT@1789..1796 "isArray" + SUFFIX_APPLY@1796..1801 + ARGS_DESC@1796..1801 + L_PAREN@1796..1797 "(" + ARG@1797..1800 + EXPR@1797..1800 + EXPR_VAR@1797..1800 + NAME@1797..1800 + IDENT@1797..1800 "arr" + R_PAREN@1800..1801 ")" + WHITESPACE@1801..1802 " " + THEN_KW@1802..1806 "then" + WHITESPACE@1806..1813 "\n " + TRUE_EXPR@1813..1857 + EXPR@1813..1857 + EXPR_VAR@1813..1816 + NAME@1813..1816 + IDENT@1813..1816 "std" + SUFFIX_INDEX@1816..1821 + DOT@1816..1817 "." + NAME@1817..1821 + IDENT@1817..1821 "join" + SUFFIX_APPLY@1821..1857 + ARGS_DESC@1821..1857 + L_PAREN@1821..1822 "(" + ARG@1822..1824 + EXPR@1822..1824 + EXPR_STRING@1822..1824 + STRING_SINGLE@1822..1824 "''" + COMMA@1824..1825 "," + WHITESPACE@1825..1826 " " + ARG@1826..1856 + EXPR@1826..1856 + EXPR_ARRAY_COMP@1826..1856 + L_BRACK@1826..1827 "[" + EXPR@1827..1842 + EXPR_VAR@1827..1830 + NAME@1827..1830 + IDENT@1827..1830 "std" + SUFFIX_INDEX@1830..1839 + DOT@1830..1831 "." + NAME@1831..1839 + IDENT@1831..1839 "deepJoin" + SUFFIX_APPLY@1839..1842 + ARGS_DESC@1839..1842 + L_PAREN@1839..1840 "(" + ARG@1840..1841 + EXPR@1840..1841 + EXPR_VAR@1840..1841 + NAME@1840..1841 + IDENT@1840..1841 "x" + R_PAREN@1841..1842 ")" + WHITESPACE@1842..1843 " " + FOR_SPEC@1843..1855 + FOR_KW@1843..1846 "for" + WHITESPACE@1846..1847 " " + DESTRUCT_FULL@1847..1848 + NAME@1847..1848 + IDENT@1847..1848 "x" + WHITESPACE@1848..1849 " " + IN_KW@1849..1851 "in" + WHITESPACE@1851..1852 " " + EXPR@1852..1855 + EXPR_VAR@1852..1855 + NAME@1852..1855 + IDENT@1852..1855 "arr" + R_BRACK@1855..1856 "]" + R_PAREN@1856..1857 ")" + WHITESPACE@1857..1862 "\n " + ELSE_KW@1862..1866 "else" + WHITESPACE@1866..1873 "\n " + FALSE_EXPR@1873..1929 + EXPR@1873..1929 + EXPR_ERROR@1873..1929 + ERROR_KW@1873..1878 "error" + WHITESPACE@1878..1879 " " + EXPR@1879..1929 + EXPR_BINARY@1879..1929 + EXPR@1879..1913 + EXPR_STRING@1879..1913 + STRING_SINGLE@1879..1913 "'Expected string or a ..." + WHITESPACE@1913..1914 " " + MODULO@1914..1915 "%" + WHITESPACE@1915..1916 " " + EXPR@1916..1929 + EXPR_VAR@1916..1919 + NAME@1916..1919 + IDENT@1916..1919 "std" + SUFFIX_INDEX@1919..1924 + DOT@1919..1920 "." + NAME@1920..1924 + IDENT@1920..1924 "type" + SUFFIX_APPLY@1924..1929 + ARGS_DESC@1924..1929 + L_PAREN@1924..1925 "(" + ARG@1925..1928 + EXPR@1925..1928 + EXPR_VAR@1925..1928 + NAME@1925..1928 + IDENT@1925..1928 "arr" + R_PAREN@1928..1929 ")" + COMMA@1929..1930 "," + WHITESPACE@1930..1934 "\n\n " + MEMBER_FIELD_METHOD@1934..2042 + FIELD_NAME_FIXED@1934..1945 + NAME@1934..1945 + IDENT@1934..1945 "assertEqual" + PARAMS_DESC@1945..1951 + L_PAREN@1945..1946 "(" + PARAM@1946..1947 + DESTRUCT_FULL@1946..1947 + NAME@1946..1947 + IDENT@1946..1947 "a" + COMMA@1947..1948 "," + WHITESPACE@1948..1949 " " + PARAM@1949..1950 + DESTRUCT_FULL@1949..1950 + NAME@1949..1950 + IDENT@1949..1950 "b" + R_PAREN@1950..1951 ")" + COLONCOLON@1951..1953 "::" + WHITESPACE@1953..1958 "\n " + EXPR@1958..2042 + EXPR_IF_THEN_ELSE@1958..2042 + IF_KW@1958..1960 "if" + WHITESPACE@1960..1961 " " + EXPR@1961..1967 + EXPR_BINARY@1961..1967 + EXPR@1961..1962 + EXPR_VAR@1961..1962 + NAME@1961..1962 + IDENT@1961..1962 "a" + WHITESPACE@1962..1963 " " + EQ@1963..1965 "==" + WHITESPACE@1965..1966 " " + EXPR@1966..1967 + EXPR_VAR@1966..1967 + NAME@1966..1967 + IDENT@1966..1967 "b" + WHITESPACE@1967..1968 " " + THEN_KW@1968..1972 "then" + WHITESPACE@1972..1979 "\n " + TRUE_EXPR@1979..1983 + EXPR@1979..1983 + EXPR_LITERAL@1979..1983 + TRUE_KW@1979..1983 "true" + WHITESPACE@1983..1988 "\n " + ELSE_KW@1988..1992 "else" + WHITESPACE@1992..1999 "\n " + FALSE_EXPR@1999..2042 + EXPR@1999..2042 + EXPR_ERROR@1999..2042 + ERROR_KW@1999..2004 "error" + WHITESPACE@2004..2005 " " + EXPR@2005..2042 + EXPR_BINARY@2005..2042 + EXPR@2005..2038 + EXPR_BINARY@2005..2038 + EXPR@2005..2029 + EXPR_BINARY@2005..2029 + EXPR@2005..2025 + EXPR_STRING@2005..2025 + STRING_SINGLE@2005..2025 "'Assertion failed. '" + WHITESPACE@2025..2026 " " + PLUS@2026..2027 "+" + WHITESPACE@2027..2028 " " + EXPR@2028..2029 + EXPR_VAR@2028..2029 + NAME@2028..2029 + IDENT@2028..2029 "a" + WHITESPACE@2029..2030 " " + PLUS@2030..2031 "+" + WHITESPACE@2031..2032 " " + EXPR@2032..2038 + EXPR_STRING@2032..2038 + STRING_SINGLE@2032..2038 "' != '" + WHITESPACE@2038..2039 " " + PLUS@2039..2040 "+" + WHITESPACE@2040..2041 " " + EXPR@2041..2042 + EXPR_VAR@2041..2042 + NAME@2041..2042 + IDENT@2041..2042 "b" + COMMA@2042..2043 "," + WHITESPACE@2043..2047 "\n\n " + MEMBER_FIELD_METHOD@2047..2149 + FIELD_NAME_FIXED@2047..2052 + NAME@2047..2052 + IDENT@2047..2052 "clamp" + PARAMS_DESC@2052..2071 + L_PAREN@2052..2053 "(" + PARAM@2053..2054 + DESTRUCT_FULL@2053..2054 + NAME@2053..2054 + IDENT@2053..2054 "x" + COMMA@2054..2055 "," + WHITESPACE@2055..2056 " " + PARAM@2056..2062 + DESTRUCT_FULL@2056..2062 + NAME@2056..2062 + IDENT@2056..2062 "minVal" + COMMA@2062..2063 "," + WHITESPACE@2063..2064 " " + PARAM@2064..2070 + DESTRUCT_FULL@2064..2070 + NAME@2064..2070 + IDENT@2064..2070 "maxVal" + R_PAREN@2070..2071 ")" + COLONCOLON@2071..2073 "::" + WHITESPACE@2073..2078 "\n " + EXPR@2078..2149 + EXPR_IF_THEN_ELSE@2078..2149 + IF_KW@2078..2080 "if" + WHITESPACE@2080..2081 " " + EXPR@2081..2091 + EXPR_BINARY@2081..2091 + EXPR@2081..2082 + EXPR_VAR@2081..2082 + NAME@2081..2082 + IDENT@2081..2082 "x" + WHITESPACE@2082..2083 " " + LT@2083..2084 "<" + WHITESPACE@2084..2085 " " + EXPR@2085..2091 + EXPR_VAR@2085..2091 + NAME@2085..2091 + IDENT@2085..2091 "minVal" + WHITESPACE@2091..2092 " " + THEN_KW@2092..2096 "then" + WHITESPACE@2096..2097 " " + TRUE_EXPR@2097..2103 + EXPR@2097..2103 + EXPR_VAR@2097..2103 + NAME@2097..2103 + IDENT@2097..2103 "minVal" + WHITESPACE@2103..2108 "\n " + ELSE_KW@2108..2112 "else" + WHITESPACE@2112..2113 " " + FALSE_EXPR@2113..2149 + EXPR@2113..2149 + EXPR_IF_THEN_ELSE@2113..2149 + IF_KW@2113..2115 "if" + WHITESPACE@2115..2116 " " + EXPR@2116..2126 + EXPR_BINARY@2116..2126 + EXPR@2116..2117 + EXPR_VAR@2116..2117 + NAME@2116..2117 + IDENT@2116..2117 "x" + WHITESPACE@2117..2118 " " + GT@2118..2119 ">" + WHITESPACE@2119..2120 " " + EXPR@2120..2126 + EXPR_VAR@2120..2126 + NAME@2120..2126 + IDENT@2120..2126 "maxVal" + WHITESPACE@2126..2127 " " + THEN_KW@2127..2131 "then" + WHITESPACE@2131..2132 " " + TRUE_EXPR@2132..2138 + EXPR@2132..2138 + EXPR_VAR@2132..2138 + NAME@2132..2138 + IDENT@2132..2138 "maxVal" + WHITESPACE@2138..2143 "\n " + ELSE_KW@2143..2147 "else" + WHITESPACE@2147..2148 " " + FALSE_EXPR@2148..2149 + EXPR@2148..2149 + EXPR_VAR@2148..2149 + NAME@2148..2149 + IDENT@2148..2149 "x" + COMMA@2149..2150 "," + WHITESPACE@2150..2154 "\n\n " + MEMBER_FIELD_METHOD@2154..2840 + FIELD_NAME_FIXED@2154..2165 + NAME@2154..2165 + IDENT@2154..2165 "manifestIni" + PARAMS_DESC@2165..2170 + L_PAREN@2165..2166 "(" + PARAM@2166..2169 + DESTRUCT_FULL@2166..2169 + NAME@2166..2169 + IDENT@2166..2169 "ini" + R_PAREN@2169..2170 ")" + COLONCOLON@2170..2172 "::" + WHITESPACE@2172..2177 "\n " + EXPR@2177..2840 + STMT_LOCAL@2177..2481 + LOCAL_KW@2177..2182 "local" + WHITESPACE@2182..2183 " " + BIND_FUNCTION@2183..2480 + NAME@2183..2193 + IDENT@2183..2193 "body_lines" + PARAMS_DESC@2193..2199 + L_PAREN@2193..2194 "(" + PARAM@2194..2198 + DESTRUCT_FULL@2194..2198 + NAME@2194..2198 + IDENT@2194..2198 "body" + R_PAREN@2198..2199 ")" + WHITESPACE@2199..2200 " " + ASSIGN@2200..2201 "=" + WHITESPACE@2201..2208 "\n " + EXPR@2208..2480 + EXPR_VAR@2208..2211 + NAME@2208..2211 + IDENT@2208..2211 "std" + SUFFIX_INDEX@2211..2216 + DOT@2211..2212 "." + NAME@2212..2216 + IDENT@2212..2216 "join" + SUFFIX_APPLY@2216..2480 + ARGS_DESC@2216..2480 + L_PAREN@2216..2217 "(" + ARG@2217..2219 + EXPR@2217..2219 + EXPR_ARRAY@2217..2219 + L_BRACK@2217..2218 "[" + R_BRACK@2218..2219 "]" + COMMA@2219..2220 "," + WHITESPACE@2220..2221 " " + ARG@2221..2479 + EXPR@2221..2479 + EXPR_ARRAY_COMP@2221..2479 + L_BRACK@2221..2222 "[" + WHITESPACE@2222..2231 "\n " + EXPR@2231..2430 + STMT_LOCAL@2231..2263 + LOCAL_KW@2231..2236 "local" + WHITESPACE@2236..2237 " " + BIND_DESTRUCT@2237..2262 + DESTRUCT_FULL@2237..2252 + NAME@2237..2252 + IDENT@2237..2252 "value_or_values" + WHITESPACE@2252..2253 " " + ASSIGN@2253..2254 "=" + WHITESPACE@2254..2255 " " + EXPR@2255..2262 + EXPR_VAR@2255..2259 + NAME@2255..2259 + IDENT@2255..2259 "body" + SUFFIX_INDEX_EXPR@2259..2262 + L_BRACK@2259..2260 "[" + EXPR@2260..2261 + EXPR_VAR@2260..2261 + NAME@2260..2261 + IDENT@2260..2261 "k" + R_BRACK@2261..2262 "]" + SEMI@2262..2263 ";" + WHITESPACE@2263..2272 "\n " + EXPR_IF_THEN_ELSE@2272..2430 + IF_KW@2272..2274 "if" + WHITESPACE@2274..2275 " " + EXPR@2275..2303 + EXPR_VAR@2275..2278 + NAME@2275..2278 + IDENT@2275..2278 "std" + SUFFIX_INDEX@2278..2286 + DOT@2278..2279 "." + NAME@2279..2286 + IDENT@2279..2286 "isArray" + SUFFIX_APPLY@2286..2303 + ARGS_DESC@2286..2303 + L_PAREN@2286..2287 "(" + ARG@2287..2302 + EXPR@2287..2302 + EXPR_VAR@2287..2302 + NAME@2287..2302 + IDENT@2287..2302 "value_or_values" + R_PAREN@2302..2303 ")" + WHITESPACE@2303..2304 " " + THEN_KW@2304..2308 "then" + WHITESPACE@2308..2319 "\n " + TRUE_EXPR@2319..2372 + EXPR@2319..2372 + EXPR_ARRAY_COMP@2319..2372 + L_BRACK@2319..2320 "[" + EXPR@2320..2342 + EXPR_BINARY@2320..2342 + EXPR@2320..2329 + EXPR_STRING@2320..2329 + STRING_SINGLE@2320..2329 "'%s = %s'" + WHITESPACE@2329..2330 " " + MODULO@2330..2331 "%" + WHITESPACE@2331..2332 " " + EXPR@2332..2342 + EXPR_ARRAY@2332..2342 + L_BRACK@2332..2333 "[" + EXPR@2333..2334 + EXPR_VAR@2333..2334 + NAME@2333..2334 + IDENT@2333..2334 "k" + COMMA@2334..2335 "," + WHITESPACE@2335..2336 " " + EXPR@2336..2341 + EXPR_VAR@2336..2341 + NAME@2336..2341 + IDENT@2336..2341 "value" + R_BRACK@2341..2342 "]" + WHITESPACE@2342..2343 " " + FOR_SPEC@2343..2371 + FOR_KW@2343..2346 "for" + WHITESPACE@2346..2347 " " + DESTRUCT_FULL@2347..2352 + NAME@2347..2352 + IDENT@2347..2352 "value" + WHITESPACE@2352..2353 " " + IN_KW@2353..2355 "in" + WHITESPACE@2355..2356 " " + EXPR@2356..2371 + EXPR_VAR@2356..2371 + NAME@2356..2371 + IDENT@2356..2371 "value_or_values" + R_BRACK@2371..2372 "]" + WHITESPACE@2372..2381 "\n " + ELSE_KW@2381..2385 "else" + WHITESPACE@2385..2396 "\n " + FALSE_EXPR@2396..2430 + EXPR@2396..2430 + EXPR_ARRAY@2396..2430 + L_BRACK@2396..2397 "[" + EXPR@2397..2429 + EXPR_BINARY@2397..2429 + EXPR@2397..2406 + EXPR_STRING@2397..2406 + STRING_SINGLE@2397..2406 "'%s = %s'" + WHITESPACE@2406..2407 " " + MODULO@2407..2408 "%" + WHITESPACE@2408..2409 " " + EXPR@2409..2429 + EXPR_ARRAY@2409..2429 + L_BRACK@2409..2410 "[" + EXPR@2410..2411 + EXPR_VAR@2410..2411 + NAME@2410..2411 + IDENT@2410..2411 "k" + COMMA@2411..2412 "," + WHITESPACE@2412..2413 " " + EXPR@2413..2428 + EXPR_VAR@2413..2428 + NAME@2413..2428 + IDENT@2413..2428 "value_or_values" + R_BRACK@2428..2429 "]" + R_BRACK@2429..2430 "]" + WHITESPACE@2430..2440 "\n\n " + FOR_SPEC@2440..2471 + FOR_KW@2440..2443 "for" + WHITESPACE@2443..2444 " " + DESTRUCT_FULL@2444..2445 + NAME@2444..2445 + IDENT@2444..2445 "k" + WHITESPACE@2445..2446 " " + IN_KW@2446..2448 "in" + WHITESPACE@2448..2449 " " + EXPR@2449..2471 + EXPR_VAR@2449..2452 + NAME@2449..2452 + IDENT@2449..2452 "std" + SUFFIX_INDEX@2452..2465 + DOT@2452..2453 "." + NAME@2453..2465 + IDENT@2453..2465 "objectFields" + SUFFIX_APPLY@2465..2471 + ARGS_DESC@2465..2471 + L_PAREN@2465..2466 "(" + ARG@2466..2470 + EXPR@2466..2470 + EXPR_VAR@2466..2470 + NAME@2466..2470 + IDENT@2466..2470 "body" + R_PAREN@2470..2471 ")" + WHITESPACE@2471..2478 "\n " + R_BRACK@2478..2479 "]" + R_PAREN@2479..2480 ")" + SEMI@2480..2481 ";" + WHITESPACE@2481..2487 "\n\n " + STMT_LOCAL@2487..2769 + LOCAL_KW@2487..2492 "local" + WHITESPACE@2492..2493 " " + BIND_FUNCTION@2493..2561 + NAME@2493..2506 + IDENT@2493..2506 "section_lines" + PARAMS_DESC@2506..2520 + L_PAREN@2506..2507 "(" + PARAM@2507..2512 + DESTRUCT_FULL@2507..2512 + NAME@2507..2512 + IDENT@2507..2512 "sname" + COMMA@2512..2513 "," + WHITESPACE@2513..2514 " " + PARAM@2514..2519 + DESTRUCT_FULL@2514..2519 + NAME@2514..2519 + IDENT@2514..2519 "sbody" + R_PAREN@2519..2520 ")" + WHITESPACE@2520..2521 " " + ASSIGN@2521..2522 "=" + WHITESPACE@2522..2523 " " + EXPR@2523..2561 + EXPR_BINARY@2523..2561 + EXPR@2523..2541 + EXPR_ARRAY@2523..2541 + L_BRACK@2523..2524 "[" + EXPR@2524..2540 + EXPR_BINARY@2524..2540 + EXPR@2524..2530 + EXPR_STRING@2524..2530 + STRING_SINGLE@2524..2530 "'[%s]'" + WHITESPACE@2530..2531 " " + MODULO@2531..2532 "%" + WHITESPACE@2532..2533 " " + EXPR@2533..2540 + EXPR_ARRAY@2533..2540 + L_BRACK@2533..2534 "[" + EXPR@2534..2539 + EXPR_VAR@2534..2539 + NAME@2534..2539 + IDENT@2534..2539 "sname" + R_BRACK@2539..2540 "]" + R_BRACK@2540..2541 "]" + WHITESPACE@2541..2542 " " + PLUS@2542..2543 "+" + WHITESPACE@2543..2544 " " + EXPR@2544..2561 + EXPR_VAR@2544..2554 + NAME@2544..2554 + IDENT@2544..2554 "body_lines" + SUFFIX_APPLY@2554..2561 + ARGS_DESC@2554..2561 + L_PAREN@2554..2555 "(" + ARG@2555..2560 + EXPR@2555..2560 + EXPR_VAR@2555..2560 + NAME@2555..2560 + IDENT@2555..2560 "sbody" + R_PAREN@2560..2561 ")" + COMMA@2561..2562 "," + WHITESPACE@2562..2573 "\n " + BIND_DESTRUCT@2573..2648 + DESTRUCT_FULL@2573..2582 + NAME@2573..2582 + IDENT@2573..2582 "main_body" + WHITESPACE@2582..2583 " " + ASSIGN@2583..2584 "=" + WHITESPACE@2584..2585 " " + EXPR@2585..2648 + EXPR_IF_THEN_ELSE@2585..2648 + IF_KW@2585..2587 "if" + WHITESPACE@2587..2588 " " + EXPR@2588..2614 + EXPR_VAR@2588..2591 + NAME@2588..2591 + IDENT@2588..2591 "std" + SUFFIX_INDEX@2591..2601 + DOT@2591..2592 "." + NAME@2592..2601 + IDENT@2592..2601 "objectHas" + SUFFIX_APPLY@2601..2614 + ARGS_DESC@2601..2614 + L_PAREN@2601..2602 "(" + ARG@2602..2605 + EXPR@2602..2605 + EXPR_VAR@2602..2605 + NAME@2602..2605 + IDENT@2602..2605 "ini" + COMMA@2605..2606 "," + WHITESPACE@2606..2607 " " + ARG@2607..2613 + EXPR@2607..2613 + EXPR_STRING@2607..2613 + STRING_SINGLE@2607..2613 "'main'" + R_PAREN@2613..2614 ")" + WHITESPACE@2614..2615 " " + THEN_KW@2615..2619 "then" + WHITESPACE@2619..2620 " " + TRUE_EXPR@2620..2640 + EXPR@2620..2640 + EXPR_VAR@2620..2630 + NAME@2620..2630 + IDENT@2620..2630 "body_lines" + SUFFIX_APPLY@2630..2640 + ARGS_DESC@2630..2640 + L_PAREN@2630..2631 "(" + ARG@2631..2639 + EXPR@2631..2639 + EXPR_VAR@2631..2634 + NAME@2631..2634 + IDENT@2631..2634 "ini" + SUFFIX_INDEX@2634..2639 + DOT@2634..2635 "." + NAME@2635..2639 + IDENT@2635..2639 "main" + R_PAREN@2639..2640 ")" + WHITESPACE@2640..2641 " " + ELSE_KW@2641..2645 "else" + WHITESPACE@2645..2646 " " + FALSE_EXPR@2646..2648 + EXPR@2646..2648 + EXPR_ARRAY@2646..2648 + L_BRACK@2646..2647 "[" + R_BRACK@2647..2648 "]" + COMMA@2648..2649 "," + WHITESPACE@2649..2660 "\n " + BIND_DESTRUCT@2660..2768 + DESTRUCT_FULL@2660..2672 + NAME@2660..2672 + IDENT@2660..2672 "all_sections" + WHITESPACE@2672..2673 " " + ASSIGN@2673..2674 "=" + WHITESPACE@2674..2675 " " + EXPR@2675..2768 + EXPR_ARRAY_COMP@2675..2768 + L_BRACK@2675..2676 "[" + WHITESPACE@2676..2683 "\n " + EXPR@2683..2716 + EXPR_VAR@2683..2696 + NAME@2683..2696 + IDENT@2683..2696 "section_lines" + SUFFIX_APPLY@2696..2716 + ARGS_DESC@2696..2716 + L_PAREN@2696..2697 "(" + ARG@2697..2698 + EXPR@2697..2698 + EXPR_VAR@2697..2698 + NAME@2697..2698 + IDENT@2697..2698 "k" + COMMA@2698..2699 "," + WHITESPACE@2699..2700 " " + ARG@2700..2715 + EXPR@2700..2715 + EXPR_VAR@2700..2703 + NAME@2700..2703 + IDENT@2700..2703 "ini" + SUFFIX_INDEX@2703..2712 + DOT@2703..2704 "." + NAME@2704..2712 + IDENT@2704..2712 "sections" + SUFFIX_INDEX_EXPR@2712..2715 + L_BRACK@2712..2713 "[" + EXPR@2713..2714 + EXPR_VAR@2713..2714 + NAME@2713..2714 + IDENT@2713..2714 "k" + R_BRACK@2714..2715 "]" + R_PAREN@2715..2716 ")" + WHITESPACE@2716..2723 "\n " + FOR_SPEC@2723..2762 + FOR_KW@2723..2726 "for" + WHITESPACE@2726..2727 " " + DESTRUCT_FULL@2727..2728 + NAME@2727..2728 + IDENT@2727..2728 "k" + WHITESPACE@2728..2729 " " + IN_KW@2729..2731 "in" + WHITESPACE@2731..2732 " " + EXPR@2732..2762 + EXPR_VAR@2732..2735 + NAME@2732..2735 + IDENT@2732..2735 "std" + SUFFIX_INDEX@2735..2748 + DOT@2735..2736 "." + NAME@2736..2748 + IDENT@2736..2748 "objectFields" + SUFFIX_APPLY@2748..2762 + ARGS_DESC@2748..2762 + L_PAREN@2748..2749 "(" + ARG@2749..2761 + EXPR@2749..2761 + EXPR_VAR@2749..2752 + NAME@2749..2752 + IDENT@2749..2752 "ini" + SUFFIX_INDEX@2752..2761 + DOT@2752..2753 "." + NAME@2753..2761 + IDENT@2753..2761 "sections" + R_PAREN@2761..2762 ")" + WHITESPACE@2762..2767 "\n " + R_BRACK@2767..2768 "]" + SEMI@2768..2769 ";" + WHITESPACE@2769..2774 "\n " + EXPR_VAR@2774..2777 + NAME@2774..2777 + IDENT@2774..2777 "std" + SUFFIX_INDEX@2777..2782 + DOT@2777..2778 "." + NAME@2778..2782 + IDENT@2778..2782 "join" + SUFFIX_APPLY@2782..2840 + ARGS_DESC@2782..2840 + L_PAREN@2782..2783 "(" + ARG@2783..2787 + EXPR@2783..2787 + EXPR_STRING@2783..2787 + STRING_SINGLE@2783..2787 "'\\n'" + COMMA@2787..2788 "," + WHITESPACE@2788..2789 " " + ARG@2789..2839 + EXPR@2789..2839 + EXPR_BINARY@2789..2839 + EXPR@2789..2832 + EXPR_BINARY@2789..2832 + EXPR@2789..2798 + EXPR_VAR@2789..2798 + NAME@2789..2798 + IDENT@2789..2798 "main_body" + WHITESPACE@2798..2799 " " + PLUS@2799..2800 "+" + WHITESPACE@2800..2801 " " + EXPR@2801..2832 + EXPR_VAR@2801..2804 + NAME@2801..2804 + IDENT@2801..2804 "std" + SUFFIX_INDEX@2804..2818 + DOT@2804..2805 "." + NAME@2805..2818 + IDENT@2805..2818 "flattenArrays" + SUFFIX_APPLY@2818..2832 + ARGS_DESC@2818..2832 + L_PAREN@2818..2819 "(" + ARG@2819..2831 + EXPR@2819..2831 + EXPR_VAR@2819..2831 + NAME@2819..2831 + IDENT@2819..2831 "all_sections" + R_PAREN@2831..2832 ")" + WHITESPACE@2832..2833 " " + PLUS@2833..2834 "+" + WHITESPACE@2834..2835 " " + EXPR@2835..2839 + EXPR_ARRAY@2835..2839 + L_BRACK@2835..2836 "[" + EXPR@2836..2838 + EXPR_STRING@2836..2838 + STRING_SINGLE@2836..2838 "''" + R_BRACK@2838..2839 "]" + R_PAREN@2839..2840 ")" + COMMA@2840..2841 "," + WHITESPACE@2841..2845 "\n\n " + MEMBER_FIELD_METHOD@2845..2898 + FIELD_NAME_FIXED@2845..2857 + NAME@2845..2857 + IDENT@2845..2857 "manifestToml" + PARAMS_DESC@2857..2864 + L_PAREN@2857..2858 "(" + PARAM@2858..2863 + DESTRUCT_FULL@2858..2863 + NAME@2858..2863 + IDENT@2858..2863 "value" + R_PAREN@2863..2864 ")" + COLONCOLON@2864..2866 "::" + WHITESPACE@2866..2867 " " + EXPR@2867..2898 + EXPR_VAR@2867..2870 + NAME@2867..2870 + IDENT@2867..2870 "std" + SUFFIX_INDEX@2870..2885 + DOT@2870..2871 "." + NAME@2871..2885 + IDENT@2871..2885 "manifestTomlEx" + SUFFIX_APPLY@2885..2898 + ARGS_DESC@2885..2898 + L_PAREN@2885..2886 "(" + ARG@2886..2891 + EXPR@2886..2891 + EXPR_VAR@2886..2891 + NAME@2886..2891 + IDENT@2886..2891 "value" + COMMA@2891..2892 "," + WHITESPACE@2892..2893 " " + ARG@2893..2897 + EXPR@2893..2897 + EXPR_STRING@2893..2897 + STRING_SINGLE@2893..2897 "' '" + R_PAREN@2897..2898 ")" + COMMA@2898..2899 "," + WHITESPACE@2899..2903 "\n\n " + MEMBER_FIELD_METHOD@2903..2958 + FIELD_NAME_FIXED@2903..2921 + NAME@2903..2921 + IDENT@2903..2921 "escapeStringPython" + PARAMS_DESC@2921..2926 + L_PAREN@2921..2922 "(" + PARAM@2922..2925 + DESTRUCT_FULL@2922..2925 + NAME@2922..2925 + IDENT@2922..2925 "str" + R_PAREN@2925..2926 ")" + COLONCOLON@2926..2928 "::" + WHITESPACE@2928..2933 "\n " + EXPR@2933..2958 + EXPR_VAR@2933..2936 + NAME@2933..2936 + IDENT@2933..2936 "std" + SUFFIX_INDEX@2936..2953 + DOT@2936..2937 "." + NAME@2937..2953 + IDENT@2937..2953 "escapeStringJson" + SUFFIX_APPLY@2953..2958 + ARGS_DESC@2953..2958 + L_PAREN@2953..2954 "(" + ARG@2954..2957 + EXPR@2954..2957 + EXPR_VAR@2954..2957 + NAME@2954..2957 + IDENT@2954..2957 "str" + R_PAREN@2957..2958 ")" + COMMA@2958..2959 "," + WHITESPACE@2959..2963 "\n\n " + MEMBER_FIELD_METHOD@2963..3180 + FIELD_NAME_FIXED@2963..2979 + NAME@2963..2979 + IDENT@2963..2979 "escapeStringBash" + PARAMS_DESC@2979..2985 + L_PAREN@2979..2980 "(" + PARAM@2980..2984 + DESTRUCT_FULL@2980..2984 + NAME@2980..2984 + IDENT@2980..2984 "str_" + R_PAREN@2984..2985 ")" + COLONCOLON@2985..2987 "::" + WHITESPACE@2987..2992 "\n " + EXPR@2992..3180 + STMT_LOCAL@2992..3023 + LOCAL_KW@2992..2997 "local" + WHITESPACE@2997..2998 " " + BIND_DESTRUCT@2998..3022 + DESTRUCT_FULL@2998..3001 + NAME@2998..3001 + IDENT@2998..3001 "str" + WHITESPACE@3001..3002 " " + ASSIGN@3002..3003 "=" + WHITESPACE@3003..3004 " " + EXPR@3004..3022 + EXPR_VAR@3004..3007 + NAME@3004..3007 + IDENT@3004..3007 "std" + SUFFIX_INDEX@3007..3016 + DOT@3007..3008 "." + NAME@3008..3016 + IDENT@3008..3016 "toString" + SUFFIX_APPLY@3016..3022 + ARGS_DESC@3016..3022 + L_PAREN@3016..3017 "(" + ARG@3017..3021 + EXPR@3017..3021 + EXPR_VAR@3017..3021 + NAME@3017..3021 + IDENT@3017..3021 "str_" + R_PAREN@3021..3022 ")" + SEMI@3022..3023 ";" + WHITESPACE@3023..3028 "\n " + STMT_LOCAL@3028..3110 + LOCAL_KW@3028..3033 "local" + WHITESPACE@3033..3034 " " + BIND_FUNCTION@3034..3109 + NAME@3034..3039 + IDENT@3034..3039 "trans" + PARAMS_DESC@3039..3043 + L_PAREN@3039..3040 "(" + PARAM@3040..3042 + DESTRUCT_FULL@3040..3042 + NAME@3040..3042 + IDENT@3040..3042 "ch" + R_PAREN@3042..3043 ")" + WHITESPACE@3043..3044 " " + ASSIGN@3044..3045 "=" + WHITESPACE@3045..3052 "\n " + EXPR@3052..3109 + EXPR_IF_THEN_ELSE@3052..3109 + IF_KW@3052..3054 "if" + WHITESPACE@3054..3055 " " + EXPR@3055..3064 + EXPR_BINARY@3055..3064 + EXPR@3055..3057 + EXPR_VAR@3055..3057 + NAME@3055..3057 + IDENT@3055..3057 "ch" + WHITESPACE@3057..3058 " " + EQ@3058..3060 "==" + WHITESPACE@3060..3061 " " + EXPR@3061..3064 + EXPR_STRING@3061..3064 + STRING_DOUBLE@3061..3064 "\"'\"" + WHITESPACE@3064..3065 " " + THEN_KW@3065..3069 "then" + WHITESPACE@3069..3078 "\n " + TRUE_EXPR@3078..3087 + EXPR@3078..3087 + EXPR_STRING@3078..3087 + STRING_DOUBLE@3078..3087 "\"'\\\"'\\\"'\"" + WHITESPACE@3087..3094 "\n " + ELSE_KW@3094..3098 "else" + WHITESPACE@3098..3107 "\n " + FALSE_EXPR@3107..3109 + EXPR@3107..3109 + EXPR_VAR@3107..3109 + NAME@3107..3109 + IDENT@3107..3109 "ch" + SEMI@3109..3110 ";" + WHITESPACE@3110..3115 "\n " + EXPR_BINARY@3115..3180 + EXPR@3115..3121 + EXPR_STRING@3115..3121 + STRING_DOUBLE@3115..3121 "\"'%s'\"" + WHITESPACE@3121..3122 " " + MODULO@3122..3123 "%" + WHITESPACE@3123..3124 " " + EXPR@3124..3180 + EXPR_VAR@3124..3127 + NAME@3124..3127 + IDENT@3124..3127 "std" + SUFFIX_INDEX@3127..3132 + DOT@3127..3128 "." + NAME@3128..3132 + IDENT@3128..3132 "join" + SUFFIX_APPLY@3132..3180 + ARGS_DESC@3132..3180 + L_PAREN@3132..3133 "(" + ARG@3133..3135 + EXPR@3133..3135 + EXPR_STRING@3133..3135 + STRING_SINGLE@3133..3135 "''" + COMMA@3135..3136 "," + WHITESPACE@3136..3137 " " + ARG@3137..3179 + EXPR@3137..3179 + EXPR_ARRAY_COMP@3137..3179 + L_BRACK@3137..3138 "[" + EXPR@3138..3147 + EXPR_VAR@3138..3143 + NAME@3138..3143 + IDENT@3138..3143 "trans" + SUFFIX_APPLY@3143..3147 + ARGS_DESC@3143..3147 + L_PAREN@3143..3144 "(" + ARG@3144..3146 + EXPR@3144..3146 + EXPR_VAR@3144..3146 + NAME@3144..3146 + IDENT@3144..3146 "ch" + R_PAREN@3146..3147 ")" + WHITESPACE@3147..3148 " " + FOR_SPEC@3148..3178 + FOR_KW@3148..3151 "for" + WHITESPACE@3151..3152 " " + DESTRUCT_FULL@3152..3154 + NAME@3152..3154 + IDENT@3152..3154 "ch" + WHITESPACE@3154..3155 " " + IN_KW@3155..3157 "in" + WHITESPACE@3157..3158 " " + EXPR@3158..3178 + EXPR_VAR@3158..3161 + NAME@3158..3161 + IDENT@3158..3161 "std" + SUFFIX_INDEX@3161..3173 + DOT@3161..3162 "." + NAME@3162..3173 + IDENT@3162..3173 "stringChars" + SUFFIX_APPLY@3173..3178 + ARGS_DESC@3173..3178 + L_PAREN@3173..3174 "(" + ARG@3174..3177 + EXPR@3174..3177 + EXPR_VAR@3174..3177 + NAME@3174..3177 + IDENT@3174..3177 "str" + R_PAREN@3177..3178 ")" + R_BRACK@3178..3179 "]" + R_PAREN@3179..3180 ")" + COMMA@3180..3181 "," + WHITESPACE@3181..3185 "\n\n " + MEMBER_FIELD_METHOD@3185..3399 + FIELD_NAME_FIXED@3185..3204 + NAME@3185..3204 + IDENT@3185..3204 "escapeStringDollars" + PARAMS_DESC@3204..3210 + L_PAREN@3204..3205 "(" + PARAM@3205..3209 + DESTRUCT_FULL@3205..3209 + NAME@3205..3209 + IDENT@3205..3209 "str_" + R_PAREN@3209..3210 ")" + COLONCOLON@3210..3212 "::" + WHITESPACE@3212..3217 "\n " + EXPR@3217..3399 + STMT_LOCAL@3217..3248 + LOCAL_KW@3217..3222 "local" + WHITESPACE@3222..3223 " " + BIND_DESTRUCT@3223..3247 + DESTRUCT_FULL@3223..3226 + NAME@3223..3226 + IDENT@3223..3226 "str" + WHITESPACE@3226..3227 " " + ASSIGN@3227..3228 "=" + WHITESPACE@3228..3229 " " + EXPR@3229..3247 + EXPR_VAR@3229..3232 + NAME@3229..3232 + IDENT@3229..3232 "std" + SUFFIX_INDEX@3232..3241 + DOT@3232..3233 "." + NAME@3233..3241 + IDENT@3233..3241 "toString" + SUFFIX_APPLY@3241..3247 + ARGS_DESC@3241..3247 + L_PAREN@3241..3242 "(" + ARG@3242..3246 + EXPR@3242..3246 + EXPR_VAR@3242..3246 + NAME@3242..3246 + IDENT@3242..3246 "str_" + R_PAREN@3246..3247 ")" + SEMI@3247..3248 ";" + WHITESPACE@3248..3253 "\n " + STMT_LOCAL@3253..3330 + LOCAL_KW@3253..3258 "local" + WHITESPACE@3258..3259 " " + BIND_FUNCTION@3259..3329 + NAME@3259..3264 + IDENT@3259..3264 "trans" + PARAMS_DESC@3264..3268 + L_PAREN@3264..3265 "(" + PARAM@3265..3267 + DESTRUCT_FULL@3265..3267 + NAME@3265..3267 + IDENT@3265..3267 "ch" + R_PAREN@3267..3268 ")" + WHITESPACE@3268..3269 " " + ASSIGN@3269..3270 "=" + WHITESPACE@3270..3277 "\n " + EXPR@3277..3329 + EXPR_IF_THEN_ELSE@3277..3329 + IF_KW@3277..3279 "if" + WHITESPACE@3279..3280 " " + EXPR@3280..3289 + EXPR_BINARY@3280..3289 + EXPR@3280..3282 + EXPR_VAR@3280..3282 + NAME@3280..3282 + IDENT@3280..3282 "ch" + WHITESPACE@3282..3283 " " + EQ@3283..3285 "==" + WHITESPACE@3285..3286 " " + EXPR@3286..3289 + EXPR_STRING@3286..3289 + STRING_SINGLE@3286..3289 "'$'" + WHITESPACE@3289..3290 " " + THEN_KW@3290..3294 "then" + WHITESPACE@3294..3303 "\n " + TRUE_EXPR@3303..3307 + EXPR@3303..3307 + EXPR_STRING@3303..3307 + STRING_SINGLE@3303..3307 "'$$'" + WHITESPACE@3307..3314 "\n " + ELSE_KW@3314..3318 "else" + WHITESPACE@3318..3327 "\n " + FALSE_EXPR@3327..3329 + EXPR@3327..3329 + EXPR_VAR@3327..3329 + NAME@3327..3329 + IDENT@3327..3329 "ch" + SEMI@3329..3330 ";" + WHITESPACE@3330..3335 "\n " + EXPR_VAR@3335..3338 + NAME@3335..3338 + IDENT@3335..3338 "std" + SUFFIX_INDEX@3338..3344 + DOT@3338..3339 "." + NAME@3339..3344 + IDENT@3339..3344 "foldl" + SUFFIX_APPLY@3344..3399 + ARGS_DESC@3344..3399 + L_PAREN@3344..3345 "(" + ARG@3345..3372 + EXPR@3345..3372 + EXPR_FUNCTION@3345..3372 + FUNCTION_KW@3345..3353 "function" + PARAMS_DESC@3353..3359 + L_PAREN@3353..3354 "(" + PARAM@3354..3355 + DESTRUCT_FULL@3354..3355 + NAME@3354..3355 + IDENT@3354..3355 "a" + COMMA@3355..3356 "," + WHITESPACE@3356..3357 " " + PARAM@3357..3358 + DESTRUCT_FULL@3357..3358 + NAME@3357..3358 + IDENT@3357..3358 "b" + R_PAREN@3358..3359 ")" + WHITESPACE@3359..3360 " " + EXPR@3360..3372 + EXPR_BINARY@3360..3372 + EXPR@3360..3361 + EXPR_VAR@3360..3361 + NAME@3360..3361 + IDENT@3360..3361 "a" + WHITESPACE@3361..3362 " " + PLUS@3362..3363 "+" + WHITESPACE@3363..3364 " " + EXPR@3364..3372 + EXPR_VAR@3364..3369 + NAME@3364..3369 + IDENT@3364..3369 "trans" + SUFFIX_APPLY@3369..3372 + ARGS_DESC@3369..3372 + L_PAREN@3369..3370 "(" + ARG@3370..3371 + EXPR@3370..3371 + EXPR_VAR@3370..3371 + NAME@3370..3371 + IDENT@3370..3371 "b" + R_PAREN@3371..3372 ")" + COMMA@3372..3373 "," + WHITESPACE@3373..3374 " " + ARG@3374..3394 + EXPR@3374..3394 + EXPR_VAR@3374..3377 + NAME@3374..3377 + IDENT@3374..3377 "std" + SUFFIX_INDEX@3377..3389 + DOT@3377..3378 "." + NAME@3378..3389 + IDENT@3378..3389 "stringChars" + SUFFIX_APPLY@3389..3394 + ARGS_DESC@3389..3394 + L_PAREN@3389..3390 "(" + ARG@3390..3393 + EXPR@3390..3393 + EXPR_VAR@3390..3393 + NAME@3390..3393 + IDENT@3390..3393 "str" + R_PAREN@3393..3394 ")" + COMMA@3394..3395 "," + WHITESPACE@3395..3396 " " + ARG@3396..3398 + EXPR@3396..3398 + EXPR_STRING@3396..3398 + STRING_SINGLE@3396..3398 "''" + R_PAREN@3398..3399 ")" + COMMA@3399..3400 "," + WHITESPACE@3400..3404 "\n\n " + MEMBER_BIND_STMT@3404..3519 + OBJ_LOCAL@3404..3519 + LOCAL_KW@3404..3409 "local" + WHITESPACE@3409..3410 " " + BIND_DESTRUCT@3410..3519 + DESTRUCT_FULL@3410..3421 + NAME@3410..3421 + IDENT@3410..3421 "xml_escapes" + WHITESPACE@3421..3422 " " + ASSIGN@3422..3423 "=" + WHITESPACE@3423..3424 " " + EXPR@3424..3519 + EXPR_OBJECT@3424..3519 + OBJ_BODY_MEMBER_LIST@3424..3519 + L_BRACE@3424..3425 "{" + WHITESPACE@3425..3430 "\n " + MEMBER_FIELD_NORMAL@3430..3441 + FIELD_NAME_FIXED@3430..3433 + STRING_SINGLE@3430..3433 "'<'" + COLON@3433..3434 ":" + WHITESPACE@3434..3435 " " + EXPR@3435..3441 + EXPR_STRING@3435..3441 + STRING_SINGLE@3435..3441 "'<'" + COMMA@3441..3442 "," + WHITESPACE@3442..3447 "\n " + MEMBER_FIELD_NORMAL@3447..3458 + FIELD_NAME_FIXED@3447..3450 + STRING_SINGLE@3447..3450 "'>'" + COLON@3450..3451 ":" + WHITESPACE@3451..3452 " " + EXPR@3452..3458 + EXPR_STRING@3452..3458 + STRING_SINGLE@3452..3458 "'>'" + COMMA@3458..3459 "," + WHITESPACE@3459..3464 "\n " + MEMBER_FIELD_NORMAL@3464..3476 + FIELD_NAME_FIXED@3464..3467 + STRING_SINGLE@3464..3467 "'&'" + COLON@3467..3468 ":" + WHITESPACE@3468..3469 " " + EXPR@3469..3476 + EXPR_STRING@3469..3476 + STRING_SINGLE@3469..3476 "'&'" + COMMA@3476..3477 "," + WHITESPACE@3477..3482 "\n " + MEMBER_FIELD_NORMAL@3482..3495 + FIELD_NAME_FIXED@3482..3485 + STRING_SINGLE@3482..3485 "'\"'" + COLON@3485..3486 ":" + WHITESPACE@3486..3487 " " + EXPR@3487..3495 + EXPR_STRING@3487..3495 + STRING_SINGLE@3487..3495 "'"'" + COMMA@3495..3496 "," + WHITESPACE@3496..3501 "\n " + MEMBER_FIELD_NORMAL@3501..3514 + FIELD_NAME_FIXED@3501..3504 + STRING_DOUBLE@3501..3504 "\"'\"" + COLON@3504..3505 ":" + WHITESPACE@3505..3506 " " + EXPR@3506..3514 + EXPR_STRING@3506..3514 + STRING_SINGLE@3506..3514 "'''" + COMMA@3514..3515 "," + WHITESPACE@3515..3518 "\n " + R_BRACE@3518..3519 "}" + COMMA@3519..3520 "," + WHITESPACE@3520..3524 "\n\n " + MEMBER_FIELD_METHOD@3524..3663 + FIELD_NAME_FIXED@3524..3539 + NAME@3524..3539 + IDENT@3524..3539 "escapeStringXML" + PARAMS_DESC@3539..3545 + L_PAREN@3539..3540 "(" + PARAM@3540..3544 + DESTRUCT_FULL@3540..3544 + NAME@3540..3544 + IDENT@3540..3544 "str_" + R_PAREN@3544..3545 ")" + COLONCOLON@3545..3547 "::" + WHITESPACE@3547..3552 "\n " + EXPR@3552..3663 + STMT_LOCAL@3552..3583 + LOCAL_KW@3552..3557 "local" + WHITESPACE@3557..3558 " " + BIND_DESTRUCT@3558..3582 + DESTRUCT_FULL@3558..3561 + NAME@3558..3561 + IDENT@3558..3561 "str" + WHITESPACE@3561..3562 " " + ASSIGN@3562..3563 "=" + WHITESPACE@3563..3564 " " + EXPR@3564..3582 + EXPR_VAR@3564..3567 + NAME@3564..3567 + IDENT@3564..3567 "std" + SUFFIX_INDEX@3567..3576 + DOT@3567..3568 "." + NAME@3568..3576 + IDENT@3568..3576 "toString" + SUFFIX_APPLY@3576..3582 + ARGS_DESC@3576..3582 + L_PAREN@3576..3577 "(" + ARG@3577..3581 + EXPR@3577..3581 + EXPR_VAR@3577..3581 + NAME@3577..3581 + IDENT@3577..3581 "str_" + R_PAREN@3581..3582 ")" + SEMI@3582..3583 ";" + WHITESPACE@3583..3588 "\n " + EXPR_VAR@3588..3591 + NAME@3588..3591 + IDENT@3588..3591 "std" + SUFFIX_INDEX@3591..3596 + DOT@3591..3592 "." + NAME@3592..3596 + IDENT@3592..3596 "join" + SUFFIX_APPLY@3596..3663 + ARGS_DESC@3596..3663 + L_PAREN@3596..3597 "(" + ARG@3597..3599 + EXPR@3597..3599 + EXPR_STRING@3597..3599 + STRING_SINGLE@3597..3599 "''" + COMMA@3599..3600 "," + WHITESPACE@3600..3601 " " + ARG@3601..3662 + EXPR@3601..3662 + EXPR_ARRAY_COMP@3601..3662 + L_BRACK@3601..3602 "[" + EXPR@3602..3630 + EXPR_VAR@3602..3605 + NAME@3602..3605 + IDENT@3602..3605 "std" + SUFFIX_INDEX@3605..3609 + DOT@3605..3606 "." + NAME@3606..3609 + IDENT@3606..3609 "get" + SUFFIX_APPLY@3609..3630 + ARGS_DESC@3609..3630 + L_PAREN@3609..3610 "(" + ARG@3610..3621 + EXPR@3610..3621 + EXPR_VAR@3610..3621 + NAME@3610..3621 + IDENT@3610..3621 "xml_escapes" + COMMA@3621..3622 "," + WHITESPACE@3622..3623 " " + ARG@3623..3625 + EXPR@3623..3625 + EXPR_VAR@3623..3625 + NAME@3623..3625 + IDENT@3623..3625 "ch" + COMMA@3625..3626 "," + WHITESPACE@3626..3627 " " + ARG@3627..3629 + EXPR@3627..3629 + EXPR_VAR@3627..3629 + NAME@3627..3629 + IDENT@3627..3629 "ch" + R_PAREN@3629..3630 ")" + WHITESPACE@3630..3631 " " + FOR_SPEC@3631..3661 + FOR_KW@3631..3634 "for" + WHITESPACE@3634..3635 " " + DESTRUCT_FULL@3635..3637 + NAME@3635..3637 + IDENT@3635..3637 "ch" + WHITESPACE@3637..3638 " " + IN_KW@3638..3640 "in" + WHITESPACE@3640..3641 " " + EXPR@3641..3661 + EXPR_VAR@3641..3644 + NAME@3641..3644 + IDENT@3641..3644 "std" + SUFFIX_INDEX@3644..3656 + DOT@3644..3645 "." + NAME@3645..3656 + IDENT@3645..3656 "stringChars" + SUFFIX_APPLY@3656..3661 + ARGS_DESC@3656..3661 + L_PAREN@3656..3657 "(" + ARG@3657..3660 + EXPR@3657..3660 + EXPR_VAR@3657..3660 + NAME@3657..3660 + IDENT@3657..3660 "str" + R_PAREN@3660..3661 ")" + R_BRACK@3661..3662 "]" + R_PAREN@3662..3663 ")" + COMMA@3663..3664 "," + WHITESPACE@3664..3668 "\n\n " + MEMBER_FIELD_METHOD@3668..3734 + FIELD_NAME_FIXED@3668..3680 + NAME@3668..3680 + IDENT@3668..3680 "manifestJson" + PARAMS_DESC@3680..3687 + L_PAREN@3680..3681 "(" + PARAM@3681..3686 + DESTRUCT_FULL@3681..3686 + NAME@3681..3686 + IDENT@3681..3686 "value" + R_PAREN@3686..3687 ")" + COLONCOLON@3687..3689 "::" + WHITESPACE@3689..3690 " " + EXPR@3690..3734 + EXPR_VAR@3690..3693 + NAME@3690..3693 + IDENT@3690..3693 "std" + SUFFIX_INDEX@3693..3708 + DOT@3693..3694 "." + NAME@3694..3708 + IDENT@3694..3708 "manifestJsonEx" + SUFFIX_APPLY@3708..3734 + ARGS_DESC@3708..3734 + L_PAREN@3708..3709 "(" + ARG@3709..3714 + EXPR@3709..3714 + EXPR_VAR@3709..3714 + NAME@3709..3714 + IDENT@3709..3714 "value" + COMMA@3714..3715 "," + WHITESPACE@3715..3716 " " + ARG@3716..3722 + EXPR@3716..3722 + EXPR_STRING@3716..3722 + STRING_SINGLE@3716..3722 "' '" + R_PAREN@3722..3723 ")" + WHITESPACE@3723..3724 " " + TAILSTRICT_KW@3724..3734 "tailstrict" + COMMA@3734..3735 "," + WHITESPACE@3735..3739 "\n\n " + MEMBER_FIELD_METHOD@3739..3807 + FIELD_NAME_FIXED@3739..3759 + NAME@3739..3759 + IDENT@3739..3759 "manifestJsonMinified" + PARAMS_DESC@3759..3766 + L_PAREN@3759..3760 "(" + PARAM@3760..3765 + DESTRUCT_FULL@3760..3765 + NAME@3760..3765 + IDENT@3760..3765 "value" + R_PAREN@3765..3766 ")" + COLONCOLON@3766..3768 "::" + WHITESPACE@3768..3769 " " + EXPR@3769..3807 + EXPR_VAR@3769..3772 + NAME@3769..3772 + IDENT@3769..3772 "std" + SUFFIX_INDEX@3772..3787 + DOT@3772..3773 "." + NAME@3773..3787 + IDENT@3773..3787 "manifestJsonEx" + SUFFIX_APPLY@3787..3807 + ARGS_DESC@3787..3807 + L_PAREN@3787..3788 "(" + ARG@3788..3793 + EXPR@3788..3793 + EXPR_VAR@3788..3793 + NAME@3788..3793 + IDENT@3788..3793 "value" + COMMA@3793..3794 "," + WHITESPACE@3794..3795 " " + ARG@3795..3797 + EXPR@3795..3797 + EXPR_STRING@3795..3797 + STRING_SINGLE@3795..3797 "''" + COMMA@3797..3798 "," + WHITESPACE@3798..3799 " " + ARG@3799..3801 + EXPR@3799..3801 + EXPR_STRING@3799..3801 + STRING_SINGLE@3799..3801 "''" + COMMA@3801..3802 "," + WHITESPACE@3802..3803 " " + ARG@3803..3806 + EXPR@3803..3806 + EXPR_STRING@3803..3806 + STRING_SINGLE@3803..3806 "':'" + R_PAREN@3806..3807 ")" + COMMA@3807..3808 "," + WHITESPACE@3808..3812 "\n\n " + MEMBER_FIELD_METHOD@3812..4197 + FIELD_NAME_FIXED@3812..3830 + NAME@3812..3830 + IDENT@3812..3830 "manifestYamlStream" + PARAMS_DESC@3830..3905 + L_PAREN@3830..3831 "(" + PARAM@3831..3836 + DESTRUCT_FULL@3831..3836 + NAME@3831..3836 + IDENT@3831..3836 "value" + COMMA@3836..3837 "," + WHITESPACE@3837..3838 " " + PARAM@3838..3866 + DESTRUCT_FULL@3838..3860 + NAME@3838..3860 + IDENT@3838..3860 "indent_array_in_object" + ASSIGN@3860..3861 "=" + EXPR@3861..3866 + EXPR_LITERAL@3861..3866 + FALSE_KW@3861..3866 "false" + COMMA@3866..3867 "," + WHITESPACE@3867..3868 " " + PARAM@3868..3887 + DESTRUCT_FULL@3868..3882 + NAME@3868..3882 + IDENT@3868..3882 "c_document_end" + ASSIGN@3882..3883 "=" + EXPR@3883..3887 + EXPR_LITERAL@3883..3887 + TRUE_KW@3883..3887 "true" + COMMA@3887..3888 "," + WHITESPACE@3888..3889 " " + PARAM@3889..3904 + DESTRUCT_FULL@3889..3899 + NAME@3889..3899 + IDENT@3889..3899 "quote_keys" + ASSIGN@3899..3900 "=" + EXPR@3900..3904 + EXPR_LITERAL@3900..3904 + TRUE_KW@3900..3904 "true" + R_PAREN@3904..3905 ")" + COLONCOLON@3905..3907 "::" + WHITESPACE@3907..3912 "\n " + EXPR@3912..4197 + EXPR_IF_THEN_ELSE@3912..4197 + IF_KW@3912..3914 "if" + WHITESPACE@3914..3915 " " + EXPR@3915..3934 + EXPR_UNARY@3915..3934 + NOT@3915..3916 "!" + EXPR_VAR@3916..3919 + NAME@3916..3919 + IDENT@3916..3919 "std" + SUFFIX_INDEX@3919..3927 + DOT@3919..3920 "." + NAME@3920..3927 + IDENT@3920..3927 "isArray" + SUFFIX_APPLY@3927..3934 + ARGS_DESC@3927..3934 + L_PAREN@3927..3928 "(" + ARG@3928..3933 + EXPR@3928..3933 + EXPR_VAR@3928..3933 + NAME@3928..3933 + IDENT@3928..3933 "value" + R_PAREN@3933..3934 ")" + WHITESPACE@3934..3935 " " + THEN_KW@3935..3939 "then" + WHITESPACE@3939..3946 "\n " + TRUE_EXPR@3946..4014 + EXPR@3946..4014 + EXPR_ERROR@3946..4014 + ERROR_KW@3946..3951 "error" + WHITESPACE@3951..3952 " " + EXPR@3952..4014 + EXPR_BINARY@3952..4014 + EXPR@3952..3996 + EXPR_STRING@3952..3996 + STRING_SINGLE@3952..3996 "'manifestYamlStream o ..." + WHITESPACE@3996..3997 " " + PLUS@3997..3998 "+" + WHITESPACE@3998..3999 " " + EXPR@3999..4014 + EXPR_VAR@3999..4002 + NAME@3999..4002 + IDENT@3999..4002 "std" + SUFFIX_INDEX@4002..4007 + DOT@4002..4003 "." + NAME@4003..4007 + IDENT@4003..4007 "type" + SUFFIX_APPLY@4007..4014 + ARGS_DESC@4007..4014 + L_PAREN@4007..4008 "(" + ARG@4008..4013 + EXPR@4008..4013 + EXPR_VAR@4008..4013 + NAME@4008..4013 + IDENT@4008..4013 "value" + R_PAREN@4013..4014 ")" + WHITESPACE@4014..4019 "\n " + ELSE_KW@4019..4023 "else" + WHITESPACE@4023..4030 "\n " + FALSE_EXPR@4030..4197 + EXPR@4030..4197 + EXPR_BINARY@4030..4197 + EXPR@4030..4152 + EXPR_BINARY@4030..4152 + EXPR@4030..4037 + EXPR_STRING@4030..4037 + STRING_SINGLE@4030..4037 "'---\\n'" + WHITESPACE@4037..4038 " " + PLUS@4038..4039 "+" + WHITESPACE@4039..4040 " " + EXPR@4040..4152 + EXPR_VAR@4040..4043 + NAME@4040..4043 + IDENT@4040..4043 "std" + SUFFIX_INDEX@4043..4048 + DOT@4043..4044 "." + NAME@4044..4048 + IDENT@4044..4048 "join" + SUFFIX_APPLY@4048..4152 + ARGS_DESC@4048..4152 + L_PAREN@4048..4049 "(" + WHITESPACE@4049..4058 "\n " + ARG@4058..4067 + EXPR@4058..4067 + EXPR_STRING@4058..4067 + STRING_SINGLE@4058..4067 "'\\n---\\n'" + COMMA@4067..4068 "," + WHITESPACE@4068..4069 " " + ARG@4069..4144 + EXPR@4069..4144 + EXPR_ARRAY_COMP@4069..4144 + L_BRACK@4069..4070 "[" + EXPR@4070..4128 + EXPR_VAR@4070..4073 + NAME@4070..4073 + IDENT@4070..4073 "std" + SUFFIX_INDEX@4073..4089 + DOT@4073..4074 "." + NAME@4074..4089 + IDENT@4074..4089 "manifestYamlDoc" + SUFFIX_APPLY@4089..4128 + ARGS_DESC@4089..4128 + L_PAREN@4089..4090 "(" + ARG@4090..4091 + EXPR@4090..4091 + EXPR_VAR@4090..4091 + NAME@4090..4091 + IDENT@4090..4091 "e" + COMMA@4091..4092 "," + WHITESPACE@4092..4093 " " + ARG@4093..4115 + EXPR@4093..4115 + EXPR_VAR@4093..4115 + NAME@4093..4115 + IDENT@4093..4115 "indent_array_in_object" + COMMA@4115..4116 "," + WHITESPACE@4116..4117 " " + ARG@4117..4127 + EXPR@4117..4127 + EXPR_VAR@4117..4127 + NAME@4117..4127 + IDENT@4117..4127 "quote_keys" + R_PAREN@4127..4128 ")" + WHITESPACE@4128..4129 " " + FOR_SPEC@4129..4143 + FOR_KW@4129..4132 "for" + WHITESPACE@4132..4133 " " + DESTRUCT_FULL@4133..4134 + NAME@4133..4134 + IDENT@4133..4134 "e" + WHITESPACE@4134..4135 " " + IN_KW@4135..4137 "in" + WHITESPACE@4137..4138 " " + EXPR@4138..4143 + EXPR_VAR@4138..4143 + NAME@4138..4143 + IDENT@4138..4143 "value" + R_BRACK@4143..4144 "]" + WHITESPACE@4144..4151 "\n " + R_PAREN@4151..4152 ")" + WHITESPACE@4152..4153 " " + PLUS@4153..4154 "+" + WHITESPACE@4154..4155 " " + EXPR@4155..4197 + EXPR_IF_THEN_ELSE@4155..4197 + IF_KW@4155..4157 "if" + WHITESPACE@4157..4158 " " + EXPR@4158..4172 + EXPR_VAR@4158..4172 + NAME@4158..4172 + IDENT@4158..4172 "c_document_end" + WHITESPACE@4172..4173 " " + THEN_KW@4173..4177 "then" + WHITESPACE@4177..4178 " " + TRUE_EXPR@4178..4187 + EXPR@4178..4187 + EXPR_STRING@4178..4187 + STRING_SINGLE@4178..4187 "'\\n...\\n'" + WHITESPACE@4187..4188 " " + ELSE_KW@4188..4192 "else" + WHITESPACE@4192..4193 " " + FALSE_EXPR@4193..4197 + EXPR@4193..4197 + EXPR_STRING@4193..4197 + STRING_SINGLE@4193..4197 "'\\n'" + COMMA@4197..4198 "," + WHITESPACE@4198..4202 "\n\n " + MEMBER_FIELD_METHOD@4202..4858 + FIELD_NAME_FIXED@4202..4216 + NAME@4202..4216 + IDENT@4202..4216 "manifestPython" + PARAMS_DESC@4216..4219 + L_PAREN@4216..4217 "(" + PARAM@4217..4218 + DESTRUCT_FULL@4217..4218 + NAME@4217..4218 + IDENT@4217..4218 "v" + R_PAREN@4218..4219 ")" + COLONCOLON@4219..4221 "::" + WHITESPACE@4221..4226 "\n " + EXPR@4226..4858 + EXPR_IF_THEN_ELSE@4226..4858 + IF_KW@4226..4228 "if" + WHITESPACE@4228..4229 " " + EXPR@4229..4244 + EXPR_VAR@4229..4232 + NAME@4229..4232 + IDENT@4229..4232 "std" + SUFFIX_INDEX@4232..4241 + DOT@4232..4233 "." + NAME@4233..4241 + IDENT@4233..4241 "isObject" + SUFFIX_APPLY@4241..4244 + ARGS_DESC@4241..4244 + L_PAREN@4241..4242 "(" + ARG@4242..4243 + EXPR@4242..4243 + EXPR_VAR@4242..4243 + NAME@4242..4243 + IDENT@4242..4243 "v" + R_PAREN@4243..4244 ")" + WHITESPACE@4244..4245 " " + THEN_KW@4245..4249 "then" + WHITESPACE@4249..4256 "\n " + TRUE_EXPR@4256..4431 + EXPR@4256..4431 + STMT_LOCAL@4256..4391 + LOCAL_KW@4256..4261 "local" + WHITESPACE@4261..4262 " " + BIND_DESTRUCT@4262..4390 + DESTRUCT_FULL@4262..4268 + NAME@4262..4268 + IDENT@4262..4268 "fields" + WHITESPACE@4268..4269 " " + ASSIGN@4269..4270 "=" + WHITESPACE@4270..4271 " " + EXPR@4271..4390 + EXPR_ARRAY_COMP@4271..4390 + L_BRACK@4271..4272 "[" + WHITESPACE@4272..4281 "\n " + EXPR@4281..4345 + EXPR_BINARY@4281..4345 + EXPR@4281..4289 + EXPR_STRING@4281..4289 + STRING_SINGLE@4281..4289 "'%s: %s'" + WHITESPACE@4289..4290 " " + MODULO@4290..4291 "%" + WHITESPACE@4291..4292 " " + EXPR@4292..4345 + EXPR_ARRAY@4292..4345 + L_BRACK@4292..4293 "[" + EXPR@4293..4318 + EXPR_VAR@4293..4296 + NAME@4293..4296 + IDENT@4293..4296 "std" + SUFFIX_INDEX@4296..4315 + DOT@4296..4297 "." + NAME@4297..4315 + IDENT@4297..4315 "escapeStringPython" + SUFFIX_APPLY@4315..4318 + ARGS_DESC@4315..4318 + L_PAREN@4315..4316 "(" + ARG@4316..4317 + EXPR@4316..4317 + EXPR_VAR@4316..4317 + NAME@4316..4317 + IDENT@4316..4317 "k" + R_PAREN@4317..4318 ")" + COMMA@4318..4319 "," + WHITESPACE@4319..4320 " " + EXPR@4320..4344 + EXPR_VAR@4320..4323 + NAME@4320..4323 + IDENT@4320..4323 "std" + SUFFIX_INDEX@4323..4338 + DOT@4323..4324 "." + NAME@4324..4338 + IDENT@4324..4338 "manifestPython" + SUFFIX_APPLY@4338..4344 + ARGS_DESC@4338..4344 + L_PAREN@4338..4339 "(" + ARG@4339..4343 + EXPR@4339..4343 + EXPR_VAR@4339..4340 + NAME@4339..4340 + IDENT@4339..4340 "v" + SUFFIX_INDEX_EXPR@4340..4343 + L_BRACK@4340..4341 "[" + EXPR@4341..4342 + EXPR_VAR@4341..4342 + NAME@4341..4342 + IDENT@4341..4342 "k" + R_BRACK@4342..4343 "]" + R_PAREN@4343..4344 ")" + R_BRACK@4344..4345 "]" + WHITESPACE@4345..4354 "\n " + FOR_SPEC@4354..4382 + FOR_KW@4354..4357 "for" + WHITESPACE@4357..4358 " " + DESTRUCT_FULL@4358..4359 + NAME@4358..4359 + IDENT@4358..4359 "k" + WHITESPACE@4359..4360 " " + IN_KW@4360..4362 "in" + WHITESPACE@4362..4363 " " + EXPR@4363..4382 + EXPR_VAR@4363..4366 + NAME@4363..4366 + IDENT@4363..4366 "std" + SUFFIX_INDEX@4366..4379 + DOT@4366..4367 "." + NAME@4367..4379 + IDENT@4367..4379 "objectFields" + SUFFIX_APPLY@4379..4382 + ARGS_DESC@4379..4382 + L_PAREN@4379..4380 "(" + ARG@4380..4381 + EXPR@4380..4381 + EXPR_VAR@4380..4381 + NAME@4380..4381 + IDENT@4380..4381 "v" + R_PAREN@4381..4382 ")" + WHITESPACE@4382..4389 "\n " + R_BRACK@4389..4390 "]" + SEMI@4390..4391 ";" + WHITESPACE@4391..4398 "\n " + EXPR_BINARY@4398..4431 + EXPR@4398..4404 + EXPR_STRING@4398..4404 + STRING_SINGLE@4398..4404 "'{%s}'" + WHITESPACE@4404..4405 " " + MODULO@4405..4406 "%" + WHITESPACE@4406..4407 " " + EXPR@4407..4431 + EXPR_ARRAY@4407..4431 + L_BRACK@4407..4408 "[" + EXPR@4408..4430 + EXPR_VAR@4408..4411 + NAME@4408..4411 + IDENT@4408..4411 "std" + SUFFIX_INDEX@4411..4416 + DOT@4411..4412 "." + NAME@4412..4416 + IDENT@4412..4416 "join" + SUFFIX_APPLY@4416..4430 + ARGS_DESC@4416..4430 + L_PAREN@4416..4417 "(" + ARG@4417..4421 + EXPR@4417..4421 + EXPR_STRING@4417..4421 + STRING_SINGLE@4417..4421 "', '" + COMMA@4421..4422 "," + WHITESPACE@4422..4423 " " + ARG@4423..4429 + EXPR@4423..4429 + EXPR_VAR@4423..4429 + NAME@4423..4429 + IDENT@4423..4429 "fields" + R_PAREN@4429..4430 ")" + R_BRACK@4430..4431 "]" + WHITESPACE@4431..4436 "\n " + ELSE_KW@4436..4440 "else" + WHITESPACE@4440..4441 " " + FALSE_EXPR@4441..4858 + EXPR@4441..4858 + EXPR_IF_THEN_ELSE@4441..4858 + IF_KW@4441..4443 "if" + WHITESPACE@4443..4444 " " + EXPR@4444..4458 + EXPR_VAR@4444..4447 + NAME@4444..4447 + IDENT@4444..4447 "std" + SUFFIX_INDEX@4447..4455 + DOT@4447..4448 "." + NAME@4448..4455 + IDENT@4448..4455 "isArray" + SUFFIX_APPLY@4455..4458 + ARGS_DESC@4455..4458 + L_PAREN@4455..4456 "(" + ARG@4456..4457 + EXPR@4456..4457 + EXPR_VAR@4456..4457 + NAME@4456..4457 + IDENT@4456..4457 "v" + R_PAREN@4457..4458 ")" + WHITESPACE@4458..4459 " " + THEN_KW@4459..4463 "then" + WHITESPACE@4463..4470 "\n " + TRUE_EXPR@4470..4533 + EXPR@4470..4533 + EXPR_BINARY@4470..4533 + EXPR@4470..4476 + EXPR_STRING@4470..4476 + STRING_SINGLE@4470..4476 "'[%s]'" + WHITESPACE@4476..4477 " " + MODULO@4477..4478 "%" + WHITESPACE@4478..4479 " " + EXPR@4479..4533 + EXPR_ARRAY@4479..4533 + L_BRACK@4479..4480 "[" + EXPR@4480..4532 + EXPR_VAR@4480..4483 + NAME@4480..4483 + IDENT@4480..4483 "std" + SUFFIX_INDEX@4483..4488 + DOT@4483..4484 "." + NAME@4484..4488 + IDENT@4484..4488 "join" + SUFFIX_APPLY@4488..4532 + ARGS_DESC@4488..4532 + L_PAREN@4488..4489 "(" + ARG@4489..4493 + EXPR@4489..4493 + EXPR_STRING@4489..4493 + STRING_SINGLE@4489..4493 "', '" + COMMA@4493..4494 "," + WHITESPACE@4494..4495 " " + ARG@4495..4531 + EXPR@4495..4531 + EXPR_ARRAY_COMP@4495..4531 + L_BRACK@4495..4496 "[" + EXPR@4496..4518 + EXPR_VAR@4496..4499 + NAME@4496..4499 + IDENT@4496..4499 "std" + SUFFIX_INDEX@4499..4514 + DOT@4499..4500 "." + NAME@4500..4514 + IDENT@4500..4514 "manifestPython" + SUFFIX_APPLY@4514..4518 + ARGS_DESC@4514..4518 + L_PAREN@4514..4515 "(" + ARG@4515..4517 + EXPR@4515..4517 + EXPR_VAR@4515..4517 + NAME@4515..4517 + IDENT@4515..4517 "v2" + R_PAREN@4517..4518 ")" + WHITESPACE@4518..4519 " " + FOR_SPEC@4519..4530 + FOR_KW@4519..4522 "for" + WHITESPACE@4522..4523 " " + DESTRUCT_FULL@4523..4525 + NAME@4523..4525 + IDENT@4523..4525 "v2" + WHITESPACE@4525..4526 " " + IN_KW@4526..4528 "in" + WHITESPACE@4528..4529 " " + EXPR@4529..4530 + EXPR_VAR@4529..4530 + NAME@4529..4530 + IDENT@4529..4530 "v" + R_BRACK@4530..4531 "]" + R_PAREN@4531..4532 ")" + R_BRACK@4532..4533 "]" + WHITESPACE@4533..4538 "\n " + ELSE_KW@4538..4542 "else" + WHITESPACE@4542..4543 " " + FALSE_EXPR@4543..4858 + EXPR@4543..4858 + EXPR_IF_THEN_ELSE@4543..4858 + IF_KW@4543..4545 "if" + WHITESPACE@4545..4546 " " + EXPR@4546..4561 + EXPR_VAR@4546..4549 + NAME@4546..4549 + IDENT@4546..4549 "std" + SUFFIX_INDEX@4549..4558 + DOT@4549..4550 "." + NAME@4550..4558 + IDENT@4550..4558 "isString" + SUFFIX_APPLY@4558..4561 + ARGS_DESC@4558..4561 + L_PAREN@4558..4559 "(" + ARG@4559..4560 + EXPR@4559..4560 + EXPR_VAR@4559..4560 + NAME@4559..4560 + IDENT@4559..4560 "v" + R_PAREN@4560..4561 ")" + WHITESPACE@4561..4562 " " + THEN_KW@4562..4566 "then" + WHITESPACE@4566..4573 "\n " + TRUE_EXPR@4573..4607 + EXPR@4573..4607 + EXPR_BINARY@4573..4607 + EXPR@4573..4577 + EXPR_STRING@4573..4577 + STRING_SINGLE@4573..4577 "'%s'" + WHITESPACE@4577..4578 " " + MODULO@4578..4579 "%" + WHITESPACE@4579..4580 " " + EXPR@4580..4607 + EXPR_ARRAY@4580..4607 + L_BRACK@4580..4581 "[" + EXPR@4581..4606 + EXPR_VAR@4581..4584 + NAME@4581..4584 + IDENT@4581..4584 "std" + SUFFIX_INDEX@4584..4603 + DOT@4584..4585 "." + NAME@4585..4603 + IDENT@4585..4603 "escapeStringPython" + SUFFIX_APPLY@4603..4606 + ARGS_DESC@4603..4606 + L_PAREN@4603..4604 "(" + ARG@4604..4605 + EXPR@4604..4605 + EXPR_VAR@4604..4605 + NAME@4604..4605 + IDENT@4604..4605 "v" + R_PAREN@4605..4606 ")" + R_BRACK@4606..4607 "]" + WHITESPACE@4607..4612 "\n " + ELSE_KW@4612..4616 "else" + WHITESPACE@4616..4617 " " + FALSE_EXPR@4617..4858 + EXPR@4617..4858 + EXPR_IF_THEN_ELSE@4617..4858 + IF_KW@4617..4619 "if" + WHITESPACE@4619..4620 " " + EXPR@4620..4637 + EXPR_VAR@4620..4623 + NAME@4620..4623 + IDENT@4620..4623 "std" + SUFFIX_INDEX@4623..4634 + DOT@4623..4624 "." + NAME@4624..4634 + IDENT@4624..4634 "isFunction" + SUFFIX_APPLY@4634..4637 + ARGS_DESC@4634..4637 + L_PAREN@4634..4635 "(" + ARG@4635..4636 + EXPR@4635..4636 + EXPR_VAR@4635..4636 + NAME@4635..4636 + IDENT@4635..4636 "v" + R_PAREN@4636..4637 ")" + WHITESPACE@4637..4638 " " + THEN_KW@4638..4642 "then" + WHITESPACE@4642..4649 "\n " + TRUE_EXPR@4649..4681 + EXPR@4649..4681 + EXPR_ERROR@4649..4681 + ERROR_KW@4649..4654 "error" + WHITESPACE@4654..4655 " " + EXPR@4655..4681 + EXPR_STRING@4655..4681 + STRING_SINGLE@4655..4681 "'cannot manifest func ..." + WHITESPACE@4681..4686 "\n " + ELSE_KW@4686..4690 "else" + WHITESPACE@4690..4691 " " + FALSE_EXPR@4691..4858 + EXPR@4691..4858 + EXPR_IF_THEN_ELSE@4691..4858 + IF_KW@4691..4693 "if" + WHITESPACE@4693..4694 " " + EXPR@4694..4709 + EXPR_VAR@4694..4697 + NAME@4694..4697 + IDENT@4694..4697 "std" + SUFFIX_INDEX@4697..4706 + DOT@4697..4698 "." + NAME@4698..4706 + IDENT@4698..4706 "isNumber" + SUFFIX_APPLY@4706..4709 + ARGS_DESC@4706..4709 + L_PAREN@4706..4707 "(" + ARG@4707..4708 + EXPR@4707..4708 + EXPR_VAR@4707..4708 + NAME@4707..4708 + IDENT@4707..4708 "v" + R_PAREN@4708..4709 ")" + WHITESPACE@4709..4710 " " + THEN_KW@4710..4714 "then" + WHITESPACE@4714..4721 "\n " + TRUE_EXPR@4721..4736 + EXPR@4721..4736 + EXPR_VAR@4721..4724 + NAME@4721..4724 + IDENT@4721..4724 "std" + SUFFIX_INDEX@4724..4733 + DOT@4724..4725 "." + NAME@4725..4733 + IDENT@4725..4733 "toString" + SUFFIX_APPLY@4733..4736 + ARGS_DESC@4733..4736 + L_PAREN@4733..4734 "(" + ARG@4734..4735 + EXPR@4734..4735 + EXPR_VAR@4734..4735 + NAME@4734..4735 + IDENT@4734..4735 "v" + R_PAREN@4735..4736 ")" + WHITESPACE@4736..4741 "\n " + ELSE_KW@4741..4745 "else" + WHITESPACE@4745..4746 " " + FALSE_EXPR@4746..4858 + EXPR@4746..4858 + EXPR_IF_THEN_ELSE@4746..4858 + IF_KW@4746..4748 "if" + WHITESPACE@4748..4749 " " + EXPR@4749..4758 + EXPR_BINARY@4749..4758 + EXPR@4749..4750 + EXPR_VAR@4749..4750 + NAME@4749..4750 + IDENT@4749..4750 "v" + WHITESPACE@4750..4751 " " + EQ@4751..4753 "==" + WHITESPACE@4753..4754 " " + EXPR@4754..4758 + EXPR_LITERAL@4754..4758 + TRUE_KW@4754..4758 "true" + WHITESPACE@4758..4759 " " + THEN_KW@4759..4763 "then" + WHITESPACE@4763..4770 "\n " + TRUE_EXPR@4770..4776 + EXPR@4770..4776 + EXPR_STRING@4770..4776 + STRING_SINGLE@4770..4776 "'True'" + WHITESPACE@4776..4781 "\n " + ELSE_KW@4781..4785 "else" + WHITESPACE@4785..4786 " " + FALSE_EXPR@4786..4858 + EXPR@4786..4858 + EXPR_IF_THEN_ELSE@4786..4858 + IF_KW@4786..4788 "if" + WHITESPACE@4788..4789 " " + EXPR@4789..4799 + EXPR_BINARY@4789..4799 + EXPR@4789..4790 + EXPR_VAR@4789..4790 + NAME@4789..4790 + IDENT@4789..4790 "v" + WHITESPACE@4790..4791 " " + EQ@4791..4793 "==" + WHITESPACE@4793..4794 " " + EXPR@4794..4799 + EXPR_LITERAL@4794..4799 + FALSE_KW@4794..4799 "false" + WHITESPACE@4799..4800 " " + THEN_KW@4800..4804 "then" + WHITESPACE@4804..4811 "\n " + TRUE_EXPR@4811..4818 + EXPR@4811..4818 + EXPR_STRING@4811..4818 + STRING_SINGLE@4811..4818 "'False'" + WHITESPACE@4818..4823 "\n " + ELSE_KW@4823..4827 "else" + WHITESPACE@4827..4828 " " + FALSE_EXPR@4828..4858 + EXPR@4828..4858 + EXPR_IF_THEN_ELSE@4828..4858 + IF_KW@4828..4830 "if" + WHITESPACE@4830..4831 " " + EXPR@4831..4840 + EXPR_BINARY@4831..4840 + EXPR@4831..4832 + EXPR_VAR@4831..4832 + NAME@4831..4832 + IDENT@4831..4832 "v" + WHITESPACE@4832..4833 " " + EQ@4833..4835 "==" + WHITESPACE@4835..4836 " " + EXPR@4836..4840 + EXPR_LITERAL@4836..4840 + NULL_KW@4836..4840 "null" + WHITESPACE@4840..4841 " " + THEN_KW@4841..4845 "then" + WHITESPACE@4845..4852 "\n " + TRUE_EXPR@4852..4858 + EXPR@4852..4858 + EXPR_STRING@4852..4858 + STRING_SINGLE@4852..4858 "'None'" + COMMA@4858..4859 "," + WHITESPACE@4859..4863 "\n\n " + MEMBER_FIELD_METHOD@4863..5018 + FIELD_NAME_FIXED@4863..4881 + NAME@4863..4881 + IDENT@4863..4881 "manifestPythonVars" + PARAMS_DESC@4881..4887 + L_PAREN@4881..4882 "(" + PARAM@4882..4886 + DESTRUCT_FULL@4882..4886 + NAME@4882..4886 + IDENT@4882..4886 "conf" + R_PAREN@4886..4887 ")" + COLONCOLON@4887..4889 "::" + WHITESPACE@4889..4894 "\n " + EXPR@4894..5018 + STMT_LOCAL@4894..4986 + LOCAL_KW@4894..4899 "local" + WHITESPACE@4899..4900 " " + BIND_DESTRUCT@4900..4985 + DESTRUCT_FULL@4900..4904 + NAME@4900..4904 + IDENT@4900..4904 "vars" + WHITESPACE@4904..4905 " " + ASSIGN@4905..4906 "=" + WHITESPACE@4906..4907 " " + EXPR@4907..4985 + EXPR_ARRAY_COMP@4907..4985 + L_BRACK@4907..4908 "[" + EXPR@4908..4952 + EXPR_BINARY@4908..4952 + EXPR@4908..4917 + EXPR_STRING@4908..4917 + STRING_SINGLE@4908..4917 "'%s = %s'" + WHITESPACE@4917..4918 " " + MODULO@4918..4919 "%" + WHITESPACE@4919..4920 " " + EXPR@4920..4952 + EXPR_ARRAY@4920..4952 + L_BRACK@4920..4921 "[" + EXPR@4921..4922 + EXPR_VAR@4921..4922 + NAME@4921..4922 + IDENT@4921..4922 "k" + COMMA@4922..4923 "," + WHITESPACE@4923..4924 " " + EXPR@4924..4951 + EXPR_VAR@4924..4927 + NAME@4924..4927 + IDENT@4924..4927 "std" + SUFFIX_INDEX@4927..4942 + DOT@4927..4928 "." + NAME@4928..4942 + IDENT@4928..4942 "manifestPython" + SUFFIX_APPLY@4942..4951 + ARGS_DESC@4942..4951 + L_PAREN@4942..4943 "(" + ARG@4943..4950 + EXPR@4943..4950 + EXPR_VAR@4943..4947 + NAME@4943..4947 + IDENT@4943..4947 "conf" + SUFFIX_INDEX_EXPR@4947..4950 + L_BRACK@4947..4948 "[" + EXPR@4948..4949 + EXPR_VAR@4948..4949 + NAME@4948..4949 + IDENT@4948..4949 "k" + R_BRACK@4949..4950 "]" + R_PAREN@4950..4951 ")" + R_BRACK@4951..4952 "]" + WHITESPACE@4952..4953 " " + FOR_SPEC@4953..4984 + FOR_KW@4953..4956 "for" + WHITESPACE@4956..4957 " " + DESTRUCT_FULL@4957..4958 + NAME@4957..4958 + IDENT@4957..4958 "k" + WHITESPACE@4958..4959 " " + IN_KW@4959..4961 "in" + WHITESPACE@4961..4962 " " + EXPR@4962..4984 + EXPR_VAR@4962..4965 + NAME@4962..4965 + IDENT@4962..4965 "std" + SUFFIX_INDEX@4965..4978 + DOT@4965..4966 "." + NAME@4966..4978 + IDENT@4966..4978 "objectFields" + SUFFIX_APPLY@4978..4984 + ARGS_DESC@4978..4984 + L_PAREN@4978..4979 "(" + ARG@4979..4983 + EXPR@4979..4983 + EXPR_VAR@4979..4983 + NAME@4979..4983 + IDENT@4979..4983 "conf" + R_PAREN@4983..4984 ")" + R_BRACK@4984..4985 "]" + SEMI@4985..4986 ";" + WHITESPACE@4986..4991 "\n " + EXPR_VAR@4991..4994 + NAME@4991..4994 + IDENT@4991..4994 "std" + SUFFIX_INDEX@4994..4999 + DOT@4994..4995 "." + NAME@4995..4999 + IDENT@4995..4999 "join" + SUFFIX_APPLY@4999..5018 + ARGS_DESC@4999..5018 + L_PAREN@4999..5000 "(" + ARG@5000..5004 + EXPR@5000..5004 + EXPR_STRING@5000..5004 + STRING_SINGLE@5000..5004 "'\\n'" + COMMA@5004..5005 "," + WHITESPACE@5005..5006 " " + ARG@5006..5017 + EXPR@5006..5017 + EXPR_BINARY@5006..5017 + EXPR@5006..5010 + EXPR_VAR@5006..5010 + NAME@5006..5010 + IDENT@5006..5010 "vars" + WHITESPACE@5010..5011 " " + PLUS@5011..5012 "+" + WHITESPACE@5012..5013 " " + EXPR@5013..5017 + EXPR_ARRAY@5013..5017 + L_BRACK@5013..5014 "[" + EXPR@5014..5016 + EXPR_STRING@5014..5016 + STRING_SINGLE@5014..5016 "''" + R_BRACK@5016..5017 "]" + R_PAREN@5017..5018 ")" + COMMA@5018..5019 "," + WHITESPACE@5019..5023 "\n\n " + MEMBER_FIELD_METHOD@5023..5690 + FIELD_NAME_FIXED@5023..5040 + NAME@5023..5040 + IDENT@5023..5040 "manifestXmlJsonml" + PARAMS_DESC@5040..5047 + L_PAREN@5040..5041 "(" + PARAM@5041..5046 + DESTRUCT_FULL@5041..5046 + NAME@5041..5046 + IDENT@5041..5046 "value" + R_PAREN@5046..5047 ")" + COLONCOLON@5047..5049 "::" + WHITESPACE@5049..5054 "\n " + EXPR@5054..5690 + EXPR_IF_THEN_ELSE@5054..5690 + IF_KW@5054..5056 "if" + WHITESPACE@5056..5057 " " + EXPR@5057..5076 + EXPR_UNARY@5057..5076 + NOT@5057..5058 "!" + EXPR_VAR@5058..5061 + NAME@5058..5061 + IDENT@5058..5061 "std" + SUFFIX_INDEX@5061..5069 + DOT@5061..5062 "." + NAME@5062..5069 + IDENT@5062..5069 "isArray" + SUFFIX_APPLY@5069..5076 + ARGS_DESC@5069..5076 + L_PAREN@5069..5070 "(" + ARG@5070..5075 + EXPR@5070..5075 + EXPR_VAR@5070..5075 + NAME@5070..5075 + IDENT@5070..5075 "value" + R_PAREN@5075..5076 ")" + WHITESPACE@5076..5077 " " + THEN_KW@5077..5081 "then" + WHITESPACE@5081..5088 "\n " + TRUE_EXPR@5088..5156 + EXPR@5088..5156 + EXPR_ERROR@5088..5156 + ERROR_KW@5088..5093 "error" + WHITESPACE@5093..5094 " " + EXPR@5094..5156 + EXPR_BINARY@5094..5156 + EXPR@5094..5138 + EXPR_STRING@5094..5138 + STRING_SINGLE@5094..5138 "'Expected a JSONML va ..." + WHITESPACE@5138..5139 " " + MODULO@5139..5140 "%" + WHITESPACE@5140..5141 " " + EXPR@5141..5156 + EXPR_VAR@5141..5144 + NAME@5141..5144 + IDENT@5141..5144 "std" + SUFFIX_INDEX@5144..5149 + DOT@5144..5145 "." + NAME@5145..5149 + IDENT@5145..5149 "type" + SUFFIX_APPLY@5149..5156 + ARGS_DESC@5149..5156 + L_PAREN@5149..5150 "(" + ARG@5150..5155 + EXPR@5150..5155 + EXPR_VAR@5150..5155 + NAME@5150..5155 + IDENT@5150..5155 "value" + R_PAREN@5155..5156 ")" + WHITESPACE@5156..5161 "\n " + ELSE_KW@5161..5165 "else" + WHITESPACE@5165..5172 "\n " + FALSE_EXPR@5172..5690 + EXPR@5172..5690 + STMT_LOCAL@5172..5672 + LOCAL_KW@5172..5177 "local" + WHITESPACE@5177..5178 " " + BIND_FUNCTION@5178..5671 + NAME@5178..5181 + IDENT@5178..5181 "aux" + PARAMS_DESC@5181..5184 + L_PAREN@5181..5182 "(" + PARAM@5182..5183 + DESTRUCT_FULL@5182..5183 + NAME@5182..5183 + IDENT@5182..5183 "v" + R_PAREN@5183..5184 ")" + WHITESPACE@5184..5185 " " + ASSIGN@5185..5186 "=" + WHITESPACE@5186..5195 "\n " + EXPR@5195..5671 + EXPR_IF_THEN_ELSE@5195..5671 + IF_KW@5195..5197 "if" + WHITESPACE@5197..5198 " " + EXPR@5198..5213 + EXPR_VAR@5198..5201 + NAME@5198..5201 + IDENT@5198..5201 "std" + SUFFIX_INDEX@5201..5210 + DOT@5201..5202 "." + NAME@5202..5210 + IDENT@5202..5210 "isString" + SUFFIX_APPLY@5210..5213 + ARGS_DESC@5210..5213 + L_PAREN@5210..5211 "(" + ARG@5211..5212 + EXPR@5211..5212 + EXPR_VAR@5211..5212 + NAME@5211..5212 + IDENT@5211..5212 "v" + R_PAREN@5212..5213 ")" + WHITESPACE@5213..5214 " " + THEN_KW@5214..5218 "then" + WHITESPACE@5218..5229 "\n " + TRUE_EXPR@5229..5230 + EXPR@5229..5230 + EXPR_VAR@5229..5230 + NAME@5229..5230 + IDENT@5229..5230 "v" + WHITESPACE@5230..5239 "\n " + ELSE_KW@5239..5243 "else" + WHITESPACE@5243..5254 "\n " + FALSE_EXPR@5254..5671 + EXPR@5254..5671 + STMT_LOCAL@5254..5271 + LOCAL_KW@5254..5259 "local" + WHITESPACE@5259..5260 " " + BIND_DESTRUCT@5260..5270 + DESTRUCT_FULL@5260..5263 + NAME@5260..5263 + IDENT@5260..5263 "tag" + WHITESPACE@5263..5264 " " + ASSIGN@5264..5265 "=" + WHITESPACE@5265..5266 " " + EXPR@5266..5270 + EXPR_VAR@5266..5267 + NAME@5266..5267 + IDENT@5266..5267 "v" + SUFFIX_INDEX_EXPR@5267..5270 + L_BRACK@5267..5268 "[" + EXPR@5268..5269 + EXPR_NUMBER@5268..5269 + FLOAT@5268..5269 "0" + R_BRACK@5269..5270 "]" + SEMI@5270..5271 ";" + WHITESPACE@5271..5282 "\n " + STMT_LOCAL@5282..5340 + LOCAL_KW@5282..5287 "local" + WHITESPACE@5287..5288 " " + BIND_DESTRUCT@5288..5339 + DESTRUCT_FULL@5288..5297 + NAME@5288..5297 + IDENT@5288..5297 "has_attrs" + WHITESPACE@5297..5298 " " + ASSIGN@5298..5299 "=" + WHITESPACE@5299..5300 " " + EXPR@5300..5339 + EXPR_BINARY@5300..5339 + EXPR@5300..5317 + EXPR_BINARY@5300..5317 + EXPR@5300..5303 + EXPR_VAR@5300..5303 + NAME@5300..5303 + IDENT@5300..5303 "std" + SUFFIX_INDEX@5303..5310 + DOT@5303..5304 "." + NAME@5304..5310 + IDENT@5304..5310 "length" + SUFFIX_APPLY@5310..5313 + ARGS_DESC@5310..5313 + L_PAREN@5310..5311 "(" + ARG@5311..5312 + EXPR@5311..5312 + EXPR_VAR@5311..5312 + NAME@5311..5312 + IDENT@5311..5312 "v" + R_PAREN@5312..5313 ")" + WHITESPACE@5313..5314 " " + GT@5314..5315 ">" + WHITESPACE@5315..5316 " " + EXPR@5316..5317 + EXPR_NUMBER@5316..5317 + FLOAT@5316..5317 "1" + WHITESPACE@5317..5318 " " + AND@5318..5320 "&&" + WHITESPACE@5320..5321 " " + EXPR@5321..5339 + EXPR_VAR@5321..5324 + NAME@5321..5324 + IDENT@5321..5324 "std" + SUFFIX_INDEX@5324..5333 + DOT@5324..5325 "." + NAME@5325..5333 + IDENT@5325..5333 "isObject" + SUFFIX_APPLY@5333..5339 + ARGS_DESC@5333..5339 + L_PAREN@5333..5334 "(" + ARG@5334..5338 + EXPR@5334..5338 + EXPR_VAR@5334..5335 + NAME@5334..5335 + IDENT@5334..5335 "v" + SUFFIX_INDEX_EXPR@5335..5338 + L_BRACK@5335..5336 "[" + EXPR@5336..5337 + EXPR_NUMBER@5336..5337 + FLOAT@5336..5337 "1" + R_BRACK@5337..5338 "]" + R_PAREN@5338..5339 ")" + SEMI@5339..5340 ";" + WHITESPACE@5340..5351 "\n " + STMT_LOCAL@5351..5396 + LOCAL_KW@5351..5356 "local" + WHITESPACE@5356..5357 " " + BIND_DESTRUCT@5357..5395 + DESTRUCT_FULL@5357..5362 + NAME@5357..5362 + IDENT@5357..5362 "attrs" + WHITESPACE@5362..5363 " " + ASSIGN@5363..5364 "=" + WHITESPACE@5364..5365 " " + EXPR@5365..5395 + EXPR_IF_THEN_ELSE@5365..5395 + IF_KW@5365..5367 "if" + WHITESPACE@5367..5368 " " + EXPR@5368..5377 + EXPR_VAR@5368..5377 + NAME@5368..5377 + IDENT@5368..5377 "has_attrs" + WHITESPACE@5377..5378 " " + THEN_KW@5378..5382 "then" + WHITESPACE@5382..5383 " " + TRUE_EXPR@5383..5387 + EXPR@5383..5387 + EXPR_VAR@5383..5384 + NAME@5383..5384 + IDENT@5383..5384 "v" + SUFFIX_INDEX_EXPR@5384..5387 + L_BRACK@5384..5385 "[" + EXPR@5385..5386 + EXPR_NUMBER@5385..5386 + FLOAT@5385..5386 "1" + R_BRACK@5386..5387 "]" + WHITESPACE@5387..5388 " " + ELSE_KW@5388..5392 "else" + WHITESPACE@5392..5393 " " + FALSE_EXPR@5393..5395 + EXPR@5393..5395 + EXPR_OBJECT@5393..5395 + OBJ_BODY_MEMBER_LIST@5393..5395 + L_BRACE@5393..5394 "{" + R_BRACE@5394..5395 "}" + SEMI@5395..5396 ";" + WHITESPACE@5396..5407 "\n " + STMT_LOCAL@5407..5459 + LOCAL_KW@5407..5412 "local" + WHITESPACE@5412..5413 " " + BIND_DESTRUCT@5413..5458 + DESTRUCT_FULL@5413..5421 + NAME@5413..5421 + IDENT@5413..5421 "children" + WHITESPACE@5421..5422 " " + ASSIGN@5422..5423 "=" + WHITESPACE@5423..5424 " " + EXPR@5424..5458 + EXPR_IF_THEN_ELSE@5424..5458 + IF_KW@5424..5426 "if" + WHITESPACE@5426..5427 " " + EXPR@5427..5436 + EXPR_VAR@5427..5436 + NAME@5427..5436 + IDENT@5427..5436 "has_attrs" + WHITESPACE@5436..5437 " " + THEN_KW@5437..5441 "then" + WHITESPACE@5441..5442 " " + TRUE_EXPR@5442..5447 + EXPR@5442..5447 + EXPR_VAR@5442..5443 + NAME@5442..5443 + IDENT@5442..5443 "v" + SUFFIX_SLICE@5443..5447 + SLICE_DESC@5443..5447 + L_BRACK@5443..5444 "[" + EXPR@5444..5445 + EXPR_NUMBER@5444..5445 + FLOAT@5444..5445 "2" + COLON@5445..5446 ":" + R_BRACK@5446..5447 "]" + WHITESPACE@5447..5448 " " + ELSE_KW@5448..5452 "else" + WHITESPACE@5452..5453 " " + FALSE_EXPR@5453..5458 + EXPR@5453..5458 + EXPR_VAR@5453..5454 + NAME@5453..5454 + IDENT@5453..5454 "v" + SUFFIX_SLICE@5454..5458 + SLICE_DESC@5454..5458 + L_BRACK@5454..5455 "[" + EXPR@5455..5456 + EXPR_NUMBER@5455..5456 + FLOAT@5455..5456 "1" + COLON@5456..5457 ":" + R_BRACK@5457..5458 "]" + SEMI@5458..5459 ";" + WHITESPACE@5459..5470 "\n " + STMT_LOCAL@5470..5576 + LOCAL_KW@5470..5475 "local" + WHITESPACE@5475..5476 " " + BIND_DESTRUCT@5476..5575 + DESTRUCT_FULL@5476..5485 + NAME@5476..5485 + IDENT@5476..5485 "attrs_str" + WHITESPACE@5485..5486 " " + ASSIGN@5486..5487 "=" + WHITESPACE@5487..5500 "\n " + EXPR@5500..5575 + EXPR_VAR@5500..5503 + NAME@5500..5503 + IDENT@5500..5503 "std" + SUFFIX_INDEX@5503..5508 + DOT@5503..5504 "." + NAME@5504..5508 + IDENT@5504..5508 "join" + SUFFIX_APPLY@5508..5575 + ARGS_DESC@5508..5575 + L_PAREN@5508..5509 "(" + ARG@5509..5511 + EXPR@5509..5511 + EXPR_STRING@5509..5511 + STRING_SINGLE@5509..5511 "''" + COMMA@5511..5512 "," + WHITESPACE@5512..5513 " " + ARG@5513..5574 + EXPR@5513..5574 + EXPR_ARRAY_COMP@5513..5574 + L_BRACK@5513..5514 "[" + EXPR@5514..5540 + EXPR_BINARY@5514..5540 + EXPR@5514..5524 + EXPR_STRING@5514..5524 + STRING_SINGLE@5514..5524 "' %s=\"%s\"'" + WHITESPACE@5524..5525 " " + MODULO@5525..5526 "%" + WHITESPACE@5526..5527 " " + EXPR@5527..5540 + EXPR_ARRAY@5527..5540 + L_BRACK@5527..5528 "[" + EXPR@5528..5529 + EXPR_VAR@5528..5529 + NAME@5528..5529 + IDENT@5528..5529 "k" + COMMA@5529..5530 "," + WHITESPACE@5530..5531 " " + EXPR@5531..5539 + EXPR_VAR@5531..5536 + NAME@5531..5536 + IDENT@5531..5536 "attrs" + SUFFIX_INDEX_EXPR@5536..5539 + L_BRACK@5536..5537 "[" + EXPR@5537..5538 + EXPR_VAR@5537..5538 + NAME@5537..5538 + IDENT@5537..5538 "k" + R_BRACK@5538..5539 "]" + R_BRACK@5539..5540 "]" + WHITESPACE@5540..5541 " " + FOR_SPEC@5541..5573 + FOR_KW@5541..5544 "for" + WHITESPACE@5544..5545 " " + DESTRUCT_FULL@5545..5546 + NAME@5545..5546 + IDENT@5545..5546 "k" + WHITESPACE@5546..5547 " " + IN_KW@5547..5549 "in" + WHITESPACE@5549..5550 " " + EXPR@5550..5573 + EXPR_VAR@5550..5553 + NAME@5550..5553 + IDENT@5550..5553 "std" + SUFFIX_INDEX@5553..5566 + DOT@5553..5554 "." + NAME@5554..5566 + IDENT@5554..5566 "objectFields" + SUFFIX_APPLY@5566..5573 + ARGS_DESC@5566..5573 + L_PAREN@5566..5567 "(" + ARG@5567..5572 + EXPR@5567..5572 + EXPR_VAR@5567..5572 + NAME@5567..5572 + IDENT@5567..5572 "attrs" + R_PAREN@5572..5573 ")" + R_BRACK@5573..5574 "]" + R_PAREN@5574..5575 ")" + SEMI@5575..5576 ";" + WHITESPACE@5576..5587 "\n " + EXPR_VAR@5587..5590 + NAME@5587..5590 + IDENT@5587..5590 "std" + SUFFIX_INDEX@5590..5599 + DOT@5590..5591 "." + NAME@5591..5599 + IDENT@5591..5599 "deepJoin" + SUFFIX_APPLY@5599..5671 + ARGS_DESC@5599..5671 + L_PAREN@5599..5600 "(" + ARG@5600..5670 + EXPR@5600..5670 + EXPR_ARRAY@5600..5670 + L_BRACK@5600..5601 "[" + EXPR@5601..5604 + EXPR_STRING@5601..5604 + STRING_SINGLE@5601..5604 "'<'" + COMMA@5604..5605 "," + WHITESPACE@5605..5606 " " + EXPR@5606..5609 + EXPR_VAR@5606..5609 + NAME@5606..5609 + IDENT@5606..5609 "tag" + COMMA@5609..5610 "," + WHITESPACE@5610..5611 " " + EXPR@5611..5620 + EXPR_VAR@5611..5620 + NAME@5611..5620 + IDENT@5611..5620 "attrs_str" + COMMA@5620..5621 "," + WHITESPACE@5621..5622 " " + EXPR@5622..5625 + EXPR_STRING@5622..5625 + STRING_SINGLE@5622..5625 "'>'" + COMMA@5625..5626 "," + WHITESPACE@5626..5627 " " + EXPR@5627..5653 + EXPR_ARRAY_COMP@5627..5653 + L_BRACK@5627..5628 "[" + EXPR@5628..5634 + EXPR_VAR@5628..5631 + NAME@5628..5631 + IDENT@5628..5631 "aux" + SUFFIX_APPLY@5631..5634 + ARGS_DESC@5631..5634 + L_PAREN@5631..5632 "(" + ARG@5632..5633 + EXPR@5632..5633 + EXPR_VAR@5632..5633 + NAME@5632..5633 + IDENT@5632..5633 "x" + R_PAREN@5633..5634 ")" + WHITESPACE@5634..5635 " " + FOR_SPEC@5635..5652 + FOR_KW@5635..5638 "for" + WHITESPACE@5638..5639 " " + DESTRUCT_FULL@5639..5640 + NAME@5639..5640 + IDENT@5639..5640 "x" + WHITESPACE@5640..5641 " " + IN_KW@5641..5643 "in" + WHITESPACE@5643..5644 " " + EXPR@5644..5652 + EXPR_VAR@5644..5652 + NAME@5644..5652 + IDENT@5644..5652 "children" + R_BRACK@5652..5653 "]" + COMMA@5653..5654 "," + WHITESPACE@5654..5655 " " + EXPR@5655..5659 + EXPR_STRING@5655..5659 + STRING_SINGLE@5655..5659 "''" + R_BRACK@5669..5670 "]" + R_PAREN@5670..5671 ")" + SEMI@5671..5672 ";" + WHITESPACE@5672..5680 "\n\n " + EXPR_VAR@5680..5683 + NAME@5680..5683 + IDENT@5680..5683 "aux" + SUFFIX_APPLY@5683..5690 + ARGS_DESC@5683..5690 + L_PAREN@5683..5684 "(" + ARG@5684..5689 + EXPR@5684..5689 + EXPR_VAR@5684..5689 + NAME@5684..5689 + IDENT@5684..5689 "value" + R_PAREN@5689..5690 ")" + COMMA@5690..5691 "," + WHITESPACE@5691..5695 "\n\n " + MEMBER_FIELD_METHOD@5695..6483 + FIELD_NAME_FIXED@5695..5705 + NAME@5695..5705 + IDENT@5695..5705 "mergePatch" + PARAMS_DESC@5705..5720 + L_PAREN@5705..5706 "(" + PARAM@5706..5712 + DESTRUCT_FULL@5706..5712 + NAME@5706..5712 + IDENT@5706..5712 "target" + COMMA@5712..5713 "," + WHITESPACE@5713..5714 " " + PARAM@5714..5719 + DESTRUCT_FULL@5714..5719 + NAME@5714..5719 + IDENT@5714..5719 "patch" + R_PAREN@5719..5720 ")" + COLONCOLON@5720..5722 "::" + WHITESPACE@5722..5727 "\n " + EXPR@5727..6483 + EXPR_IF_THEN_ELSE@5727..6483 + IF_KW@5727..5729 "if" + WHITESPACE@5729..5730 " " + EXPR@5730..5749 + EXPR_VAR@5730..5733 + NAME@5730..5733 + IDENT@5730..5733 "std" + SUFFIX_INDEX@5733..5742 + DOT@5733..5734 "." + NAME@5734..5742 + IDENT@5734..5742 "isObject" + SUFFIX_APPLY@5742..5749 + ARGS_DESC@5742..5749 + L_PAREN@5742..5743 "(" + ARG@5743..5748 + EXPR@5743..5748 + EXPR_VAR@5743..5748 + NAME@5743..5748 + IDENT@5743..5748 "patch" + R_PAREN@5748..5749 ")" + WHITESPACE@5749..5750 " " + THEN_KW@5750..5754 "then" + WHITESPACE@5754..5761 "\n " + TRUE_EXPR@5761..6462 + EXPR@5761..6462 + STMT_LOCAL@5761..5835 + LOCAL_KW@5761..5766 "local" + WHITESPACE@5766..5767 " " + BIND_DESTRUCT@5767..5834 + DESTRUCT_FULL@5767..5780 + NAME@5767..5780 + IDENT@5767..5780 "target_object" + WHITESPACE@5780..5781 " " + ASSIGN@5781..5782 "=" + WHITESPACE@5782..5791 "\n " + EXPR@5791..5834 + EXPR_IF_THEN_ELSE@5791..5834 + IF_KW@5791..5793 "if" + WHITESPACE@5793..5794 " " + EXPR@5794..5814 + EXPR_VAR@5794..5797 + NAME@5794..5797 + IDENT@5794..5797 "std" + SUFFIX_INDEX@5797..5806 + DOT@5797..5798 "." + NAME@5798..5806 + IDENT@5798..5806 "isObject" + SUFFIX_APPLY@5806..5814 + ARGS_DESC@5806..5814 + L_PAREN@5806..5807 "(" + ARG@5807..5813 + EXPR@5807..5813 + EXPR_VAR@5807..5813 + NAME@5807..5813 + IDENT@5807..5813 "target" + R_PAREN@5813..5814 ")" + WHITESPACE@5814..5815 " " + THEN_KW@5815..5819 "then" + WHITESPACE@5819..5820 " " + TRUE_EXPR@5820..5826 + EXPR@5820..5826 + EXPR_VAR@5820..5826 + NAME@5820..5826 + IDENT@5820..5826 "target" + WHITESPACE@5826..5827 " " + ELSE_KW@5827..5831 "else" + WHITESPACE@5831..5832 " " + FALSE_EXPR@5832..5834 + EXPR@5832..5834 + EXPR_OBJECT@5832..5834 + OBJ_BODY_MEMBER_LIST@5832..5834 + L_BRACE@5832..5833 "{" + R_BRACE@5833..5834 "}" + SEMI@5834..5835 ";" + WHITESPACE@5835..5843 "\n\n " + STMT_LOCAL@5843..5949 + LOCAL_KW@5843..5848 "local" + WHITESPACE@5848..5849 " " + BIND_DESTRUCT@5849..5948 + DESTRUCT_FULL@5849..5862 + NAME@5849..5862 + IDENT@5849..5862 "target_fields" + WHITESPACE@5862..5863 " " + ASSIGN@5863..5864 "=" + WHITESPACE@5864..5873 "\n " + EXPR@5873..5948 + EXPR_IF_THEN_ELSE@5873..5948 + IF_KW@5873..5875 "if" + WHITESPACE@5875..5876 " " + EXPR@5876..5903 + EXPR_VAR@5876..5879 + NAME@5876..5879 + IDENT@5876..5879 "std" + SUFFIX_INDEX@5879..5888 + DOT@5879..5880 "." + NAME@5880..5888 + IDENT@5880..5888 "isObject" + SUFFIX_APPLY@5888..5903 + ARGS_DESC@5888..5903 + L_PAREN@5888..5889 "(" + ARG@5889..5902 + EXPR@5889..5902 + EXPR_VAR@5889..5902 + NAME@5889..5902 + IDENT@5889..5902 "target_object" + R_PAREN@5902..5903 ")" + WHITESPACE@5903..5904 " " + THEN_KW@5904..5908 "then" + WHITESPACE@5908..5909 " " + TRUE_EXPR@5909..5940 + EXPR@5909..5940 + EXPR_VAR@5909..5912 + NAME@5909..5912 + IDENT@5909..5912 "std" + SUFFIX_INDEX@5912..5925 + DOT@5912..5913 "." + NAME@5913..5925 + IDENT@5913..5925 "objectFields" + SUFFIX_APPLY@5925..5940 + ARGS_DESC@5925..5940 + L_PAREN@5925..5926 "(" + ARG@5926..5939 + EXPR@5926..5939 + EXPR_VAR@5926..5939 + NAME@5926..5939 + IDENT@5926..5939 "target_object" + R_PAREN@5939..5940 ")" + WHITESPACE@5940..5941 " " + ELSE_KW@5941..5945 "else" + WHITESPACE@5945..5946 " " + FALSE_EXPR@5946..5948 + EXPR@5946..5948 + EXPR_ARRAY@5946..5948 + L_BRACK@5946..5947 "[" + R_BRACK@5947..5948 "]" + SEMI@5948..5949 ";" + WHITESPACE@5949..5957 "\n\n " + STMT_LOCAL@5957..6034 + LOCAL_KW@5957..5962 "local" + WHITESPACE@5962..5963 " " + BIND_DESTRUCT@5963..6033 + DESTRUCT_FULL@5963..5974 + NAME@5963..5974 + IDENT@5963..5974 "null_fields" + WHITESPACE@5974..5975 " " + ASSIGN@5975..5976 "=" + WHITESPACE@5976..5977 " " + EXPR@5977..6033 + EXPR_ARRAY_COMP@5977..6033 + L_BRACK@5977..5978 "[" + EXPR@5978..5979 + EXPR_VAR@5978..5979 + NAME@5978..5979 + IDENT@5978..5979 "k" + WHITESPACE@5979..5980 " " + FOR_SPEC@5980..6012 + FOR_KW@5980..5983 "for" + WHITESPACE@5983..5984 " " + DESTRUCT_FULL@5984..5985 + NAME@5984..5985 + IDENT@5984..5985 "k" + WHITESPACE@5985..5986 " " + IN_KW@5986..5988 "in" + WHITESPACE@5988..5989 " " + EXPR@5989..6012 + EXPR_VAR@5989..5992 + NAME@5989..5992 + IDENT@5989..5992 "std" + SUFFIX_INDEX@5992..6005 + DOT@5992..5993 "." + NAME@5993..6005 + IDENT@5993..6005 "objectFields" + SUFFIX_APPLY@6005..6012 + ARGS_DESC@6005..6012 + L_PAREN@6005..6006 "(" + ARG@6006..6011 + EXPR@6006..6011 + EXPR_VAR@6006..6011 + NAME@6006..6011 + IDENT@6006..6011 "patch" + R_PAREN@6011..6012 ")" + WHITESPACE@6012..6013 " " + IF_SPEC@6013..6032 + IF_KW@6013..6015 "if" + WHITESPACE@6015..6016 " " + EXPR@6016..6032 + EXPR_BINARY@6016..6032 + EXPR@6016..6021 + EXPR_VAR@6016..6021 + NAME@6016..6021 + IDENT@6016..6021 "patch" + SUFFIX_INDEX_EXPR@6021..6024 + L_BRACK@6021..6022 "[" + EXPR@6022..6023 + EXPR_VAR@6022..6023 + NAME@6022..6023 + IDENT@6022..6023 "k" + R_BRACK@6023..6024 "]" + WHITESPACE@6024..6025 " " + EQ@6025..6027 "==" + WHITESPACE@6027..6028 " " + EXPR@6028..6032 + EXPR_LITERAL@6028..6032 + NULL_KW@6028..6032 "null" + R_BRACK@6032..6033 "]" + SEMI@6033..6034 ";" + WHITESPACE@6034..6041 "\n " + STMT_LOCAL@6041..6114 + LOCAL_KW@6041..6046 "local" + WHITESPACE@6046..6047 " " + BIND_DESTRUCT@6047..6113 + DESTRUCT_FULL@6047..6058 + NAME@6047..6058 + IDENT@6047..6058 "both_fields" + WHITESPACE@6058..6059 " " + ASSIGN@6059..6060 "=" + WHITESPACE@6060..6061 " " + EXPR@6061..6113 + EXPR_VAR@6061..6064 + NAME@6061..6064 + IDENT@6061..6064 "std" + SUFFIX_INDEX@6064..6073 + DOT@6064..6065 "." + NAME@6065..6073 + IDENT@6065..6073 "setUnion" + SUFFIX_APPLY@6073..6113 + ARGS_DESC@6073..6113 + L_PAREN@6073..6074 "(" + ARG@6074..6087 + EXPR@6074..6087 + EXPR_VAR@6074..6087 + NAME@6074..6087 + IDENT@6074..6087 "target_fields" + COMMA@6087..6088 "," + WHITESPACE@6088..6089 " " + ARG@6089..6112 + EXPR@6089..6112 + EXPR_VAR@6089..6092 + NAME@6089..6092 + IDENT@6089..6092 "std" + SUFFIX_INDEX@6092..6105 + DOT@6092..6093 "." + NAME@6093..6105 + IDENT@6093..6105 "objectFields" + SUFFIX_APPLY@6105..6112 + ARGS_DESC@6105..6112 + L_PAREN@6105..6106 "(" + ARG@6106..6111 + EXPR@6106..6111 + EXPR_VAR@6106..6111 + NAME@6106..6111 + IDENT@6106..6111 "patch" + R_PAREN@6111..6112 ")" + R_PAREN@6112..6113 ")" + SEMI@6113..6114 ";" + WHITESPACE@6114..6122 "\n\n " + EXPR_OBJECT@6122..6462 + OBJ_BODY_COMP@6122..6462 + L_BRACE@6122..6123 "{" + WHITESPACE@6123..6132 "\n " + MEMBER_FIELD_NORMAL@6132..6399 + FIELD_NAME_DYNAMIC@6132..6135 + L_BRACK@6132..6133 "[" + EXPR@6133..6134 + EXPR_VAR@6133..6134 + NAME@6133..6134 + IDENT@6133..6134 "k" + R_BRACK@6134..6135 "]" + COLON@6135..6136 ":" + WHITESPACE@6136..6147 "\n " + EXPR@6147..6399 + EXPR_IF_THEN_ELSE@6147..6399 + IF_KW@6147..6149 "if" + WHITESPACE@6149..6150 " " + EXPR@6150..6174 + EXPR_UNARY@6150..6174 + NOT@6150..6151 "!" + EXPR_VAR@6151..6154 + NAME@6151..6154 + IDENT@6151..6154 "std" + SUFFIX_INDEX@6154..6164 + DOT@6154..6155 "." + NAME@6155..6164 + IDENT@6155..6164 "objectHas" + SUFFIX_APPLY@6164..6174 + ARGS_DESC@6164..6174 + L_PAREN@6164..6165 "(" + ARG@6165..6170 + EXPR@6165..6170 + EXPR_VAR@6165..6170 + NAME@6165..6170 + IDENT@6165..6170 "patch" + COMMA@6170..6171 "," + WHITESPACE@6171..6172 " " + ARG@6172..6173 + EXPR@6172..6173 + EXPR_VAR@6172..6173 + NAME@6172..6173 + IDENT@6172..6173 "k" + R_PAREN@6173..6174 ")" + WHITESPACE@6174..6175 " " + THEN_KW@6175..6179 "then" + WHITESPACE@6179..6192 "\n " + TRUE_EXPR@6192..6208 + EXPR@6192..6208 + EXPR_VAR@6192..6205 + NAME@6192..6205 + IDENT@6192..6205 "target_object" + SUFFIX_INDEX_EXPR@6205..6208 + L_BRACK@6205..6206 "[" + EXPR@6206..6207 + EXPR_VAR@6206..6207 + NAME@6206..6207 + IDENT@6206..6207 "k" + R_BRACK@6207..6208 "]" + WHITESPACE@6208..6219 "\n " + ELSE_KW@6219..6223 "else" + WHITESPACE@6223..6224 " " + FALSE_EXPR@6224..6399 + EXPR@6224..6399 + EXPR_IF_THEN_ELSE@6224..6399 + IF_KW@6224..6226 "if" + WHITESPACE@6226..6227 " " + EXPR@6227..6259 + EXPR_UNARY@6227..6259 + NOT@6227..6228 "!" + EXPR_VAR@6228..6231 + NAME@6228..6231 + IDENT@6228..6231 "std" + SUFFIX_INDEX@6231..6241 + DOT@6231..6232 "." + NAME@6232..6241 + IDENT@6232..6241 "objectHas" + SUFFIX_APPLY@6241..6259 + ARGS_DESC@6241..6259 + L_PAREN@6241..6242 "(" + ARG@6242..6255 + EXPR@6242..6255 + EXPR_VAR@6242..6255 + NAME@6242..6255 + IDENT@6242..6255 "target_object" + COMMA@6255..6256 "," + WHITESPACE@6256..6257 " " + ARG@6257..6258 + EXPR@6257..6258 + EXPR_VAR@6257..6258 + NAME@6257..6258 + IDENT@6257..6258 "k" + R_PAREN@6258..6259 ")" + WHITESPACE@6259..6260 " " + THEN_KW@6260..6264 "then" + WHITESPACE@6264..6277 "\n " + TRUE_EXPR@6277..6318 + EXPR@6277..6318 + EXPR_VAR@6277..6280 + NAME@6277..6280 + IDENT@6277..6280 "std" + SUFFIX_INDEX@6280..6291 + DOT@6280..6281 "." + NAME@6281..6291 + IDENT@6281..6291 "mergePatch" + SUFFIX_APPLY@6291..6318 + ARGS_DESC@6291..6318 + L_PAREN@6291..6292 "(" + ARG@6292..6296 + EXPR@6292..6296 + EXPR_LITERAL@6292..6296 + NULL_KW@6292..6296 "null" + COMMA@6296..6297 "," + WHITESPACE@6297..6298 " " + ARG@6298..6306 + EXPR@6298..6306 + EXPR_VAR@6298..6303 + NAME@6298..6303 + IDENT@6298..6303 "patch" + SUFFIX_INDEX_EXPR@6303..6306 + L_BRACK@6303..6304 "[" + EXPR@6304..6305 + EXPR_VAR@6304..6305 + NAME@6304..6305 + IDENT@6304..6305 "k" + R_BRACK@6305..6306 "]" + R_PAREN@6306..6307 ")" + WHITESPACE@6307..6308 " " + TAILSTRICT_KW@6308..6318 "tailstrict" + WHITESPACE@6318..6329 "\n " + ELSE_KW@6329..6333 "else" + WHITESPACE@6333..6346 "\n " + FALSE_EXPR@6346..6399 + EXPR@6346..6399 + EXPR_VAR@6346..6349 + NAME@6346..6349 + IDENT@6346..6349 "std" + SUFFIX_INDEX@6349..6360 + DOT@6349..6350 "." + NAME@6350..6360 + IDENT@6350..6360 "mergePatch" + SUFFIX_APPLY@6360..6399 + ARGS_DESC@6360..6399 + L_PAREN@6360..6361 "(" + ARG@6361..6377 + EXPR@6361..6377 + EXPR_VAR@6361..6374 + NAME@6361..6374 + IDENT@6361..6374 "target_object" + SUFFIX_INDEX_EXPR@6374..6377 + L_BRACK@6374..6375 "[" + EXPR@6375..6376 + EXPR_VAR@6375..6376 + NAME@6375..6376 + IDENT@6375..6376 "k" + R_BRACK@6376..6377 "]" + COMMA@6377..6378 "," + WHITESPACE@6378..6379 " " + ARG@6379..6387 + EXPR@6379..6387 + EXPR_VAR@6379..6384 + NAME@6379..6384 + IDENT@6379..6384 "patch" + SUFFIX_INDEX_EXPR@6384..6387 + L_BRACK@6384..6385 "[" + EXPR@6385..6386 + EXPR_VAR@6385..6386 + NAME@6385..6386 + IDENT@6385..6386 "k" + R_BRACK@6386..6387 "]" + R_PAREN@6387..6388 ")" + WHITESPACE@6388..6389 " " + TAILSTRICT_KW@6389..6399 "tailstrict" + WHITESPACE@6399..6408 "\n " + FOR_SPEC@6408..6454 + FOR_KW@6408..6411 "for" + WHITESPACE@6411..6412 " " + DESTRUCT_FULL@6412..6413 + NAME@6412..6413 + IDENT@6412..6413 "k" + WHITESPACE@6413..6414 " " + IN_KW@6414..6416 "in" + WHITESPACE@6416..6417 " " + EXPR@6417..6454 + EXPR_VAR@6417..6420 + NAME@6417..6420 + IDENT@6417..6420 "std" + SUFFIX_INDEX@6420..6428 + DOT@6420..6421 "." + NAME@6421..6428 + IDENT@6421..6428 "setDiff" + SUFFIX_APPLY@6428..6454 + ARGS_DESC@6428..6454 + L_PAREN@6428..6429 "(" + ARG@6429..6440 + EXPR@6429..6440 + EXPR_VAR@6429..6440 + NAME@6429..6440 + IDENT@6429..6440 "both_fields" + COMMA@6440..6441 "," + WHITESPACE@6441..6442 " " + ARG@6442..6453 + EXPR@6442..6453 + EXPR_VAR@6442..6453 + NAME@6442..6453 + IDENT@6442..6453 "null_fields" + R_PAREN@6453..6454 ")" + WHITESPACE@6454..6461 "\n " + R_BRACE@6461..6462 "}" + WHITESPACE@6462..6467 "\n " + ELSE_KW@6467..6471 "else" + WHITESPACE@6471..6478 "\n " + FALSE_EXPR@6478..6483 + EXPR@6478..6483 + EXPR_VAR@6478..6483 + NAME@6478..6483 + IDENT@6478..6483 "patch" + COMMA@6483..6484 "," + WHITESPACE@6484..6488 "\n\n " + MEMBER_FIELD_METHOD@6488..6594 + FIELD_NAME_FIXED@6488..6491 + NAME@6488..6491 + IDENT@6488..6491 "get" + PARAMS_DESC@6491..6528 + L_PAREN@6491..6492 "(" + PARAM@6492..6493 + DESTRUCT_FULL@6492..6493 + NAME@6492..6493 + IDENT@6492..6493 "o" + COMMA@6493..6494 "," + WHITESPACE@6494..6495 " " + PARAM@6495..6496 + DESTRUCT_FULL@6495..6496 + NAME@6495..6496 + IDENT@6495..6496 "f" + COMMA@6496..6497 "," + WHITESPACE@6497..6498 " " + PARAM@6498..6510 + DESTRUCT_FULL@6498..6505 + NAME@6498..6505 + IDENT@6498..6505 "default" + ASSIGN@6505..6506 "=" + EXPR@6506..6510 + EXPR_LITERAL@6506..6510 + NULL_KW@6506..6510 "null" + COMMA@6510..6511 "," + WHITESPACE@6511..6512 " " + PARAM@6512..6527 + DESTRUCT_FULL@6512..6522 + NAME@6512..6522 + IDENT@6512..6522 "inc_hidden" + ASSIGN@6522..6523 "=" + EXPR@6523..6527 + EXPR_LITERAL@6523..6527 + TRUE_KW@6523..6527 "true" + R_PAREN@6527..6528 ")" + COLONCOLON@6528..6530 "::" + WHITESPACE@6530..6535 "\n " + EXPR@6535..6594 + EXPR_IF_THEN_ELSE@6535..6594 + IF_KW@6535..6537 "if" + WHITESPACE@6537..6538 " " + EXPR@6538..6571 + EXPR_VAR@6538..6541 + NAME@6538..6541 + IDENT@6538..6541 "std" + SUFFIX_INDEX@6541..6553 + DOT@6541..6542 "." + NAME@6542..6553 + IDENT@6542..6553 "objectHasEx" + SUFFIX_APPLY@6553..6571 + ARGS_DESC@6553..6571 + L_PAREN@6553..6554 "(" + ARG@6554..6555 + EXPR@6554..6555 + EXPR_VAR@6554..6555 + NAME@6554..6555 + IDENT@6554..6555 "o" + COMMA@6555..6556 "," + WHITESPACE@6556..6557 " " + ARG@6557..6558 + EXPR@6557..6558 + EXPR_VAR@6557..6558 + NAME@6557..6558 + IDENT@6557..6558 "f" + COMMA@6558..6559 "," + WHITESPACE@6559..6560 " " + ARG@6560..6570 + EXPR@6560..6570 + EXPR_VAR@6560..6570 + NAME@6560..6570 + IDENT@6560..6570 "inc_hidden" + R_PAREN@6570..6571 ")" + WHITESPACE@6571..6572 " " + THEN_KW@6572..6576 "then" + WHITESPACE@6576..6577 " " + TRUE_EXPR@6577..6581 + EXPR@6577..6581 + EXPR_VAR@6577..6578 + NAME@6577..6578 + IDENT@6577..6578 "o" + SUFFIX_INDEX_EXPR@6578..6581 + L_BRACK@6578..6579 "[" + EXPR@6579..6580 + EXPR_VAR@6579..6580 + NAME@6579..6580 + IDENT@6579..6580 "f" + R_BRACK@6580..6581 "]" + WHITESPACE@6581..6582 " " + ELSE_KW@6582..6586 "else" + WHITESPACE@6586..6587 " " + FALSE_EXPR@6587..6594 + EXPR@6587..6594 + EXPR_VAR@6587..6594 + NAME@6587..6594 + IDENT@6587..6594 "default" + COMMA@6594..6595 "," + WHITESPACE@6595..6599 "\n\n " + MEMBER_FIELD_METHOD@6599..6733 + FIELD_NAME_FIXED@6599..6610 + NAME@6599..6610 + IDENT@6599..6610 "resolvePath" + PARAMS_DESC@6610..6616 + L_PAREN@6610..6611 "(" + PARAM@6611..6612 + DESTRUCT_FULL@6611..6612 + NAME@6611..6612 + IDENT@6611..6612 "f" + COMMA@6612..6613 "," + WHITESPACE@6613..6614 " " + PARAM@6614..6615 + DESTRUCT_FULL@6614..6615 + NAME@6614..6615 + IDENT@6614..6615 "r" + R_PAREN@6615..6616 ")" + COLONCOLON@6616..6618 "::" + WHITESPACE@6618..6623 "\n " + EXPR@6623..6733 + STMT_LOCAL@6623..6653 + LOCAL_KW@6623..6628 "local" + WHITESPACE@6628..6629 " " + BIND_DESTRUCT@6629..6652 + DESTRUCT_FULL@6629..6632 + NAME@6629..6632 + IDENT@6629..6632 "arr" + WHITESPACE@6632..6633 " " + ASSIGN@6633..6634 "=" + WHITESPACE@6634..6635 " " + EXPR@6635..6652 + EXPR_VAR@6635..6638 + NAME@6635..6638 + IDENT@6635..6638 "std" + SUFFIX_INDEX@6638..6644 + DOT@6638..6639 "." + NAME@6639..6644 + IDENT@6639..6644 "split" + SUFFIX_APPLY@6644..6652 + ARGS_DESC@6644..6652 + L_PAREN@6644..6645 "(" + ARG@6645..6646 + EXPR@6645..6646 + EXPR_VAR@6645..6646 + NAME@6645..6646 + IDENT@6645..6646 "f" + COMMA@6646..6647 "," + WHITESPACE@6647..6648 " " + ARG@6648..6651 + EXPR@6648..6651 + EXPR_STRING@6648..6651 + STRING_SINGLE@6648..6651 "'/'" + R_PAREN@6651..6652 ")" + SEMI@6652..6653 ";" + WHITESPACE@6653..6658 "\n " + EXPR_VAR@6658..6661 + NAME@6658..6661 + IDENT@6658..6661 "std" + SUFFIX_INDEX@6661..6666 + DOT@6661..6662 "." + NAME@6662..6666 + IDENT@6662..6666 "join" + SUFFIX_APPLY@6666..6733 + ARGS_DESC@6666..6733 + L_PAREN@6666..6667 "(" + ARG@6667..6670 + EXPR@6667..6670 + EXPR_STRING@6667..6670 + STRING_SINGLE@6667..6670 "'/'" + COMMA@6670..6671 "," + WHITESPACE@6671..6672 " " + ARG@6672..6732 + EXPR@6672..6732 + EXPR_BINARY@6672..6732 + EXPR@6672..6675 + EXPR_VAR@6672..6675 + NAME@6672..6675 + IDENT@6672..6675 "std" + SUFFIX_INDEX@6675..6685 + DOT@6675..6676 "." + NAME@6676..6685 + IDENT@6676..6685 "makeArray" + SUFFIX_APPLY@6685..6726 + ARGS_DESC@6685..6726 + L_PAREN@6685..6686 "(" + ARG@6686..6705 + EXPR@6686..6705 + EXPR_BINARY@6686..6705 + EXPR@6686..6689 + EXPR_VAR@6686..6689 + NAME@6686..6689 + IDENT@6686..6689 "std" + SUFFIX_INDEX@6689..6696 + DOT@6689..6690 "." + NAME@6690..6696 + IDENT@6690..6696 "length" + SUFFIX_APPLY@6696..6701 + ARGS_DESC@6696..6701 + L_PAREN@6696..6697 "(" + ARG@6697..6700 + EXPR@6697..6700 + EXPR_VAR@6697..6700 + NAME@6697..6700 + IDENT@6697..6700 "arr" + R_PAREN@6700..6701 ")" + WHITESPACE@6701..6702 " " + MINUS@6702..6703 "-" + WHITESPACE@6703..6704 " " + EXPR@6704..6705 + EXPR_NUMBER@6704..6705 + FLOAT@6704..6705 "1" + COMMA@6705..6706 "," + WHITESPACE@6706..6707 " " + ARG@6707..6725 + EXPR@6707..6725 + EXPR_FUNCTION@6707..6725 + FUNCTION_KW@6707..6715 "function" + PARAMS_DESC@6715..6718 + L_PAREN@6715..6716 "(" + PARAM@6716..6717 + DESTRUCT_FULL@6716..6717 + NAME@6716..6717 + IDENT@6716..6717 "i" + R_PAREN@6717..6718 ")" + WHITESPACE@6718..6719 " " + EXPR@6719..6725 + EXPR_VAR@6719..6722 + NAME@6719..6722 + IDENT@6719..6722 "arr" + SUFFIX_INDEX_EXPR@6722..6725 + L_BRACK@6722..6723 "[" + EXPR@6723..6724 + EXPR_VAR@6723..6724 + NAME@6723..6724 + IDENT@6723..6724 "i" + R_BRACK@6724..6725 "]" + R_PAREN@6725..6726 ")" + WHITESPACE@6726..6727 " " + PLUS@6727..6728 "+" + WHITESPACE@6728..6729 " " + EXPR@6729..6732 + EXPR_ARRAY@6729..6732 + L_BRACK@6729..6730 "[" + EXPR@6730..6731 + EXPR_VAR@6730..6731 + NAME@6730..6731 + IDENT@6730..6731 "r" + R_BRACK@6731..6732 "]" + R_PAREN@6732..6733 ")" + COMMA@6733..6734 "," + WHITESPACE@6734..6738 "\n\n " + MEMBER_FIELD_METHOD@6738..7191 + FIELD_NAME_FIXED@6738..6743 + NAME@6738..6743 + IDENT@6738..6743 "prune" + PARAMS_DESC@6743..6746 + L_PAREN@6743..6744 "(" + PARAM@6744..6745 + DESTRUCT_FULL@6744..6745 + NAME@6744..6745 + IDENT@6744..6745 "a" + R_PAREN@6745..6746 ")" + COLONCOLON@6746..6748 "::" + WHITESPACE@6748..6753 "\n " + EXPR@6753..7191 + STMT_LOCAL@6753..6957 + LOCAL_KW@6753..6758 "local" + WHITESPACE@6758..6759 " " + BIND_FUNCTION@6759..6956 + NAME@6759..6768 + IDENT@6759..6768 "isContent" + PARAMS_DESC@6768..6771 + L_PAREN@6768..6769 "(" + PARAM@6769..6770 + DESTRUCT_FULL@6769..6770 + NAME@6769..6770 + IDENT@6769..6770 "b" + R_PAREN@6770..6771 ")" + WHITESPACE@6771..6772 " " + ASSIGN@6772..6773 "=" + WHITESPACE@6773..6780 "\n " + EXPR@6780..6956 + EXPR_IF_THEN_ELSE@6780..6956 + IF_KW@6780..6782 "if" + WHITESPACE@6782..6783 " " + EXPR@6783..6792 + EXPR_BINARY@6783..6792 + EXPR@6783..6784 + EXPR_VAR@6783..6784 + NAME@6783..6784 + IDENT@6783..6784 "b" + WHITESPACE@6784..6785 " " + EQ@6785..6787 "==" + WHITESPACE@6787..6788 " " + EXPR@6788..6792 + EXPR_LITERAL@6788..6792 + NULL_KW@6788..6792 "null" + WHITESPACE@6792..6793 " " + THEN_KW@6793..6797 "then" + WHITESPACE@6797..6806 "\n " + TRUE_EXPR@6806..6811 + EXPR@6806..6811 + EXPR_LITERAL@6806..6811 + FALSE_KW@6806..6811 "false" + WHITESPACE@6811..6818 "\n " + ELSE_KW@6818..6822 "else" + WHITESPACE@6822..6823 " " + FALSE_EXPR@6823..6956 + EXPR@6823..6956 + EXPR_IF_THEN_ELSE@6823..6956 + IF_KW@6823..6825 "if" + WHITESPACE@6825..6826 " " + EXPR@6826..6840 + EXPR_VAR@6826..6829 + NAME@6826..6829 + IDENT@6826..6829 "std" + SUFFIX_INDEX@6829..6837 + DOT@6829..6830 "." + NAME@6830..6837 + IDENT@6830..6837 "isArray" + SUFFIX_APPLY@6837..6840 + ARGS_DESC@6837..6840 + L_PAREN@6837..6838 "(" + ARG@6838..6839 + EXPR@6838..6839 + EXPR_VAR@6838..6839 + NAME@6838..6839 + IDENT@6838..6839 "b" + R_PAREN@6839..6840 ")" + WHITESPACE@6840..6841 " " + THEN_KW@6841..6845 "then" + WHITESPACE@6845..6854 "\n " + TRUE_EXPR@6854..6871 + EXPR@6854..6871 + EXPR_BINARY@6854..6871 + EXPR@6854..6857 + EXPR_VAR@6854..6857 + NAME@6854..6857 + IDENT@6854..6857 "std" + SUFFIX_INDEX@6857..6864 + DOT@6857..6858 "." + NAME@6858..6864 + IDENT@6858..6864 "length" + SUFFIX_APPLY@6864..6867 + ARGS_DESC@6864..6867 + L_PAREN@6864..6865 "(" + ARG@6865..6866 + EXPR@6865..6866 + EXPR_VAR@6865..6866 + NAME@6865..6866 + IDENT@6865..6866 "b" + R_PAREN@6866..6867 ")" + WHITESPACE@6867..6868 " " + GT@6868..6869 ">" + WHITESPACE@6869..6870 " " + EXPR@6870..6871 + EXPR_NUMBER@6870..6871 + FLOAT@6870..6871 "0" + WHITESPACE@6871..6878 "\n " + ELSE_KW@6878..6882 "else" + WHITESPACE@6882..6883 " " + FALSE_EXPR@6883..6956 + EXPR@6883..6956 + EXPR_IF_THEN_ELSE@6883..6956 + IF_KW@6883..6885 "if" + WHITESPACE@6885..6886 " " + EXPR@6886..6901 + EXPR_VAR@6886..6889 + NAME@6886..6889 + IDENT@6886..6889 "std" + SUFFIX_INDEX@6889..6898 + DOT@6889..6890 "." + NAME@6890..6898 + IDENT@6890..6898 "isObject" + SUFFIX_APPLY@6898..6901 + ARGS_DESC@6898..6901 + L_PAREN@6898..6899 "(" + ARG@6899..6900 + EXPR@6899..6900 + EXPR_VAR@6899..6900 + NAME@6899..6900 + IDENT@6899..6900 "b" + R_PAREN@6900..6901 ")" + WHITESPACE@6901..6902 " " + THEN_KW@6902..6906 "then" + WHITESPACE@6906..6915 "\n " + TRUE_EXPR@6915..6932 + EXPR@6915..6932 + EXPR_BINARY@6915..6932 + EXPR@6915..6918 + EXPR_VAR@6915..6918 + NAME@6915..6918 + IDENT@6915..6918 "std" + SUFFIX_INDEX@6918..6925 + DOT@6918..6919 "." + NAME@6919..6925 + IDENT@6919..6925 "length" + SUFFIX_APPLY@6925..6928 + ARGS_DESC@6925..6928 + L_PAREN@6925..6926 "(" + ARG@6926..6927 + EXPR@6926..6927 + EXPR_VAR@6926..6927 + NAME@6926..6927 + IDENT@6926..6927 "b" + R_PAREN@6927..6928 ")" + WHITESPACE@6928..6929 " " + GT@6929..6930 ">" + WHITESPACE@6930..6931 " " + EXPR@6931..6932 + EXPR_NUMBER@6931..6932 + FLOAT@6931..6932 "0" + WHITESPACE@6932..6939 "\n " + ELSE_KW@6939..6943 "else" + WHITESPACE@6943..6952 "\n " + FALSE_EXPR@6952..6956 + EXPR@6952..6956 + EXPR_LITERAL@6952..6956 + TRUE_KW@6952..6956 "true" + SEMI@6956..6957 ";" + WHITESPACE@6957..6962 "\n " + EXPR_IF_THEN_ELSE@6962..7191 + IF_KW@6962..6964 "if" + WHITESPACE@6964..6965 " " + EXPR@6965..6979 + EXPR_VAR@6965..6968 + NAME@6965..6968 + IDENT@6965..6968 "std" + SUFFIX_INDEX@6968..6976 + DOT@6968..6969 "." + NAME@6969..6976 + IDENT@6969..6976 "isArray" + SUFFIX_APPLY@6976..6979 + ARGS_DESC@6976..6979 + L_PAREN@6976..6977 "(" + ARG@6977..6978 + EXPR@6977..6978 + EXPR_VAR@6977..6978 + NAME@6977..6978 + IDENT@6977..6978 "a" + R_PAREN@6978..6979 ")" + WHITESPACE@6979..6980 " " + THEN_KW@6980..6984 "then" + WHITESPACE@6984..6991 "\n " + TRUE_EXPR@6991..7041 + EXPR@6991..7041 + EXPR_ARRAY_COMP@6991..7041 + L_BRACK@6991..6992 "[" + EXPR@6992..7004 + EXPR_VAR@6992..6995 + NAME@6992..6995 + IDENT@6992..6995 "std" + SUFFIX_INDEX@6995..7001 + DOT@6995..6996 "." + NAME@6996..7001 + IDENT@6996..7001 "prune" + SUFFIX_APPLY@7001..7004 + ARGS_DESC@7001..7004 + L_PAREN@7001..7002 "(" + ARG@7002..7003 + EXPR@7002..7003 + EXPR_VAR@7002..7003 + NAME@7002..7003 + IDENT@7002..7003 "x" + R_PAREN@7003..7004 ")" + WHITESPACE@7004..7005 " " + FOR_SPEC@7005..7015 + FOR_KW@7005..7008 "for" + WHITESPACE@7008..7009 " " + DESTRUCT_FULL@7009..7010 + NAME@7009..7010 + IDENT@7009..7010 "x" + WHITESPACE@7010..7011 " " + IN_KW@7011..7013 "in" + WHITESPACE@7013..7014 " " + EXPR@7014..7015 + EXPR_VAR@7014..7015 + NAME@7014..7015 + IDENT@7014..7015 "a" + WHITESPACE@7015..7016 " " + IF_SPEC@7016..7040 + IF_KW@7016..7018 "if" + WHITESPACE@7018..7019 " " + EXPR@7019..7040 + EXPR_VAR@7019..7028 + NAME@7019..7028 + IDENT@7019..7028 "isContent" + SUFFIX_APPLY@7028..7040 + ARGS_DESC@7028..7040 + L_PAREN@7028..7029 "(" + ARG@7029..7039 + EXPR@7029..7039 + EXPR_LITERAL@7029..7030 + DOLLAR@7029..7030 "$" + SUFFIX_INDEX@7030..7036 + DOT@7030..7031 "." + NAME@7031..7036 + IDENT@7031..7036 "prune" + SUFFIX_APPLY@7036..7039 + ARGS_DESC@7036..7039 + L_PAREN@7036..7037 "(" + ARG@7037..7038 + EXPR@7037..7038 + EXPR_VAR@7037..7038 + NAME@7037..7038 + IDENT@7037..7038 "x" + R_PAREN@7038..7039 ")" + R_PAREN@7039..7040 ")" + R_BRACK@7040..7041 "]" + WHITESPACE@7041..7046 "\n " + ELSE_KW@7046..7050 "else" + WHITESPACE@7050..7051 " " + FALSE_EXPR@7051..7191 + EXPR@7051..7191 + EXPR_IF_THEN_ELSE@7051..7191 + IF_KW@7051..7053 "if" + WHITESPACE@7053..7054 " " + EXPR@7054..7069 + EXPR_VAR@7054..7057 + NAME@7054..7057 + IDENT@7054..7057 "std" + SUFFIX_INDEX@7057..7066 + DOT@7057..7058 "." + NAME@7058..7066 + IDENT@7058..7066 "isObject" + SUFFIX_APPLY@7066..7069 + ARGS_DESC@7066..7069 + L_PAREN@7066..7067 "(" + ARG@7067..7068 + EXPR@7067..7068 + EXPR_VAR@7067..7068 + NAME@7067..7068 + IDENT@7067..7068 "a" + R_PAREN@7068..7069 ")" + WHITESPACE@7069..7070 " " + THEN_KW@7070..7074 "then" + WHITESPACE@7074..7075 " " + TRUE_EXPR@7075..7178 + EXPR@7075..7178 + EXPR_OBJECT@7075..7178 + OBJ_BODY_COMP@7075..7178 + L_BRACE@7075..7076 "{" + WHITESPACE@7076..7083 "\n " + MEMBER_FIELD_NORMAL@7083..7101 + FIELD_NAME_DYNAMIC@7083..7086 + L_BRACK@7083..7084 "[" + EXPR@7084..7085 + EXPR_VAR@7084..7085 + NAME@7084..7085 + IDENT@7084..7085 "x" + R_BRACK@7085..7086 "]" + COLON@7086..7087 ":" + WHITESPACE@7087..7088 " " + EXPR@7088..7101 + EXPR_LITERAL@7088..7089 + DOLLAR@7088..7089 "$" + SUFFIX_INDEX@7089..7095 + DOT@7089..7090 "." + NAME@7090..7095 + IDENT@7090..7095 "prune" + SUFFIX_APPLY@7095..7101 + ARGS_DESC@7095..7101 + L_PAREN@7095..7096 "(" + ARG@7096..7100 + EXPR@7096..7100 + EXPR_VAR@7096..7097 + NAME@7096..7097 + IDENT@7096..7097 "a" + SUFFIX_INDEX_EXPR@7097..7100 + L_BRACK@7097..7098 "[" + EXPR@7098..7099 + EXPR_VAR@7098..7099 + NAME@7098..7099 + IDENT@7098..7099 "x" + R_BRACK@7099..7100 "]" + R_PAREN@7100..7101 ")" + WHITESPACE@7101..7108 "\n " + FOR_SPEC@7108..7136 + FOR_KW@7108..7111 "for" + WHITESPACE@7111..7112 " " + DESTRUCT_FULL@7112..7113 + NAME@7112..7113 + IDENT@7112..7113 "x" + WHITESPACE@7113..7114 " " + IN_KW@7114..7116 "in" + WHITESPACE@7116..7117 " " + EXPR@7117..7136 + EXPR_VAR@7117..7120 + NAME@7117..7120 + IDENT@7117..7120 "std" + SUFFIX_INDEX@7120..7133 + DOT@7120..7121 "." + NAME@7121..7133 + IDENT@7121..7133 "objectFields" + SUFFIX_APPLY@7133..7136 + ARGS_DESC@7133..7136 + L_PAREN@7133..7134 "(" + ARG@7134..7135 + EXPR@7134..7135 + EXPR_VAR@7134..7135 + NAME@7134..7135 + IDENT@7134..7135 "a" + R_PAREN@7135..7136 ")" + WHITESPACE@7136..7143 "\n " + IF_SPEC@7143..7172 + IF_KW@7143..7145 "if" + WHITESPACE@7145..7146 " " + EXPR@7146..7172 + EXPR_VAR@7146..7155 + NAME@7146..7155 + IDENT@7146..7155 "isContent" + SUFFIX_APPLY@7155..7172 + ARGS_DESC@7155..7172 + L_PAREN@7155..7156 "(" + ARG@7156..7171 + EXPR@7156..7171 + EXPR_VAR@7156..7159 + NAME@7156..7159 + IDENT@7156..7159 "std" + SUFFIX_INDEX@7159..7165 + DOT@7159..7160 "." + NAME@7160..7165 + IDENT@7160..7165 "prune" + SUFFIX_APPLY@7165..7171 + ARGS_DESC@7165..7171 + L_PAREN@7165..7166 "(" + ARG@7166..7170 + EXPR@7166..7170 + EXPR_VAR@7166..7167 + NAME@7166..7167 + IDENT@7166..7167 "a" + SUFFIX_INDEX_EXPR@7167..7170 + L_BRACK@7167..7168 "[" + EXPR@7168..7169 + EXPR_VAR@7168..7169 + NAME@7168..7169 + IDENT@7168..7169 "x" + R_BRACK@7169..7170 "]" + R_PAREN@7170..7171 ")" + R_PAREN@7171..7172 ")" + WHITESPACE@7172..7177 "\n " + R_BRACE@7177..7178 "}" + WHITESPACE@7178..7179 " " + ELSE_KW@7179..7183 "else" + WHITESPACE@7183..7190 "\n " + FALSE_EXPR@7190..7191 + EXPR@7190..7191 + EXPR_VAR@7190..7191 + NAME@7190..7191 + IDENT@7190..7191 "a" + COMMA@7191..7192 "," + WHITESPACE@7192..7196 "\n\n " + MEMBER_FIELD_METHOD@7196..7411 + FIELD_NAME_FIXED@7196..7200 + NAME@7196..7200 + IDENT@7196..7200 "find" + PARAMS_DESC@7200..7212 + L_PAREN@7200..7201 "(" + PARAM@7201..7206 + DESTRUCT_FULL@7201..7206 + NAME@7201..7206 + IDENT@7201..7206 "value" + COMMA@7206..7207 "," + WHITESPACE@7207..7208 " " + PARAM@7208..7211 + DESTRUCT_FULL@7208..7211 + NAME@7208..7211 + IDENT@7208..7211 "arr" + R_PAREN@7211..7212 ")" + COLONCOLON@7212..7214 "::" + WHITESPACE@7214..7219 "\n " + EXPR@7219..7411 + EXPR_IF_THEN_ELSE@7219..7411 + IF_KW@7219..7221 "if" + WHITESPACE@7221..7222 " " + EXPR@7222..7239 + EXPR_UNARY@7222..7239 + NOT@7222..7223 "!" + EXPR_VAR@7223..7226 + NAME@7223..7226 + IDENT@7223..7226 "std" + SUFFIX_INDEX@7226..7234 + DOT@7226..7227 "." + NAME@7227..7234 + IDENT@7227..7234 "isArray" + SUFFIX_APPLY@7234..7239 + ARGS_DESC@7234..7239 + L_PAREN@7234..7235 "(" + ARG@7235..7238 + EXPR@7235..7238 + EXPR_VAR@7235..7238 + NAME@7235..7238 + IDENT@7235..7238 "arr" + R_PAREN@7238..7239 ")" + WHITESPACE@7239..7240 " " + THEN_KW@7240..7244 "then" + WHITESPACE@7244..7251 "\n " + TRUE_EXPR@7251..7321 + EXPR@7251..7321 + EXPR_ERROR@7251..7321 + ERROR_KW@7251..7256 "error" + WHITESPACE@7256..7257 " " + EXPR@7257..7321 + EXPR_BINARY@7257..7321 + EXPR@7257..7305 + EXPR_STRING@7257..7305 + STRING_SINGLE@7257..7305 "'find second paramete ..." + WHITESPACE@7305..7306 " " + PLUS@7306..7307 "+" + WHITESPACE@7307..7308 " " + EXPR@7308..7321 + EXPR_VAR@7308..7311 + NAME@7308..7311 + IDENT@7308..7311 "std" + SUFFIX_INDEX@7311..7316 + DOT@7311..7312 "." + NAME@7312..7316 + IDENT@7312..7316 "type" + SUFFIX_APPLY@7316..7321 + ARGS_DESC@7316..7321 + L_PAREN@7316..7317 "(" + ARG@7317..7320 + EXPR@7317..7320 + EXPR_VAR@7317..7320 + NAME@7317..7320 + IDENT@7317..7320 "arr" + R_PAREN@7320..7321 ")" + WHITESPACE@7321..7326 "\n " + ELSE_KW@7326..7330 "else" + WHITESPACE@7330..7337 "\n " + FALSE_EXPR@7337..7411 + EXPR@7337..7411 + EXPR_VAR@7337..7340 + NAME@7337..7340 + IDENT@7337..7340 "std" + SUFFIX_INDEX@7340..7347 + DOT@7340..7341 "." + NAME@7341..7347 + IDENT@7341..7347 "filter" + SUFFIX_APPLY@7347..7411 + ARGS_DESC@7347..7411 + L_PAREN@7347..7348 "(" + ARG@7348..7375 + EXPR@7348..7375 + EXPR_FUNCTION@7348..7375 + FUNCTION_KW@7348..7356 "function" + PARAMS_DESC@7356..7359 + L_PAREN@7356..7357 "(" + PARAM@7357..7358 + DESTRUCT_FULL@7357..7358 + NAME@7357..7358 + IDENT@7357..7358 "i" + R_PAREN@7358..7359 ")" + WHITESPACE@7359..7360 " " + EXPR@7360..7375 + EXPR_BINARY@7360..7375 + EXPR@7360..7363 + EXPR_VAR@7360..7363 + NAME@7360..7363 + IDENT@7360..7363 "arr" + SUFFIX_INDEX_EXPR@7363..7366 + L_BRACK@7363..7364 "[" + EXPR@7364..7365 + EXPR_VAR@7364..7365 + NAME@7364..7365 + IDENT@7364..7365 "i" + R_BRACK@7365..7366 "]" + WHITESPACE@7366..7367 " " + EQ@7367..7369 "==" + WHITESPACE@7369..7370 " " + EXPR@7370..7375 + EXPR_VAR@7370..7375 + NAME@7370..7375 + IDENT@7370..7375 "value" + COMMA@7375..7376 "," + WHITESPACE@7376..7377 " " + ARG@7377..7410 + EXPR@7377..7410 + EXPR_VAR@7377..7380 + NAME@7377..7380 + IDENT@7377..7380 "std" + SUFFIX_INDEX@7380..7386 + DOT@7380..7381 "." + NAME@7381..7386 + IDENT@7381..7386 "range" + SUFFIX_APPLY@7386..7410 + ARGS_DESC@7386..7410 + L_PAREN@7386..7387 "(" + ARG@7387..7388 + EXPR@7387..7388 + EXPR_NUMBER@7387..7388 + FLOAT@7387..7388 "0" + COMMA@7388..7389 "," + WHITESPACE@7389..7390 " " + ARG@7390..7409 + EXPR@7390..7409 + EXPR_BINARY@7390..7409 + EXPR@7390..7393 + EXPR_VAR@7390..7393 + NAME@7390..7393 + IDENT@7390..7393 "std" + SUFFIX_INDEX@7393..7400 + DOT@7393..7394 "." + NAME@7394..7400 + IDENT@7394..7400 "length" + SUFFIX_APPLY@7400..7405 + ARGS_DESC@7400..7405 + L_PAREN@7400..7401 "(" + ARG@7401..7404 + EXPR@7401..7404 + EXPR_VAR@7401..7404 + NAME@7401..7404 + IDENT@7401..7404 "arr" + R_PAREN@7404..7405 ")" + WHITESPACE@7405..7406 " " + MINUS@7406..7407 "-" + WHITESPACE@7407..7408 " " + EXPR@7408..7409 + EXPR_NUMBER@7408..7409 + FLOAT@7408..7409 "1" + R_PAREN@7409..7410 ")" + R_PAREN@7410..7411 ")" + COMMA@7411..7412 "," + WHITESPACE@7412..7416 "\n\n " + SINGLE_LINE_SLASH_COMMENT@7416..7426 "// Compat\n" + WHITESPACE@7426..7428 " " + MEMBER_FIELD_METHOD@7428..7538 + FIELD_NAME_FIXED@7428..7443 + NAME@7428..7443 + IDENT@7428..7443 "__compare_array" + PARAMS_DESC@7443..7455 + L_PAREN@7443..7444 "(" + PARAM@7444..7448 + DESTRUCT_FULL@7444..7448 + NAME@7444..7448 + IDENT@7444..7448 "arr1" + COMMA@7448..7449 "," + WHITESPACE@7449..7450 " " + PARAM@7450..7454 + DESTRUCT_FULL@7450..7454 + NAME@7450..7454 + IDENT@7450..7454 "arr2" + R_PAREN@7454..7455 ")" + COLONCOLON@7455..7457 "::" + WHITESPACE@7457..7462 "\n " + EXPR@7462..7538 + STMT_ASSERT@7462..7508 + ASSERTION@7462..7507 + ASSERT_KW@7462..7468 "assert" + WHITESPACE@7468..7469 " " + EXPR@7469..7507 + EXPR_BINARY@7469..7507 + EXPR@7469..7472 + EXPR_VAR@7469..7472 + NAME@7469..7472 + IDENT@7469..7472 "std" + SUFFIX_INDEX@7472..7480 + DOT@7472..7473 "." + NAME@7473..7480 + IDENT@7473..7480 "isArray" + SUFFIX_APPLY@7480..7486 + ARGS_DESC@7480..7486 + L_PAREN@7480..7481 "(" + ARG@7481..7485 + EXPR@7481..7485 + EXPR_VAR@7481..7485 + NAME@7481..7485 + IDENT@7481..7485 "arr1" + R_PAREN@7485..7486 ")" + WHITESPACE@7486..7487 " " + AND@7487..7489 "&&" + WHITESPACE@7489..7490 " " + EXPR@7490..7507 + EXPR_VAR@7490..7493 + NAME@7490..7493 + IDENT@7490..7493 "std" + SUFFIX_INDEX@7493..7501 + DOT@7493..7494 "." + NAME@7494..7501 + IDENT@7494..7501 "isArray" + SUFFIX_APPLY@7501..7507 + ARGS_DESC@7501..7507 + L_PAREN@7501..7502 "(" + ARG@7502..7506 + EXPR@7502..7506 + EXPR_VAR@7502..7506 + NAME@7502..7506 + IDENT@7502..7506 "arr2" + R_PAREN@7506..7507 ")" + SEMI@7507..7508 ";" + WHITESPACE@7508..7513 "\n " + EXPR_VAR@7513..7516 + NAME@7513..7516 + IDENT@7513..7516 "std" + SUFFIX_INDEX@7516..7526 + DOT@7516..7517 "." + NAME@7517..7526 + IDENT@7517..7526 "__compare" + SUFFIX_APPLY@7526..7538 + ARGS_DESC@7526..7538 + L_PAREN@7526..7527 "(" + ARG@7527..7531 + EXPR@7527..7531 + EXPR_VAR@7527..7531 + NAME@7527..7531 + IDENT@7527..7531 "arr1" + COMMA@7531..7532 "," + WHITESPACE@7532..7533 " " + ARG@7533..7537 + EXPR@7533..7537 + EXPR_VAR@7533..7537 + NAME@7533..7537 + IDENT@7533..7537 "arr2" + R_PAREN@7537..7538 ")" + COMMA@7538..7539 "," + WHITESPACE@7539..7542 "\n " + MEMBER_FIELD_METHOD@7542..7606 + FIELD_NAME_FIXED@7542..7554 + NAME@7542..7554 + IDENT@7542..7554 "__array_less" + PARAMS_DESC@7554..7566 + L_PAREN@7554..7555 "(" + PARAM@7555..7559 + DESTRUCT_FULL@7555..7559 + NAME@7555..7559 + IDENT@7555..7559 "arr1" + COMMA@7559..7560 "," + WHITESPACE@7560..7561 " " + PARAM@7561..7565 + DESTRUCT_FULL@7561..7565 + NAME@7561..7565 + IDENT@7561..7565 "arr2" + R_PAREN@7565..7566 ")" + COLONCOLON@7566..7568 "::" + WHITESPACE@7568..7569 " " + EXPR@7569..7606 + EXPR_BINARY@7569..7606 + EXPR@7569..7572 + EXPR_VAR@7569..7572 + NAME@7569..7572 + IDENT@7569..7572 "std" + SUFFIX_INDEX@7572..7588 + DOT@7572..7573 "." + NAME@7573..7588 + IDENT@7573..7588 "__compare_array" + SUFFIX_APPLY@7588..7600 + ARGS_DESC@7588..7600 + L_PAREN@7588..7589 "(" + ARG@7589..7593 + EXPR@7589..7593 + EXPR_VAR@7589..7593 + NAME@7589..7593 + IDENT@7589..7593 "arr1" + COMMA@7593..7594 "," + WHITESPACE@7594..7595 " " + ARG@7595..7599 + EXPR@7595..7599 + EXPR_VAR@7595..7599 + NAME@7595..7599 + IDENT@7595..7599 "arr2" + R_PAREN@7599..7600 ")" + WHITESPACE@7600..7601 " " + EQ@7601..7603 "==" + WHITESPACE@7603..7604 " " + EXPR@7604..7606 + EXPR_UNARY@7604..7606 + MINUS@7604..7605 "-" + EXPR_NUMBER@7605..7606 + FLOAT@7605..7606 "1" + COMMA@7606..7607 "," + WHITESPACE@7607..7610 "\n " + MEMBER_FIELD_METHOD@7610..7676 + FIELD_NAME_FIXED@7610..7625 + NAME@7610..7625 + IDENT@7610..7625 "__array_greater" + PARAMS_DESC@7625..7637 + L_PAREN@7625..7626 "(" + PARAM@7626..7630 + DESTRUCT_FULL@7626..7630 + NAME@7626..7630 + IDENT@7626..7630 "arr1" + COMMA@7630..7631 "," + WHITESPACE@7631..7632 " " + PARAM@7632..7636 + DESTRUCT_FULL@7632..7636 + NAME@7632..7636 + IDENT@7632..7636 "arr2" + R_PAREN@7636..7637 ")" + COLONCOLON@7637..7639 "::" + WHITESPACE@7639..7640 " " + EXPR@7640..7676 + EXPR_BINARY@7640..7676 + EXPR@7640..7643 + EXPR_VAR@7640..7643 + NAME@7640..7643 + IDENT@7640..7643 "std" + SUFFIX_INDEX@7643..7659 + DOT@7643..7644 "." + NAME@7644..7659 + IDENT@7644..7659 "__compare_array" + SUFFIX_APPLY@7659..7671 + ARGS_DESC@7659..7671 + L_PAREN@7659..7660 "(" + ARG@7660..7664 + EXPR@7660..7664 + EXPR_VAR@7660..7664 + NAME@7660..7664 + IDENT@7660..7664 "arr1" + COMMA@7664..7665 "," + WHITESPACE@7665..7666 " " + ARG@7666..7670 + EXPR@7666..7670 + EXPR_VAR@7666..7670 + NAME@7666..7670 + IDENT@7666..7670 "arr2" + R_PAREN@7670..7671 ")" + WHITESPACE@7671..7672 " " + EQ@7672..7674 "==" + WHITESPACE@7674..7675 " " + EXPR@7675..7676 + EXPR_NUMBER@7675..7676 + FLOAT@7675..7676 "1" + COMMA@7676..7677 "," + WHITESPACE@7677..7680 "\n " + MEMBER_FIELD_METHOD@7680..7752 + FIELD_NAME_FIXED@7680..7701 + NAME@7680..7701 + IDENT@7680..7701 "__array_less_or_equal" + PARAMS_DESC@7701..7713 + L_PAREN@7701..7702 "(" + PARAM@7702..7706 + DESTRUCT_FULL@7702..7706 + NAME@7702..7706 + IDENT@7702..7706 "arr1" + COMMA@7706..7707 "," + WHITESPACE@7707..7708 " " + PARAM@7708..7712 + DESTRUCT_FULL@7708..7712 + NAME@7708..7712 + IDENT@7708..7712 "arr2" + R_PAREN@7712..7713 ")" + COLONCOLON@7713..7715 "::" + WHITESPACE@7715..7716 " " + EXPR@7716..7752 + EXPR_BINARY@7716..7752 + EXPR@7716..7719 + EXPR_VAR@7716..7719 + NAME@7716..7719 + IDENT@7716..7719 "std" + SUFFIX_INDEX@7719..7735 + DOT@7719..7720 "." + NAME@7720..7735 + IDENT@7720..7735 "__compare_array" + SUFFIX_APPLY@7735..7747 + ARGS_DESC@7735..7747 + L_PAREN@7735..7736 "(" + ARG@7736..7740 + EXPR@7736..7740 + EXPR_VAR@7736..7740 + NAME@7736..7740 + IDENT@7736..7740 "arr1" + COMMA@7740..7741 "," + WHITESPACE@7741..7742 " " + ARG@7742..7746 + EXPR@7742..7746 + EXPR_VAR@7742..7746 + NAME@7742..7746 + IDENT@7742..7746 "arr2" + R_PAREN@7746..7747 ")" + WHITESPACE@7747..7748 " " + LE@7748..7750 "<=" + WHITESPACE@7750..7751 " " + EXPR@7751..7752 + EXPR_NUMBER@7751..7752 + FLOAT@7751..7752 "0" + COMMA@7752..7753 "," + WHITESPACE@7753..7756 "\n " + MEMBER_FIELD_METHOD@7756..7831 + FIELD_NAME_FIXED@7756..7780 + NAME@7756..7780 + IDENT@7756..7780 "__array_greater_or_equal" + PARAMS_DESC@7780..7792 + L_PAREN@7780..7781 "(" + PARAM@7781..7785 + DESTRUCT_FULL@7781..7785 + NAME@7781..7785 + IDENT@7781..7785 "arr1" + COMMA@7785..7786 "," + WHITESPACE@7786..7787 " " + PARAM@7787..7791 + DESTRUCT_FULL@7787..7791 + NAME@7787..7791 + IDENT@7787..7791 "arr2" + R_PAREN@7791..7792 ")" + COLONCOLON@7792..7794 "::" + WHITESPACE@7794..7795 " " + EXPR@7795..7831 + EXPR_BINARY@7795..7831 + EXPR@7795..7798 + EXPR_VAR@7795..7798 + NAME@7795..7798 + IDENT@7795..7798 "std" + SUFFIX_INDEX@7798..7814 + DOT@7798..7799 "." + NAME@7799..7814 + IDENT@7799..7814 "__compare_array" + SUFFIX_APPLY@7814..7826 + ARGS_DESC@7814..7826 + L_PAREN@7814..7815 "(" + ARG@7815..7819 + EXPR@7815..7819 + EXPR_VAR@7815..7819 + NAME@7815..7819 + IDENT@7815..7819 "arr1" + COMMA@7819..7820 "," + WHITESPACE@7820..7821 " " + ARG@7821..7825 + EXPR@7821..7825 + EXPR_VAR@7821..7825 + NAME@7821..7825 + IDENT@7821..7825 "arr2" + R_PAREN@7825..7826 ")" + WHITESPACE@7826..7827 " " + GE@7827..7829 ">=" + WHITESPACE@7829..7830 " " + EXPR@7830..7831 + EXPR_NUMBER@7830..7831 + FLOAT@7830..7831 "0" + COMMA@7831..7832 "," + WHITESPACE@7832..7833 "\n" + R_BRACE@7833..7834 "}" + WHITESPACE@7834..7835 "\n" --- /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" --- /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" --- /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" --- /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" --- /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 + `---- --- /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 | } + `---- --- /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) { + let _ = lex_str_block(lex); +} + +pub fn lex_str_block(lex: &mut Lexer) -> 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 { + if self.index == self.source.len() { + return None; + } + + match self.rest().chars().next() { + None => None, + Some(c) => { + self.index += c.len_utf8(); + Some(c) + } + } + } + + fn peek(&self) -> Option { + if self.index == self.source.len() { + return None; + } + + self.rest().chars().next() + } + + fn eat_while(&mut self, f: impl Fn(char) -> bool) -> usize { + if self.index == self.source.len() { + return 0; + } + + let next_char = self.rest().char_indices().find(|(_, c)| !f(*c)); + + match next_char { + None => { + let diff = self.source.len() - self.index; + self.index = self.source.len(); + diff + } + Some((idx, _)) => { + self.index += idx; + idx + } + } + } + + fn skip(&mut self, len: usize) { + self.index = match self.index + len { + n if n > self.source.len() => self.source.len(), + n => n, + }; + } + + fn pos(&self) -> Range { + if self.index == self.source.len() { + self.offset + self.index..self.offset + self.index + } else { + // TODO: char size + self.offset + self.index..self.offset + self.index + 1 + } + } + } + + // Check that b has at least the same whitespace prefix as a and returns the + // amount of this whitespace, otherwise returns 0. If a has no whitespace + // prefix than return 0. + fn check_whitespace(a: &str, b: &str) -> usize { + let a = a.as_bytes(); + let b = b.as_bytes(); + + for i in 0..a.len() { + if a[i] != b' ' && a[i] != b'\t' { + // a has run out of whitespace and b matched up to this point. Return result. + return i; + } + + if i >= b.len() { + // We ran off the edge of b while a still has whitespace. Return 0 as failure. + return 0; + } + + if a[i] != b[i] { + // a has whitespace but b does not. Return 0 as failure. + return 0; + } + } + + // We ran off the end of a and b kept up + a.len() + } + + fn guess_token_end_and_bump<'a>(lex: &mut 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(()) +} --- /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, +} +impl Diagnostic for MyDiagnostic { + fn code<'a>(&'a self) -> Option> { + None + } + + fn severity(&self) -> Option { + None + } + + fn help<'a>(&'a self) -> Option> { + None + } + + fn url<'a>(&'a self) -> Option> { + None + } + + fn source_code(&self) -> Option<&dyn miette::SourceCode> { + Some(&self.code) + } + + fn labels(&self) -> Option + '_>> { + Some(Box::new(self.spans.clone().into_iter())) + } + + fn related<'a>(&'a self) -> Option + 'a>> { + None + } +} + +fn process(text: &str) -> String { + use std::fmt::Write; + let mut out = String::new(); + let (node, 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::>() + .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); +} --- /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 = >::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 = >::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 +} --- 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() --- a/flake.nix +++ b/flake.nix @@ -131,6 +131,8 @@ cargo-edit cargo-asm cargo-outdated + cargo-watch + cargo-insta lld hyperfine graphviz --- /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" --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,7 @@ +use anyhow::Result; + +mod sourcegen; + +fn main() -> Result<()> { + sourcegen::generate_ungrammar() +} --- /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) { + to_remove.into_iter().rev().for_each(|idx| { + self.fields.remove(idx); + }); + } +} + +#[allow(dead_code)] +#[derive(Default, Debug)] +pub struct AstSrc { + pub nodes: Vec, + pub enums: Vec, + pub token_enums: Vec, +} +#[derive(Debug)] +pub struct AstNodeSrc { + pub doc: Vec, + pub name: String, + pub traits: Vec, + pub fields: Vec, +} + +#[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, + pub name: String, + pub traits: Vec, + pub variants: Vec, +} + +#[derive(Debug, Clone)] +pub struct AstTokenEnumSrc { + pub doc: Vec, + pub name: String, + pub variants: Vec, +} + +impl Field { + pub fn is_many(&self) -> bool { + matches!( + self, + Field::Node { + cardinality: Cardinality::Many, + .. + } + ) + } + + pub fn token_name(&self) -> Option { + match self { + Field::Token(token) => Some(token.clone()), + _ => None, + } + } + pub fn token_kind(&self, kinds: &KindsSrc) -> Option { + 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::>(); + + 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> { + 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> { + 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, + 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, + 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::>()) + }) + .unwrap_or_else(|| { + enums + .iter() + .find_map(|node| { + if &node.name != var { + return None; + } + Some(node.traits.iter().cloned().collect::>()) + }) + .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(); + } +} --- /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, + defined_node_names: HashSet, + pub nodes: Vec, +} + +#[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, + priority: Option, + }, + /// 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, + }, +} + +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 { + 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 { + 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 +} --- /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 { + 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::>(); + + 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 { + 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 { + 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 { + 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 { + 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 { + 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(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::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)* } + } +} --- /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 { + // 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 + )) +}