From abda8d0de7a605c3c18d4d256fee49715efb0b8a Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Thu, 30 Nov 2023 17:09:04 -0800 Subject: [PATCH] chore: :construction: subtree prism-sys --- Cargo.lock | 183 +- crates/components/Cargo.toml | 4 - crates/prism-sys/Cargo.toml | 30 + crates/prism-sys/README.md | 75 + crates/prism-sys/build/main.rs | 123 + crates/prism-sys/build/vendored.rs | 122 + crates/prism-sys/src/lib.rs | 35 + crates/prism-sys/tests/node_tests.rs | 27 + crates/prism-sys/tests/pack_tests.rs | 57 + crates/prism-sys/tests/parser_tests.rs | 98 + crates/prism-sys/tests/utils_tests.rs | 121 + .../vendor/prism-0.18.0/include/prism.h | 274 + .../vendor/prism-0.18.0/include/prism/ast.h | 4440 ++++ .../prism-0.18.0/include/prism/defines.h | 77 + .../prism-0.18.0/include/prism/diagnostic.h | 296 + .../include/prism/enc/pm_encoding.h | 231 + .../vendor/prism-0.18.0/include/prism/node.h | 57 + .../prism-0.18.0/include/prism/options.h | 204 + .../vendor/prism-0.18.0/include/prism/pack.h | 152 + .../prism-0.18.0/include/prism/parser.h | 711 + .../prism-0.18.0/include/prism/prettyprint.h | 26 + .../prism-0.18.0/include/prism/regexp.h | 33 + .../include/prism/util/pm_buffer.h | 146 + .../prism-0.18.0/include/prism/util/pm_char.h | 205 + .../include/prism/util/pm_constant_pool.h | 191 + .../prism-0.18.0/include/prism/util/pm_list.h | 97 + .../include/prism/util/pm_memchr.h | 29 + .../include/prism/util/pm_newline_list.h | 104 + .../include/prism/util/pm_state_stack.h | 42 + .../include/prism/util/pm_string.h | 150 + .../include/prism/util/pm_string_list.h | 44 + .../include/prism/util/pm_strncasecmp.h | 32 + .../include/prism/util/pm_strpbrk.h | 43 + .../prism-0.18.0/include/prism/version.h | 29 + .../vendor/prism-0.18.0/src/diagnostic.c | 354 + .../vendor/prism-0.18.0/src/enc/pm_big5.c | 116 + .../vendor/prism-0.18.0/src/enc/pm_cp51932.c | 57 + .../vendor/prism-0.18.0/src/enc/pm_cp949.c | 57 + .../vendor/prism-0.18.0/src/enc/pm_cp950.c | 57 + .../vendor/prism-0.18.0/src/enc/pm_euc_jp.c | 69 + .../vendor/prism-0.18.0/src/enc/pm_gbk.c | 65 + .../prism-0.18.0/src/enc/pm_mac_japanese.c | 57 + .../prism-0.18.0/src/enc/pm_shift_jis.c | 57 + .../vendor/prism-0.18.0/src/enc/pm_tables.c | 2108 ++ .../vendor/prism-0.18.0/src/enc/pm_unicode.c | 2369 +++ .../prism-0.18.0/src/enc/pm_windows_31j.c | 57 + .../prism-sys/vendor/prism-0.18.0/src/node.c | 2736 +++ .../vendor/prism-0.18.0/src/options.c | 189 + .../prism-sys/vendor/prism-0.18.0/src/pack.c | 493 + .../vendor/prism-0.18.0/src/prettyprint.c | 8384 ++++++++ .../prism-sys/vendor/prism-0.18.0/src/prism.c | 17287 ++++++++++++++++ .../vendor/prism-0.18.0/src/regexp.c | 638 + .../vendor/prism-0.18.0/src/serialize.c | 2040 ++ .../vendor/prism-0.18.0/src/token_type.c | 351 + .../vendor/prism-0.18.0/src/util/pm_buffer.c | 179 + .../vendor/prism-0.18.0/src/util/pm_char.c | 318 + .../prism-0.18.0/src/util/pm_constant_pool.c | 296 + .../vendor/prism-0.18.0/src/util/pm_list.c | 49 + .../vendor/prism-0.18.0/src/util/pm_memchr.c | 35 + .../prism-0.18.0/src/util/pm_newline_list.c | 96 + .../prism-0.18.0/src/util/pm_state_stack.c | 25 + .../vendor/prism-0.18.0/src/util/pm_string.c | 210 + .../prism-0.18.0/src/util/pm_string_list.c | 28 + .../prism-0.18.0/src/util/pm_strncasecmp.c | 24 + .../vendor/prism-0.18.0/src/util/pm_strpbrk.c | 72 + 65 files changed, 47223 insertions(+), 138 deletions(-) create mode 100644 crates/prism-sys/Cargo.toml create mode 100644 crates/prism-sys/README.md create mode 100644 crates/prism-sys/build/main.rs create mode 100644 crates/prism-sys/build/vendored.rs create mode 100644 crates/prism-sys/src/lib.rs create mode 100644 crates/prism-sys/tests/node_tests.rs create mode 100644 crates/prism-sys/tests/pack_tests.rs create mode 100644 crates/prism-sys/tests/parser_tests.rs create mode 100644 crates/prism-sys/tests/utils_tests.rs create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/ast.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/defines.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/diagnostic.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/enc/pm_encoding.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/node.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/options.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/pack.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/parser.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/prettyprint.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/regexp.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_buffer.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_char.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_constant_pool.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_list.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_memchr.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_newline_list.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_state_stack.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_string.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_string_list.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_strncasecmp.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_strpbrk.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/include/prism/version.h create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/diagnostic.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_big5.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp51932.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp949.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp950.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_euc_jp.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_gbk.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_mac_japanese.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_shift_jis.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_tables.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_unicode.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_windows_31j.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/node.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/options.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/pack.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/prettyprint.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/prism.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/regexp.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/serialize.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/token_type.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_buffer.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_char.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_constant_pool.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_list.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_memchr.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_newline_list.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_state_stack.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_string.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_string_list.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_strncasecmp.c create mode 100644 crates/prism-sys/vendor/prism-0.18.0/src/util/pm_strpbrk.c diff --git a/Cargo.lock b/Cargo.lock index 97cc41da..950cf6cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -538,12 +538,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] -name = "bincode" -version = "1.3.3" +name = "bindgen" +version = "0.66.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" dependencies = [ - "serde", + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.39", + "which", ] [[package]] @@ -970,7 +984,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3120ebb80a9de008e638ad833d4127d50ea3d3a960ea23ea69bc66d9358a028" dependencies = [ - "bindgen", + "bindgen 0.69.1", ] [[package]] @@ -1183,15 +1197,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" -[[package]] -name = "deranged" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" -dependencies = [ - "powerfmt", -] - [[package]] name = "derivative" version = "2.2.0" @@ -1615,16 +1620,6 @@ dependencies = [ "zune-inflate", ] -[[package]] -name = "fancy-regex" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" -dependencies = [ - "bit-set", - "regex", -] - [[package]] name = "fancy_constructor" version = "1.2.2" @@ -2640,21 +2635,6 @@ dependencies = [ "redox_syscall 0.4.1", ] -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -2801,7 +2781,6 @@ dependencies = [ "serde", "slab", "strum", - "syntect", "wgpu", ] @@ -3955,20 +3934,6 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" -[[package]] -name = "plist" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" -dependencies = [ - "base64 0.21.5", - "indexmap 2.1.0", - "line-wrap", - "quick-xml", - "serde", - "time", -] - [[package]] name = "png" version = "0.17.10" @@ -4057,12 +4022,6 @@ dependencies = [ "winreg 0.10.1", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "pp-rs" version = "0.2.1" @@ -4078,6 +4037,24 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn 2.0.39", +] + +[[package]] +name = "prism-sys" +version = "0.18.0" +dependencies = [ + "bindgen 0.66.1", + "cc", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -4145,15 +4122,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", -] - [[package]] name = "quote" version = "1.0.33" @@ -4501,12 +4469,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "same-file" version = "1.0.6" @@ -5030,27 +4992,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syntect" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02b4b303bf8d08bfeb0445cba5068a3d306b6baece1d5582171a9bf49188f91" -dependencies = [ - "bincode", - "bitflags 1.3.2", - "fancy-regex", - "flate2", - "fnv", - "once_cell", - "plist", - "regex-syntax 0.7.5", - "serde", - "serde_json", - "thiserror", - "walkdir", - "yaml-rust", -] - [[package]] name = "system-configuration" version = "0.5.1" @@ -5249,35 +5190,6 @@ dependencies = [ "weezl", ] -[[package]] -name = "time" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" -dependencies = [ - "deranged", - "itoa", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" -dependencies = [ - "time-core", -] - [[package]] name = "tiny-keccak" version = "2.0.2" @@ -6104,6 +6016,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.25", +] + [[package]] name = "widestring" version = "1.0.2" @@ -6478,15 +6402,6 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "zbus" version = "3.14.1" diff --git a/crates/components/Cargo.toml b/crates/components/Cargo.toml index 1611ca7f..54bac418 100644 --- a/crates/components/Cargo.toml +++ b/crates/components/Cargo.toml @@ -29,10 +29,6 @@ egui-wgpu.workspace = true wgpu.workspace = true glam.workspace = true -syntect = { version = "5.1.0", default-features = false, features = [ - "default-fancy", -] } - parking_lot.workspace = true itertools.workspace = true diff --git a/crates/prism-sys/Cargo.toml b/crates/prism-sys/Cargo.toml new file mode 100644 index 00000000..8dbf4462 --- /dev/null +++ b/crates/prism-sys/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "prism-sys" +version = "0.18.0" +edition = "2021" +license-file = "../../LICENSE.md" +repository = "https://github.com/ruby/prism" +description = "Rust bindings to Ruby's prism parsing library" +links = "prism" +authors = [ + "Steve Loveless ", + "Ian Ker-Seymer ", + "Kevin Newton ", +] +keywords = ["ruby", "parser", "ffi", "bindings"] +categories = [ + "api-bindings", + "development-tools::ffi", + "external-ffi-bindings", + "parsing", +] +build = "build/main.rs" +include = ["src/", "build/", "Cargo.toml", "Cargo.lock", "README.md", "vendor"] + +[build-dependencies] +bindgen = "0.66" +cc = { version = "1.0", optional = true } + +[features] +default = ["vendored"] +vendored = ["dep:cc"] diff --git a/crates/prism-sys/README.md b/crates/prism-sys/README.md new file mode 100644 index 00000000..9ebc8c72 --- /dev/null +++ b/crates/prism-sys/README.md @@ -0,0 +1,75 @@ +> [!IMPORTANT] +> luminol-prism-sys is currently based on ruby/prism@0.18.0 + +> [!IMPORTANT] +> luminol-prism-sys uses a vendored version of prism. +> We are temporarily vendoring our own version as one of the prism build steps requires rake (which needs ruby.) +> This step is only necessary to generate code, however, and the resulting code is independent of ruby. +> +> When updating from upstream prism you MUST update the prism source in `vendor/prism-{prism version}`. +> You can do this by cloning prism and running `rake templates`, and then copying over the source files into the vendor folder. +> +> Eventually when ruby releases prism on crates.io this will not be an issue and we can remove prism from our source tree. + +> [!NOTE] +> This is Luminol's modified version of prism-sys. The original version is licensed under MIT. +> +> To merge changes from upstream into this crate, first add prism as a remote: +> +> ``` +> git remote add -f --no-tags prism https://github.com/ruby/prism +> ``` +> +> Now, decide on which upstream prism commit you want to merge from and figure out the prism commit that the previous upstream merge was based on. The basis of the previous upstream merge should be written at the top of this README. **Please update the top of this README after merging.** +> +> In this example, we are merging from commit `6ca8896` (prism 0.18.0) and the previous merge was based on commit `d76f295` (prism 0.17.0). +> +> ``` +> git diff \ +> d76f295:rust/prism-sys \ +> 6ca8896:rust/prism-sys | +> git apply -3 --directory=crates/prism-sys +> ``` +> +> Fix any merge conflicts, and then do `git commit`. + +# prism-sys + +Rust bindings to [ruby/prism](https://github.com/ruby/prism)'s C API. + +## Examples + +Currently the best examples are found in the integration tests (in `tests/`). + +## Documentation + +Since this crate has not been released, docs are not yet online anywhere. You can generate them, +however, from this directory in this repo by running `cargo doc`, then opening +`target/doc/prism_sys/index.html` in your browser. (You could, instead, combine those two steps by +doing `cargo doc --open`!) + +## Development + +### Dependencies + +In addition to the Ruby prism dependencies, you shouldn't need anything else besides Rust. + +### Updating bindings + +`build.rs` (which gets called as part of running `cargo build`, `cargo test`, etc) is where we tell +`bindgen` which types, functions, etc. that we want it to generate for us. It's smart enough to know +to generate dependencies for items we specify in there (ex. `pm_parser_t` has fields of type +`pm_token_t`, but we don't need to tell `bindgen` about `pm_token_t`--it'll figure it out and +generate bindings for that type too). + +If you want to generate new bindings, update `build.rs` accordingly, then run `cargo doc` and check +the docs; that should tell you if `bindgen` generated all the things you need or not. + +### Testing + +Since almost all of the code is generated by the well-tested +[`bindgen`](https://github.com/rust-lang/rust-bindgen) crate, we only have some cursory integration +tests in `tests/`, really just validating types and functions got generated appropriately. (They +also give some hints about how to use the API from Rust!) To run the tests, run `cargo test`. + +Any new publicly exposed C API additions should get a test or two. diff --git a/crates/prism-sys/build/main.rs b/crates/prism-sys/build/main.rs new file mode 100644 index 00000000..23267945 --- /dev/null +++ b/crates/prism-sys/build/main.rs @@ -0,0 +1,123 @@ +#[cfg(feature = "vendored")] +mod vendored; + +use std::path::{Path, PathBuf}; + +fn main() { + #[cfg(feature = "vendored")] + vendored::build().expect("failed to build Prism from source"); + + let ruby_build_path = prism_lib_path(); + let ruby_include_path = prism_include_path(); + + // Tell cargo/rustc that we want to link against `libprism.a`. + println!("cargo:rustc-link-lib=static=prism"); + + // Add `[root]/build/` to the search paths, so it can find `libprism.a`. + println!("cargo:rustc-link-search=native={}", ruby_build_path.to_str().unwrap()); + + // This is where the magic happens. + let bindings = generate_bindings(&ruby_include_path); + + // Write the bindings to file. + write_bindings(&bindings); +} + +/// Gets the path to project files (`libprism*`) at `[root]/build/`. +/// +fn prism_lib_path() -> PathBuf { + if let Ok(lib_dir) = std::env::var("PRISM_LIB_DIR") { + return PathBuf::from(lib_dir); + } + + cargo_manifest_path().join("../../build/").canonicalize().unwrap() +} + +/// Gets the path to the header files that `bindgen` needs for doing code +/// generation. +/// +fn prism_include_path() -> PathBuf { + if let Ok(include_dir) = std::env::var("PRISM_INCLUDE_DIR") { + return PathBuf::from(include_dir); + } + + cargo_manifest_path().join("../../include/").canonicalize().unwrap() +} + +fn cargo_manifest_path() -> PathBuf { + PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()) +} + +/// Uses `bindgen` to generate bindings to the C API. Update this to allow new +/// types/functions/etc to be generated (it's allowlisted to only expose +/// functions that'd make sense for public consumption). +/// +/// This method only generates code in memory here--it doesn't write it to file. +/// +fn generate_bindings(ruby_include_path: &Path) -> bindgen::Bindings { + bindgen::Builder::default() + .derive_default(true) + .generate_block(true) + .generate_comments(true) + .header(ruby_include_path.join("prism/defines.h").to_str().unwrap()) + .header(ruby_include_path.join("prism.h").to_str().unwrap()) + .clang_arg(format!("-I{}", ruby_include_path.to_str().unwrap())) + .clang_arg("-fparse-all-comments") + .impl_debug(true) + .layout_tests(true) + .merge_extern_blocks(true) + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .prepend_enum_name(false) + .size_t_is_usize(true) + .sort_semantically(true) + // Structs + .allowlist_type("pm_comment_t") + .allowlist_type("pm_diagnostic_t") + .allowlist_type("pm_list_t") + .allowlist_type("pm_node_t") + .allowlist_type("pm_node_type") + .allowlist_type("pm_pack_size") + .allowlist_type("pm_parser_t") + .allowlist_type("pm_string_t") + .allowlist_type(r"^pm_\w+_node_t") + .allowlist_type(r"^pm_\w+_flags") + // Enums + .rustified_non_exhaustive_enum("pm_comment_type_t") + .rustified_non_exhaustive_enum(r"pm_\w+_flags") + .rustified_non_exhaustive_enum("pm_node_type") + .rustified_non_exhaustive_enum("pm_pack_encoding") + .rustified_non_exhaustive_enum("pm_pack_endian") + .rustified_non_exhaustive_enum("pm_pack_length_type") + .rustified_non_exhaustive_enum("pm_pack_result") + .rustified_non_exhaustive_enum("pm_pack_signed") + .rustified_non_exhaustive_enum("pm_pack_size") + .rustified_non_exhaustive_enum("pm_pack_type") + .rustified_non_exhaustive_enum("pm_pack_variant") + // Functions + .allowlist_function("pm_list_empty_p") + .allowlist_function("pm_list_free") + .allowlist_function("pm_node_destroy") + .allowlist_function("pm_pack_parse") + .allowlist_function("pm_parse") + .allowlist_function("pm_parser_free") + .allowlist_function("pm_parser_init") + .allowlist_function("pm_size_to_native") + .allowlist_function("pm_string_free") + .allowlist_function("pm_string_length") + .allowlist_function("pm_string_source") + .allowlist_function("pm_version") + // Vars + .allowlist_var(r"^pm_encoding\S+") + .generate() + .expect("Unable to generate prism bindings") +} + +/// Write the bindings to the `$OUT_DIR/bindings.rs` file. We'll pull these into +/// the actual library in `src/lib.rs`. +fn write_bindings(bindings: &bindgen::Bindings) { + let out_path = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/crates/prism-sys/build/vendored.rs b/crates/prism-sys/build/vendored.rs new file mode 100644 index 00000000..122fd3e2 --- /dev/null +++ b/crates/prism-sys/build/vendored.rs @@ -0,0 +1,122 @@ +use std::{ + fs, + path::{Path, PathBuf}, +}; + +/// Builds libprism.a from source, and configures the build script to use it. +pub fn build() -> Result<(), Box> { + assert!( + vendor_dir().exists(), + "Prism source directory does not exist, expected: {}", + vendor_dir().display(), + ); + + println!("cargo:rerun-if-changed={}", vendor_dir().display()); + + let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").expect("OUT_DIR is not set")); + let mut build = cc::Build::new(); + + let mut defines = vec![("PRISM_EXPORT_SYMBOLS", "1")]; + let mut includes = vec![include_dir()]; + let mut flags = vec![]; + + if let Ok(target) = std::env::var("TARGET") { + if target.contains("wasm32") && target.contains("wasi") { + println!("cargo:rerun-if-env-changed=WASI_SDK_PATH"); + + if let Ok(wasi_sdk_path) = std::env::var("WASI_SDK_PATH").map(PathBuf::from) { + let wasi_sdk_clang = wasi_sdk_path.join("bin").join("clang"); + build.compiler(wasi_sdk_clang); + + let wasi_sysroot = wasi_sdk_path.join("share").join("wasi-sysroot"); + flags.push(format!("--sysroot={}", wasi_sysroot.display())); + + let wasi_libc_include = wasi_sysroot.join("include"); + includes.push(wasi_libc_include.clone()); + + let wasi_lib_dir = wasi_sysroot.join("lib/wasm32-wasi"); + println!("cargo:rustc-link-search=native={}", wasi_lib_dir.display()); + + defines.push(("_WASI_EMULATED_MMAN", "1")); + println!("cargo:rustc-link-lib=wasi-emulated-mman"); + } else { + panic!("WASI_SDK_PATH is not set, cannot build for {target}"); + } + } + } + + for (key, value) in defines { + build.define(key, value); + push_bindgen_extra_clang_args(format!("-D{key}={value}")); + } + + for include in includes { + build.include(&include); + push_bindgen_extra_clang_args(format!("-I{}", include.display())); + } + + for flag in flags { + build.flag(&flag); + push_bindgen_extra_clang_args(flag); + } + + build.files(source_files(src_dir())); + build.out_dir(&out_dir); + build.try_compile("prism")?; + + std::env::set_var("PRISM_INCLUDE_DIR", include_dir()); + std::env::set_var("PRISM_LIB_DIR", out_dir); + + Ok(()) +} + +fn version() -> &'static str { + env!("CARGO_PKG_VERSION") +} + +fn vendor_dir() -> PathBuf { + let prism_dir = format!("prism-{}", version()); + Path::new(env!("CARGO_MANIFEST_DIR")).join("vendor").join(prism_dir) +} + +fn src_dir() -> PathBuf { + vendor_dir().join("src") +} + +fn include_dir() -> PathBuf { + vendor_dir().join("include") +} + +fn push_bindgen_extra_clang_args>(arg: T) { + let env_var_name = format!("BINDGEN_EXTRA_CLANG_ARGS_{}", std::env::var("TARGET").unwrap()); + + if let Ok(preexisting_arg) = std::env::var(&env_var_name) { + std::env::set_var(env_var_name, format!("{} {}", preexisting_arg, arg.as_ref())); + } else { + std::env::set_var(env_var_name, arg.as_ref()); + } +} + +fn source_files>(root_dir: P) -> Vec { + let mut files = Vec::new(); + + for entry in fs::read_dir(root_dir.as_ref()).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + + if path.is_file() { + let path = path.to_str().unwrap().to_string(); + + if Path::new(&path) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("c")) + { + files.push(path); + } + } else if path.is_dir() { + files.extend(source_files(path)); + } + } + + files +} diff --git a/crates/prism-sys/src/lib.rs b/crates/prism-sys/src/lib.rs new file mode 100644 index 00000000..702e0579 --- /dev/null +++ b/crates/prism-sys/src/lib.rs @@ -0,0 +1,35 @@ +//! # prism-sys +//! +//! FFI-bindings for `prism`. +//! +#![deny(unused_extern_crates)] +#![warn( + box_pointers, + clippy::all, + clippy::nursery, + clippy::pedantic, + future_incompatible, + missing_copy_implementations, + missing_docs, + nonstandard_style, + rust_2018_idioms, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unused_qualifications +)] + +#[allow(clippy::all, clippy::pedantic, clippy::cognitive_complexity)] +#[allow(missing_copy_implementations)] +#[allow(missing_docs)] +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +#[allow(non_upper_case_globals)] +mod bindings { + // In `build.rs`, we use `bindgen` to generate bindings based on C headers + // and `libprism`. Here is where we pull in those bindings and make + // them part of our library. + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +pub use self::bindings::*; diff --git a/crates/prism-sys/tests/node_tests.rs b/crates/prism-sys/tests/node_tests.rs new file mode 100644 index 00000000..ce36e037 --- /dev/null +++ b/crates/prism-sys/tests/node_tests.rs @@ -0,0 +1,27 @@ +use std::{ffi::CString, mem::MaybeUninit}; + +use prism_sys::{pm_node_destroy, pm_node_type}; +use prism_sys::{pm_parse, pm_parser_free, pm_parser_init, pm_parser_t}; + +#[test] +fn node_test() { + let mut parser = MaybeUninit::::uninit(); + let code = CString::new("class Foo; end").unwrap(); + + unsafe { + pm_parser_init( + parser.as_mut_ptr(), + code.as_ptr().cast::(), + code.as_bytes().len(), + std::ptr::null(), + ); + + let parser = parser.assume_init_mut(); + let parsed_node = pm_parse(parser); + + assert_eq!((*parsed_node).type_, pm_node_type::PM_PROGRAM_NODE as u16); + + pm_node_destroy(parser, parsed_node); + pm_parser_free(parser); + } +} diff --git a/crates/prism-sys/tests/pack_tests.rs b/crates/prism-sys/tests/pack_tests.rs new file mode 100644 index 00000000..6f10a2de --- /dev/null +++ b/crates/prism-sys/tests/pack_tests.rs @@ -0,0 +1,57 @@ +use std::{ffi::CString, mem::MaybeUninit}; + +use prism_sys::{ + pm_pack_encoding, pm_pack_endian, pm_pack_length_type, pm_pack_parse, pm_pack_result, pm_pack_signed, pm_pack_size, + pm_pack_type, pm_pack_variant, pm_size_to_native, +}; + +#[test] +fn pack_parse_test() { + let variant_arg = pm_pack_variant::PM_PACK_VARIANT_PACK; + let first_format = CString::new("C").unwrap(); + let end_format = CString::new("").unwrap(); + let mut format = vec![first_format.as_ptr(), end_format.as_ptr()]; + + let mut type_out = MaybeUninit::::uninit(); + let mut signed_type_out = MaybeUninit::::uninit(); + let mut endian_out = MaybeUninit::::uninit(); + let mut size_out = MaybeUninit::::uninit(); + let mut length_type_out = MaybeUninit::::uninit(); + let mut length_out = 0_u64; + let mut encoding_out = MaybeUninit::::uninit(); + + unsafe { + let result = pm_pack_parse( + variant_arg, + format.as_mut_ptr(), + end_format.as_ptr(), + type_out.as_mut_ptr(), + signed_type_out.as_mut_ptr(), + endian_out.as_mut_ptr(), + size_out.as_mut_ptr(), + length_type_out.as_mut_ptr(), + &mut length_out, + encoding_out.as_mut_ptr(), + ); + + assert_eq!(result, pm_pack_result::PM_PACK_OK); + + let type_out = type_out.assume_init(); + let signed_type_out = signed_type_out.assume_init(); + let endian_out = endian_out.assume_init(); + let size_out = size_out.assume_init(); + let length_type_out = length_type_out.assume_init(); + let encoding_out = encoding_out.assume_init(); + + assert_eq!(type_out, pm_pack_type::PM_PACK_INTEGER); + assert_eq!(signed_type_out, pm_pack_signed::PM_PACK_UNSIGNED); + assert_eq!(endian_out, pm_pack_endian::PM_PACK_AGNOSTIC_ENDIAN); + assert_eq!(size_out, pm_pack_size::PM_PACK_SIZE_8); + assert_eq!(length_type_out, pm_pack_length_type::PM_PACK_LENGTH_FIXED); + assert_eq!(length_out, 1); + assert_eq!(encoding_out, pm_pack_encoding::PM_PACK_ENCODING_ASCII_8BIT); + + let native_size = pm_size_to_native(size_out); + assert_eq!(native_size, 1); + } +} diff --git a/crates/prism-sys/tests/parser_tests.rs b/crates/prism-sys/tests/parser_tests.rs new file mode 100644 index 00000000..523fd8b5 --- /dev/null +++ b/crates/prism-sys/tests/parser_tests.rs @@ -0,0 +1,98 @@ +use std::{ + ffi::{CStr, CString}, + mem::MaybeUninit, + path::Path, +}; + +use prism_sys::{ + pm_comment_t, pm_comment_type_t, pm_diagnostic_t, pm_node_destroy, pm_parse, pm_parser_free, pm_parser_init, + pm_parser_t, +}; + +fn ruby_file_contents() -> (CString, usize) { + let rust_path = Path::new(env!("CARGO_MANIFEST_DIR")); + let ruby_file_path = rust_path.join("../../lib/prism.rb").canonicalize().unwrap(); + let file_contents = std::fs::read_to_string(ruby_file_path).unwrap(); + let len = file_contents.len(); + + (CString::new(file_contents).unwrap(), len) +} + +#[test] +fn init_test() { + let (ruby_file_contents, len) = ruby_file_contents(); + let source = ruby_file_contents.as_ptr().cast::(); + let mut parser = MaybeUninit::::uninit(); + + unsafe { + pm_parser_init(parser.as_mut_ptr(), source, len, std::ptr::null()); + let parser = parser.assume_init_mut(); + + pm_parser_free(parser); + } +} + +#[test] +fn comments_test() { + let source = CString::new("# Meow!").unwrap(); + let mut parser = MaybeUninit::::uninit(); + + unsafe { + pm_parser_init( + parser.as_mut_ptr(), + source.as_ptr().cast::(), + source.as_bytes().len(), + std::ptr::null(), + ); + let parser = parser.assume_init_mut(); + let node = pm_parse(parser); + + let comment_list = &parser.comment_list; + let comment = comment_list.head as *const pm_comment_t; + assert_eq!((*comment).type_, pm_comment_type_t::PM_COMMENT_INLINE); + + let location = { + let start = (*comment).start.offset_from(parser.start); + let end = (*comment).end.offset_from(parser.start); + start..end + }; + assert_eq!(location, 0..7); + + pm_node_destroy(parser, node); + pm_parser_free(parser); + } +} + +#[test] +fn diagnostics_test() { + let source = CString::new("class Foo;").unwrap(); + let mut parser = MaybeUninit::::uninit(); + + unsafe { + pm_parser_init( + parser.as_mut_ptr(), + source.as_ptr().cast::(), + source.as_bytes().len(), + std::ptr::null(), + ); + let parser = parser.assume_init_mut(); + let node = pm_parse(parser); + + let error_list = &parser.error_list; + assert!(!error_list.head.is_null()); + + let error = error_list.head as *const pm_diagnostic_t; + let message = CStr::from_ptr((*error).message); + assert_eq!(message.to_string_lossy(), "Cannot parse the expression"); + + let location = { + let start = (*error).start.offset_from(parser.start); + let end = (*error).end.offset_from(parser.start); + start..end + }; + assert_eq!(location, 10..10); + + pm_node_destroy(parser, node); + pm_parser_free(parser); + } +} diff --git a/crates/prism-sys/tests/utils_tests.rs b/crates/prism-sys/tests/utils_tests.rs new file mode 100644 index 00000000..b5880801 --- /dev/null +++ b/crates/prism-sys/tests/utils_tests.rs @@ -0,0 +1,121 @@ +use std::{ + ffi::{CStr, CString}, + mem::MaybeUninit, +}; + +#[test] +fn version_test() { + use prism_sys::pm_version; + + let cstring = unsafe { + let version = pm_version(); + CStr::from_ptr(version) + }; + + assert_eq!(&cstring.to_string_lossy(), "0.18.0"); +} + +#[test] +fn list_test() { + use prism_sys::{pm_list_empty_p, pm_list_free, pm_list_t}; + + let mut list = MaybeUninit::::zeroed(); + + unsafe { + let list = list.assume_init_mut(); + + assert!(pm_list_empty_p(list)); + + pm_list_free(list); + } +} + +mod string { + use prism_sys::{ + pm_string_free, pm_string_length, pm_string_source, pm_string_t, pm_string_t__bindgen_ty_1, PM_STRING_CONSTANT, + PM_STRING_MAPPED, PM_STRING_OWNED, PM_STRING_SHARED, + }; + + use super::*; + + struct S { + c_string: CString, + pm_string: pm_string_t, + } + + impl S { + fn start_ptr(&self) -> *const u8 { + self.c_string.as_ptr().cast::() + } + } + + fn make_string(string_type: pm_string_t__bindgen_ty_1) -> S { + let c_string = CString::new("0123456789012345").unwrap(); + + let pm_string = pm_string_t { + type_: string_type, + source: c_string.as_ptr().cast::(), + length: c_string.as_bytes().len(), + }; + + S { c_string, pm_string } + } + + #[test] + fn shared_string_test() { + let mut s = make_string(PM_STRING_SHARED); + + unsafe { + let len = pm_string_length(&s.pm_string); + assert_eq!(len, 16); + + let result_start = pm_string_source(&s.pm_string); + assert_eq!(s.start_ptr(), result_start); + + pm_string_free(&mut s.pm_string); + } + } + + #[test] + fn owned_string_test() { + let s = make_string(PM_STRING_OWNED); + + unsafe { + let result_len = pm_string_length(&s.pm_string); + assert_eq!(result_len, 16); + + let result_start = pm_string_source(&s.pm_string); + assert_eq!(s.pm_string.source, result_start); + + // Don't drop the pm_string--we don't own it anymore! + } + } + + #[test] + fn constant_string_test() { + let mut s = make_string(PM_STRING_CONSTANT); + + unsafe { + let result_len = pm_string_length(&s.pm_string); + assert_eq!(result_len, 16); + + let result_start = pm_string_source(&s.pm_string); + assert_eq!(s.pm_string.source, result_start); + + pm_string_free(&mut s.pm_string); + } + } + + #[test] + fn mapped_string_test() { + let s = make_string(PM_STRING_MAPPED); + + unsafe { + let result_len = pm_string_length(&s.pm_string); + assert_eq!(result_len, 16); + + let result_start = pm_string_source(&s.pm_string); + assert_eq!(s.pm_string.source, result_start); + } + } +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism.h new file mode 100644 index 00000000..5eec5f49 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism.h @@ -0,0 +1,274 @@ +/** + * @file prism.h + * + * The main header file for the prism parser. + */ +#ifndef PRISM_H +#define PRISM_H + +#include "prism/defines.h" +#include "prism/util/pm_buffer.h" +#include "prism/util/pm_char.h" +#include "prism/util/pm_memchr.h" +#include "prism/util/pm_strncasecmp.h" +#include "prism/util/pm_strpbrk.h" +#include "prism/ast.h" +#include "prism/diagnostic.h" +#include "prism/node.h" +#include "prism/options.h" +#include "prism/pack.h" +#include "prism/parser.h" +#include "prism/prettyprint.h" +#include "prism/regexp.h" +#include "prism/version.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +/** + * The prism version and the serialization format. + * + * @returns The prism version as a constant string. + */ +PRISM_EXPORTED_FUNCTION const char * pm_version(void); + +/** + * Initialize a parser with the given start and end pointers. + * + * @param parser The parser to initialize. + * @param source The source to parse. + * @param size The size of the source. + * @param options The optional options to use when parsing. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options); + +/** + * Register a callback that will be called whenever prism changes the encoding + * it is using to parse based on the magic comment. + * + * @param parser The parser to register the callback with. + * @param callback The callback to register. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_register_encoding_changed_callback(pm_parser_t *parser, pm_encoding_changed_callback_t callback); + +/** + * Register a callback that will be called when prism encounters a magic comment + * with an encoding referenced that it doesn't understand. The callback should + * return NULL if it also doesn't understand the encoding or it should return a + * pointer to a pm_encoding_t struct that contains the functions necessary to + * parse identifiers. + * + * @param parser The parser to register the callback with. + * @param callback The callback to register. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_register_encoding_decode_callback(pm_parser_t *parser, pm_encoding_decode_callback_t callback); + +/** + * Free any memory associated with the given parser. + * + * @param parser The parser to free. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser); + +/** + * Initiate the parser with the given parser. + * + * @param parser The parser to use. + * @return The AST representing the source. + */ +PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser); + +/** + * Serialize the given list of comments to the given buffer. + * + * @param parser The parser to serialize. + * @param list The list of comments to serialize. + * @param buffer The buffer to serialize to. + */ +void pm_serialize_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer); + +/** + * Serialize the name of the encoding to the buffer. + * + * @param encoding The encoding to serialize. + * @param buffer The buffer to serialize to. + */ +void pm_serialize_encoding(pm_encoding_t *encoding, pm_buffer_t *buffer); + +/** + * Serialize the encoding, metadata, nodes, and constant pool. + * + * @param parser The parser to serialize. + * @param node The node to serialize. + * @param buffer The buffer to serialize to. + */ +void pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer); + +/** + * Serialize the AST represented by the given node to the given buffer. + * + * @param parser The parser to serialize. + * @param node The node to serialize. + * @param buffer The buffer to serialize to. + */ +PRISM_EXPORTED_FUNCTION void pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer); + +/** + * Parse the given source to the AST and dump the AST to the given buffer. + * + * @param buffer The buffer to serialize to. + * @param source The source to parse. + * @param size The size of the source. + * @param data The optional data to pass to the parser. + */ +PRISM_EXPORTED_FUNCTION void pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data); + +/** + * Parse and serialize the comments in the given source to the given buffer. + * + * @param buffer The buffer to serialize to. + * @param source The source to parse. + * @param size The size of the source. + * @param data The optional data to pass to the parser. + */ +PRISM_EXPORTED_FUNCTION void pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data); + +/** + * Lex the given source and serialize to the given buffer. + * + * @param source The source to lex. + * @param size The size of the source. + * @param buffer The buffer to serialize to. + * @param data The optional data to pass to the lexer. + */ +PRISM_EXPORTED_FUNCTION void pm_serialize_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data); + +/** + * Parse and serialize both the AST and the tokens represented by the given + * source to the given buffer. + * + * @param buffer The buffer to serialize to. + * @param source The source to parse. + * @param size The size of the source. + * @param data The optional data to pass to the parser. + */ +PRISM_EXPORTED_FUNCTION void pm_serialize_parse_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data); + +/** + * Returns a string representation of the given token type. + * + * @param token_type The token type to convert to a string. + * @return A string representation of the given token type. + */ +PRISM_EXPORTED_FUNCTION const char * pm_token_type_to_str(pm_token_type_t token_type); + +/** + * @mainpage + * + * Prism is a parser for the Ruby programming language. It is designed to be + * portable, error tolerant, and maintainable. It is written in C99 and has no + * dependencies. It is currently being integrated into + * [CRuby](https://github.com/ruby/ruby), + * [JRuby](https://github.com/jruby/jruby), + * [TruffleRuby](https://github.com/oracle/truffleruby), + * [Sorbet](https://github.com/sorbet/sorbet), and + * [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree). + * + * @section getting-started Getting started + * + * If you're vendoring this project and compiling it statically then as long as + * you have a C99 compiler you will be fine. If you're linking against it as + * shared library, then you should compile with `-fvisibility=hidden` and + * `-DPRISM_EXPORT_SYMBOLS` to tell prism to make only its public interface + * visible. + * + * @section parsing Parsing + * + * In order to parse Ruby code, the structures and functions that you're going + * to want to use and be aware of are: + * + * * `pm_parser_t` - the main parser structure + * * `pm_parser_init` - initialize a parser + * * `pm_parse` - parse and return the root node + * * `pm_node_destroy` - deallocate the root node returned by `pm_parse` + * * `pm_parser_free` - free the internal memory of the parser + * + * Putting all of this together would look something like: + * + * ```c + * void parse(const uint8_t *source, size_t length) { + * pm_parser_t parser; + * pm_parser_init(&parser, source, length, NULL); + * + * pm_node_t *root = pm_parse(&parser); + * printf("PARSED!\n"); + * + * pm_node_destroy(root); + * pm_parser_free(&parser); + * } + * ``` + * + * All of the nodes "inherit" from `pm_node_t` by embedding those structures as + * their first member. This means you can downcast and upcast any node in the + * tree to a `pm_node_t`. + * + * @section serializing Serializing + * + * Prism provides the ability to serialize the AST and its related metadata into + * a binary format. This format is designed to be portable to different + * languages and runtimes so that you only need to make one FFI call in order to + * parse Ruby code. The structures and functions that you're going to want to + * use and be aware of are: + * + * * `pm_buffer_t` - a small buffer object that will hold the serialized AST + * * `pm_buffer_free` - free the memory associated with the buffer + * * `pm_serialize` - serialize the AST into a buffer + * * `pm_serialize_parse` - parse and serialize the AST into a buffer + * + * Putting all of this together would look something like: + * + * ```c + * void serialize(const uint8_t *source, size_t length) { + * pm_buffer_t buffer = { 0 }; + * + * pm_serialize_parse(&buffer, source, length, NULL); + * printf("SERIALIZED!\n"); + * + * pm_buffer_free(&buffer); + * } + * ``` + * + * @section inspecting Inspecting + * + * Prism provides the ability to inspect the AST by pretty-printing nodes. You + * can do this with the `pm_prettyprint` function, which you would use like: + * + * ```c + * void prettyprint(const uint8_t *source, size_t length) { + * pm_parser_t parser; + * pm_parser_init(&parser, source, length, NULL); + * + * pm_node_t *root = pm_parse(&parser); + * pm_buffer_t buffer = { 0 }; + * + * pm_prettyprint(&buffer, &parser, root); + * printf("*.s%\n", (int) buffer.length, buffer.value); + * + * pm_buffer_free(&buffer); + * pm_node_destroy(root); + * pm_parser_free(&parser); + * } + * ``` + */ + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/ast.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/ast.h new file mode 100644 index 00000000..10130c9a --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/ast.h @@ -0,0 +1,4440 @@ +/******************************************************************************/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/include/prism/ast.h.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +/** + * @file ast.h + * + * The abstract syntax tree. + */ +#ifndef PRISM_AST_H +#define PRISM_AST_H + +#include "prism/defines.h" +#include "prism/util/pm_constant_pool.h" +#include "prism/util/pm_string.h" + +#include +#include +#include + +/** + * This enum represents every type of token in the Ruby source. + */ +typedef enum pm_token_type { + /** final token in the file */ + PM_TOKEN_EOF = 1, + + /** a token that was expected but not found */ + PM_TOKEN_MISSING, + + /** a token that was not present but it is okay */ + PM_TOKEN_NOT_PROVIDED, + + /** & */ + PM_TOKEN_AMPERSAND, + + /** && */ + PM_TOKEN_AMPERSAND_AMPERSAND, + + /** &&= */ + PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL, + + /** &. */ + PM_TOKEN_AMPERSAND_DOT, + + /** &= */ + PM_TOKEN_AMPERSAND_EQUAL, + + /** ` */ + PM_TOKEN_BACKTICK, + + /** a back reference */ + PM_TOKEN_BACK_REFERENCE, + + /** ! or !@ */ + PM_TOKEN_BANG, + + /** != */ + PM_TOKEN_BANG_EQUAL, + + /** !~ */ + PM_TOKEN_BANG_TILDE, + + /** { */ + PM_TOKEN_BRACE_LEFT, + + /** } */ + PM_TOKEN_BRACE_RIGHT, + + /** [ */ + PM_TOKEN_BRACKET_LEFT, + + /** [ for the beginning of an array */ + PM_TOKEN_BRACKET_LEFT_ARRAY, + + /** [] */ + PM_TOKEN_BRACKET_LEFT_RIGHT, + + /** []= */ + PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL, + + /** ] */ + PM_TOKEN_BRACKET_RIGHT, + + /** ^ */ + PM_TOKEN_CARET, + + /** ^= */ + PM_TOKEN_CARET_EQUAL, + + /** a character literal */ + PM_TOKEN_CHARACTER_LITERAL, + + /** a class variable */ + PM_TOKEN_CLASS_VARIABLE, + + /** : */ + PM_TOKEN_COLON, + + /** :: */ + PM_TOKEN_COLON_COLON, + + /** , */ + PM_TOKEN_COMMA, + + /** a comment */ + PM_TOKEN_COMMENT, + + /** a constant */ + PM_TOKEN_CONSTANT, + + /** the . call operator */ + PM_TOKEN_DOT, + + /** the .. range operator */ + PM_TOKEN_DOT_DOT, + + /** the ... range operator or forwarding parameter */ + PM_TOKEN_DOT_DOT_DOT, + + /** =begin */ + PM_TOKEN_EMBDOC_BEGIN, + + /** =end */ + PM_TOKEN_EMBDOC_END, + + /** a line inside of embedded documentation */ + PM_TOKEN_EMBDOC_LINE, + + /** #{ */ + PM_TOKEN_EMBEXPR_BEGIN, + + /** } */ + PM_TOKEN_EMBEXPR_END, + + /** # */ + PM_TOKEN_EMBVAR, + + /** = */ + PM_TOKEN_EQUAL, + + /** == */ + PM_TOKEN_EQUAL_EQUAL, + + /** === */ + PM_TOKEN_EQUAL_EQUAL_EQUAL, + + /** => */ + PM_TOKEN_EQUAL_GREATER, + + /** =~ */ + PM_TOKEN_EQUAL_TILDE, + + /** a floating point number */ + PM_TOKEN_FLOAT, + + /** a floating pointer number with an imaginary suffix */ + PM_TOKEN_FLOAT_IMAGINARY, + + /** a floating pointer number with a rational suffix */ + PM_TOKEN_FLOAT_RATIONAL, + + /** a floating pointer number with a rational and imaginary suffix */ + PM_TOKEN_FLOAT_RATIONAL_IMAGINARY, + + /** a global variable */ + PM_TOKEN_GLOBAL_VARIABLE, + + /** > */ + PM_TOKEN_GREATER, + + /** >= */ + PM_TOKEN_GREATER_EQUAL, + + /** >> */ + PM_TOKEN_GREATER_GREATER, + + /** >>= */ + PM_TOKEN_GREATER_GREATER_EQUAL, + + /** the end of a heredoc */ + PM_TOKEN_HEREDOC_END, + + /** the start of a heredoc */ + PM_TOKEN_HEREDOC_START, + + /** an identifier */ + PM_TOKEN_IDENTIFIER, + + /** an ignored newline */ + PM_TOKEN_IGNORED_NEWLINE, + + /** an instance variable */ + PM_TOKEN_INSTANCE_VARIABLE, + + /** an integer (any base) */ + PM_TOKEN_INTEGER, + + /** an integer with an imaginary suffix */ + PM_TOKEN_INTEGER_IMAGINARY, + + /** an integer with a rational suffix */ + PM_TOKEN_INTEGER_RATIONAL, + + /** an integer with a rational and imaginary suffix */ + PM_TOKEN_INTEGER_RATIONAL_IMAGINARY, + + /** alias */ + PM_TOKEN_KEYWORD_ALIAS, + + /** and */ + PM_TOKEN_KEYWORD_AND, + + /** begin */ + PM_TOKEN_KEYWORD_BEGIN, + + /** BEGIN */ + PM_TOKEN_KEYWORD_BEGIN_UPCASE, + + /** break */ + PM_TOKEN_KEYWORD_BREAK, + + /** case */ + PM_TOKEN_KEYWORD_CASE, + + /** class */ + PM_TOKEN_KEYWORD_CLASS, + + /** def */ + PM_TOKEN_KEYWORD_DEF, + + /** defined? */ + PM_TOKEN_KEYWORD_DEFINED, + + /** do */ + PM_TOKEN_KEYWORD_DO, + + /** do keyword for a predicate in a while, until, or for loop */ + PM_TOKEN_KEYWORD_DO_LOOP, + + /** else */ + PM_TOKEN_KEYWORD_ELSE, + + /** elsif */ + PM_TOKEN_KEYWORD_ELSIF, + + /** end */ + PM_TOKEN_KEYWORD_END, + + /** END */ + PM_TOKEN_KEYWORD_END_UPCASE, + + /** ensure */ + PM_TOKEN_KEYWORD_ENSURE, + + /** false */ + PM_TOKEN_KEYWORD_FALSE, + + /** for */ + PM_TOKEN_KEYWORD_FOR, + + /** if */ + PM_TOKEN_KEYWORD_IF, + + /** if in the modifier form */ + PM_TOKEN_KEYWORD_IF_MODIFIER, + + /** in */ + PM_TOKEN_KEYWORD_IN, + + /** module */ + PM_TOKEN_KEYWORD_MODULE, + + /** next */ + PM_TOKEN_KEYWORD_NEXT, + + /** nil */ + PM_TOKEN_KEYWORD_NIL, + + /** not */ + PM_TOKEN_KEYWORD_NOT, + + /** or */ + PM_TOKEN_KEYWORD_OR, + + /** redo */ + PM_TOKEN_KEYWORD_REDO, + + /** rescue */ + PM_TOKEN_KEYWORD_RESCUE, + + /** rescue in the modifier form */ + PM_TOKEN_KEYWORD_RESCUE_MODIFIER, + + /** retry */ + PM_TOKEN_KEYWORD_RETRY, + + /** return */ + PM_TOKEN_KEYWORD_RETURN, + + /** self */ + PM_TOKEN_KEYWORD_SELF, + + /** super */ + PM_TOKEN_KEYWORD_SUPER, + + /** then */ + PM_TOKEN_KEYWORD_THEN, + + /** true */ + PM_TOKEN_KEYWORD_TRUE, + + /** undef */ + PM_TOKEN_KEYWORD_UNDEF, + + /** unless */ + PM_TOKEN_KEYWORD_UNLESS, + + /** unless in the modifier form */ + PM_TOKEN_KEYWORD_UNLESS_MODIFIER, + + /** until */ + PM_TOKEN_KEYWORD_UNTIL, + + /** until in the modifier form */ + PM_TOKEN_KEYWORD_UNTIL_MODIFIER, + + /** when */ + PM_TOKEN_KEYWORD_WHEN, + + /** while */ + PM_TOKEN_KEYWORD_WHILE, + + /** while in the modifier form */ + PM_TOKEN_KEYWORD_WHILE_MODIFIER, + + /** yield */ + PM_TOKEN_KEYWORD_YIELD, + + /** __ENCODING__ */ + PM_TOKEN_KEYWORD___ENCODING__, + + /** __FILE__ */ + PM_TOKEN_KEYWORD___FILE__, + + /** __LINE__ */ + PM_TOKEN_KEYWORD___LINE__, + + /** a label */ + PM_TOKEN_LABEL, + + /** the end of a label */ + PM_TOKEN_LABEL_END, + + /** { */ + PM_TOKEN_LAMBDA_BEGIN, + + /** < */ + PM_TOKEN_LESS, + + /** <= */ + PM_TOKEN_LESS_EQUAL, + + /** <=> */ + PM_TOKEN_LESS_EQUAL_GREATER, + + /** << */ + PM_TOKEN_LESS_LESS, + + /** <<= */ + PM_TOKEN_LESS_LESS_EQUAL, + + /** a method name */ + PM_TOKEN_METHOD_NAME, + + /** - */ + PM_TOKEN_MINUS, + + /** -= */ + PM_TOKEN_MINUS_EQUAL, + + /** -> */ + PM_TOKEN_MINUS_GREATER, + + /** a newline character outside of other tokens */ + PM_TOKEN_NEWLINE, + + /** a numbered reference to a capture group in the previous regular expression match */ + PM_TOKEN_NUMBERED_REFERENCE, + + /** ( */ + PM_TOKEN_PARENTHESIS_LEFT, + + /** ( for a parentheses node */ + PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES, + + /** ) */ + PM_TOKEN_PARENTHESIS_RIGHT, + + /** % */ + PM_TOKEN_PERCENT, + + /** %= */ + PM_TOKEN_PERCENT_EQUAL, + + /** %i */ + PM_TOKEN_PERCENT_LOWER_I, + + /** %w */ + PM_TOKEN_PERCENT_LOWER_W, + + /** %x */ + PM_TOKEN_PERCENT_LOWER_X, + + /** %I */ + PM_TOKEN_PERCENT_UPPER_I, + + /** %W */ + PM_TOKEN_PERCENT_UPPER_W, + + /** | */ + PM_TOKEN_PIPE, + + /** |= */ + PM_TOKEN_PIPE_EQUAL, + + /** || */ + PM_TOKEN_PIPE_PIPE, + + /** ||= */ + PM_TOKEN_PIPE_PIPE_EQUAL, + + /** + */ + PM_TOKEN_PLUS, + + /** += */ + PM_TOKEN_PLUS_EQUAL, + + /** ? */ + PM_TOKEN_QUESTION_MARK, + + /** the beginning of a regular expression */ + PM_TOKEN_REGEXP_BEGIN, + + /** the end of a regular expression */ + PM_TOKEN_REGEXP_END, + + /** ; */ + PM_TOKEN_SEMICOLON, + + /** / */ + PM_TOKEN_SLASH, + + /** /= */ + PM_TOKEN_SLASH_EQUAL, + + /** * */ + PM_TOKEN_STAR, + + /** *= */ + PM_TOKEN_STAR_EQUAL, + + /** ** */ + PM_TOKEN_STAR_STAR, + + /** **= */ + PM_TOKEN_STAR_STAR_EQUAL, + + /** the beginning of a string */ + PM_TOKEN_STRING_BEGIN, + + /** the contents of a string */ + PM_TOKEN_STRING_CONTENT, + + /** the end of a string */ + PM_TOKEN_STRING_END, + + /** the beginning of a symbol */ + PM_TOKEN_SYMBOL_BEGIN, + + /** ~ or ~@ */ + PM_TOKEN_TILDE, + + /** unary & */ + PM_TOKEN_UAMPERSAND, + + /** unary :: */ + PM_TOKEN_UCOLON_COLON, + + /** unary .. operator */ + PM_TOKEN_UDOT_DOT, + + /** unary ... operator */ + PM_TOKEN_UDOT_DOT_DOT, + + /** -@ */ + PM_TOKEN_UMINUS, + + /** -@ for a number */ + PM_TOKEN_UMINUS_NUM, + + /** +@ */ + PM_TOKEN_UPLUS, + + /** unary * */ + PM_TOKEN_USTAR, + + /** unary ** */ + PM_TOKEN_USTAR_STAR, + + /** a separator between words in a list */ + PM_TOKEN_WORDS_SEP, + + /** marker for the point in the file at which the parser should stop */ + PM_TOKEN___END__, + + /** The maximum token value. */ + PM_TOKEN_MAXIMUM, +} pm_token_type_t; + +/** + * This struct represents a token in the Ruby source. We use it to track both + * type and location information. + */ +typedef struct { + /** The type of the token. */ + pm_token_type_t type; + + /** A pointer to the start location of the token in the source. */ + const uint8_t *start; + + /** A pointer to the end location of the token in the source. */ + const uint8_t *end; +} pm_token_t; + +/** + * This represents a range of bytes in the source string to which a node or + * token corresponds. + */ +typedef struct { + /** A pointer to the start location of the range in the source. */ + const uint8_t *start; + + /** A pointer to the end location of the range in the source. */ + const uint8_t *end; +} pm_location_t; + +struct pm_node; + +/** + * A list of nodes in the source, most often used for lists of children. + */ +typedef struct pm_node_list { + /** The number of nodes in the list. */ + size_t size; + + /** The capacity of the list that has been allocated. */ + size_t capacity; + + /** The nodes in the list. */ + struct pm_node **nodes; +} pm_node_list_t; + +/** + * This enum represents every type of node in the Ruby syntax tree. + */ +enum pm_node_type { + /** AliasGlobalVariableNode */ + PM_ALIAS_GLOBAL_VARIABLE_NODE = 1, + + /** AliasMethodNode */ + PM_ALIAS_METHOD_NODE = 2, + + /** AlternationPatternNode */ + PM_ALTERNATION_PATTERN_NODE = 3, + + /** AndNode */ + PM_AND_NODE = 4, + + /** ArgumentsNode */ + PM_ARGUMENTS_NODE = 5, + + /** ArrayNode */ + PM_ARRAY_NODE = 6, + + /** ArrayPatternNode */ + PM_ARRAY_PATTERN_NODE = 7, + + /** AssocNode */ + PM_ASSOC_NODE = 8, + + /** AssocSplatNode */ + PM_ASSOC_SPLAT_NODE = 9, + + /** BackReferenceReadNode */ + PM_BACK_REFERENCE_READ_NODE = 10, + + /** BeginNode */ + PM_BEGIN_NODE = 11, + + /** BlockArgumentNode */ + PM_BLOCK_ARGUMENT_NODE = 12, + + /** BlockLocalVariableNode */ + PM_BLOCK_LOCAL_VARIABLE_NODE = 13, + + /** BlockNode */ + PM_BLOCK_NODE = 14, + + /** BlockParameterNode */ + PM_BLOCK_PARAMETER_NODE = 15, + + /** BlockParametersNode */ + PM_BLOCK_PARAMETERS_NODE = 16, + + /** BreakNode */ + PM_BREAK_NODE = 17, + + /** CallAndWriteNode */ + PM_CALL_AND_WRITE_NODE = 18, + + /** CallNode */ + PM_CALL_NODE = 19, + + /** CallOperatorWriteNode */ + PM_CALL_OPERATOR_WRITE_NODE = 20, + + /** CallOrWriteNode */ + PM_CALL_OR_WRITE_NODE = 21, + + /** CapturePatternNode */ + PM_CAPTURE_PATTERN_NODE = 22, + + /** CaseMatchNode */ + PM_CASE_MATCH_NODE = 23, + + /** CaseNode */ + PM_CASE_NODE = 24, + + /** ClassNode */ + PM_CLASS_NODE = 25, + + /** ClassVariableAndWriteNode */ + PM_CLASS_VARIABLE_AND_WRITE_NODE = 26, + + /** ClassVariableOperatorWriteNode */ + PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE = 27, + + /** ClassVariableOrWriteNode */ + PM_CLASS_VARIABLE_OR_WRITE_NODE = 28, + + /** ClassVariableReadNode */ + PM_CLASS_VARIABLE_READ_NODE = 29, + + /** ClassVariableTargetNode */ + PM_CLASS_VARIABLE_TARGET_NODE = 30, + + /** ClassVariableWriteNode */ + PM_CLASS_VARIABLE_WRITE_NODE = 31, + + /** ConstantAndWriteNode */ + PM_CONSTANT_AND_WRITE_NODE = 32, + + /** ConstantOperatorWriteNode */ + PM_CONSTANT_OPERATOR_WRITE_NODE = 33, + + /** ConstantOrWriteNode */ + PM_CONSTANT_OR_WRITE_NODE = 34, + + /** ConstantPathAndWriteNode */ + PM_CONSTANT_PATH_AND_WRITE_NODE = 35, + + /** ConstantPathNode */ + PM_CONSTANT_PATH_NODE = 36, + + /** ConstantPathOperatorWriteNode */ + PM_CONSTANT_PATH_OPERATOR_WRITE_NODE = 37, + + /** ConstantPathOrWriteNode */ + PM_CONSTANT_PATH_OR_WRITE_NODE = 38, + + /** ConstantPathTargetNode */ + PM_CONSTANT_PATH_TARGET_NODE = 39, + + /** ConstantPathWriteNode */ + PM_CONSTANT_PATH_WRITE_NODE = 40, + + /** ConstantReadNode */ + PM_CONSTANT_READ_NODE = 41, + + /** ConstantTargetNode */ + PM_CONSTANT_TARGET_NODE = 42, + + /** ConstantWriteNode */ + PM_CONSTANT_WRITE_NODE = 43, + + /** DefNode */ + PM_DEF_NODE = 44, + + /** DefinedNode */ + PM_DEFINED_NODE = 45, + + /** ElseNode */ + PM_ELSE_NODE = 46, + + /** EmbeddedStatementsNode */ + PM_EMBEDDED_STATEMENTS_NODE = 47, + + /** EmbeddedVariableNode */ + PM_EMBEDDED_VARIABLE_NODE = 48, + + /** EnsureNode */ + PM_ENSURE_NODE = 49, + + /** FalseNode */ + PM_FALSE_NODE = 50, + + /** FindPatternNode */ + PM_FIND_PATTERN_NODE = 51, + + /** FlipFlopNode */ + PM_FLIP_FLOP_NODE = 52, + + /** FloatNode */ + PM_FLOAT_NODE = 53, + + /** ForNode */ + PM_FOR_NODE = 54, + + /** ForwardingArgumentsNode */ + PM_FORWARDING_ARGUMENTS_NODE = 55, + + /** ForwardingParameterNode */ + PM_FORWARDING_PARAMETER_NODE = 56, + + /** ForwardingSuperNode */ + PM_FORWARDING_SUPER_NODE = 57, + + /** GlobalVariableAndWriteNode */ + PM_GLOBAL_VARIABLE_AND_WRITE_NODE = 58, + + /** GlobalVariableOperatorWriteNode */ + PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE = 59, + + /** GlobalVariableOrWriteNode */ + PM_GLOBAL_VARIABLE_OR_WRITE_NODE = 60, + + /** GlobalVariableReadNode */ + PM_GLOBAL_VARIABLE_READ_NODE = 61, + + /** GlobalVariableTargetNode */ + PM_GLOBAL_VARIABLE_TARGET_NODE = 62, + + /** GlobalVariableWriteNode */ + PM_GLOBAL_VARIABLE_WRITE_NODE = 63, + + /** HashNode */ + PM_HASH_NODE = 64, + + /** HashPatternNode */ + PM_HASH_PATTERN_NODE = 65, + + /** IfNode */ + PM_IF_NODE = 66, + + /** ImaginaryNode */ + PM_IMAGINARY_NODE = 67, + + /** ImplicitNode */ + PM_IMPLICIT_NODE = 68, + + /** ImplicitRestNode */ + PM_IMPLICIT_REST_NODE = 69, + + /** InNode */ + PM_IN_NODE = 70, + + /** IndexAndWriteNode */ + PM_INDEX_AND_WRITE_NODE = 71, + + /** IndexOperatorWriteNode */ + PM_INDEX_OPERATOR_WRITE_NODE = 72, + + /** IndexOrWriteNode */ + PM_INDEX_OR_WRITE_NODE = 73, + + /** InstanceVariableAndWriteNode */ + PM_INSTANCE_VARIABLE_AND_WRITE_NODE = 74, + + /** InstanceVariableOperatorWriteNode */ + PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE = 75, + + /** InstanceVariableOrWriteNode */ + PM_INSTANCE_VARIABLE_OR_WRITE_NODE = 76, + + /** InstanceVariableReadNode */ + PM_INSTANCE_VARIABLE_READ_NODE = 77, + + /** InstanceVariableTargetNode */ + PM_INSTANCE_VARIABLE_TARGET_NODE = 78, + + /** InstanceVariableWriteNode */ + PM_INSTANCE_VARIABLE_WRITE_NODE = 79, + + /** IntegerNode */ + PM_INTEGER_NODE = 80, + + /** InterpolatedMatchLastLineNode */ + PM_INTERPOLATED_MATCH_LAST_LINE_NODE = 81, + + /** InterpolatedRegularExpressionNode */ + PM_INTERPOLATED_REGULAR_EXPRESSION_NODE = 82, + + /** InterpolatedStringNode */ + PM_INTERPOLATED_STRING_NODE = 83, + + /** InterpolatedSymbolNode */ + PM_INTERPOLATED_SYMBOL_NODE = 84, + + /** InterpolatedXStringNode */ + PM_INTERPOLATED_X_STRING_NODE = 85, + + /** KeywordHashNode */ + PM_KEYWORD_HASH_NODE = 86, + + /** KeywordRestParameterNode */ + PM_KEYWORD_REST_PARAMETER_NODE = 87, + + /** LambdaNode */ + PM_LAMBDA_NODE = 88, + + /** LocalVariableAndWriteNode */ + PM_LOCAL_VARIABLE_AND_WRITE_NODE = 89, + + /** LocalVariableOperatorWriteNode */ + PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE = 90, + + /** LocalVariableOrWriteNode */ + PM_LOCAL_VARIABLE_OR_WRITE_NODE = 91, + + /** LocalVariableReadNode */ + PM_LOCAL_VARIABLE_READ_NODE = 92, + + /** LocalVariableTargetNode */ + PM_LOCAL_VARIABLE_TARGET_NODE = 93, + + /** LocalVariableWriteNode */ + PM_LOCAL_VARIABLE_WRITE_NODE = 94, + + /** MatchLastLineNode */ + PM_MATCH_LAST_LINE_NODE = 95, + + /** MatchPredicateNode */ + PM_MATCH_PREDICATE_NODE = 96, + + /** MatchRequiredNode */ + PM_MATCH_REQUIRED_NODE = 97, + + /** MatchWriteNode */ + PM_MATCH_WRITE_NODE = 98, + + /** MissingNode */ + PM_MISSING_NODE = 99, + + /** ModuleNode */ + PM_MODULE_NODE = 100, + + /** MultiTargetNode */ + PM_MULTI_TARGET_NODE = 101, + + /** MultiWriteNode */ + PM_MULTI_WRITE_NODE = 102, + + /** NextNode */ + PM_NEXT_NODE = 103, + + /** NilNode */ + PM_NIL_NODE = 104, + + /** NoKeywordsParameterNode */ + PM_NO_KEYWORDS_PARAMETER_NODE = 105, + + /** NumberedReferenceReadNode */ + PM_NUMBERED_REFERENCE_READ_NODE = 106, + + /** OptionalKeywordParameterNode */ + PM_OPTIONAL_KEYWORD_PARAMETER_NODE = 107, + + /** OptionalParameterNode */ + PM_OPTIONAL_PARAMETER_NODE = 108, + + /** OrNode */ + PM_OR_NODE = 109, + + /** ParametersNode */ + PM_PARAMETERS_NODE = 110, + + /** ParenthesesNode */ + PM_PARENTHESES_NODE = 111, + + /** PinnedExpressionNode */ + PM_PINNED_EXPRESSION_NODE = 112, + + /** PinnedVariableNode */ + PM_PINNED_VARIABLE_NODE = 113, + + /** PostExecutionNode */ + PM_POST_EXECUTION_NODE = 114, + + /** PreExecutionNode */ + PM_PRE_EXECUTION_NODE = 115, + + /** ProgramNode */ + PM_PROGRAM_NODE = 116, + + /** RangeNode */ + PM_RANGE_NODE = 117, + + /** RationalNode */ + PM_RATIONAL_NODE = 118, + + /** RedoNode */ + PM_REDO_NODE = 119, + + /** RegularExpressionNode */ + PM_REGULAR_EXPRESSION_NODE = 120, + + /** RequiredKeywordParameterNode */ + PM_REQUIRED_KEYWORD_PARAMETER_NODE = 121, + + /** RequiredParameterNode */ + PM_REQUIRED_PARAMETER_NODE = 122, + + /** RescueModifierNode */ + PM_RESCUE_MODIFIER_NODE = 123, + + /** RescueNode */ + PM_RESCUE_NODE = 124, + + /** RestParameterNode */ + PM_REST_PARAMETER_NODE = 125, + + /** RetryNode */ + PM_RETRY_NODE = 126, + + /** ReturnNode */ + PM_RETURN_NODE = 127, + + /** SelfNode */ + PM_SELF_NODE = 128, + + /** SingletonClassNode */ + PM_SINGLETON_CLASS_NODE = 129, + + /** SourceEncodingNode */ + PM_SOURCE_ENCODING_NODE = 130, + + /** SourceFileNode */ + PM_SOURCE_FILE_NODE = 131, + + /** SourceLineNode */ + PM_SOURCE_LINE_NODE = 132, + + /** SplatNode */ + PM_SPLAT_NODE = 133, + + /** StatementsNode */ + PM_STATEMENTS_NODE = 134, + + /** StringNode */ + PM_STRING_NODE = 135, + + /** SuperNode */ + PM_SUPER_NODE = 136, + + /** SymbolNode */ + PM_SYMBOL_NODE = 137, + + /** TrueNode */ + PM_TRUE_NODE = 138, + + /** UndefNode */ + PM_UNDEF_NODE = 139, + + /** UnlessNode */ + PM_UNLESS_NODE = 140, + + /** UntilNode */ + PM_UNTIL_NODE = 141, + + /** WhenNode */ + PM_WHEN_NODE = 142, + + /** WhileNode */ + PM_WHILE_NODE = 143, + + /** XStringNode */ + PM_X_STRING_NODE = 144, + + /** YieldNode */ + PM_YIELD_NODE = 145, + + /** A special kind of node used for compilation. */ + PM_SCOPE_NODE +}; + +/** + * This is the type of node embedded in the node struct. We explicitly control + * the size of it here to avoid having the variable-width enum. + */ +typedef uint16_t pm_node_type_t; + +/** + * These are the flags embedded in the node struct. We explicitly control the + * size of it here to avoid having the variable-width enum. + */ +typedef uint16_t pm_node_flags_t; + +/** + * We store the flags enum in every node in the tree. Some flags are common to + * all nodes (the ones listed below). Others are specific to certain node types. + */ +#define PM_NODE_FLAG_BITS (sizeof(pm_node_flags_t) * 8) + +static const pm_node_flags_t PM_NODE_FLAG_NEWLINE = (1 << (PM_NODE_FLAG_BITS - 1)); +static const pm_node_flags_t PM_NODE_FLAG_STATIC_LITERAL = (1 << (PM_NODE_FLAG_BITS - 2)); +static const pm_node_flags_t PM_NODE_FLAG_COMMON_MASK = (1 << (PM_NODE_FLAG_BITS - 1)) | (1 << (PM_NODE_FLAG_BITS - 2)); + +/** + * Cast the type to an enum to allow the compiler to provide exhaustiveness + * checking. + */ +#define PM_NODE_TYPE(node) ((enum pm_node_type) node->type) + +/** + * Return true if the type of the given node matches the given type. + */ +#define PM_NODE_TYPE_P(node, type) (PM_NODE_TYPE(node) == (type)) + +/** + * This is the base structure that represents a node in the syntax tree. It is + * embedded into every node type. + */ +typedef struct pm_node { + /** + * This represents the type of the node. It somewhat maps to the nodes that + * existed in the original grammar and ripper, but it's not a 1:1 mapping. + */ + pm_node_type_t type; + + /** + * This represents any flags on the node. Some are common to all nodes, and + * some are specific to the type of node. + */ + pm_node_flags_t flags; + + /** + * This is the location of the node in the source. It's a range of bytes + * containing a start and an end. + */ + pm_location_t location; +} pm_node_t; + +/** + * AliasGlobalVariableNode + * + * Type: PM_ALIAS_GLOBAL_VARIABLE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_alias_global_variable_node { + /** The embedded base node. */ + pm_node_t base; + + /** AliasGlobalVariableNode#new_name */ + struct pm_node *new_name; + + /** AliasGlobalVariableNode#old_name */ + struct pm_node *old_name; + + /** AliasGlobalVariableNode#keyword_loc */ + pm_location_t keyword_loc; +} pm_alias_global_variable_node_t; + +/** + * AliasMethodNode + * + * Type: PM_ALIAS_METHOD_NODE + * + * @extends pm_node_t + */ +typedef struct pm_alias_method_node { + /** The embedded base node. */ + pm_node_t base; + + /** AliasMethodNode#new_name */ + struct pm_node *new_name; + + /** AliasMethodNode#old_name */ + struct pm_node *old_name; + + /** AliasMethodNode#keyword_loc */ + pm_location_t keyword_loc; +} pm_alias_method_node_t; + +/** + * AlternationPatternNode + * + * Type: PM_ALTERNATION_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_alternation_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + /** AlternationPatternNode#left */ + struct pm_node *left; + + /** AlternationPatternNode#right */ + struct pm_node *right; + + /** AlternationPatternNode#operator_loc */ + pm_location_t operator_loc; +} pm_alternation_pattern_node_t; + +/** + * AndNode + * + * Type: PM_AND_NODE + * + * @extends pm_node_t + */ +typedef struct pm_and_node { + /** The embedded base node. */ + pm_node_t base; + + /** AndNode#left */ + struct pm_node *left; + + /** AndNode#right */ + struct pm_node *right; + + /** AndNode#operator_loc */ + pm_location_t operator_loc; +} pm_and_node_t; + +/** + * ArgumentsNode + * + * Type: PM_ARGUMENTS_NODE + * Flags: + * PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT + * + * @extends pm_node_t + */ +typedef struct pm_arguments_node { + /** The embedded base node. */ + pm_node_t base; + + /** ArgumentsNode#arguments */ + struct pm_node_list arguments; +} pm_arguments_node_t; + +/** + * ArrayNode + * + * Type: PM_ARRAY_NODE + * Flags: + * PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT + * + * @extends pm_node_t + */ +typedef struct pm_array_node { + /** The embedded base node. */ + pm_node_t base; + + /** ArrayNode#elements */ + struct pm_node_list elements; + + /** ArrayNode#opening_loc */ + pm_location_t opening_loc; + + /** ArrayNode#closing_loc */ + pm_location_t closing_loc; +} pm_array_node_t; + +/** + * ArrayPatternNode + * + * Type: PM_ARRAY_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_array_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + /** ArrayPatternNode#constant */ + struct pm_node *constant; + + /** ArrayPatternNode#requireds */ + struct pm_node_list requireds; + + /** ArrayPatternNode#rest */ + struct pm_node *rest; + + /** ArrayPatternNode#posts */ + struct pm_node_list posts; + + /** ArrayPatternNode#opening_loc */ + pm_location_t opening_loc; + + /** ArrayPatternNode#closing_loc */ + pm_location_t closing_loc; +} pm_array_pattern_node_t; + +/** + * AssocNode + * + * Type: PM_ASSOC_NODE + * + * @extends pm_node_t + */ +typedef struct pm_assoc_node { + /** The embedded base node. */ + pm_node_t base; + + /** AssocNode#key */ + struct pm_node *key; + + /** AssocNode#value */ + struct pm_node *value; + + /** AssocNode#operator_loc */ + pm_location_t operator_loc; +} pm_assoc_node_t; + +/** + * AssocSplatNode + * + * Type: PM_ASSOC_SPLAT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_assoc_splat_node { + /** The embedded base node. */ + pm_node_t base; + + /** AssocSplatNode#value */ + struct pm_node *value; + + /** AssocSplatNode#operator_loc */ + pm_location_t operator_loc; +} pm_assoc_splat_node_t; + +/** + * BackReferenceReadNode + * + * Type: PM_BACK_REFERENCE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_back_reference_read_node { + /** The embedded base node. */ + pm_node_t base; + + /** BackReferenceReadNode#name */ + pm_constant_id_t name; +} pm_back_reference_read_node_t; + +/** + * BeginNode + * + * Type: PM_BEGIN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_begin_node { + /** The embedded base node. */ + pm_node_t base; + + /** BeginNode#begin_keyword_loc */ + pm_location_t begin_keyword_loc; + + /** BeginNode#statements */ + struct pm_statements_node *statements; + + /** BeginNode#rescue_clause */ + struct pm_rescue_node *rescue_clause; + + /** BeginNode#else_clause */ + struct pm_else_node *else_clause; + + /** BeginNode#ensure_clause */ + struct pm_ensure_node *ensure_clause; + + /** BeginNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_begin_node_t; + +/** + * BlockArgumentNode + * + * Type: PM_BLOCK_ARGUMENT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_block_argument_node { + /** The embedded base node. */ + pm_node_t base; + + /** BlockArgumentNode#expression */ + struct pm_node *expression; + + /** BlockArgumentNode#operator_loc */ + pm_location_t operator_loc; +} pm_block_argument_node_t; + +/** + * BlockLocalVariableNode + * + * Type: PM_BLOCK_LOCAL_VARIABLE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_block_local_variable_node { + /** The embedded base node. */ + pm_node_t base; + + /** BlockLocalVariableNode#name */ + pm_constant_id_t name; +} pm_block_local_variable_node_t; + +/** + * BlockNode + * + * Type: PM_BLOCK_NODE + * + * @extends pm_node_t + */ +typedef struct pm_block_node { + /** The embedded base node. */ + pm_node_t base; + + /** BlockNode#locals */ + pm_constant_id_list_t locals; + + /** BlockNode#parameters */ + struct pm_block_parameters_node *parameters; + + /** BlockNode#body */ + struct pm_node *body; + + /** BlockNode#opening_loc */ + pm_location_t opening_loc; + + /** BlockNode#closing_loc */ + pm_location_t closing_loc; + + /** BlockNode#numbered_parameters */ + uint32_t numbered_parameters; +} pm_block_node_t; + +/** + * BlockParameterNode + * + * Type: PM_BLOCK_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_block_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + /** BlockParameterNode#name */ + pm_constant_id_t name; + + /** BlockParameterNode#name_loc */ + pm_location_t name_loc; + + /** BlockParameterNode#operator_loc */ + pm_location_t operator_loc; +} pm_block_parameter_node_t; + +/** + * BlockParametersNode + * + * Type: PM_BLOCK_PARAMETERS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_block_parameters_node { + /** The embedded base node. */ + pm_node_t base; + + /** BlockParametersNode#parameters */ + struct pm_parameters_node *parameters; + + /** BlockParametersNode#locals */ + struct pm_node_list locals; + + /** BlockParametersNode#opening_loc */ + pm_location_t opening_loc; + + /** BlockParametersNode#closing_loc */ + pm_location_t closing_loc; +} pm_block_parameters_node_t; + +/** + * BreakNode + * + * Type: PM_BREAK_NODE + * + * @extends pm_node_t + */ +typedef struct pm_break_node { + /** The embedded base node. */ + pm_node_t base; + + /** BreakNode#arguments */ + struct pm_arguments_node *arguments; + + /** BreakNode#keyword_loc */ + pm_location_t keyword_loc; +} pm_break_node_t; + +/** + * CallAndWriteNode + * + * Type: PM_CALL_AND_WRITE_NODE + * Flags: + * PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * PM_CALL_NODE_FLAGS_VARIABLE_CALL + * + * @extends pm_node_t + */ +typedef struct pm_call_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** CallAndWriteNode#receiver */ + struct pm_node *receiver; + + /** CallAndWriteNode#call_operator_loc */ + pm_location_t call_operator_loc; + + /** CallAndWriteNode#message_loc */ + pm_location_t message_loc; + + /** CallAndWriteNode#read_name */ + pm_constant_id_t read_name; + + /** CallAndWriteNode#write_name */ + pm_constant_id_t write_name; + + /** CallAndWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** CallAndWriteNode#value */ + struct pm_node *value; +} pm_call_and_write_node_t; + +/** + * CallNode + * + * Type: PM_CALL_NODE + * Flags: + * PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * PM_CALL_NODE_FLAGS_VARIABLE_CALL + * + * @extends pm_node_t + */ +typedef struct pm_call_node { + /** The embedded base node. */ + pm_node_t base; + + /** CallNode#receiver */ + struct pm_node *receiver; + + /** CallNode#call_operator_loc */ + pm_location_t call_operator_loc; + + /** CallNode#name */ + pm_constant_id_t name; + + /** CallNode#message_loc */ + pm_location_t message_loc; + + /** CallNode#opening_loc */ + pm_location_t opening_loc; + + /** CallNode#arguments */ + struct pm_arguments_node *arguments; + + /** CallNode#closing_loc */ + pm_location_t closing_loc; + + /** CallNode#block */ + struct pm_node *block; +} pm_call_node_t; + +/** + * CallOperatorWriteNode + * + * Type: PM_CALL_OPERATOR_WRITE_NODE + * Flags: + * PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * PM_CALL_NODE_FLAGS_VARIABLE_CALL + * + * @extends pm_node_t + */ +typedef struct pm_call_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** CallOperatorWriteNode#receiver */ + struct pm_node *receiver; + + /** CallOperatorWriteNode#call_operator_loc */ + pm_location_t call_operator_loc; + + /** CallOperatorWriteNode#message_loc */ + pm_location_t message_loc; + + /** CallOperatorWriteNode#read_name */ + pm_constant_id_t read_name; + + /** CallOperatorWriteNode#write_name */ + pm_constant_id_t write_name; + + /** CallOperatorWriteNode#operator */ + pm_constant_id_t operator; + + /** CallOperatorWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** CallOperatorWriteNode#value */ + struct pm_node *value; +} pm_call_operator_write_node_t; + +/** + * CallOrWriteNode + * + * Type: PM_CALL_OR_WRITE_NODE + * Flags: + * PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * PM_CALL_NODE_FLAGS_VARIABLE_CALL + * + * @extends pm_node_t + */ +typedef struct pm_call_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** CallOrWriteNode#receiver */ + struct pm_node *receiver; + + /** CallOrWriteNode#call_operator_loc */ + pm_location_t call_operator_loc; + + /** CallOrWriteNode#message_loc */ + pm_location_t message_loc; + + /** CallOrWriteNode#read_name */ + pm_constant_id_t read_name; + + /** CallOrWriteNode#write_name */ + pm_constant_id_t write_name; + + /** CallOrWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** CallOrWriteNode#value */ + struct pm_node *value; +} pm_call_or_write_node_t; + +/** + * CapturePatternNode + * + * Type: PM_CAPTURE_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_capture_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + /** CapturePatternNode#value */ + struct pm_node *value; + + /** CapturePatternNode#target */ + struct pm_node *target; + + /** CapturePatternNode#operator_loc */ + pm_location_t operator_loc; +} pm_capture_pattern_node_t; + +/** + * CaseMatchNode + * + * Type: PM_CASE_MATCH_NODE + * + * @extends pm_node_t + */ +typedef struct pm_case_match_node { + /** The embedded base node. */ + pm_node_t base; + + /** CaseMatchNode#predicate */ + struct pm_node *predicate; + + /** CaseMatchNode#conditions */ + struct pm_node_list conditions; + + /** CaseMatchNode#consequent */ + struct pm_else_node *consequent; + + /** CaseMatchNode#case_keyword_loc */ + pm_location_t case_keyword_loc; + + /** CaseMatchNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_case_match_node_t; + +/** + * CaseNode + * + * Type: PM_CASE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_case_node { + /** The embedded base node. */ + pm_node_t base; + + /** CaseNode#predicate */ + struct pm_node *predicate; + + /** CaseNode#conditions */ + struct pm_node_list conditions; + + /** CaseNode#consequent */ + struct pm_else_node *consequent; + + /** CaseNode#case_keyword_loc */ + pm_location_t case_keyword_loc; + + /** CaseNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_case_node_t; + +/** + * ClassNode + * + * Type: PM_CLASS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_node { + /** The embedded base node. */ + pm_node_t base; + + /** ClassNode#locals */ + pm_constant_id_list_t locals; + + /** ClassNode#class_keyword_loc */ + pm_location_t class_keyword_loc; + + /** ClassNode#constant_path */ + struct pm_node *constant_path; + + /** ClassNode#inheritance_operator_loc */ + pm_location_t inheritance_operator_loc; + + /** ClassNode#superclass */ + struct pm_node *superclass; + + /** ClassNode#body */ + struct pm_node *body; + + /** ClassNode#end_keyword_loc */ + pm_location_t end_keyword_loc; + + /** ClassNode#name */ + pm_constant_id_t name; +} pm_class_node_t; + +/** + * ClassVariableAndWriteNode + * + * Type: PM_CLASS_VARIABLE_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ClassVariableAndWriteNode#name */ + pm_constant_id_t name; + + /** ClassVariableAndWriteNode#name_loc */ + pm_location_t name_loc; + + /** ClassVariableAndWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ClassVariableAndWriteNode#value */ + struct pm_node *value; +} pm_class_variable_and_write_node_t; + +/** + * ClassVariableOperatorWriteNode + * + * Type: PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ClassVariableOperatorWriteNode#name */ + pm_constant_id_t name; + + /** ClassVariableOperatorWriteNode#name_loc */ + pm_location_t name_loc; + + /** ClassVariableOperatorWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ClassVariableOperatorWriteNode#value */ + struct pm_node *value; + + /** ClassVariableOperatorWriteNode#operator */ + pm_constant_id_t operator; +} pm_class_variable_operator_write_node_t; + +/** + * ClassVariableOrWriteNode + * + * Type: PM_CLASS_VARIABLE_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ClassVariableOrWriteNode#name */ + pm_constant_id_t name; + + /** ClassVariableOrWriteNode#name_loc */ + pm_location_t name_loc; + + /** ClassVariableOrWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ClassVariableOrWriteNode#value */ + struct pm_node *value; +} pm_class_variable_or_write_node_t; + +/** + * ClassVariableReadNode + * + * Type: PM_CLASS_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + + /** ClassVariableReadNode#name */ + pm_constant_id_t name; +} pm_class_variable_read_node_t; + +/** + * ClassVariableTargetNode + * + * Type: PM_CLASS_VARIABLE_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_target_node { + /** The embedded base node. */ + pm_node_t base; + + /** ClassVariableTargetNode#name */ + pm_constant_id_t name; +} pm_class_variable_target_node_t; + +/** + * ClassVariableWriteNode + * + * Type: PM_CLASS_VARIABLE_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ClassVariableWriteNode#name */ + pm_constant_id_t name; + + /** ClassVariableWriteNode#name_loc */ + pm_location_t name_loc; + + /** ClassVariableWriteNode#value */ + struct pm_node *value; + + /** ClassVariableWriteNode#operator_loc */ + pm_location_t operator_loc; +} pm_class_variable_write_node_t; + +/** + * ConstantAndWriteNode + * + * Type: PM_CONSTANT_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantAndWriteNode#name */ + pm_constant_id_t name; + + /** ConstantAndWriteNode#name_loc */ + pm_location_t name_loc; + + /** ConstantAndWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ConstantAndWriteNode#value */ + struct pm_node *value; +} pm_constant_and_write_node_t; + +/** + * ConstantOperatorWriteNode + * + * Type: PM_CONSTANT_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantOperatorWriteNode#name */ + pm_constant_id_t name; + + /** ConstantOperatorWriteNode#name_loc */ + pm_location_t name_loc; + + /** ConstantOperatorWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ConstantOperatorWriteNode#value */ + struct pm_node *value; + + /** ConstantOperatorWriteNode#operator */ + pm_constant_id_t operator; +} pm_constant_operator_write_node_t; + +/** + * ConstantOrWriteNode + * + * Type: PM_CONSTANT_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantOrWriteNode#name */ + pm_constant_id_t name; + + /** ConstantOrWriteNode#name_loc */ + pm_location_t name_loc; + + /** ConstantOrWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ConstantOrWriteNode#value */ + struct pm_node *value; +} pm_constant_or_write_node_t; + +/** + * ConstantPathAndWriteNode + * + * Type: PM_CONSTANT_PATH_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantPathAndWriteNode#target */ + struct pm_constant_path_node *target; + + /** ConstantPathAndWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ConstantPathAndWriteNode#value */ + struct pm_node *value; +} pm_constant_path_and_write_node_t; + +/** + * ConstantPathNode + * + * Type: PM_CONSTANT_PATH_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantPathNode#parent */ + struct pm_node *parent; + + /** ConstantPathNode#child */ + struct pm_node *child; + + /** ConstantPathNode#delimiter_loc */ + pm_location_t delimiter_loc; +} pm_constant_path_node_t; + +/** + * ConstantPathOperatorWriteNode + * + * Type: PM_CONSTANT_PATH_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantPathOperatorWriteNode#target */ + struct pm_constant_path_node *target; + + /** ConstantPathOperatorWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ConstantPathOperatorWriteNode#value */ + struct pm_node *value; + + /** ConstantPathOperatorWriteNode#operator */ + pm_constant_id_t operator; +} pm_constant_path_operator_write_node_t; + +/** + * ConstantPathOrWriteNode + * + * Type: PM_CONSTANT_PATH_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantPathOrWriteNode#target */ + struct pm_constant_path_node *target; + + /** ConstantPathOrWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ConstantPathOrWriteNode#value */ + struct pm_node *value; +} pm_constant_path_or_write_node_t; + +/** + * ConstantPathTargetNode + * + * Type: PM_CONSTANT_PATH_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_target_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantPathTargetNode#parent */ + struct pm_node *parent; + + /** ConstantPathTargetNode#child */ + struct pm_node *child; + + /** ConstantPathTargetNode#delimiter_loc */ + pm_location_t delimiter_loc; +} pm_constant_path_target_node_t; + +/** + * ConstantPathWriteNode + * + * Type: PM_CONSTANT_PATH_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantPathWriteNode#target */ + struct pm_constant_path_node *target; + + /** ConstantPathWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** ConstantPathWriteNode#value */ + struct pm_node *value; +} pm_constant_path_write_node_t; + +/** + * ConstantReadNode + * + * Type: PM_CONSTANT_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_read_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantReadNode#name */ + pm_constant_id_t name; +} pm_constant_read_node_t; + +/** + * ConstantTargetNode + * + * Type: PM_CONSTANT_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_target_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantTargetNode#name */ + pm_constant_id_t name; +} pm_constant_target_node_t; + +/** + * ConstantWriteNode + * + * Type: PM_CONSTANT_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** ConstantWriteNode#name */ + pm_constant_id_t name; + + /** ConstantWriteNode#name_loc */ + pm_location_t name_loc; + + /** ConstantWriteNode#value */ + struct pm_node *value; + + /** ConstantWriteNode#operator_loc */ + pm_location_t operator_loc; +} pm_constant_write_node_t; + +/** + * DefNode + * + * Type: PM_DEF_NODE + * + * @extends pm_node_t + */ +typedef struct pm_def_node { + /** The embedded base node. */ + pm_node_t base; + + /** DefNode#name */ + pm_constant_id_t name; + + /** DefNode#name_loc */ + pm_location_t name_loc; + + /** DefNode#receiver */ + struct pm_node *receiver; + + /** DefNode#parameters */ + struct pm_parameters_node *parameters; + + /** DefNode#body */ + struct pm_node *body; + + /** DefNode#locals */ + pm_constant_id_list_t locals; + + /** DefNode#def_keyword_loc */ + pm_location_t def_keyword_loc; + + /** DefNode#operator_loc */ + pm_location_t operator_loc; + + /** DefNode#lparen_loc */ + pm_location_t lparen_loc; + + /** DefNode#rparen_loc */ + pm_location_t rparen_loc; + + /** DefNode#equal_loc */ + pm_location_t equal_loc; + + /** DefNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_def_node_t; + +/** + * DefinedNode + * + * Type: PM_DEFINED_NODE + * + * @extends pm_node_t + */ +typedef struct pm_defined_node { + /** The embedded base node. */ + pm_node_t base; + + /** DefinedNode#lparen_loc */ + pm_location_t lparen_loc; + + /** DefinedNode#value */ + struct pm_node *value; + + /** DefinedNode#rparen_loc */ + pm_location_t rparen_loc; + + /** DefinedNode#keyword_loc */ + pm_location_t keyword_loc; +} pm_defined_node_t; + +/** + * ElseNode + * + * Type: PM_ELSE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_else_node { + /** The embedded base node. */ + pm_node_t base; + + /** ElseNode#else_keyword_loc */ + pm_location_t else_keyword_loc; + + /** ElseNode#statements */ + struct pm_statements_node *statements; + + /** ElseNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_else_node_t; + +/** + * EmbeddedStatementsNode + * + * Type: PM_EMBEDDED_STATEMENTS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_embedded_statements_node { + /** The embedded base node. */ + pm_node_t base; + + /** EmbeddedStatementsNode#opening_loc */ + pm_location_t opening_loc; + + /** EmbeddedStatementsNode#statements */ + struct pm_statements_node *statements; + + /** EmbeddedStatementsNode#closing_loc */ + pm_location_t closing_loc; +} pm_embedded_statements_node_t; + +/** + * EmbeddedVariableNode + * + * Type: PM_EMBEDDED_VARIABLE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_embedded_variable_node { + /** The embedded base node. */ + pm_node_t base; + + /** EmbeddedVariableNode#operator_loc */ + pm_location_t operator_loc; + + /** EmbeddedVariableNode#variable */ + struct pm_node *variable; +} pm_embedded_variable_node_t; + +/** + * EnsureNode + * + * Type: PM_ENSURE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_ensure_node { + /** The embedded base node. */ + pm_node_t base; + + /** EnsureNode#ensure_keyword_loc */ + pm_location_t ensure_keyword_loc; + + /** EnsureNode#statements */ + struct pm_statements_node *statements; + + /** EnsureNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_ensure_node_t; + +/** + * FalseNode + * + * Type: PM_FALSE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_false_node { + /** The embedded base node. */ + pm_node_t base; +} pm_false_node_t; + +/** + * FindPatternNode + * + * Type: PM_FIND_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_find_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + /** FindPatternNode#constant */ + struct pm_node *constant; + + /** FindPatternNode#left */ + struct pm_node *left; + + /** FindPatternNode#requireds */ + struct pm_node_list requireds; + + /** FindPatternNode#right */ + struct pm_node *right; + + /** FindPatternNode#opening_loc */ + pm_location_t opening_loc; + + /** FindPatternNode#closing_loc */ + pm_location_t closing_loc; +} pm_find_pattern_node_t; + +/** + * FlipFlopNode + * + * Type: PM_FLIP_FLOP_NODE + * Flags: + * PM_RANGE_FLAGS_EXCLUDE_END + * + * @extends pm_node_t + */ +typedef struct pm_flip_flop_node { + /** The embedded base node. */ + pm_node_t base; + + /** FlipFlopNode#left */ + struct pm_node *left; + + /** FlipFlopNode#right */ + struct pm_node *right; + + /** FlipFlopNode#operator_loc */ + pm_location_t operator_loc; +} pm_flip_flop_node_t; + +/** + * FloatNode + * + * Type: PM_FLOAT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_float_node { + /** The embedded base node. */ + pm_node_t base; +} pm_float_node_t; + +/** + * ForNode + * + * Type: PM_FOR_NODE + * + * @extends pm_node_t + */ +typedef struct pm_for_node { + /** The embedded base node. */ + pm_node_t base; + + /** ForNode#index */ + struct pm_node *index; + + /** ForNode#collection */ + struct pm_node *collection; + + /** ForNode#statements */ + struct pm_statements_node *statements; + + /** ForNode#for_keyword_loc */ + pm_location_t for_keyword_loc; + + /** ForNode#in_keyword_loc */ + pm_location_t in_keyword_loc; + + /** ForNode#do_keyword_loc */ + pm_location_t do_keyword_loc; + + /** ForNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_for_node_t; + +/** + * ForwardingArgumentsNode + * + * Type: PM_FORWARDING_ARGUMENTS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_forwarding_arguments_node { + /** The embedded base node. */ + pm_node_t base; +} pm_forwarding_arguments_node_t; + +/** + * ForwardingParameterNode + * + * Type: PM_FORWARDING_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_forwarding_parameter_node { + /** The embedded base node. */ + pm_node_t base; +} pm_forwarding_parameter_node_t; + +/** + * ForwardingSuperNode + * + * Type: PM_FORWARDING_SUPER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_forwarding_super_node { + /** The embedded base node. */ + pm_node_t base; + + /** ForwardingSuperNode#block */ + struct pm_block_node *block; +} pm_forwarding_super_node_t; + +/** + * GlobalVariableAndWriteNode + * + * Type: PM_GLOBAL_VARIABLE_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** GlobalVariableAndWriteNode#name */ + pm_constant_id_t name; + + /** GlobalVariableAndWriteNode#name_loc */ + pm_location_t name_loc; + + /** GlobalVariableAndWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** GlobalVariableAndWriteNode#value */ + struct pm_node *value; +} pm_global_variable_and_write_node_t; + +/** + * GlobalVariableOperatorWriteNode + * + * Type: PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** GlobalVariableOperatorWriteNode#name */ + pm_constant_id_t name; + + /** GlobalVariableOperatorWriteNode#name_loc */ + pm_location_t name_loc; + + /** GlobalVariableOperatorWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** GlobalVariableOperatorWriteNode#value */ + struct pm_node *value; + + /** GlobalVariableOperatorWriteNode#operator */ + pm_constant_id_t operator; +} pm_global_variable_operator_write_node_t; + +/** + * GlobalVariableOrWriteNode + * + * Type: PM_GLOBAL_VARIABLE_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** GlobalVariableOrWriteNode#name */ + pm_constant_id_t name; + + /** GlobalVariableOrWriteNode#name_loc */ + pm_location_t name_loc; + + /** GlobalVariableOrWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** GlobalVariableOrWriteNode#value */ + struct pm_node *value; +} pm_global_variable_or_write_node_t; + +/** + * GlobalVariableReadNode + * + * Type: PM_GLOBAL_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + + /** GlobalVariableReadNode#name */ + pm_constant_id_t name; +} pm_global_variable_read_node_t; + +/** + * GlobalVariableTargetNode + * + * Type: PM_GLOBAL_VARIABLE_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_target_node { + /** The embedded base node. */ + pm_node_t base; + + /** GlobalVariableTargetNode#name */ + pm_constant_id_t name; +} pm_global_variable_target_node_t; + +/** + * GlobalVariableWriteNode + * + * Type: PM_GLOBAL_VARIABLE_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** GlobalVariableWriteNode#name */ + pm_constant_id_t name; + + /** GlobalVariableWriteNode#name_loc */ + pm_location_t name_loc; + + /** GlobalVariableWriteNode#value */ + struct pm_node *value; + + /** GlobalVariableWriteNode#operator_loc */ + pm_location_t operator_loc; +} pm_global_variable_write_node_t; + +/** + * HashNode + * + * Type: PM_HASH_NODE + * + * @extends pm_node_t + */ +typedef struct pm_hash_node { + /** The embedded base node. */ + pm_node_t base; + + /** HashNode#opening_loc */ + pm_location_t opening_loc; + + /** HashNode#elements */ + struct pm_node_list elements; + + /** HashNode#closing_loc */ + pm_location_t closing_loc; +} pm_hash_node_t; + +/** + * HashPatternNode + * + * Type: PM_HASH_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_hash_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + /** HashPatternNode#constant */ + struct pm_node *constant; + + /** HashPatternNode#elements */ + struct pm_node_list elements; + + /** HashPatternNode#rest */ + struct pm_node *rest; + + /** HashPatternNode#opening_loc */ + pm_location_t opening_loc; + + /** HashPatternNode#closing_loc */ + pm_location_t closing_loc; +} pm_hash_pattern_node_t; + +/** + * IfNode + * + * Type: PM_IF_NODE + * + * @extends pm_node_t + */ +typedef struct pm_if_node { + /** The embedded base node. */ + pm_node_t base; + + /** IfNode#if_keyword_loc */ + pm_location_t if_keyword_loc; + + /** IfNode#predicate */ + struct pm_node *predicate; + + /** IfNode#then_keyword_loc */ + pm_location_t then_keyword_loc; + + /** IfNode#statements */ + struct pm_statements_node *statements; + + /** IfNode#consequent */ + struct pm_node *consequent; + + /** IfNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_if_node_t; + +/** + * ImaginaryNode + * + * Type: PM_IMAGINARY_NODE + * + * @extends pm_node_t + */ +typedef struct pm_imaginary_node { + /** The embedded base node. */ + pm_node_t base; + + /** ImaginaryNode#numeric */ + struct pm_node *numeric; +} pm_imaginary_node_t; + +/** + * ImplicitNode + * + * Type: PM_IMPLICIT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_implicit_node { + /** The embedded base node. */ + pm_node_t base; + + /** ImplicitNode#value */ + struct pm_node *value; +} pm_implicit_node_t; + +/** + * ImplicitRestNode + * + * Type: PM_IMPLICIT_REST_NODE + * + * @extends pm_node_t + */ +typedef struct pm_implicit_rest_node { + /** The embedded base node. */ + pm_node_t base; +} pm_implicit_rest_node_t; + +/** + * InNode + * + * Type: PM_IN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_in_node { + /** The embedded base node. */ + pm_node_t base; + + /** InNode#pattern */ + struct pm_node *pattern; + + /** InNode#statements */ + struct pm_statements_node *statements; + + /** InNode#in_loc */ + pm_location_t in_loc; + + /** InNode#then_loc */ + pm_location_t then_loc; +} pm_in_node_t; + +/** + * IndexAndWriteNode + * + * Type: PM_INDEX_AND_WRITE_NODE + * Flags: + * PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * PM_CALL_NODE_FLAGS_VARIABLE_CALL + * + * @extends pm_node_t + */ +typedef struct pm_index_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** IndexAndWriteNode#receiver */ + struct pm_node *receiver; + + /** IndexAndWriteNode#call_operator_loc */ + pm_location_t call_operator_loc; + + /** IndexAndWriteNode#opening_loc */ + pm_location_t opening_loc; + + /** IndexAndWriteNode#arguments */ + struct pm_arguments_node *arguments; + + /** IndexAndWriteNode#closing_loc */ + pm_location_t closing_loc; + + /** IndexAndWriteNode#block */ + struct pm_node *block; + + /** IndexAndWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** IndexAndWriteNode#value */ + struct pm_node *value; +} pm_index_and_write_node_t; + +/** + * IndexOperatorWriteNode + * + * Type: PM_INDEX_OPERATOR_WRITE_NODE + * Flags: + * PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * PM_CALL_NODE_FLAGS_VARIABLE_CALL + * + * @extends pm_node_t + */ +typedef struct pm_index_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** IndexOperatorWriteNode#receiver */ + struct pm_node *receiver; + + /** IndexOperatorWriteNode#call_operator_loc */ + pm_location_t call_operator_loc; + + /** IndexOperatorWriteNode#opening_loc */ + pm_location_t opening_loc; + + /** IndexOperatorWriteNode#arguments */ + struct pm_arguments_node *arguments; + + /** IndexOperatorWriteNode#closing_loc */ + pm_location_t closing_loc; + + /** IndexOperatorWriteNode#block */ + struct pm_node *block; + + /** IndexOperatorWriteNode#operator */ + pm_constant_id_t operator; + + /** IndexOperatorWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** IndexOperatorWriteNode#value */ + struct pm_node *value; +} pm_index_operator_write_node_t; + +/** + * IndexOrWriteNode + * + * Type: PM_INDEX_OR_WRITE_NODE + * Flags: + * PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * PM_CALL_NODE_FLAGS_VARIABLE_CALL + * + * @extends pm_node_t + */ +typedef struct pm_index_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** IndexOrWriteNode#receiver */ + struct pm_node *receiver; + + /** IndexOrWriteNode#call_operator_loc */ + pm_location_t call_operator_loc; + + /** IndexOrWriteNode#opening_loc */ + pm_location_t opening_loc; + + /** IndexOrWriteNode#arguments */ + struct pm_arguments_node *arguments; + + /** IndexOrWriteNode#closing_loc */ + pm_location_t closing_loc; + + /** IndexOrWriteNode#block */ + struct pm_node *block; + + /** IndexOrWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** IndexOrWriteNode#value */ + struct pm_node *value; +} pm_index_or_write_node_t; + +/** + * InstanceVariableAndWriteNode + * + * Type: PM_INSTANCE_VARIABLE_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** InstanceVariableAndWriteNode#name */ + pm_constant_id_t name; + + /** InstanceVariableAndWriteNode#name_loc */ + pm_location_t name_loc; + + /** InstanceVariableAndWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** InstanceVariableAndWriteNode#value */ + struct pm_node *value; +} pm_instance_variable_and_write_node_t; + +/** + * InstanceVariableOperatorWriteNode + * + * Type: PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** InstanceVariableOperatorWriteNode#name */ + pm_constant_id_t name; + + /** InstanceVariableOperatorWriteNode#name_loc */ + pm_location_t name_loc; + + /** InstanceVariableOperatorWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** InstanceVariableOperatorWriteNode#value */ + struct pm_node *value; + + /** InstanceVariableOperatorWriteNode#operator */ + pm_constant_id_t operator; +} pm_instance_variable_operator_write_node_t; + +/** + * InstanceVariableOrWriteNode + * + * Type: PM_INSTANCE_VARIABLE_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** InstanceVariableOrWriteNode#name */ + pm_constant_id_t name; + + /** InstanceVariableOrWriteNode#name_loc */ + pm_location_t name_loc; + + /** InstanceVariableOrWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** InstanceVariableOrWriteNode#value */ + struct pm_node *value; +} pm_instance_variable_or_write_node_t; + +/** + * InstanceVariableReadNode + * + * Type: PM_INSTANCE_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + + /** InstanceVariableReadNode#name */ + pm_constant_id_t name; +} pm_instance_variable_read_node_t; + +/** + * InstanceVariableTargetNode + * + * Type: PM_INSTANCE_VARIABLE_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_target_node { + /** The embedded base node. */ + pm_node_t base; + + /** InstanceVariableTargetNode#name */ + pm_constant_id_t name; +} pm_instance_variable_target_node_t; + +/** + * InstanceVariableWriteNode + * + * Type: PM_INSTANCE_VARIABLE_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** InstanceVariableWriteNode#name */ + pm_constant_id_t name; + + /** InstanceVariableWriteNode#name_loc */ + pm_location_t name_loc; + + /** InstanceVariableWriteNode#value */ + struct pm_node *value; + + /** InstanceVariableWriteNode#operator_loc */ + pm_location_t operator_loc; +} pm_instance_variable_write_node_t; + +/** + * IntegerNode + * + * Type: PM_INTEGER_NODE + * Flags: + * PM_INTEGER_BASE_FLAGS_BINARY + * PM_INTEGER_BASE_FLAGS_OCTAL + * PM_INTEGER_BASE_FLAGS_DECIMAL + * PM_INTEGER_BASE_FLAGS_HEXADECIMAL + * + * @extends pm_node_t + */ +typedef struct pm_integer_node { + /** The embedded base node. */ + pm_node_t base; +} pm_integer_node_t; + +/** + * InterpolatedMatchLastLineNode + * + * Type: PM_INTERPOLATED_MATCH_LAST_LINE_NODE + * Flags: + * PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE + * PM_REGULAR_EXPRESSION_FLAGS_EXTENDED + * PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE + * PM_REGULAR_EXPRESSION_FLAGS_ONCE + * PM_REGULAR_EXPRESSION_FLAGS_EUC_JP + * PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT + * PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J + * PM_REGULAR_EXPRESSION_FLAGS_UTF_8 + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_match_last_line_node { + /** The embedded base node. */ + pm_node_t base; + + /** InterpolatedMatchLastLineNode#opening_loc */ + pm_location_t opening_loc; + + /** InterpolatedMatchLastLineNode#parts */ + struct pm_node_list parts; + + /** InterpolatedMatchLastLineNode#closing_loc */ + pm_location_t closing_loc; +} pm_interpolated_match_last_line_node_t; + +/** + * InterpolatedRegularExpressionNode + * + * Type: PM_INTERPOLATED_REGULAR_EXPRESSION_NODE + * Flags: + * PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE + * PM_REGULAR_EXPRESSION_FLAGS_EXTENDED + * PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE + * PM_REGULAR_EXPRESSION_FLAGS_ONCE + * PM_REGULAR_EXPRESSION_FLAGS_EUC_JP + * PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT + * PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J + * PM_REGULAR_EXPRESSION_FLAGS_UTF_8 + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_regular_expression_node { + /** The embedded base node. */ + pm_node_t base; + + /** InterpolatedRegularExpressionNode#opening_loc */ + pm_location_t opening_loc; + + /** InterpolatedRegularExpressionNode#parts */ + struct pm_node_list parts; + + /** InterpolatedRegularExpressionNode#closing_loc */ + pm_location_t closing_loc; +} pm_interpolated_regular_expression_node_t; + +/** + * InterpolatedStringNode + * + * Type: PM_INTERPOLATED_STRING_NODE + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_string_node { + /** The embedded base node. */ + pm_node_t base; + + /** InterpolatedStringNode#opening_loc */ + pm_location_t opening_loc; + + /** InterpolatedStringNode#parts */ + struct pm_node_list parts; + + /** InterpolatedStringNode#closing_loc */ + pm_location_t closing_loc; +} pm_interpolated_string_node_t; + +/** + * InterpolatedSymbolNode + * + * Type: PM_INTERPOLATED_SYMBOL_NODE + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_symbol_node { + /** The embedded base node. */ + pm_node_t base; + + /** InterpolatedSymbolNode#opening_loc */ + pm_location_t opening_loc; + + /** InterpolatedSymbolNode#parts */ + struct pm_node_list parts; + + /** InterpolatedSymbolNode#closing_loc */ + pm_location_t closing_loc; +} pm_interpolated_symbol_node_t; + +/** + * InterpolatedXStringNode + * + * Type: PM_INTERPOLATED_X_STRING_NODE + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_x_string_node { + /** The embedded base node. */ + pm_node_t base; + + /** InterpolatedXStringNode#opening_loc */ + pm_location_t opening_loc; + + /** InterpolatedXStringNode#parts */ + struct pm_node_list parts; + + /** InterpolatedXStringNode#closing_loc */ + pm_location_t closing_loc; +} pm_interpolated_x_string_node_t; + +/** + * KeywordHashNode + * + * Type: PM_KEYWORD_HASH_NODE + * + * @extends pm_node_t + */ +typedef struct pm_keyword_hash_node { + /** The embedded base node. */ + pm_node_t base; + + /** KeywordHashNode#elements */ + struct pm_node_list elements; +} pm_keyword_hash_node_t; + +/** + * KeywordRestParameterNode + * + * Type: PM_KEYWORD_REST_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_keyword_rest_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + /** KeywordRestParameterNode#name */ + pm_constant_id_t name; + + /** KeywordRestParameterNode#name_loc */ + pm_location_t name_loc; + + /** KeywordRestParameterNode#operator_loc */ + pm_location_t operator_loc; +} pm_keyword_rest_parameter_node_t; + +/** + * LambdaNode + * + * Type: PM_LAMBDA_NODE + * + * @extends pm_node_t + */ +typedef struct pm_lambda_node { + /** The embedded base node. */ + pm_node_t base; + + /** LambdaNode#locals */ + pm_constant_id_list_t locals; + + /** LambdaNode#operator_loc */ + pm_location_t operator_loc; + + /** LambdaNode#opening_loc */ + pm_location_t opening_loc; + + /** LambdaNode#closing_loc */ + pm_location_t closing_loc; + + /** LambdaNode#parameters */ + struct pm_block_parameters_node *parameters; + + /** LambdaNode#body */ + struct pm_node *body; + + /** LambdaNode#numbered_parameters */ + uint32_t numbered_parameters; +} pm_lambda_node_t; + +/** + * LocalVariableAndWriteNode + * + * Type: PM_LOCAL_VARIABLE_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** LocalVariableAndWriteNode#name_loc */ + pm_location_t name_loc; + + /** LocalVariableAndWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** LocalVariableAndWriteNode#value */ + struct pm_node *value; + + /** LocalVariableAndWriteNode#name */ + pm_constant_id_t name; + + /** LocalVariableAndWriteNode#depth */ + uint32_t depth; +} pm_local_variable_and_write_node_t; + +/** + * LocalVariableOperatorWriteNode + * + * Type: PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** LocalVariableOperatorWriteNode#name_loc */ + pm_location_t name_loc; + + /** LocalVariableOperatorWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** LocalVariableOperatorWriteNode#value */ + struct pm_node *value; + + /** LocalVariableOperatorWriteNode#name */ + pm_constant_id_t name; + + /** LocalVariableOperatorWriteNode#operator */ + pm_constant_id_t operator; + + /** LocalVariableOperatorWriteNode#depth */ + uint32_t depth; +} pm_local_variable_operator_write_node_t; + +/** + * LocalVariableOrWriteNode + * + * Type: PM_LOCAL_VARIABLE_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** LocalVariableOrWriteNode#name_loc */ + pm_location_t name_loc; + + /** LocalVariableOrWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** LocalVariableOrWriteNode#value */ + struct pm_node *value; + + /** LocalVariableOrWriteNode#name */ + pm_constant_id_t name; + + /** LocalVariableOrWriteNode#depth */ + uint32_t depth; +} pm_local_variable_or_write_node_t; + +/** + * LocalVariableReadNode + * + * Type: PM_LOCAL_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + + /** LocalVariableReadNode#name */ + pm_constant_id_t name; + + /** LocalVariableReadNode#depth */ + uint32_t depth; +} pm_local_variable_read_node_t; + +/** + * LocalVariableTargetNode + * + * Type: PM_LOCAL_VARIABLE_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_target_node { + /** The embedded base node. */ + pm_node_t base; + + /** LocalVariableTargetNode#name */ + pm_constant_id_t name; + + /** LocalVariableTargetNode#depth */ + uint32_t depth; +} pm_local_variable_target_node_t; + +/** + * LocalVariableWriteNode + * + * Type: PM_LOCAL_VARIABLE_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** LocalVariableWriteNode#name */ + pm_constant_id_t name; + + /** LocalVariableWriteNode#depth */ + uint32_t depth; + + /** LocalVariableWriteNode#name_loc */ + pm_location_t name_loc; + + /** LocalVariableWriteNode#value */ + struct pm_node *value; + + /** LocalVariableWriteNode#operator_loc */ + pm_location_t operator_loc; +} pm_local_variable_write_node_t; + +/** + * MatchLastLineNode + * + * Type: PM_MATCH_LAST_LINE_NODE + * Flags: + * PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE + * PM_REGULAR_EXPRESSION_FLAGS_EXTENDED + * PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE + * PM_REGULAR_EXPRESSION_FLAGS_ONCE + * PM_REGULAR_EXPRESSION_FLAGS_EUC_JP + * PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT + * PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J + * PM_REGULAR_EXPRESSION_FLAGS_UTF_8 + * + * @extends pm_node_t + */ +typedef struct pm_match_last_line_node { + /** The embedded base node. */ + pm_node_t base; + + /** MatchLastLineNode#opening_loc */ + pm_location_t opening_loc; + + /** MatchLastLineNode#content_loc */ + pm_location_t content_loc; + + /** MatchLastLineNode#closing_loc */ + pm_location_t closing_loc; + + /** MatchLastLineNode#unescaped */ + pm_string_t unescaped; +} pm_match_last_line_node_t; + +/** + * MatchPredicateNode + * + * Type: PM_MATCH_PREDICATE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_match_predicate_node { + /** The embedded base node. */ + pm_node_t base; + + /** MatchPredicateNode#value */ + struct pm_node *value; + + /** MatchPredicateNode#pattern */ + struct pm_node *pattern; + + /** MatchPredicateNode#operator_loc */ + pm_location_t operator_loc; +} pm_match_predicate_node_t; + +/** + * MatchRequiredNode + * + * Type: PM_MATCH_REQUIRED_NODE + * + * @extends pm_node_t + */ +typedef struct pm_match_required_node { + /** The embedded base node. */ + pm_node_t base; + + /** MatchRequiredNode#value */ + struct pm_node *value; + + /** MatchRequiredNode#pattern */ + struct pm_node *pattern; + + /** MatchRequiredNode#operator_loc */ + pm_location_t operator_loc; +} pm_match_required_node_t; + +/** + * MatchWriteNode + * + * Type: PM_MATCH_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_match_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** MatchWriteNode#call */ + struct pm_call_node *call; + + /** MatchWriteNode#targets */ + struct pm_node_list targets; +} pm_match_write_node_t; + +/** + * MissingNode + * + * Type: PM_MISSING_NODE + * + * @extends pm_node_t + */ +typedef struct pm_missing_node { + /** The embedded base node. */ + pm_node_t base; +} pm_missing_node_t; + +/** + * ModuleNode + * + * Type: PM_MODULE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_module_node { + /** The embedded base node. */ + pm_node_t base; + + /** ModuleNode#locals */ + pm_constant_id_list_t locals; + + /** ModuleNode#module_keyword_loc */ + pm_location_t module_keyword_loc; + + /** ModuleNode#constant_path */ + struct pm_node *constant_path; + + /** ModuleNode#body */ + struct pm_node *body; + + /** ModuleNode#end_keyword_loc */ + pm_location_t end_keyword_loc; + + /** ModuleNode#name */ + pm_constant_id_t name; +} pm_module_node_t; + +/** + * MultiTargetNode + * + * Type: PM_MULTI_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_multi_target_node { + /** The embedded base node. */ + pm_node_t base; + + /** MultiTargetNode#lefts */ + struct pm_node_list lefts; + + /** MultiTargetNode#rest */ + struct pm_node *rest; + + /** MultiTargetNode#rights */ + struct pm_node_list rights; + + /** MultiTargetNode#lparen_loc */ + pm_location_t lparen_loc; + + /** MultiTargetNode#rparen_loc */ + pm_location_t rparen_loc; +} pm_multi_target_node_t; + +/** + * MultiWriteNode + * + * Type: PM_MULTI_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_multi_write_node { + /** The embedded base node. */ + pm_node_t base; + + /** MultiWriteNode#lefts */ + struct pm_node_list lefts; + + /** MultiWriteNode#rest */ + struct pm_node *rest; + + /** MultiWriteNode#rights */ + struct pm_node_list rights; + + /** MultiWriteNode#lparen_loc */ + pm_location_t lparen_loc; + + /** MultiWriteNode#rparen_loc */ + pm_location_t rparen_loc; + + /** MultiWriteNode#operator_loc */ + pm_location_t operator_loc; + + /** MultiWriteNode#value */ + struct pm_node *value; +} pm_multi_write_node_t; + +/** + * NextNode + * + * Type: PM_NEXT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_next_node { + /** The embedded base node. */ + pm_node_t base; + + /** NextNode#arguments */ + struct pm_arguments_node *arguments; + + /** NextNode#keyword_loc */ + pm_location_t keyword_loc; +} pm_next_node_t; + +/** + * NilNode + * + * Type: PM_NIL_NODE + * + * @extends pm_node_t + */ +typedef struct pm_nil_node { + /** The embedded base node. */ + pm_node_t base; +} pm_nil_node_t; + +/** + * NoKeywordsParameterNode + * + * Type: PM_NO_KEYWORDS_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_no_keywords_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + /** NoKeywordsParameterNode#operator_loc */ + pm_location_t operator_loc; + + /** NoKeywordsParameterNode#keyword_loc */ + pm_location_t keyword_loc; +} pm_no_keywords_parameter_node_t; + +/** + * NumberedReferenceReadNode + * + * Type: PM_NUMBERED_REFERENCE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_numbered_reference_read_node { + /** The embedded base node. */ + pm_node_t base; + + /** NumberedReferenceReadNode#number */ + uint32_t number; +} pm_numbered_reference_read_node_t; + +/** + * OptionalKeywordParameterNode + * + * Type: PM_OPTIONAL_KEYWORD_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_optional_keyword_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + /** OptionalKeywordParameterNode#name */ + pm_constant_id_t name; + + /** OptionalKeywordParameterNode#name_loc */ + pm_location_t name_loc; + + /** OptionalKeywordParameterNode#value */ + struct pm_node *value; +} pm_optional_keyword_parameter_node_t; + +/** + * OptionalParameterNode + * + * Type: PM_OPTIONAL_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_optional_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + /** OptionalParameterNode#name */ + pm_constant_id_t name; + + /** OptionalParameterNode#name_loc */ + pm_location_t name_loc; + + /** OptionalParameterNode#operator_loc */ + pm_location_t operator_loc; + + /** OptionalParameterNode#value */ + struct pm_node *value; +} pm_optional_parameter_node_t; + +/** + * OrNode + * + * Type: PM_OR_NODE + * + * @extends pm_node_t + */ +typedef struct pm_or_node { + /** The embedded base node. */ + pm_node_t base; + + /** OrNode#left */ + struct pm_node *left; + + /** OrNode#right */ + struct pm_node *right; + + /** OrNode#operator_loc */ + pm_location_t operator_loc; +} pm_or_node_t; + +/** + * ParametersNode + * + * Type: PM_PARAMETERS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_parameters_node { + /** The embedded base node. */ + pm_node_t base; + + /** ParametersNode#requireds */ + struct pm_node_list requireds; + + /** ParametersNode#optionals */ + struct pm_node_list optionals; + + /** ParametersNode#rest */ + struct pm_node *rest; + + /** ParametersNode#posts */ + struct pm_node_list posts; + + /** ParametersNode#keywords */ + struct pm_node_list keywords; + + /** ParametersNode#keyword_rest */ + struct pm_node *keyword_rest; + + /** ParametersNode#block */ + struct pm_block_parameter_node *block; +} pm_parameters_node_t; + +/** + * ParenthesesNode + * + * Type: PM_PARENTHESES_NODE + * + * @extends pm_node_t + */ +typedef struct pm_parentheses_node { + /** The embedded base node. */ + pm_node_t base; + + /** ParenthesesNode#body */ + struct pm_node *body; + + /** ParenthesesNode#opening_loc */ + pm_location_t opening_loc; + + /** ParenthesesNode#closing_loc */ + pm_location_t closing_loc; +} pm_parentheses_node_t; + +/** + * PinnedExpressionNode + * + * Type: PM_PINNED_EXPRESSION_NODE + * + * @extends pm_node_t + */ +typedef struct pm_pinned_expression_node { + /** The embedded base node. */ + pm_node_t base; + + /** PinnedExpressionNode#expression */ + struct pm_node *expression; + + /** PinnedExpressionNode#operator_loc */ + pm_location_t operator_loc; + + /** PinnedExpressionNode#lparen_loc */ + pm_location_t lparen_loc; + + /** PinnedExpressionNode#rparen_loc */ + pm_location_t rparen_loc; +} pm_pinned_expression_node_t; + +/** + * PinnedVariableNode + * + * Type: PM_PINNED_VARIABLE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_pinned_variable_node { + /** The embedded base node. */ + pm_node_t base; + + /** PinnedVariableNode#variable */ + struct pm_node *variable; + + /** PinnedVariableNode#operator_loc */ + pm_location_t operator_loc; +} pm_pinned_variable_node_t; + +/** + * PostExecutionNode + * + * Type: PM_POST_EXECUTION_NODE + * + * @extends pm_node_t + */ +typedef struct pm_post_execution_node { + /** The embedded base node. */ + pm_node_t base; + + /** PostExecutionNode#statements */ + struct pm_statements_node *statements; + + /** PostExecutionNode#keyword_loc */ + pm_location_t keyword_loc; + + /** PostExecutionNode#opening_loc */ + pm_location_t opening_loc; + + /** PostExecutionNode#closing_loc */ + pm_location_t closing_loc; +} pm_post_execution_node_t; + +/** + * PreExecutionNode + * + * Type: PM_PRE_EXECUTION_NODE + * + * @extends pm_node_t + */ +typedef struct pm_pre_execution_node { + /** The embedded base node. */ + pm_node_t base; + + /** PreExecutionNode#statements */ + struct pm_statements_node *statements; + + /** PreExecutionNode#keyword_loc */ + pm_location_t keyword_loc; + + /** PreExecutionNode#opening_loc */ + pm_location_t opening_loc; + + /** PreExecutionNode#closing_loc */ + pm_location_t closing_loc; +} pm_pre_execution_node_t; + +/** + * ProgramNode + * + * Type: PM_PROGRAM_NODE + * + * @extends pm_node_t + */ +typedef struct pm_program_node { + /** The embedded base node. */ + pm_node_t base; + + /** ProgramNode#locals */ + pm_constant_id_list_t locals; + + /** ProgramNode#statements */ + struct pm_statements_node *statements; +} pm_program_node_t; + +/** + * RangeNode + * + * Type: PM_RANGE_NODE + * Flags: + * PM_RANGE_FLAGS_EXCLUDE_END + * + * @extends pm_node_t + */ +typedef struct pm_range_node { + /** The embedded base node. */ + pm_node_t base; + + /** RangeNode#left */ + struct pm_node *left; + + /** RangeNode#right */ + struct pm_node *right; + + /** RangeNode#operator_loc */ + pm_location_t operator_loc; +} pm_range_node_t; + +/** + * RationalNode + * + * Type: PM_RATIONAL_NODE + * + * @extends pm_node_t + */ +typedef struct pm_rational_node { + /** The embedded base node. */ + pm_node_t base; + + /** RationalNode#numeric */ + struct pm_node *numeric; +} pm_rational_node_t; + +/** + * RedoNode + * + * Type: PM_REDO_NODE + * + * @extends pm_node_t + */ +typedef struct pm_redo_node { + /** The embedded base node. */ + pm_node_t base; +} pm_redo_node_t; + +/** + * RegularExpressionNode + * + * Type: PM_REGULAR_EXPRESSION_NODE + * Flags: + * PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE + * PM_REGULAR_EXPRESSION_FLAGS_EXTENDED + * PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE + * PM_REGULAR_EXPRESSION_FLAGS_ONCE + * PM_REGULAR_EXPRESSION_FLAGS_EUC_JP + * PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT + * PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J + * PM_REGULAR_EXPRESSION_FLAGS_UTF_8 + * + * @extends pm_node_t + */ +typedef struct pm_regular_expression_node { + /** The embedded base node. */ + pm_node_t base; + + /** RegularExpressionNode#opening_loc */ + pm_location_t opening_loc; + + /** RegularExpressionNode#content_loc */ + pm_location_t content_loc; + + /** RegularExpressionNode#closing_loc */ + pm_location_t closing_loc; + + /** RegularExpressionNode#unescaped */ + pm_string_t unescaped; +} pm_regular_expression_node_t; + +/** + * RequiredKeywordParameterNode + * + * Type: PM_REQUIRED_KEYWORD_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_required_keyword_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + /** RequiredKeywordParameterNode#name */ + pm_constant_id_t name; + + /** RequiredKeywordParameterNode#name_loc */ + pm_location_t name_loc; +} pm_required_keyword_parameter_node_t; + +/** + * RequiredParameterNode + * + * Type: PM_REQUIRED_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_required_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + /** RequiredParameterNode#name */ + pm_constant_id_t name; +} pm_required_parameter_node_t; + +/** + * RescueModifierNode + * + * Type: PM_RESCUE_MODIFIER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_rescue_modifier_node { + /** The embedded base node. */ + pm_node_t base; + + /** RescueModifierNode#expression */ + struct pm_node *expression; + + /** RescueModifierNode#keyword_loc */ + pm_location_t keyword_loc; + + /** RescueModifierNode#rescue_expression */ + struct pm_node *rescue_expression; +} pm_rescue_modifier_node_t; + +/** + * RescueNode + * + * Type: PM_RESCUE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_rescue_node { + /** The embedded base node. */ + pm_node_t base; + + /** RescueNode#keyword_loc */ + pm_location_t keyword_loc; + + /** RescueNode#exceptions */ + struct pm_node_list exceptions; + + /** RescueNode#operator_loc */ + pm_location_t operator_loc; + + /** RescueNode#reference */ + struct pm_node *reference; + + /** RescueNode#statements */ + struct pm_statements_node *statements; + + /** RescueNode#consequent */ + struct pm_rescue_node *consequent; +} pm_rescue_node_t; + +/** + * RestParameterNode + * + * Type: PM_REST_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_rest_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + /** RestParameterNode#name */ + pm_constant_id_t name; + + /** RestParameterNode#name_loc */ + pm_location_t name_loc; + + /** RestParameterNode#operator_loc */ + pm_location_t operator_loc; +} pm_rest_parameter_node_t; + +/** + * RetryNode + * + * Type: PM_RETRY_NODE + * + * @extends pm_node_t + */ +typedef struct pm_retry_node { + /** The embedded base node. */ + pm_node_t base; +} pm_retry_node_t; + +/** + * ReturnNode + * + * Type: PM_RETURN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_return_node { + /** The embedded base node. */ + pm_node_t base; + + /** ReturnNode#keyword_loc */ + pm_location_t keyword_loc; + + /** ReturnNode#arguments */ + struct pm_arguments_node *arguments; +} pm_return_node_t; + +/** + * SelfNode + * + * Type: PM_SELF_NODE + * + * @extends pm_node_t + */ +typedef struct pm_self_node { + /** The embedded base node. */ + pm_node_t base; +} pm_self_node_t; + +/** + * SingletonClassNode + * + * Type: PM_SINGLETON_CLASS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_singleton_class_node { + /** The embedded base node. */ + pm_node_t base; + + /** SingletonClassNode#locals */ + pm_constant_id_list_t locals; + + /** SingletonClassNode#class_keyword_loc */ + pm_location_t class_keyword_loc; + + /** SingletonClassNode#operator_loc */ + pm_location_t operator_loc; + + /** SingletonClassNode#expression */ + struct pm_node *expression; + + /** SingletonClassNode#body */ + struct pm_node *body; + + /** SingletonClassNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_singleton_class_node_t; + +/** + * SourceEncodingNode + * + * Type: PM_SOURCE_ENCODING_NODE + * + * @extends pm_node_t + */ +typedef struct pm_source_encoding_node { + /** The embedded base node. */ + pm_node_t base; +} pm_source_encoding_node_t; + +/** + * SourceFileNode + * + * Type: PM_SOURCE_FILE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_source_file_node { + /** The embedded base node. */ + pm_node_t base; + + /** SourceFileNode#filepath */ + pm_string_t filepath; +} pm_source_file_node_t; + +/** + * SourceLineNode + * + * Type: PM_SOURCE_LINE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_source_line_node { + /** The embedded base node. */ + pm_node_t base; +} pm_source_line_node_t; + +/** + * SplatNode + * + * Type: PM_SPLAT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_splat_node { + /** The embedded base node. */ + pm_node_t base; + + /** SplatNode#operator_loc */ + pm_location_t operator_loc; + + /** SplatNode#expression */ + struct pm_node *expression; +} pm_splat_node_t; + +/** + * StatementsNode + * + * Type: PM_STATEMENTS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_statements_node { + /** The embedded base node. */ + pm_node_t base; + + /** StatementsNode#body */ + struct pm_node_list body; +} pm_statements_node_t; + +/** + * StringNode + * + * Type: PM_STRING_NODE + * Flags: + * PM_STRING_FLAGS_FROZEN + * + * @extends pm_node_t + */ +typedef struct pm_string_node { + /** The embedded base node. */ + pm_node_t base; + + /** StringNode#opening_loc */ + pm_location_t opening_loc; + + /** StringNode#content_loc */ + pm_location_t content_loc; + + /** StringNode#closing_loc */ + pm_location_t closing_loc; + + /** StringNode#unescaped */ + pm_string_t unescaped; +} pm_string_node_t; + +/** + * SuperNode + * + * Type: PM_SUPER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_super_node { + /** The embedded base node. */ + pm_node_t base; + + /** SuperNode#keyword_loc */ + pm_location_t keyword_loc; + + /** SuperNode#lparen_loc */ + pm_location_t lparen_loc; + + /** SuperNode#arguments */ + struct pm_arguments_node *arguments; + + /** SuperNode#rparen_loc */ + pm_location_t rparen_loc; + + /** SuperNode#block */ + struct pm_node *block; +} pm_super_node_t; + +/** + * SymbolNode + * + * Type: PM_SYMBOL_NODE + * + * @extends pm_node_t + */ +typedef struct pm_symbol_node { + /** The embedded base node. */ + pm_node_t base; + + /** SymbolNode#opening_loc */ + pm_location_t opening_loc; + + /** SymbolNode#value_loc */ + pm_location_t value_loc; + + /** SymbolNode#closing_loc */ + pm_location_t closing_loc; + + /** SymbolNode#unescaped */ + pm_string_t unescaped; +} pm_symbol_node_t; + +/** + * TrueNode + * + * Type: PM_TRUE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_true_node { + /** The embedded base node. */ + pm_node_t base; +} pm_true_node_t; + +/** + * UndefNode + * + * Type: PM_UNDEF_NODE + * + * @extends pm_node_t + */ +typedef struct pm_undef_node { + /** The embedded base node. */ + pm_node_t base; + + /** UndefNode#names */ + struct pm_node_list names; + + /** UndefNode#keyword_loc */ + pm_location_t keyword_loc; +} pm_undef_node_t; + +/** + * UnlessNode + * + * Type: PM_UNLESS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_unless_node { + /** The embedded base node. */ + pm_node_t base; + + /** UnlessNode#keyword_loc */ + pm_location_t keyword_loc; + + /** UnlessNode#predicate */ + struct pm_node *predicate; + + /** UnlessNode#then_keyword_loc */ + pm_location_t then_keyword_loc; + + /** UnlessNode#statements */ + struct pm_statements_node *statements; + + /** UnlessNode#consequent */ + struct pm_else_node *consequent; + + /** UnlessNode#end_keyword_loc */ + pm_location_t end_keyword_loc; +} pm_unless_node_t; + +/** + * UntilNode + * + * Type: PM_UNTIL_NODE + * Flags: + * PM_LOOP_FLAGS_BEGIN_MODIFIER + * + * @extends pm_node_t + */ +typedef struct pm_until_node { + /** The embedded base node. */ + pm_node_t base; + + /** UntilNode#keyword_loc */ + pm_location_t keyword_loc; + + /** UntilNode#closing_loc */ + pm_location_t closing_loc; + + /** UntilNode#predicate */ + struct pm_node *predicate; + + /** UntilNode#statements */ + struct pm_statements_node *statements; +} pm_until_node_t; + +/** + * WhenNode + * + * Type: PM_WHEN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_when_node { + /** The embedded base node. */ + pm_node_t base; + + /** WhenNode#keyword_loc */ + pm_location_t keyword_loc; + + /** WhenNode#conditions */ + struct pm_node_list conditions; + + /** WhenNode#statements */ + struct pm_statements_node *statements; +} pm_when_node_t; + +/** + * WhileNode + * + * Type: PM_WHILE_NODE + * Flags: + * PM_LOOP_FLAGS_BEGIN_MODIFIER + * + * @extends pm_node_t + */ +typedef struct pm_while_node { + /** The embedded base node. */ + pm_node_t base; + + /** WhileNode#keyword_loc */ + pm_location_t keyword_loc; + + /** WhileNode#closing_loc */ + pm_location_t closing_loc; + + /** WhileNode#predicate */ + struct pm_node *predicate; + + /** WhileNode#statements */ + struct pm_statements_node *statements; +} pm_while_node_t; + +/** + * XStringNode + * + * Type: PM_X_STRING_NODE + * + * @extends pm_node_t + */ +typedef struct pm_x_string_node { + /** The embedded base node. */ + pm_node_t base; + + /** XStringNode#opening_loc */ + pm_location_t opening_loc; + + /** XStringNode#content_loc */ + pm_location_t content_loc; + + /** XStringNode#closing_loc */ + pm_location_t closing_loc; + + /** XStringNode#unescaped */ + pm_string_t unescaped; +} pm_x_string_node_t; + +/** + * YieldNode + * + * Type: PM_YIELD_NODE + * + * @extends pm_node_t + */ +typedef struct pm_yield_node { + /** The embedded base node. */ + pm_node_t base; + + /** YieldNode#keyword_loc */ + pm_location_t keyword_loc; + + /** YieldNode#lparen_loc */ + pm_location_t lparen_loc; + + /** YieldNode#arguments */ + struct pm_arguments_node *arguments; + + /** YieldNode#rparen_loc */ + pm_location_t rparen_loc; +} pm_yield_node_t; + +/** + * Flags for arguments nodes. + */ +typedef enum pm_arguments_node_flags { + /** if arguments contain keyword splat */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT = 1, +} pm_arguments_node_flags_t; + +/** + * Flags for array nodes. + */ +typedef enum pm_array_node_flags { + /** if array contains splat nodes */ + PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT = 1, +} pm_array_node_flags_t; + +/** + * Flags for call nodes. + */ +typedef enum pm_call_node_flags { + /** &. operator */ + PM_CALL_NODE_FLAGS_SAFE_NAVIGATION = 1, + + /** a call that could have been a local variable */ + PM_CALL_NODE_FLAGS_VARIABLE_CALL = 2, +} pm_call_node_flags_t; + +/** + * Flags for integer nodes that correspond to the base of the integer. + */ +typedef enum pm_integer_base_flags { + /** 0b prefix */ + PM_INTEGER_BASE_FLAGS_BINARY = 1, + + /** 0o or 0 prefix */ + PM_INTEGER_BASE_FLAGS_OCTAL = 2, + + /** 0d or no prefix */ + PM_INTEGER_BASE_FLAGS_DECIMAL = 4, + + /** 0x prefix */ + PM_INTEGER_BASE_FLAGS_HEXADECIMAL = 8, +} pm_integer_base_flags_t; + +/** + * Flags for while and until loop nodes. + */ +typedef enum pm_loop_flags { + /** a loop after a begin statement, so the body is executed first before the condition */ + PM_LOOP_FLAGS_BEGIN_MODIFIER = 1, +} pm_loop_flags_t; + +/** + * Flags for range and flip-flop nodes. + */ +typedef enum pm_range_flags { + /** ... operator */ + PM_RANGE_FLAGS_EXCLUDE_END = 1, +} pm_range_flags_t; + +/** + * Flags for regular expression and match last line nodes. + */ +typedef enum pm_regular_expression_flags { + /** i - ignores the case of characters when matching */ + PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE = 1, + + /** x - ignores whitespace and allows comments in regular expressions */ + PM_REGULAR_EXPRESSION_FLAGS_EXTENDED = 2, + + /** m - allows $ to match the end of lines within strings */ + PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE = 4, + + /** o - only interpolates values into the regular expression once */ + PM_REGULAR_EXPRESSION_FLAGS_ONCE = 8, + + /** e - forces the EUC-JP encoding */ + PM_REGULAR_EXPRESSION_FLAGS_EUC_JP = 16, + + /** n - forces the ASCII-8BIT encoding */ + PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT = 32, + + /** s - forces the Windows-31J encoding */ + PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J = 64, + + /** u - forces the UTF-8 encoding */ + PM_REGULAR_EXPRESSION_FLAGS_UTF_8 = 128, +} pm_regular_expression_flags_t; + +/** + * Flags for string nodes. + */ +typedef enum pm_string_flags { + /** frozen by virtue of a `frozen_string_literal` comment */ + PM_STRING_FLAGS_FROZEN = 1, +} pm_string_flags_t; + +/** + * When we're serializing to Java, we want to skip serializing the location + * fields as they won't be used by JRuby or TruffleRuby. This boolean allows us + * to specify that through the environment. It will never be true except for in + * those build systems. + */ +#define PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS false + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/defines.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/defines.h new file mode 100644 index 00000000..f89a0bed --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/defines.h @@ -0,0 +1,77 @@ +/** + * @file defines.h + * + * Macro definitions used throughout the prism library. + * + * This file should be included first by any *.h or *.c in prism for consistency + * and to ensure that the macros are defined before they are used. + */ +#ifndef PRISM_DEFINES_H +#define PRISM_DEFINES_H + +#include +#include +#include +#include +#include +#include + +/** + * By default, we compile with -fvisibility=hidden. When this is enabled, we + * need to mark certain functions as being publically-visible. This macro does + * that in a compiler-agnostic way. + */ +#ifndef PRISM_EXPORTED_FUNCTION +# ifdef PRISM_EXPORT_SYMBOLS +# ifdef _WIN32 +# define PRISM_EXPORTED_FUNCTION __declspec(dllexport) extern +# else +# define PRISM_EXPORTED_FUNCTION __attribute__((__visibility__("default"))) extern +# endif +# else +# define PRISM_EXPORTED_FUNCTION +# endif +#endif + +/** + * Certain compilers support specifying that a function accepts variadic + * parameters that look like printf format strings to provide a better developer + * experience when someone is using the function. This macro does that in a + * compiler-agnostic way. + */ +#if defined(__GNUC__) +# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((format(printf, string_index, argument_index))) +#elif defined(__clang__) +# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((__format__(__printf__, string_index, argument_index))) +#else +# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) +#endif + +/** + * GCC will warn if you specify a function or parameter that is unused at + * runtime. This macro allows you to mark a function or parameter as unused in a + * compiler-agnostic way. + */ +#if defined(__GNUC__) +# define PRISM_ATTRIBUTE_UNUSED __attribute__((unused)) +#else +# define PRISM_ATTRIBUTE_UNUSED +#endif + +/** + * Old Visual Studio versions do not support the inline keyword, so we need to + * define it to be __inline. + */ +#if defined(_MSC_VER) && !defined(inline) +# define inline __inline +#endif + +/** + * Old Visual Studio versions before 2015 do not implement sprintf, but instead + * implement _snprintf. We standard that here. + */ +#if !defined(snprintf) && defined(_MSC_VER) && (_MSC_VER < 1900) +# define snprintf _snprintf +#endif + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/diagnostic.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/diagnostic.h new file mode 100644 index 00000000..3614a38a --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/diagnostic.h @@ -0,0 +1,296 @@ +/** + * @file diagnostic.h + * + * A list of diagnostics generated during parsing. + */ +#ifndef PRISM_DIAGNOSTIC_H +#define PRISM_DIAGNOSTIC_H + +#include "prism/defines.h" +#include "prism/util/pm_list.h" + +#include +#include +#include + +/** + * This struct represents a diagnostic generated during parsing. + * + * @extends pm_list_node_t + */ +typedef struct { + /** The embedded base node. */ + pm_list_node_t node; + + /** A pointer to the start of the source that generated the diagnostic. */ + const uint8_t *start; + + /** A pointer to the end of the source that generated the diagnostic. */ + const uint8_t *end; + + /** The message associated with the diagnostic. */ + const char *message; + + /** + * Whether or not the memory related to the message of this diagnostic is + * owned by this diagnostic. If it is, it needs to be freed when the + * diagnostic is freed. + */ + bool owned; +} pm_diagnostic_t; + +/** + * The diagnostic IDs of all of the diagnostics, used to communicate the types + * of errors between the parser and the user. + */ +typedef enum { + PM_ERR_ALIAS_ARGUMENT, + PM_ERR_AMPAMPEQ_MULTI_ASSIGN, + PM_ERR_ARGUMENT_AFTER_BLOCK, + PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES, + PM_ERR_ARGUMENT_BARE_HASH, + PM_ERR_ARGUMENT_BLOCK_MULTI, + PM_ERR_ARGUMENT_FORMAL_CLASS, + PM_ERR_ARGUMENT_FORMAL_CONSTANT, + PM_ERR_ARGUMENT_FORMAL_GLOBAL, + PM_ERR_ARGUMENT_FORMAL_IVAR, + PM_ERR_ARGUMENT_FORWARDING_UNBOUND, + PM_ERR_ARGUMENT_NO_FORWARDING_AMP, + PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES, + PM_ERR_ARGUMENT_NO_FORWARDING_STAR, + PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT, + PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT, + PM_ERR_ARGUMENT_TERM_PAREN, + PM_ERR_ARGUMENT_UNEXPECTED_BLOCK, + PM_ERR_ARRAY_ELEMENT, + PM_ERR_ARRAY_EXPRESSION, + PM_ERR_ARRAY_EXPRESSION_AFTER_STAR, + PM_ERR_ARRAY_SEPARATOR, + PM_ERR_ARRAY_TERM, + PM_ERR_BEGIN_LONELY_ELSE, + PM_ERR_BEGIN_TERM, + PM_ERR_BEGIN_UPCASE_BRACE, + PM_ERR_BEGIN_UPCASE_TERM, + PM_ERR_BEGIN_UPCASE_TOPLEVEL, + PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE, + PM_ERR_BLOCK_PARAM_PIPE_TERM, + PM_ERR_BLOCK_TERM_BRACE, + PM_ERR_BLOCK_TERM_END, + PM_ERR_CANNOT_PARSE_EXPRESSION, + PM_ERR_CANNOT_PARSE_STRING_PART, + PM_ERR_CASE_EXPRESSION_AFTER_CASE, + PM_ERR_CASE_EXPRESSION_AFTER_WHEN, + PM_ERR_CASE_MATCH_MISSING_PREDICATE, + PM_ERR_CASE_MISSING_CONDITIONS, + PM_ERR_CASE_TERM, + PM_ERR_CLASS_IN_METHOD, + PM_ERR_CLASS_NAME, + PM_ERR_CLASS_SUPERCLASS, + PM_ERR_CLASS_TERM, + PM_ERR_CLASS_UNEXPECTED_END, + PM_ERR_CONDITIONAL_ELSIF_PREDICATE, + PM_ERR_CONDITIONAL_IF_PREDICATE, + PM_ERR_CONDITIONAL_PREDICATE_TERM, + PM_ERR_CONDITIONAL_TERM, + PM_ERR_CONDITIONAL_TERM_ELSE, + PM_ERR_CONDITIONAL_UNLESS_PREDICATE, + PM_ERR_CONDITIONAL_UNTIL_PREDICATE, + PM_ERR_CONDITIONAL_WHILE_PREDICATE, + PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT, + PM_ERR_DEF_ENDLESS, + PM_ERR_DEF_ENDLESS_SETTER, + PM_ERR_DEF_NAME, + PM_ERR_DEF_NAME_AFTER_RECEIVER, + PM_ERR_DEF_PARAMS_TERM, + PM_ERR_DEF_PARAMS_TERM_PAREN, + PM_ERR_DEF_RECEIVER, + PM_ERR_DEF_RECEIVER_TERM, + PM_ERR_DEF_TERM, + PM_ERR_DEFINED_EXPRESSION, + PM_ERR_EMBDOC_TERM, + PM_ERR_EMBEXPR_END, + PM_ERR_EMBVAR_INVALID, + PM_ERR_END_UPCASE_BRACE, + PM_ERR_END_UPCASE_TERM, + PM_ERR_ESCAPE_INVALID_CONTROL, + PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT, + PM_ERR_ESCAPE_INVALID_HEXADECIMAL, + PM_ERR_ESCAPE_INVALID_META, + PM_ERR_ESCAPE_INVALID_META_REPEAT, + PM_ERR_ESCAPE_INVALID_UNICODE, + PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS, + PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL, + PM_ERR_ESCAPE_INVALID_UNICODE_LONG, + PM_ERR_ESCAPE_INVALID_UNICODE_TERM, + PM_ERR_EXPECT_ARGUMENT, + PM_ERR_EXPECT_EOL_AFTER_STATEMENT, + PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, + PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, + PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, + PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, + PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS, + PM_ERR_EXPECT_EXPRESSION_AFTER_LPAREN, + PM_ERR_EXPECT_EXPRESSION_AFTER_QUESTION, + PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, + PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT, + PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, + PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, + PM_ERR_EXPECT_IDENT_REQ_PARAMETER, + PM_ERR_EXPECT_LPAREN_REQ_PARAMETER, + PM_ERR_EXPECT_RBRACKET, + PM_ERR_EXPECT_RPAREN, + PM_ERR_EXPECT_RPAREN_AFTER_MULTI, + PM_ERR_EXPECT_RPAREN_REQ_PARAMETER, + PM_ERR_EXPECT_STRING_CONTENT, + PM_ERR_EXPECT_WHEN_DELIMITER, + PM_ERR_EXPRESSION_BARE_HASH, + PM_ERR_FOR_COLLECTION, + PM_ERR_FOR_IN, + PM_ERR_FOR_INDEX, + PM_ERR_FOR_TERM, + PM_ERR_HASH_EXPRESSION_AFTER_LABEL, + PM_ERR_HASH_KEY, + PM_ERR_HASH_ROCKET, + PM_ERR_HASH_TERM, + PM_ERR_HASH_VALUE, + PM_ERR_HEREDOC_TERM, + PM_ERR_INCOMPLETE_QUESTION_MARK, + PM_ERR_INCOMPLETE_VARIABLE_CLASS, + PM_ERR_INCOMPLETE_VARIABLE_INSTANCE, + PM_ERR_INVALID_ENCODING_MAGIC_COMMENT, + PM_ERR_INVALID_FLOAT_EXPONENT, + PM_ERR_INVALID_NUMBER_BINARY, + PM_ERR_INVALID_NUMBER_DECIMAL, + PM_ERR_INVALID_NUMBER_HEXADECIMAL, + PM_ERR_INVALID_NUMBER_OCTAL, + PM_ERR_INVALID_NUMBER_UNDERSCORE, + PM_ERR_INVALID_PERCENT, + PM_ERR_INVALID_TOKEN, + PM_ERR_INVALID_VARIABLE_GLOBAL, + PM_ERR_LAMBDA_OPEN, + PM_ERR_LAMBDA_TERM_BRACE, + PM_ERR_LAMBDA_TERM_END, + PM_ERR_LIST_I_LOWER_ELEMENT, + PM_ERR_LIST_I_LOWER_TERM, + PM_ERR_LIST_I_UPPER_ELEMENT, + PM_ERR_LIST_I_UPPER_TERM, + PM_ERR_LIST_W_LOWER_ELEMENT, + PM_ERR_LIST_W_LOWER_TERM, + PM_ERR_LIST_W_UPPER_ELEMENT, + PM_ERR_LIST_W_UPPER_TERM, + PM_ERR_MALLOC_FAILED, + PM_ERR_MODULE_IN_METHOD, + PM_ERR_MODULE_NAME, + PM_ERR_MODULE_TERM, + PM_ERR_MULTI_ASSIGN_MULTI_SPLATS, + PM_ERR_NOT_EXPRESSION, + PM_ERR_NUMBER_LITERAL_UNDERSCORE, + PM_ERR_NUMBERED_PARAMETER_NOT_ALLOWED, + PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE, + PM_ERR_OPERATOR_MULTI_ASSIGN, + PM_ERR_OPERATOR_WRITE_ARGUMENTS, + PM_ERR_OPERATOR_WRITE_BLOCK, + PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI, + PM_ERR_PARAMETER_BLOCK_MULTI, + PM_ERR_PARAMETER_CIRCULAR, + PM_ERR_PARAMETER_METHOD_NAME, + PM_ERR_PARAMETER_NAME_REPEAT, + PM_ERR_PARAMETER_NO_DEFAULT, + PM_ERR_PARAMETER_NO_DEFAULT_KW, + PM_ERR_PARAMETER_NUMBERED_RESERVED, + PM_ERR_PARAMETER_ORDER, + PM_ERR_PARAMETER_SPLAT_MULTI, + PM_ERR_PARAMETER_STAR, + PM_ERR_PARAMETER_UNEXPECTED_FWD, + PM_ERR_PARAMETER_WILD_LOOSE_COMMA, + PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, + PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET, + PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, + PM_ERR_PATTERN_EXPRESSION_AFTER_IN, + PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, + PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, + PM_ERR_PATTERN_EXPRESSION_AFTER_PIN, + PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, + PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, + PM_ERR_PATTERN_HASH_KEY, + PM_ERR_PATTERN_HASH_KEY_LABEL, + PM_ERR_PATTERN_IDENT_AFTER_HROCKET, + PM_ERR_PATTERN_LABEL_AFTER_COMMA, + PM_ERR_PATTERN_REST, + PM_ERR_PATTERN_TERM_BRACE, + PM_ERR_PATTERN_TERM_BRACKET, + PM_ERR_PATTERN_TERM_PAREN, + PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN, + PM_ERR_REGEXP_TERM, + PM_ERR_RESCUE_EXPRESSION, + PM_ERR_RESCUE_MODIFIER_VALUE, + PM_ERR_RESCUE_TERM, + PM_ERR_RESCUE_VARIABLE, + PM_ERR_RETURN_INVALID, + PM_ERR_STATEMENT_ALIAS, + PM_ERR_STATEMENT_POSTEXE_END, + PM_ERR_STATEMENT_PREEXE_BEGIN, + PM_ERR_STATEMENT_UNDEF, + PM_ERR_STRING_CONCATENATION, + PM_ERR_STRING_INTERPOLATED_TERM, + PM_ERR_STRING_LITERAL_TERM, + PM_ERR_SYMBOL_INVALID, + PM_ERR_SYMBOL_TERM_DYNAMIC, + PM_ERR_SYMBOL_TERM_INTERPOLATED, + PM_ERR_TERNARY_COLON, + PM_ERR_TERNARY_EXPRESSION_FALSE, + PM_ERR_TERNARY_EXPRESSION_TRUE, + PM_ERR_UNARY_RECEIVER_BANG, + PM_ERR_UNARY_RECEIVER_MINUS, + PM_ERR_UNARY_RECEIVER_PLUS, + PM_ERR_UNARY_RECEIVER_TILDE, + PM_ERR_UNDEF_ARGUMENT, + PM_ERR_UNTIL_TERM, + PM_ERR_VOID_EXPRESSION, + PM_ERR_WHILE_TERM, + PM_ERR_WRITE_TARGET_READONLY, + PM_ERR_WRITE_TARGET_UNEXPECTED, + PM_ERR_XSTRING_TERM, + PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS, + PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS, + PM_WARN_AMBIGUOUS_PREFIX_STAR, + PM_WARN_AMBIGUOUS_SLASH, + PM_WARN_END_IN_METHOD, + + /* This must be the last member. */ + PM_DIAGNOSTIC_ID_LEN, +} pm_diagnostic_id_t; + +/** + * Append a diagnostic to the given list of diagnostics that is using shared + * memory for its message. + * + * @param list The list to append to. + * @param start The start of the diagnostic. + * @param end The end of the diagnostic. + * @param diag_id The diagnostic ID. + * @return Whether the diagnostic was successfully appended. + */ +bool pm_diagnostic_list_append(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id); + +/** + * Append a diagnostic to the given list of diagnostics that is using a format + * string for its message. + * + * @param list The list to append to. + * @param start The start of the diagnostic. + * @param end The end of the diagnostic. + * @param diag_id The diagnostic ID. + * @param ... The arguments to the format string for the message. + * @return Whether the diagnostic was successfully appended. + */ +bool pm_diagnostic_list_append_format(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id, ...); + +/** + * Deallocate the internal state of the given diagnostic list. + * + * @param list The list to deallocate. + */ +void pm_diagnostic_list_free(pm_list_t *list); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/enc/pm_encoding.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/enc/pm_encoding.h new file mode 100644 index 00000000..79702936 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/enc/pm_encoding.h @@ -0,0 +1,231 @@ +/** + * @file pm_encoding.h + * + * The encoding interface and implementations used by the parser. + */ +#ifndef PRISM_ENCODING_H +#define PRISM_ENCODING_H + +#include "prism/defines.h" + +#include +#include +#include +#include + +/** + * This struct defines the functions necessary to implement the encoding + * interface so we can determine how many bytes the subsequent character takes. + * Each callback should return the number of bytes, or 0 if the next bytes are + * invalid for the encoding and type. + */ +typedef struct { + /** + * Return the number of bytes that the next character takes if it is valid + * in the encoding. Does not read more than n bytes. It is assumed that n is + * at least 1. + */ + size_t (*char_width)(const uint8_t *b, ptrdiff_t n); + + /** + * Return the number of bytes that the next character takes if it is valid + * in the encoding and is alphabetical. Does not read more than n bytes. It + * is assumed that n is at least 1. + */ + size_t (*alpha_char)(const uint8_t *b, ptrdiff_t n); + + /** + * Return the number of bytes that the next character takes if it is valid + * in the encoding and is alphanumeric. Does not read more than n bytes. It + * is assumed that n is at least 1. + */ + size_t (*alnum_char)(const uint8_t *b, ptrdiff_t n); + + /** + * Return true if the next character is valid in the encoding and is an + * uppercase character. Does not read more than n bytes. It is assumed that + * n is at least 1. + */ + bool (*isupper_char)(const uint8_t *b, ptrdiff_t n); + + /** + * The name of the encoding. This should correspond to a value that can be + * passed to Encoding.find in Ruby. + */ + const char *name; + + /** + * Return true if the encoding is a multibyte encoding. + */ + bool multibyte; +} pm_encoding_t; + +/** + * All of the lookup tables use the first bit of each embedded byte to indicate + * whether the codepoint is alphabetical. + */ +#define PRISM_ENCODING_ALPHABETIC_BIT 1 << 0 + +/** + * All of the lookup tables use the second bit of each embedded byte to indicate + * whether the codepoint is alphanumeric. + */ +#define PRISM_ENCODING_ALPHANUMERIC_BIT 1 << 1 + +/** + * All of the lookup tables use the third bit of each embedded byte to indicate + * whether the codepoint is uppercase. + */ +#define PRISM_ENCODING_UPPERCASE_BIT 1 << 2 + +/** + * Return the size of the next character in the ASCII encoding if it is an + * alphabetical character. + * + * @param b The bytes to read. + * @param n The number of bytes that can be read. + * @returns The number of bytes that the next character takes if it is valid in + * the encoding, or 0 if it is not. + */ +size_t pm_encoding_ascii_alpha_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n); + +/** + * Return the size of the next character in the ASCII encoding if it is an + * alphanumeric character. + * + * @param b The bytes to read. + * @param n The number of bytes that can be read. + * @returns The number of bytes that the next character takes if it is valid in + * the encoding, or 0 if it is not. + */ +size_t pm_encoding_ascii_alnum_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n); + +/** + * Return true if the next character in the ASCII encoding if it is an uppercase + * character. + * + * @param b The bytes to read. + * @param n The number of bytes that can be read. + * @returns True if the next character is valid in the encoding and is an + * uppercase character, or false if it is not. + */ +bool pm_encoding_ascii_isupper_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n); + +/** + * Return the size of the next character in the UTF-8 encoding if it is an + * alphabetical character. + * + * @param b The bytes to read. + * @param n The number of bytes that can be read. + * @returns The number of bytes that the next character takes if it is valid in + * the encoding, or 0 if it is not. + */ +size_t pm_encoding_utf_8_alpha_char(const uint8_t *b, ptrdiff_t n); + +/** + * Return the size of the next character in the UTF-8 encoding if it is an + * alphanumeric character. + * + * @param b The bytes to read. + * @param n The number of bytes that can be read. + * @returns The number of bytes that the next character takes if it is valid in + * the encoding, or 0 if it is not. + */ +size_t pm_encoding_utf_8_alnum_char(const uint8_t *b, ptrdiff_t n); + +/** + * Return true if the next character in the UTF-8 encoding if it is an uppercase + * character. + * + * @param b The bytes to read. + * @param n The number of bytes that can be read. + * @returns True if the next character is valid in the encoding and is an + * uppercase character, or false if it is not. + */ +bool pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n); + +/** + * This lookup table is referenced in both the UTF-8 encoding file and the + * parser directly in order to speed up the default encoding processing. It is + * used to indicate whether a character is alphabetical, alphanumeric, or + * uppercase in unicode mappings. + */ +extern const uint8_t pm_encoding_unicode_table[256]; + +// Below are the encodings that are supported by the parser. They are defined in +// their own files in the src/enc directory. + +extern pm_encoding_t pm_encoding_ascii; +extern pm_encoding_t pm_encoding_ascii_8bit; +extern pm_encoding_t pm_encoding_big5; +extern pm_encoding_t pm_encoding_big5_hkscs; +extern pm_encoding_t pm_encoding_big5_uao; +extern pm_encoding_t pm_encoding_cp51932; +extern pm_encoding_t pm_encoding_cp850; +extern pm_encoding_t pm_encoding_cp852; +extern pm_encoding_t pm_encoding_cp855; +extern pm_encoding_t pm_encoding_cp949; +extern pm_encoding_t pm_encoding_cp950; +extern pm_encoding_t pm_encoding_euc_jp; +extern pm_encoding_t pm_encoding_gb1988; +extern pm_encoding_t pm_encoding_gbk; +extern pm_encoding_t pm_encoding_ibm437; +extern pm_encoding_t pm_encoding_ibm720; +extern pm_encoding_t pm_encoding_ibm737; +extern pm_encoding_t pm_encoding_ibm775; +extern pm_encoding_t pm_encoding_ibm852; +extern pm_encoding_t pm_encoding_ibm855; +extern pm_encoding_t pm_encoding_ibm857; +extern pm_encoding_t pm_encoding_ibm860; +extern pm_encoding_t pm_encoding_ibm861; +extern pm_encoding_t pm_encoding_ibm862; +extern pm_encoding_t pm_encoding_ibm863; +extern pm_encoding_t pm_encoding_ibm864; +extern pm_encoding_t pm_encoding_ibm865; +extern pm_encoding_t pm_encoding_ibm866; +extern pm_encoding_t pm_encoding_ibm869; +extern pm_encoding_t pm_encoding_iso_8859_1; +extern pm_encoding_t pm_encoding_iso_8859_2; +extern pm_encoding_t pm_encoding_iso_8859_3; +extern pm_encoding_t pm_encoding_iso_8859_4; +extern pm_encoding_t pm_encoding_iso_8859_5; +extern pm_encoding_t pm_encoding_iso_8859_6; +extern pm_encoding_t pm_encoding_iso_8859_7; +extern pm_encoding_t pm_encoding_iso_8859_8; +extern pm_encoding_t pm_encoding_iso_8859_9; +extern pm_encoding_t pm_encoding_iso_8859_10; +extern pm_encoding_t pm_encoding_iso_8859_11; +extern pm_encoding_t pm_encoding_iso_8859_13; +extern pm_encoding_t pm_encoding_iso_8859_14; +extern pm_encoding_t pm_encoding_iso_8859_15; +extern pm_encoding_t pm_encoding_iso_8859_16; +extern pm_encoding_t pm_encoding_koi8_r; +extern pm_encoding_t pm_encoding_koi8_u; +extern pm_encoding_t pm_encoding_mac_cent_euro; +extern pm_encoding_t pm_encoding_mac_croatian; +extern pm_encoding_t pm_encoding_mac_cyrillic; +extern pm_encoding_t pm_encoding_mac_greek; +extern pm_encoding_t pm_encoding_mac_iceland; +extern pm_encoding_t pm_encoding_mac_japanese; +extern pm_encoding_t pm_encoding_mac_roman; +extern pm_encoding_t pm_encoding_mac_romania; +extern pm_encoding_t pm_encoding_mac_thai; +extern pm_encoding_t pm_encoding_mac_turkish; +extern pm_encoding_t pm_encoding_mac_ukraine; +extern pm_encoding_t pm_encoding_shift_jis; +extern pm_encoding_t pm_encoding_tis_620; +extern pm_encoding_t pm_encoding_utf_8; +extern pm_encoding_t pm_encoding_utf8_mac; +extern pm_encoding_t pm_encoding_windows_1250; +extern pm_encoding_t pm_encoding_windows_1251; +extern pm_encoding_t pm_encoding_windows_1252; +extern pm_encoding_t pm_encoding_windows_1253; +extern pm_encoding_t pm_encoding_windows_1254; +extern pm_encoding_t pm_encoding_windows_1255; +extern pm_encoding_t pm_encoding_windows_1256; +extern pm_encoding_t pm_encoding_windows_1257; +extern pm_encoding_t pm_encoding_windows_1258; +extern pm_encoding_t pm_encoding_windows_31j; +extern pm_encoding_t pm_encoding_windows_874; + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/node.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/node.h new file mode 100644 index 00000000..3e15d185 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/node.h @@ -0,0 +1,57 @@ +/** + * @file node.h + * + * Functions related to nodes in the AST. + */ +#ifndef PRISM_NODE_H +#define PRISM_NODE_H + +#include "prism/defines.h" +#include "prism/parser.h" + +/** + * Append a new node onto the end of the node list. + * + * @param list The list to append to. + * @param node The node to append. + */ +void pm_node_list_append(pm_node_list_t *list, pm_node_t *node); + +/** + * Deallocate a node and all of its children. + * + * @param parser The parser that owns the node. + * @param node The node to deallocate. + */ +PRISM_EXPORTED_FUNCTION void pm_node_destroy(pm_parser_t *parser, struct pm_node *node); + +/** + * This struct stores the information gathered by the pm_node_memsize function. + * It contains both the memory footprint and additionally metadata about the + * shape of the tree. + */ +typedef struct { + /** The total memory footprint of the node and all of its children. */ + size_t memsize; + + /** The number of children the node has. */ + size_t node_count; +} pm_memsize_t; + +/** + * Calculates the memory footprint of a given node. + * + * @param node The node to calculate the memory footprint of. + * @param memsize The memory footprint of the node and all of its children. + */ +PRISM_EXPORTED_FUNCTION void pm_node_memsize(pm_node_t *node, pm_memsize_t *memsize); + +/** + * Returns a string representation of the given node type. + * + * @param node_type The node type to convert to a string. + * @return A string representation of the given node type. + */ +PRISM_EXPORTED_FUNCTION const char * pm_node_type_to_str(pm_node_type_t node_type); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/options.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/options.h new file mode 100644 index 00000000..8608838d --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/options.h @@ -0,0 +1,204 @@ +/** + * @file options.h + * + * The options that can be passed to parsing. + */ +#ifndef PRISM_OPTIONS_H +#define PRISM_OPTIONS_H + +#include "prism/defines.h" +#include "prism/util/pm_string.h" + +#include +#include +#include + +/** + * A scope of locals surrounding the code that is being parsed. + */ +typedef struct pm_options_scope { + /** The number of locals in the scope. */ + size_t locals_count; + + /** The names of the locals in the scope. */ + pm_string_t *locals; +} pm_options_scope_t; + +/** + * The options that can be passed to the parser. + */ +typedef struct { + /** The name of the file that is currently being parsed. */ + pm_string_t filepath; + + /** + * The line within the file that the parse starts on. This value is + * 0-indexed. + */ + int32_t line; + + /** + * The name of the encoding that the source file is in. Note that this must + * correspond to a name that can be found with Encoding.find in Ruby. + */ + pm_string_t encoding; + + /** + * The number of scopes surrounding the code that is being parsed. + */ + size_t scopes_count; + + /** + * The scopes surrounding the code that is being parsed. For most parses + * this will be NULL, but for evals it will be the locals that are in scope + * surrounding the eval. + */ + pm_options_scope_t *scopes; + + /** Whether or not the frozen string literal option has been set. */ + bool frozen_string_literal; + + /** + * Whether or not we should suppress warnings. This is purposefully negated + * so that the default is to not suppress warnings, which allows us to still + * create an options struct with zeroed memory. + */ + bool suppress_warnings; +} pm_options_t; + +/** + * Set the filepath option on the given options struct. + * + * @param options The options struct to set the filepath on. + * @param filepath The filepath to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_filepath_set(pm_options_t *options, const char *filepath); + +/** + * Set the line option on the given options struct. + * + * @param options The options struct to set the line on. + * @param line The line to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_line_set(pm_options_t *options, int32_t line); + +/** + * Set the encoding option on the given options struct. + * + * @param options The options struct to set the encoding on. + * @param encoding The encoding to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_encoding_set(pm_options_t *options, const char *encoding); + +/** + * Set the frozen string literal option on the given options struct. + * + * @param options The options struct to set the frozen string literal value on. + * @param frozen_string_literal The frozen string literal value to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal); + +/** + * Set the suppress warnings option on the given options struct. + * + * @param options The options struct to set the suppress warnings value on. + * @param suppress_warnings The suppress warnings value to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings); + +/** + * Allocate and zero out the scopes array on the given options struct. + * + * @param options The options struct to initialize the scopes array on. + * @param scopes_count The number of scopes to allocate. + */ +PRISM_EXPORTED_FUNCTION void pm_options_scopes_init(pm_options_t *options, size_t scopes_count); + +/** + * Return a pointer to the scope at the given index within the given options. + * + * @param options The options struct to get the scope from. + * @param index The index of the scope to get. + * @return A pointer to the scope at the given index. + */ +PRISM_EXPORTED_FUNCTION const pm_options_scope_t * pm_options_scope_get(const pm_options_t *options, size_t index); + +/** + * Create a new options scope struct. This will hold a set of locals that are in + * scope surrounding the code that is being parsed. + * + * @param scope The scope struct to initialize. + * @param locals_count The number of locals to allocate. + */ +PRISM_EXPORTED_FUNCTION void pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count); + +/** + * Return a pointer to the local at the given index within the given scope. + * + * @param scope The scope struct to get the local from. + * @param index The index of the local to get. + * @return A pointer to the local at the given index. + */ +PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index); + +/** + * Free the internal memory associated with the options. + * + * @param options The options struct whose internal memory should be freed. + */ +PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options); + +/** + * Deserialize an options struct from the given binary string. This is used to + * pass options to the parser from an FFI call so that consumers of the library + * from an FFI perspective don't have to worry about the structure of our + * options structs. Since the source of these calls will be from Ruby + * implementation internals we assume it is from a trusted source. + * + * `data` is assumed to be a valid pointer pointing to well-formed data. The + * layout of this data should be the same every time, and is described below: + * + * | # bytes | field | + * | ------- | -------------------------- | + * | `4` | the length of the filepath | + * | ... | the filepath bytes | + * | `4` | the line number | + * | `4` | the length the encoding | + * | ... | the encoding bytes | + * | `1` | frozen string literal | + * | `1` | suppress warnings | + * | `4` | the number of scopes | + * | ... | the scopes | + * + * Each scope is layed out as follows: + * + * | # bytes | field | + * | ------- | -------------------------- | + * | `4` | the number of locals | + * | ... | the locals | + * + * Each local is layed out as follows: + * + * | # bytes | field | + * | ------- | -------------------------- | + * | `4` | the length of the local | + * | ... | the local bytes | + * + * Some additional things to note about this layout: + * + * * The filepath can have a length of 0, in which case we'll consider it an + * empty string. + * * The line number should be 0-indexed. + * * The encoding can have a length of 0, in which case we'll use the default + * encoding (UTF-8). If it's not 0, it should correspond to a name of an + * encoding that can be passed to `Encoding.find` in Ruby. + * * The frozen string literal and suppress warnings fields are booleans, so + * their values should be either 0 or 1. + * * The number of scopes can be 0. + * + * @param options The options struct to deserialize into. + * @param data The binary string to deserialize from. + */ +void pm_options_read(pm_options_t *options, const char *data); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/pack.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/pack.h new file mode 100644 index 00000000..e4948483 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/pack.h @@ -0,0 +1,152 @@ +/** + * @file pack.h + * + * A pack template string parser. + */ +#ifndef PRISM_PACK_H +#define PRISM_PACK_H + +#include "prism/defines.h" + +#include +#include + +/** The version of the pack template language that we are parsing. */ +typedef enum pm_pack_version { + PM_PACK_VERSION_3_2_0 +} pm_pack_version; + +/** The type of pack template we are parsing. */ +typedef enum pm_pack_variant { + PM_PACK_VARIANT_PACK, + PM_PACK_VARIANT_UNPACK +} pm_pack_variant; + +/** A directive within the pack template. */ +typedef enum pm_pack_type { + PM_PACK_SPACE, + PM_PACK_COMMENT, + PM_PACK_INTEGER, + PM_PACK_UTF8, + PM_PACK_BER, + PM_PACK_FLOAT, + PM_PACK_STRING_SPACE_PADDED, + PM_PACK_STRING_NULL_PADDED, + PM_PACK_STRING_NULL_TERMINATED, + PM_PACK_STRING_MSB, + PM_PACK_STRING_LSB, + PM_PACK_STRING_HEX_HIGH, + PM_PACK_STRING_HEX_LOW, + PM_PACK_STRING_UU, + PM_PACK_STRING_MIME, + PM_PACK_STRING_BASE64, + PM_PACK_STRING_FIXED, + PM_PACK_STRING_POINTER, + PM_PACK_MOVE, + PM_PACK_BACK, + PM_PACK_NULL, + PM_PACK_END +} pm_pack_type; + +/** The signness of a pack directive. */ +typedef enum pm_pack_signed { + PM_PACK_UNSIGNED, + PM_PACK_SIGNED, + PM_PACK_SIGNED_NA +} pm_pack_signed; + +/** The endianness of a pack directive. */ +typedef enum pm_pack_endian { + PM_PACK_AGNOSTIC_ENDIAN, + PM_PACK_LITTLE_ENDIAN, // aka 'VAX', or 'V' + PM_PACK_BIG_ENDIAN, // aka 'network', or 'N' + PM_PACK_NATIVE_ENDIAN, + PM_PACK_ENDIAN_NA +} pm_pack_endian; + +/** The size of an integer pack directive. */ +typedef enum pm_pack_size { + PM_PACK_SIZE_SHORT, + PM_PACK_SIZE_INT, + PM_PACK_SIZE_LONG, + PM_PACK_SIZE_LONG_LONG, + PM_PACK_SIZE_8, + PM_PACK_SIZE_16, + PM_PACK_SIZE_32, + PM_PACK_SIZE_64, + PM_PACK_SIZE_P, + PM_PACK_SIZE_NA +} pm_pack_size; + +/** The type of length of a pack directive. */ +typedef enum pm_pack_length_type { + PM_PACK_LENGTH_FIXED, + PM_PACK_LENGTH_MAX, + PM_PACK_LENGTH_RELATIVE, // special case for unpack @* + PM_PACK_LENGTH_NA +} pm_pack_length_type; + +/** The type of encoding for a pack template string. */ +typedef enum pm_pack_encoding { + PM_PACK_ENCODING_START, + PM_PACK_ENCODING_ASCII_8BIT, + PM_PACK_ENCODING_US_ASCII, + PM_PACK_ENCODING_UTF_8 +} pm_pack_encoding; + +/** The result of parsing a pack template. */ +typedef enum pm_pack_result { + PM_PACK_OK, + PM_PACK_ERROR_UNSUPPORTED_DIRECTIVE, + PM_PACK_ERROR_UNKNOWN_DIRECTIVE, + PM_PACK_ERROR_LENGTH_TOO_BIG, + PM_PACK_ERROR_BANG_NOT_ALLOWED, + PM_PACK_ERROR_DOUBLE_ENDIAN +} pm_pack_result; + +/** + * Parse a single directive from a pack or unpack format string. + * + * @param variant (in) pack or unpack + * @param format (in, out) the start of the next directive to parse on calling, + * and advanced beyond the parsed directive on return, or as much of it as + * was consumed until an error was encountered + * @param format_end (in) the end of the format string + * @param type (out) the type of the directive + * @param signed_type (out) whether the value is signed + * @param endian (out) the endianness of the value + * @param size (out) the size of the value + * @param length_type (out) what kind of length is specified + * @param length (out) the length of the directive + * @param encoding (in, out) takes the current encoding of the string which + * would result from parsing the whole format string, and returns a possibly + * changed directive - the encoding should be `PM_PACK_ENCODING_START` when + * pm_pack_parse is called for the first directive in a format string + * + * @return `PM_PACK_OK` on success or `PM_PACK_ERROR_*` on error + * @note Consult Ruby documentation for the meaning of directives. + */ +PRISM_EXPORTED_FUNCTION pm_pack_result +pm_pack_parse( + pm_pack_variant variant, + const char **format, + const char *format_end, + pm_pack_type *type, + pm_pack_signed *signed_type, + pm_pack_endian *endian, + pm_pack_size *size, + pm_pack_length_type *length_type, + uint64_t *length, + pm_pack_encoding *encoding +); + +/** + * Prism abstracts sizes away from the native system - this converts an abstract + * size to a native size. + * + * @param size The abstract size to convert. + * @return The native size. + */ +PRISM_EXPORTED_FUNCTION size_t pm_size_to_native(pm_pack_size size); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/parser.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/parser.h new file mode 100644 index 00000000..dfc15e19 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/parser.h @@ -0,0 +1,711 @@ +/** + * @file parser.h + * + * The parser used to parse Ruby source. + */ +#ifndef PRISM_PARSER_H +#define PRISM_PARSER_H + +#include "prism/ast.h" +#include "prism/defines.h" +#include "prism/enc/pm_encoding.h" +#include "prism/util/pm_constant_pool.h" +#include "prism/util/pm_list.h" +#include "prism/util/pm_newline_list.h" +#include "prism/util/pm_state_stack.h" +#include "prism/util/pm_string.h" + +#include + +/** + * This enum provides various bits that represent different kinds of states that + * the lexer can track. This is used to determine which kind of token to return + * based on the context of the parser. + */ +typedef enum { + PM_LEX_STATE_BIT_BEG, + PM_LEX_STATE_BIT_END, + PM_LEX_STATE_BIT_ENDARG, + PM_LEX_STATE_BIT_ENDFN, + PM_LEX_STATE_BIT_ARG, + PM_LEX_STATE_BIT_CMDARG, + PM_LEX_STATE_BIT_MID, + PM_LEX_STATE_BIT_FNAME, + PM_LEX_STATE_BIT_DOT, + PM_LEX_STATE_BIT_CLASS, + PM_LEX_STATE_BIT_LABEL, + PM_LEX_STATE_BIT_LABELED, + PM_LEX_STATE_BIT_FITEM +} pm_lex_state_bit_t; + +/** + * This enum combines the various bits from the above enum into individual + * values that represent the various states of the lexer. + */ +typedef enum { + PM_LEX_STATE_NONE = 0, + PM_LEX_STATE_BEG = (1 << PM_LEX_STATE_BIT_BEG), + PM_LEX_STATE_END = (1 << PM_LEX_STATE_BIT_END), + PM_LEX_STATE_ENDARG = (1 << PM_LEX_STATE_BIT_ENDARG), + PM_LEX_STATE_ENDFN = (1 << PM_LEX_STATE_BIT_ENDFN), + PM_LEX_STATE_ARG = (1 << PM_LEX_STATE_BIT_ARG), + PM_LEX_STATE_CMDARG = (1 << PM_LEX_STATE_BIT_CMDARG), + PM_LEX_STATE_MID = (1 << PM_LEX_STATE_BIT_MID), + PM_LEX_STATE_FNAME = (1 << PM_LEX_STATE_BIT_FNAME), + PM_LEX_STATE_DOT = (1 << PM_LEX_STATE_BIT_DOT), + PM_LEX_STATE_CLASS = (1 << PM_LEX_STATE_BIT_CLASS), + PM_LEX_STATE_LABEL = (1 << PM_LEX_STATE_BIT_LABEL), + PM_LEX_STATE_LABELED = (1 << PM_LEX_STATE_BIT_LABELED), + PM_LEX_STATE_FITEM = (1 << PM_LEX_STATE_BIT_FITEM), + PM_LEX_STATE_BEG_ANY = PM_LEX_STATE_BEG | PM_LEX_STATE_MID | PM_LEX_STATE_CLASS, + PM_LEX_STATE_ARG_ANY = PM_LEX_STATE_ARG | PM_LEX_STATE_CMDARG, + PM_LEX_STATE_END_ANY = PM_LEX_STATE_END | PM_LEX_STATE_ENDARG | PM_LEX_STATE_ENDFN +} pm_lex_state_t; + +/** + * The type of quote that a heredoc uses. + */ +typedef enum { + PM_HEREDOC_QUOTE_NONE, + PM_HEREDOC_QUOTE_SINGLE = '\'', + PM_HEREDOC_QUOTE_DOUBLE = '"', + PM_HEREDOC_QUOTE_BACKTICK = '`', +} pm_heredoc_quote_t; + +/** + * The type of indentation that a heredoc uses. + */ +typedef enum { + PM_HEREDOC_INDENT_NONE, + PM_HEREDOC_INDENT_DASH, + PM_HEREDOC_INDENT_TILDE, +} pm_heredoc_indent_t; + +/** + * When lexing Ruby source, the lexer has a small amount of state to tell which + * kind of token it is currently lexing. For example, when we find the start of + * a string, the first token that we return is a TOKEN_STRING_BEGIN token. After + * that the lexer is now in the PM_LEX_STRING mode, and will return tokens that + * are found as part of a string. + */ +typedef struct pm_lex_mode { + /** The type of this lex mode. */ + enum { + /** This state is used when any given token is being lexed. */ + PM_LEX_DEFAULT, + + /** + * This state is used when we're lexing as normal but inside an embedded + * expression of a string. + */ + PM_LEX_EMBEXPR, + + /** + * This state is used when we're lexing a variable that is embedded + * directly inside of a string with the # shorthand. + */ + PM_LEX_EMBVAR, + + /** This state is used when you are inside the content of a heredoc. */ + PM_LEX_HEREDOC, + + /** + * This state is used when we are lexing a list of tokens, as in a %w + * word list literal or a %i symbol list literal. + */ + PM_LEX_LIST, + + /** + * This state is used when a regular expression has been begun and we + * are looking for the terminator. + */ + PM_LEX_REGEXP, + + /** + * This state is used when we are lexing a string or a string-like + * token, as in string content with either quote or an xstring. + */ + PM_LEX_STRING + } mode; + + /** The data associated with this type of lex mode. */ + union { + struct { + /** This keeps track of the nesting level of the list. */ + size_t nesting; + + /** Whether or not interpolation is allowed in this list. */ + bool interpolation; + + /** + * When lexing a list, it takes into account balancing the + * terminator if the terminator is one of (), [], {}, or <>. + */ + uint8_t incrementor; + + /** This is the terminator of the list literal. */ + uint8_t terminator; + + /** + * This is the character set that should be used to delimit the + * tokens within the list. + */ + uint8_t breakpoints[11]; + } list; + + struct { + /** + * This keeps track of the nesting level of the regular expression. + */ + size_t nesting; + + /** + * When lexing a regular expression, it takes into account balancing + * the terminator if the terminator is one of (), [], {}, or <>. + */ + uint8_t incrementor; + + /** This is the terminator of the regular expression. */ + uint8_t terminator; + + /** + * This is the character set that should be used to delimit the + * tokens within the regular expression. + */ + uint8_t breakpoints[6]; + } regexp; + + struct { + /** This keeps track of the nesting level of the string. */ + size_t nesting; + + /** Whether or not interpolation is allowed in this string. */ + bool interpolation; + + /** + * Whether or not at the end of the string we should allow a :, + * which would indicate this was a dynamic symbol instead of a + * string. + */ + bool label_allowed; + + /** + * When lexing a string, it takes into account balancing the + * terminator if the terminator is one of (), [], {}, or <>. + */ + uint8_t incrementor; + + /** + * This is the terminator of the string. It is typically either a + * single or double quote. + */ + uint8_t terminator; + + /** + * This is the character set that should be used to delimit the + * tokens within the string. + */ + uint8_t breakpoints[6]; + } string; + + struct { + /** A pointer to the start of the heredoc identifier. */ + const uint8_t *ident_start; + + /** The length of the heredoc identifier. */ + size_t ident_length; + + /** The type of quote that the heredoc uses. */ + pm_heredoc_quote_t quote; + + /** The type of indentation that the heredoc uses. */ + pm_heredoc_indent_t indent; + + /** + * This is the pointer to the character where lexing should resume + * once the heredoc has been completely processed. + */ + const uint8_t *next_start; + + /** + * This is used to track the amount of common whitespace on each + * line so that we know how much to dedent each line in the case of + * a tilde heredoc. + */ + size_t common_whitespace; + } heredoc; + } as; + + /** The previous lex state so that it knows how to pop. */ + struct pm_lex_mode *prev; +} pm_lex_mode_t; + +/** + * We pre-allocate a certain number of lex states in order to avoid having to + * call malloc too many times while parsing. You really shouldn't need more than + * this because you only really nest deeply when doing string interpolation. + */ +#define PM_LEX_STACK_SIZE 4 + +/** + * The parser used to parse Ruby source. + */ +typedef struct pm_parser pm_parser_t; + +/** + * While parsing, we keep track of a stack of contexts. This is helpful for + * error recovery so that we can pop back to a previous context when we hit a + * token that is understood by a parent context but not by the current context. + */ +typedef enum { + /** a begin statement */ + PM_CONTEXT_BEGIN, + + /** expressions in block arguments using braces */ + PM_CONTEXT_BLOCK_BRACES, + + /** expressions in block arguments using do..end */ + PM_CONTEXT_BLOCK_KEYWORDS, + + /** a case when statements */ + PM_CONTEXT_CASE_WHEN, + + /** a case in statements */ + PM_CONTEXT_CASE_IN, + + /** a class declaration */ + PM_CONTEXT_CLASS, + + /** a method definition */ + PM_CONTEXT_DEF, + + /** a method definition's parameters */ + PM_CONTEXT_DEF_PARAMS, + + /** a method definition's default parameter */ + PM_CONTEXT_DEFAULT_PARAMS, + + /** an else clause */ + PM_CONTEXT_ELSE, + + /** an elsif clause */ + PM_CONTEXT_ELSIF, + + /** an interpolated expression */ + PM_CONTEXT_EMBEXPR, + + /** an ensure statement */ + PM_CONTEXT_ENSURE, + + /** an ensure statement within a method definition */ + PM_CONTEXT_ENSURE_DEF, + + /** a for loop */ + PM_CONTEXT_FOR, + + /** a for loop's index */ + PM_CONTEXT_FOR_INDEX, + + /** an if statement */ + PM_CONTEXT_IF, + + /** a lambda expression with braces */ + PM_CONTEXT_LAMBDA_BRACES, + + /** a lambda expression with do..end */ + PM_CONTEXT_LAMBDA_DO_END, + + /** the top level context */ + PM_CONTEXT_MAIN, + + /** a module declaration */ + PM_CONTEXT_MODULE, + + /** a parenthesized expression */ + PM_CONTEXT_PARENS, + + /** an END block */ + PM_CONTEXT_POSTEXE, + + /** a predicate inside an if/elsif/unless statement */ + PM_CONTEXT_PREDICATE, + + /** a BEGIN block */ + PM_CONTEXT_PREEXE, + + /** a rescue else statement */ + PM_CONTEXT_RESCUE_ELSE, + + /** a rescue else statement within a method definition */ + PM_CONTEXT_RESCUE_ELSE_DEF, + + /** a rescue statement */ + PM_CONTEXT_RESCUE, + + /** a rescue statement within a method definition */ + PM_CONTEXT_RESCUE_DEF, + + /** a singleton class definition */ + PM_CONTEXT_SCLASS, + + /** an unless statement */ + PM_CONTEXT_UNLESS, + + /** an until statement */ + PM_CONTEXT_UNTIL, + + /** a while statement */ + PM_CONTEXT_WHILE, +} pm_context_t; + +/** This is a node in a linked list of contexts. */ +typedef struct pm_context_node { + /** The context that this node represents. */ + pm_context_t context; + + /** A pointer to the previous context in the linked list. */ + struct pm_context_node *prev; +} pm_context_node_t; + +/** This is the type of a comment that we've found while parsing. */ +typedef enum { + PM_COMMENT_INLINE, + PM_COMMENT_EMBDOC +} pm_comment_type_t; + +/** + * This is a node in the linked list of comments that we've found while parsing. + * + * @extends pm_list_node_t + */ +typedef struct pm_comment { + /** The embedded base node. */ + pm_list_node_t node; + + /** A pointer to the start of the comment in the source. */ + const uint8_t *start; + + /** A pointer to the end of the comment in the source. */ + const uint8_t *end; + + /** The type of comment that we've found. */ + pm_comment_type_t type; +} pm_comment_t; + +/** + * This is a node in the linked list of magic comments that we've found while + * parsing. + * + * @extends pm_list_node_t + */ +typedef struct { + /** The embedded base node. */ + pm_list_node_t node; + + /** A pointer to the start of the key in the source. */ + const uint8_t *key_start; + + /** A pointer to the start of the value in the source. */ + const uint8_t *value_start; + + /** The length of the key in the source. */ + uint32_t key_length; + + /** The length of the value in the source. */ + uint32_t value_length; +} pm_magic_comment_t; + +/** + * When the encoding that is being used to parse the source is changed by prism, + * we provide the ability here to call out to a user-defined function. + */ +typedef void (*pm_encoding_changed_callback_t)(pm_parser_t *parser); + +/** + * When an encoding is encountered that isn't understood by prism, we provide + * the ability here to call out to a user-defined function to get an encoding + * struct. If the function returns something that isn't NULL, we set that to + * our encoding and use it to parse identifiers. + */ +typedef pm_encoding_t *(*pm_encoding_decode_callback_t)(pm_parser_t *parser, const uint8_t *name, size_t width); + +/** + * When you are lexing through a file, the lexer needs all of the information + * that the parser additionally provides (for example, the local table). So if + * you want to properly lex Ruby, you need to actually lex it in the context of + * the parser. In order to provide this functionality, we optionally allow a + * struct to be attached to the parser that calls back out to a user-provided + * callback when each token is lexed. + */ +typedef struct { + /** + * This opaque pointer is used to provide whatever information the user + * deemed necessary to the callback. In our case we use it to pass the array + * that the tokens get appended into. + */ + void *data; + + /** + * This is the callback that is called when a token is lexed. It is passed + * the opaque data pointer, the parser, and the token that was lexed. + */ + void (*callback)(void *data, pm_parser_t *parser, pm_token_t *token); +} pm_lex_callback_t; + +/** + * This struct represents a node in a linked list of scopes. Some scopes can see + * into their parent scopes, while others cannot. + */ +typedef struct pm_scope { + /** The IDs of the locals in the given scope. */ + pm_constant_id_list_t locals; + + /** A pointer to the previous scope in the linked list. */ + struct pm_scope *previous; + + /** + * A boolean indicating whether or not this scope can see into its parent. + * If closed is true, then the scope cannot see into its parent. + */ + bool closed; + + /** + * A boolean indicating whether or not this scope has explicit parameters. + * This is necessary to determine whether or not numbered parameters are + * allowed. + */ + bool explicit_params; + + /** + * An integer indicating the number of numbered parameters on this scope. + * This is necessary to determine if child blocks are allowed to use + * numbered parameters, and to pass information to consumers of the AST + * about how many numbered parameters exist. + */ + uint32_t numbered_parameters; + + /** + * A transparent scope is a scope that cannot have locals set on itself. + * When a local is set on this scope, it will instead be set on the parent + * scope's local table. + */ + bool transparent; +} pm_scope_t; + +/** + * This struct represents the overall parser. It contains a reference to the + * source file, as well as pointers that indicate where in the source it's + * currently parsing. It also contains the most recent and current token that + * it's considering. + */ +struct pm_parser { + /** The current state of the lexer. */ + pm_lex_state_t lex_state; + + /** Tracks the current nesting of (), [], and {}. */ + int enclosure_nesting; + + /** + * Used to temporarily track the nesting of enclosures to determine if a { + * is the beginning of a lambda following the parameters of a lambda. + */ + int lambda_enclosure_nesting; + + /** + * Used to track the nesting of braces to ensure we get the correct value + * when we are interpolating blocks with braces. + */ + int brace_nesting; + + /** + * The stack used to determine if a do keyword belongs to the predicate of a + * while, until, or for loop. + */ + pm_state_stack_t do_loop_stack; + + /** + * The stack used to determine if a do keyword belongs to the beginning of a + * block. + */ + pm_state_stack_t accepts_block_stack; + + /** A stack of lex modes. */ + struct { + /** The current mode of the lexer. */ + pm_lex_mode_t *current; + + /** The stack of lexer modes. */ + pm_lex_mode_t stack[PM_LEX_STACK_SIZE]; + + /** The current index into the lexer mode stack. */ + size_t index; + } lex_modes; + + /** + * The common_whitespace value from the most-recently-popped heredoc mode of the lexer, so we + * can dedent the heredoc after popping the lex mode. + */ + size_t current_string_common_whitespace; + + /** The pointer to the start of the source. */ + const uint8_t *start; + + /** The pointer to the end of the source. */ + const uint8_t *end; + + /** The previous token we were considering. */ + pm_token_t previous; + + /** The current token we're considering. */ + pm_token_t current; + + /** + * This is a special field set on the parser when we need the parser to jump + * to a specific location when lexing the next token, as opposed to just + * using the end of the previous token. Normally this is NULL. + */ + const uint8_t *next_start; + + /** + * This field indicates the end of a heredoc whose identifier was found on + * the current line. If another heredoc is found on the same line, then this + * will be moved forward to the end of that heredoc. If no heredocs are + * found on a line then this is NULL. + */ + const uint8_t *heredoc_end; + + /** The list of comments that have been found while parsing. */ + pm_list_t comment_list; + + /** The list of magic comments that have been found while parsing. */ + pm_list_t magic_comment_list; + + /** The optional location of the __END__ keyword and its contents. */ + pm_location_t data_loc; + + /** The list of warnings that have been found while parsing. */ + pm_list_t warning_list; + + /** The list of errors that have been found while parsing. */ + pm_list_t error_list; + + /** The current local scope. */ + pm_scope_t *current_scope; + + /** The current parsing context. */ + pm_context_node_t *current_context; + + /** + * The encoding functions for the current file is attached to the parser as + * it's parsing so that it can change with a magic comment. + */ + pm_encoding_t encoding; + + /** + * When the encoding that is being used to parse the source is changed by + * prism, we provide the ability here to call out to a user-defined + * function. + */ + pm_encoding_changed_callback_t encoding_changed_callback; + + /** + * When an encoding is encountered that isn't understood by prism, we + * provide the ability here to call out to a user-defined function to get an + * encoding struct. If the function returns something that isn't NULL, we + * set that to our encoding and use it to parse identifiers. + */ + pm_encoding_decode_callback_t encoding_decode_callback; + + /** + * This pointer indicates where a comment must start if it is to be + * considered an encoding comment. + */ + const uint8_t *encoding_comment_start; + + /** + * This is an optional callback that can be attached to the parser that will + * be called whenever a new token is lexed by the parser. + */ + pm_lex_callback_t *lex_callback; + + /** + * This is the path of the file being parsed. We use the filepath when + * constructing SourceFileNodes. + */ + pm_string_t filepath_string; + + /** + * This constant pool keeps all of the constants defined throughout the file + * so that we can reference them later. + */ + pm_constant_pool_t constant_pool; + + /** This is the list of newline offsets in the source file. */ + pm_newline_list_t newline_list; + + /** + * We want to add a flag to integer nodes that indicates their base. We only + * want to parse these once, but we don't have space on the token itself to + * communicate this information. So we store it here and pass it through + * when we find tokens that we need it for. + */ + pm_node_flags_t integer_base; + + /** + * This string is used to pass information from the lexer to the parser. It + * is particularly necessary because of escape sequences. + */ + pm_string_t current_string; + + /** + * The line number at the start of the parse. This will be used to offset + * the line numbers of all of the locations. + */ + int32_t start_line; + + /** Whether or not we're at the beginning of a command. */ + bool command_start; + + /** Whether or not we're currently recovering from a syntax error. */ + bool recovering; + + /** + * Whether or not the encoding has been changed by a magic comment. We use + * this to provide a fast path for the lexer instead of going through the + * function pointer. + */ + bool encoding_changed; + + /** + * This flag indicates that we are currently parsing a pattern matching + * expression and impacts that calculation of newlines. + */ + bool pattern_matching_newlines; + + /** This flag indicates that we are currently parsing a keyword argument. */ + bool in_keyword_arg; + + /** The current parameter name id on parsing its default value. */ + pm_constant_id_t current_param_name; + + /** + * Whether or not the parser has seen a token that has semantic meaning + * (i.e., a token that is not a comment or whitespace). + */ + bool semantic_token_seen; + + /** + * Whether or not we have found a frozen_string_literal magic comment with + * a true value. + */ + bool frozen_string_literal; + + /** + * Whether or not we should emit warnings. This will be set to false if the + * consumer of the library specified it, usually because they are parsing + * when $VERBOSE is nil. + */ + bool suppress_warnings; +}; + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/prettyprint.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/prettyprint.h new file mode 100644 index 00000000..351b92df --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/prettyprint.h @@ -0,0 +1,26 @@ +/** + * @file prettyprint.h + * + * An AST node pretty-printer. + */ +#ifndef PRISM_PRETTYPRINT_H +#define PRISM_PRETTYPRINT_H + +#include "prism/defines.h" + +#include + +#include "prism/ast.h" +#include "prism/parser.h" +#include "prism/util/pm_buffer.h" + +/** + * Pretty-prints the AST represented by the given node to the given buffer. + * + * @param output_buffer The buffer to write the pretty-printed AST to. + * @param parser The parser that parsed the AST. + * @param node The root node of the AST to pretty-print. + */ +PRISM_EXPORTED_FUNCTION void pm_prettyprint(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_node_t *node); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/regexp.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/regexp.h new file mode 100644 index 00000000..09bdaca8 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/regexp.h @@ -0,0 +1,33 @@ +/** + * @file regexp.h + * + * A regular expression parser. + */ +#ifndef PRISM_REGEXP_H +#define PRISM_REGEXP_H + +#include "prism/defines.h" +#include "prism/parser.h" +#include "prism/enc/pm_encoding.h" +#include "prism/util/pm_memchr.h" +#include "prism/util/pm_string_list.h" +#include "prism/util/pm_string.h" + +#include +#include +#include + +/** + * Parse a regular expression and extract the names of all of the named capture + * groups. + * + * @param source The source code to parse. + * @param size The size of the source code. + * @param named_captures The list to add the names of the named capture groups. + * @param encoding_changed Whether or not the encoding changed from the default. + * @param encoding The encoding of the source code. + * @return Whether or not the parsing was successful. + */ +PRISM_EXPORTED_FUNCTION bool pm_regexp_named_capture_group_names(const uint8_t *source, size_t size, pm_string_list_t *named_captures, bool encoding_changed, pm_encoding_t *encoding); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_buffer.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_buffer.h new file mode 100644 index 00000000..ec11d05e --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_buffer.h @@ -0,0 +1,146 @@ +/** + * @file pm_buffer.h + * + * A wrapper around a contiguous block of allocated memory. + */ +#ifndef PRISM_BUFFER_H +#define PRISM_BUFFER_H + +#include "prism/defines.h" + +#include +#include +#include +#include +#include + +/** + * A pm_buffer_t is a simple memory buffer that stores data in a contiguous + * block of memory. + */ +typedef struct { + /** The length of the buffer in bytes. */ + size_t length; + + /** The capacity of the buffer in bytes that has been allocated. */ + size_t capacity; + + /** A pointer to the start of the buffer. */ + char *value; +} pm_buffer_t; + +/** + * Return the size of the pm_buffer_t struct. + * + * @returns The size of the pm_buffer_t struct. + */ +PRISM_EXPORTED_FUNCTION size_t pm_buffer_sizeof(void); + +/** + * Initialize a pm_buffer_t with the given capacity. + * + * @param buffer The buffer to initialize. + * @param capacity The capacity of the buffer. + * @returns True if the buffer was initialized successfully, false otherwise. + */ +bool pm_buffer_init_capacity(pm_buffer_t *buffer, size_t capacity); + +/** + * Initialize a pm_buffer_t with its default values. + * + * @param buffer The buffer to initialize. + * @returns True if the buffer was initialized successfully, false otherwise. + */ +PRISM_EXPORTED_FUNCTION bool pm_buffer_init(pm_buffer_t *buffer); + +/** + * Return the value of the buffer. + * + * @param buffer The buffer to get the value of. + * @returns The value of the buffer. + */ +PRISM_EXPORTED_FUNCTION char * pm_buffer_value(pm_buffer_t *buffer); + +/** + * Return the length of the buffer. + * + * @param buffer The buffer to get the length of. + * @returns The length of the buffer. + */ +PRISM_EXPORTED_FUNCTION size_t pm_buffer_length(pm_buffer_t *buffer); + +/** + * Append the given amount of space as zeroes to the buffer. + * + * @param buffer The buffer to append to. + * @param length The amount of space to append and zero. + */ +void pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length); + +/** + * Append a formatted string to the buffer. + * + * @param buffer The buffer to append to. + * @param format The format string to append. + * @param ... The arguments to the format string. + */ +void pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) PRISM_ATTRIBUTE_FORMAT(2, 3); + +/** + * Append a string to the buffer. + * + * @param buffer The buffer to append to. + * @param value The string to append. + * @param length The length of the string to append. + */ +void pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length); + +/** + * Append a list of bytes to the buffer. + * + * @param buffer The buffer to append to. + * @param value The bytes to append. + * @param length The length of the bytes to append. + */ +void pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length); + +/** + * Append a single byte to the buffer. + * + * @param buffer The buffer to append to. + * @param value The byte to append. + */ +void pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value); + +/** + * Append a 32-bit unsigned integer to the buffer as a variable-length integer. + * + * @param buffer The buffer to append to. + * @param value The integer to append. + */ +void pm_buffer_append_varuint(pm_buffer_t *buffer, uint32_t value); + +/** + * Append a 32-bit signed integer to the buffer as a variable-length integer. + * + * @param buffer The buffer to append to. + * @param value The integer to append. + */ +void pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value); + +/** + * Concatenate one buffer onto another. + * + * @param destination The buffer to concatenate onto. + * @param source The buffer to concatenate. + */ +void pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source); + +/** + * Free the memory associated with the buffer. + * + * @param buffer The buffer to free. + */ +PRISM_EXPORTED_FUNCTION void pm_buffer_free(pm_buffer_t *buffer); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_char.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_char.h new file mode 100644 index 00000000..32f698a4 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_char.h @@ -0,0 +1,205 @@ +/** + * @file pm_char.h + * + * Functions for working with characters and strings. + */ +#ifndef PRISM_CHAR_H +#define PRISM_CHAR_H + +#include "prism/defines.h" +#include "prism/util/pm_newline_list.h" + +#include +#include + +/** + * Returns the number of characters at the start of the string that are + * whitespace. Disallows searching past the given maximum number of characters. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @return The number of characters at the start of the string that are + * whitespace. + */ +size_t pm_strspn_whitespace(const uint8_t *string, ptrdiff_t length); + +/** + * Returns the number of characters at the start of the string that are + * whitespace while also tracking the location of each newline. Disallows + * searching past the given maximum number of characters. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @param newline_list The list of newlines to populate. + * @return The number of characters at the start of the string that are + * whitespace. + */ +size_t +pm_strspn_whitespace_newlines(const uint8_t *string, ptrdiff_t length, pm_newline_list_t *newline_list); + +/** + * Returns the number of characters at the start of the string that are inline + * whitespace. Disallows searching past the given maximum number of characters. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @return The number of characters at the start of the string that are inline + * whitespace. + */ +size_t pm_strspn_inline_whitespace(const uint8_t *string, ptrdiff_t length); + +/** + * Returns the number of characters at the start of the string that are decimal + * digits. Disallows searching past the given maximum number of characters. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @return The number of characters at the start of the string that are decimal + * digits. + */ +size_t pm_strspn_decimal_digit(const uint8_t *string, ptrdiff_t length); + +/** + * Returns the number of characters at the start of the string that are + * hexadecimal digits. Disallows searching past the given maximum number of + * characters. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @return The number of characters at the start of the string that are + * hexadecimal digits. + */ +size_t pm_strspn_hexadecimal_digit(const uint8_t *string, ptrdiff_t length); + +/** + * Returns the number of characters at the start of the string that are octal + * digits or underscores. Disallows searching past the given maximum number of + * characters. + * + * If multiple underscores are found in a row or if an underscore is + * found at the end of the number, then the invalid pointer is set to the index + * of the first invalid underscore. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @param invalid The pointer to set to the index of the first invalid + * underscore. + * @return The number of characters at the start of the string that are octal + * digits or underscores. + */ +size_t pm_strspn_octal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid); + +/** + * Returns the number of characters at the start of the string that are decimal + * digits or underscores. Disallows searching past the given maximum number of + * characters. + * + * If multiple underscores are found in a row or if an underscore is + * found at the end of the number, then the invalid pointer is set to the index + * of the first invalid underscore. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @param invalid The pointer to set to the index of the first invalid + * underscore. + * @return The number of characters at the start of the string that are decimal + * digits or underscores. + */ +size_t pm_strspn_decimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid); + +/** + * Returns the number of characters at the start of the string that are + * hexadecimal digits or underscores. Disallows searching past the given maximum + * number of characters. + * + * If multiple underscores are found in a row or if an underscore is + * found at the end of the number, then the invalid pointer is set to the index + * of the first invalid underscore. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @param invalid The pointer to set to the index of the first invalid + * underscore. + * @return The number of characters at the start of the string that are + * hexadecimal digits or underscores. + */ +size_t pm_strspn_hexadecimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid); + +/** + * Returns the number of characters at the start of the string that are regexp + * options. Disallows searching past the given maximum number of characters. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @return The number of characters at the start of the string that are regexp + * options. + */ +size_t pm_strspn_regexp_option(const uint8_t *string, ptrdiff_t length); + +/** + * Returns the number of characters at the start of the string that are binary + * digits or underscores. Disallows searching past the given maximum number of + * characters. + * + * If multiple underscores are found in a row or if an underscore is + * found at the end of the number, then the invalid pointer is set to the index + * of the first invalid underscore. + * + * @param string The string to search. + * @param length The maximum number of characters to search. + * @param invalid The pointer to set to the index of the first invalid + * underscore. + * @return The number of characters at the start of the string that are binary + * digits or underscores. + */ +size_t pm_strspn_binary_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid); + +/** + * Returns true if the given character is a whitespace character. + * + * @param b The character to check. + * @return True if the given character is a whitespace character. + */ +bool pm_char_is_whitespace(const uint8_t b); + +/** + * Returns true if the given character is an inline whitespace character. + * + * @param b The character to check. + * @return True if the given character is an inline whitespace character. + */ +bool pm_char_is_inline_whitespace(const uint8_t b); + +/** + * Returns true if the given character is a binary digit. + * + * @param b The character to check. + * @return True if the given character is a binary digit. + */ +bool pm_char_is_binary_digit(const uint8_t b); + +/** + * Returns true if the given character is an octal digit. + * + * @param b The character to check. + * @return True if the given character is an octal digit. + */ +bool pm_char_is_octal_digit(const uint8_t b); + +/** + * Returns true if the given character is a decimal digit. + * + * @param b The character to check. + * @return True if the given character is a decimal digit. + */ +bool pm_char_is_decimal_digit(const uint8_t b); + +/** + * Returns true if the given character is a hexadecimal digit. + * + * @param b The character to check. + * @return True if the given character is a hexadecimal digit. + */ +bool pm_char_is_hexadecimal_digit(const uint8_t b); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_constant_pool.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_constant_pool.h new file mode 100644 index 00000000..ae5a88fb --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_constant_pool.h @@ -0,0 +1,191 @@ +/** + * @file pm_constant_pool.h + * + * A data structure that stores a set of strings. + * + * Each string is assigned a unique id, which can be used to compare strings for + * equality. This comparison ends up being much faster than strcmp, since it + * only requires a single integer comparison. + */ +#ifndef PRISM_CONSTANT_POOL_H +#define PRISM_CONSTANT_POOL_H + +#include "prism/defines.h" + +#include +#include +#include +#include +#include + +/** + * A constant id is a unique identifier for a constant in the constant pool. + */ +typedef uint32_t pm_constant_id_t; + +/** + * A list of constant IDs. Usually used to represent a set of locals. + */ +typedef struct { + /** The number of constant ids in the list. */ + size_t size; + + /** The number of constant ids that have been allocated in the list. */ + size_t capacity; + + /** The constant ids in the list. */ + pm_constant_id_t *ids; +} pm_constant_id_list_t; + +/** + * Initialize a list of constant ids. + * + * @param list The list to initialize. + */ +void pm_constant_id_list_init(pm_constant_id_list_t *list); + +/** + * Append a constant id to a list of constant ids. Returns false if any + * potential reallocations fail. + * + * @param list The list to append to. + * @param id The id to append. + * @return Whether the append succeeded. + */ +bool pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id); + +/** + * Checks if the current constant id list includes the given constant id. + * + * @param list The list to check. + * @param id The id to check for. + * @return Whether the list includes the given id. + */ +bool pm_constant_id_list_includes(pm_constant_id_list_t *list, pm_constant_id_t id); + +/** + * Get the memory size of a list of constant ids. + * + * @param list The list to get the memory size of. + * @return The memory size of the list. + */ +size_t pm_constant_id_list_memsize(pm_constant_id_list_t *list); + +/** + * Free the memory associated with a list of constant ids. + * + * @param list The list to free. + */ +void pm_constant_id_list_free(pm_constant_id_list_t *list); + +/** + * The type of bucket in the constant pool hash map. This determines how the + * bucket should be freed. + */ +typedef unsigned int pm_constant_pool_bucket_type_t; + +/** By default, each constant is a slice of the source. */ +static const pm_constant_pool_bucket_type_t PM_CONSTANT_POOL_BUCKET_DEFAULT = 0; + +/** An owned constant is one for which memory has been allocated. */ +static const pm_constant_pool_bucket_type_t PM_CONSTANT_POOL_BUCKET_OWNED = 1; + +/** A constant constant is known at compile time. */ +static const pm_constant_pool_bucket_type_t PM_CONSTANT_POOL_BUCKET_CONSTANT = 2; + +/** A bucket in the hash map. */ +typedef struct { + /** The incremental ID used for indexing back into the pool. */ + unsigned int id: 30; + + /** The type of the bucket, which determines how to free it. */ + pm_constant_pool_bucket_type_t type: 2; + + /** The hash of the bucket. */ + uint32_t hash; +} pm_constant_pool_bucket_t; + +/** A constant in the pool which effectively stores a string. */ +typedef struct { + /** A pointer to the start of the string. */ + const uint8_t *start; + + /** The length of the string. */ + size_t length; +} pm_constant_t; + +/** The overall constant pool, which stores constants found while parsing. */ +typedef struct { + /** The buckets in the hash map. */ + pm_constant_pool_bucket_t *buckets; + + /** The constants that are stored in the buckets. */ + pm_constant_t *constants; + + /** The number of buckets in the hash map. */ + uint32_t size; + + /** The number of buckets that have been allocated in the hash map. */ + uint32_t capacity; +} pm_constant_pool_t; + +/** + * Initialize a new constant pool with a given capacity. + * + * @param pool The pool to initialize. + * @param capacity The initial capacity of the pool. + * @return Whether the initialization succeeded. + */ +bool pm_constant_pool_init(pm_constant_pool_t *pool, uint32_t capacity); + +/** + * Return a pointer to the constant indicated by the given constant id. + * + * @param pool The pool to get the constant from. + * @param constant_id The id of the constant to get. + * @return A pointer to the constant. + */ +pm_constant_t * pm_constant_pool_id_to_constant(const pm_constant_pool_t *pool, pm_constant_id_t constant_id); + +/** + * Insert a constant into a constant pool that is a slice of a source string. + * Returns the id of the constant, or 0 if any potential calls to resize fail. + * + * @param pool The pool to insert the constant into. + * @param start A pointer to the start of the constant. + * @param length The length of the constant. + * @return The id of the constant. + */ +pm_constant_id_t pm_constant_pool_insert_shared(pm_constant_pool_t *pool, const uint8_t *start, size_t length); + +/** + * Insert a constant into a constant pool from memory that is now owned by the + * constant pool. Returns the id of the constant, or 0 if any potential calls to + * resize fail. + * + * @param pool The pool to insert the constant into. + * @param start A pointer to the start of the constant. + * @param length The length of the constant. + * @return The id of the constant. + */ +pm_constant_id_t pm_constant_pool_insert_owned(pm_constant_pool_t *pool, const uint8_t *start, size_t length); + +/** + * Insert a constant into a constant pool from memory that is constant. Returns + * the id of the constant, or 0 if any potential calls to resize fail. + * + * @param pool The pool to insert the constant into. + * @param start A pointer to the start of the constant. + * @param length The length of the constant. + * @return The id of the constant. + */ +pm_constant_id_t pm_constant_pool_insert_constant(pm_constant_pool_t *pool, const uint8_t *start, size_t length); + +/** + * Free the memory associated with a constant pool. + * + * @param pool The pool to free. + */ +void pm_constant_pool_free(pm_constant_pool_t *pool); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_list.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_list.h new file mode 100644 index 00000000..d29fe07c --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_list.h @@ -0,0 +1,97 @@ +/** + * @file pm_list.h + * + * An abstract linked list. + */ +#ifndef PRISM_LIST_H +#define PRISM_LIST_H + +#include "prism/defines.h" + +#include +#include +#include +#include + +/** + * This struct represents an abstract linked list that provides common + * functionality. It is meant to be used any time a linked list is necessary to + * store data. + * + * The linked list itself operates off a set of pointers. Because the pointers + * are not necessarily sequential, they can be of any size. We use this fact to + * allow the consumer of this linked list to extend the node struct to include + * any data they want. This is done by using the pm_list_node_t as the first + * member of the struct. + * + * For example, if we want to store a list of integers, we can do the following: + * + * ```c + * typedef struct { + * pm_list_node_t node; + * int value; + * } pm_int_node_t; + * + * pm_list_t list = { 0 }; + * pm_int_node_t *node = malloc(sizeof(pm_int_node_t)); + * node->value = 5; + * + * pm_list_append(&list, &node->node); + * ``` + * + * The pm_list_t struct is used to represent the overall linked list. It + * contains a pointer to the head and tail of the list. This allows for easy + * iteration and appending of new nodes. + */ +typedef struct pm_list_node { + /** A pointer to the next node in the list. */ + struct pm_list_node *next; +} pm_list_node_t; + +/** + * This represents the overall linked list. It keeps a pointer to the head and + * tail so that iteration is easy and pushing new nodes is easy. + */ +typedef struct { + /** The size of the list. */ + size_t size; + + /** A pointer to the head of the list. */ + pm_list_node_t *head; + + /** A pointer to the tail of the list. */ + pm_list_node_t *tail; +} pm_list_t; + +/** + * Returns true if the given list is empty. + * + * @param list The list to check. + * @return True if the given list is empty, otherwise false. + */ +PRISM_EXPORTED_FUNCTION bool pm_list_empty_p(pm_list_t *list); + +/** + * Returns the size of the list. + * + * @param list The list to check. + * @return The size of the list. + */ +PRISM_EXPORTED_FUNCTION size_t pm_list_size(pm_list_t *list); + +/** + * Append a node to the given list. + * + * @param list The list to append to. + * @param node The node to append. + */ +void pm_list_append(pm_list_t *list, pm_list_node_t *node); + +/** + * Deallocate the internal state of the given list. + * + * @param list The list to free. + */ +PRISM_EXPORTED_FUNCTION void pm_list_free(pm_list_t *list); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_memchr.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_memchr.h new file mode 100644 index 00000000..1eae6ab1 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_memchr.h @@ -0,0 +1,29 @@ +/** + * @file pm_memchr.h + * + * A custom memchr implementation. + */ +#ifndef PRISM_MEMCHR_H +#define PRISM_MEMCHR_H + +#include "prism/defines.h" +#include "prism/enc/pm_encoding.h" + +#include + +/** + * We need to roll our own memchr to handle cases where the encoding changes and + * we need to search for a character in a buffer that could be the trailing byte + * of a multibyte character. + * + * @param source The source string. + * @param character The character to search for. + * @param number The maximum number of bytes to search. + * @param encoding_changed Whether the encoding changed. + * @param encoding A pointer to the encoding. + * @return A pointer to the first occurrence of the character in the source + * string, or NULL if no such character exists. + */ +void * pm_memchr(const void *source, int character, size_t number, bool encoding_changed, pm_encoding_t *encoding); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_newline_list.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_newline_list.h new file mode 100644 index 00000000..a31051f4 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_newline_list.h @@ -0,0 +1,104 @@ +/** + * @file pm_newline_list.h + * + * A list of byte offsets of newlines in a string. + * + * When compiling the syntax tree, it's necessary to know the line and column + * of many nodes. This is necessary to support things like error messages, + * tracepoints, etc. + * + * It's possible that we could store the start line, start column, end line, and + * end column on every node in addition to the offsets that we already store, + * but that would be quite a lot of memory overhead. + */ +#ifndef PRISM_NEWLINE_LIST_H +#define PRISM_NEWLINE_LIST_H + +#include "prism/defines.h" + +#include +#include +#include +#include + +/** + * A list of offsets of newlines in a string. The offsets are assumed to be + * sorted/inserted in ascending order. + */ +typedef struct { + /** A pointer to the start of the source string. */ + const uint8_t *start; + + /** The number of offsets in the list. */ + size_t size; + + /** The capacity of the list that has been allocated. */ + size_t capacity; + + /** The list of offsets. */ + size_t *offsets; +} pm_newline_list_t; + +/** + * A line and column in a string. + */ +typedef struct { + /** The line number. */ + size_t line; + + /** The column number. */ + size_t column; +} pm_line_column_t; + +/** + * Initialize a new newline list with the given capacity. Returns true if the + * allocation of the offsets succeeds, otherwise returns false. + * + * @param list The list to initialize. + * @param start A pointer to the start of the source string. + * @param capacity The initial capacity of the list. + * @return True if the allocation of the offsets succeeds, otherwise false. + */ +bool pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capacity); + +/** + * Append a new offset to the newline list. Returns true if the reallocation of + * the offsets succeeds (if one was necessary), otherwise returns false. + * + * @param list The list to append to. + * @param cursor A pointer to the offset to append. + * @return True if the reallocation of the offsets succeeds (if one was + * necessary), otherwise false. + */ +bool pm_newline_list_append(pm_newline_list_t *list, const uint8_t *cursor); + +/** + * Conditionally append a new offset to the newline list, if the value passed in + * is a newline. + * + * @param list The list to append to. + * @param cursor A pointer to the offset to append. + * @return True if the reallocation of the offsets succeeds (if one was + * necessary), otherwise false. + */ +bool pm_newline_list_check_append(pm_newline_list_t *list, const uint8_t *cursor); + +/** + * Returns the line and column of the given offset. If the offset is not in the + * list, the line and column of the closest offset less than the given offset + * are returned. + * + * @param list The list to search. + * @param cursor A pointer to the offset to search for. + * @return The line and column of the given offset. + */ +pm_line_column_t pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor); + +/** + * Free the internal memory allocated for the newline list. + * + * @param list The list to free. + */ +void pm_newline_list_free(pm_newline_list_t *list); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_state_stack.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_state_stack.h new file mode 100644 index 00000000..1ce57a22 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_state_stack.h @@ -0,0 +1,42 @@ +/** + * @file pm_state_stack.h + * + * A stack of boolean values. + */ +#ifndef PRISM_STATE_STACK_H +#define PRISM_STATE_STACK_H + +#include "prism/defines.h" + +#include +#include + +/** + * A struct that represents a stack of boolean values. + */ +typedef uint32_t pm_state_stack_t; + +/** + * Pushes a value onto the stack. + * + * @param stack The stack to push the value onto. + * @param value The value to push onto the stack. + */ +void pm_state_stack_push(pm_state_stack_t *stack, bool value); + +/** + * Pops a value off the stack. + * + * @param stack The stack to pop the value off of. + */ +void pm_state_stack_pop(pm_state_stack_t *stack); + +/** + * Returns the value at the top of the stack. + * + * @param stack The stack to get the value from. + * @return The value at the top of the stack. + */ +bool pm_state_stack_p(pm_state_stack_t *stack); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_string.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_string.h new file mode 100644 index 00000000..ddb15378 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_string.h @@ -0,0 +1,150 @@ +/** + * @file pm_string.h + * + * A generic string type that can have various ownership semantics. + */ +#ifndef PRISM_STRING_H +#define PRISM_STRING_H + +#include "prism/defines.h" + +#include +#include +#include +#include +#include + +// The following headers are necessary to read files using demand paging. +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#endif + +/** + * A generic string type that can have various ownership semantics. + */ +typedef struct { + /** A pointer to the start of the string. */ + const uint8_t *source; + + /** The length of the string in bytes of memory. */ + size_t length; + + /** The type of the string. This field determines how the string should be freed. */ + enum { + /** This string is a constant string, and should not be freed. */ + PM_STRING_CONSTANT, + + /** This is a slice of another string, and should not be freed. */ + PM_STRING_SHARED, + + /** This string owns its memory, and should be freed using `pm_string_free`. */ + PM_STRING_OWNED, + + /** This string is a memory-mapped file, and should be freed using `pm_string_free`. */ + PM_STRING_MAPPED + } type; +} pm_string_t; + +/** + * Returns the size of the pm_string_t struct. This is necessary to allocate the + * correct amount of memory in the FFI backend. + * + * @return The size of the pm_string_t struct. + */ +PRISM_EXPORTED_FUNCTION size_t pm_string_sizeof(void); + +/** + * Defines an empty string. This is useful for initializing a string that will + * be filled in later. + */ +#define PM_STRING_EMPTY ((pm_string_t) { .type = PM_STRING_CONSTANT, .source = NULL, .length = 0 }) + +/** + * Initialize a shared string that is based on initial input. + * + * @param string The string to initialize. + * @param start The start of the string. + * @param end The end of the string. + */ +void pm_string_shared_init(pm_string_t *string, const uint8_t *start, const uint8_t *end); + +/** + * Initialize an owned string that is responsible for freeing allocated memory. + * + * @param string The string to initialize. + * @param source The source of the string. + * @param length The length of the string. + */ +void pm_string_owned_init(pm_string_t *string, uint8_t *source, size_t length); + +/** + * Initialize a constant string that doesn't own its memory source. + * + * @param string The string to initialize. + * @param source The source of the string. + * @param length The length of the string. + */ +void pm_string_constant_init(pm_string_t *string, const char *source, size_t length); + +/** + * Read the file indicated by the filepath parameter into source and load its + * contents and size into the given `pm_string_t`. The given `pm_string_t` + * should be freed using `pm_string_free` when it is no longer used. + * + * We want to use demand paging as much as possible in order to avoid having to + * read the entire file into memory (which could be detrimental to performance + * for large files). This means that if we're on windows we'll use + * `MapViewOfFile`, on POSIX systems that have access to `mmap` we'll use + * `mmap`, and on other POSIX systems we'll use `read`. + * + * @param string The string to initialize. + * @param filepath The filepath to read. + * @return Whether or not the file was successfully mapped. + */ +PRISM_EXPORTED_FUNCTION bool pm_string_mapped_init(pm_string_t *string, const char *filepath); + +/** + * Returns the memory size associated with the string. + * + * @param string The string to get the memory size of. + * @return The size of the memory associated with the string. + */ +size_t pm_string_memsize(const pm_string_t *string); + +/** + * Ensure the string is owned. If it is not, then reinitialize it as owned and + * copy over the previous source. + * + * @param string The string to ensure is owned. + */ +void pm_string_ensure_owned(pm_string_t *string); + +/** + * Returns the length associated with the string. + * + * @param string The string to get the length of. + * @return The length of the string. + */ +PRISM_EXPORTED_FUNCTION size_t pm_string_length(const pm_string_t *string); + +/** + * Returns the start pointer associated with the string. + * + * @param string The string to get the start pointer of. + * @return The start pointer of the string. + */ +PRISM_EXPORTED_FUNCTION const uint8_t * pm_string_source(const pm_string_t *string); + +/** + * Free the associated memory of the given string. + * + * @param string The string to free. + */ +PRISM_EXPORTED_FUNCTION void pm_string_free(pm_string_t *string); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_string_list.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_string_list.h new file mode 100644 index 00000000..0d406cc5 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_string_list.h @@ -0,0 +1,44 @@ +/** + * @file pm_string_list.h + * + * A list of strings. + */ +#ifndef PRISM_STRING_LIST_H +#define PRISM_STRING_LIST_H + +#include "prism/defines.h" +#include "prism/util/pm_string.h" + +#include +#include + +/** + * A list of strings. + */ +typedef struct { + /** The length of the string list. */ + size_t length; + + /** The capacity of the string list that has been allocated. */ + size_t capacity; + + /** A pointer to the start of the string list. */ + pm_string_t *strings; +} pm_string_list_t; + +/** + * Append a pm_string_t to the given string list. + * + * @param string_list The string list to append to. + * @param string The string to append. + */ +void pm_string_list_append(pm_string_list_t *string_list, pm_string_t *string); + +/** + * Free the memory associated with the string list. + * + * @param string_list The string list to free. + */ +PRISM_EXPORTED_FUNCTION void pm_string_list_free(pm_string_list_t *string_list); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_strncasecmp.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_strncasecmp.h new file mode 100644 index 00000000..5cb88cb5 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_strncasecmp.h @@ -0,0 +1,32 @@ +/** + * @file pm_strncasecmp.h + * + * A custom strncasecmp implementation. + */ +#ifndef PRISM_STRNCASECMP_H +#define PRISM_STRNCASECMP_H + +#include "prism/defines.h" + +#include +#include +#include + +/** + * Compare two strings, ignoring case, up to the given length. Returns 0 if the + * strings are equal, a negative number if string1 is less than string2, or a + * positive number if string1 is greater than string2. + * + * Note that this is effectively our own implementation of strncasecmp, but it's + * not available on all of the platforms we want to support so we're rolling it + * here. + * + * @param string1 The first string to compare. + * @param string2 The second string to compare + * @param length The maximum number of characters to compare. + * @return 0 if the strings are equal, a negative number if string1 is less than + * string2, or a positive number if string1 is greater than string2. + */ +int pm_strncasecmp(const uint8_t *string1, const uint8_t *string2, size_t length); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_strpbrk.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_strpbrk.h new file mode 100644 index 00000000..61a443e5 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/util/pm_strpbrk.h @@ -0,0 +1,43 @@ +/** + * @file pm_strpbrk.h + * + * A custom strpbrk implementation. + */ +#ifndef PRISM_STRPBRK_H +#define PRISM_STRPBRK_H + +#include "prism/defines.h" +#include "prism/parser.h" + +#include +#include + +/** + * Here we have rolled our own version of strpbrk. The standard library strpbrk + * has undefined behavior when the source string is not null-terminated. We want + * to support strings that are not null-terminated because pm_parse does not + * have the contract that the string is null-terminated. (This is desirable + * because it means the extension can call pm_parse with the result of a call to + * mmap). + * + * The standard library strpbrk also does not support passing a maximum length + * to search. We want to support this for the reason mentioned above, but we + * also don't want it to stop on null bytes. Ruby actually allows null bytes + * within strings, comments, regular expressions, etc. So we need to be able to + * skip past them. + * + * Finally, we want to support encodings wherein the charset could contain + * characters that are trailing bytes of multi-byte characters. For example, in + * Shift-JIS, the backslash character can be a trailing byte. In that case we + * need to take a slower path and iterate one multi-byte character at a time. + * + * @param parser The parser. + * @param source The source string. + * @param charset The charset to search for. + * @param length The maximum length to search. + * @return A pointer to the first character in the source string that is in the + * charset, or NULL if no such character exists. + */ +const uint8_t * pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length); + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/include/prism/version.h b/crates/prism-sys/vendor/prism-0.18.0/include/prism/version.h new file mode 100644 index 00000000..67a29ed3 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/include/prism/version.h @@ -0,0 +1,29 @@ +/** + * @file version.h + * + * The version of the Prism library. + */ +#ifndef PRISM_VERSION_H +#define PRISM_VERSION_H + +/** + * The major version of the Prism library as an int. + */ +#define PRISM_VERSION_MAJOR 0 + +/** + * The minor version of the Prism library as an int. + */ +#define PRISM_VERSION_MINOR 18 + +/** + * The patch version of the Prism library as an int. + */ +#define PRISM_VERSION_PATCH 0 + +/** + * The version of the Prism library as a constant string. + */ +#define PRISM_VERSION "0.18.0" + +#endif diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/diagnostic.c b/crates/prism-sys/vendor/prism-0.18.0/src/diagnostic.c new file mode 100644 index 00000000..443ad35c --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/diagnostic.c @@ -0,0 +1,354 @@ +#include "prism/diagnostic.h" + +/** + * ## Message composition + * + * When composing an error message, use sentence fragments. + * + * Try describing the property of the code that caused the error, rather than + * the rule that is being violated. It may help to use a fragment that completes + * a sentence beginning, "the parser encountered (a) ...". If appropriate, add a + * description of the rule violation (or other helpful context) after a + * semicolon. + * + * For example:, instead of "control escape sequence cannot be doubled", prefer: + * + * > "invalid control escape sequence; control cannot be repeated" + * + * In some cases, where the failure is more general or syntax expectations are + * violated, it may make more sense to use a fragment that completes a sentence + * beginning, "the parser ...". + * + * For example: + * + * > "expected an expression after `(`" + * > "cannot parse the expression" + * + * ## Message style guide + * + * - Use articles like "a", "an", and "the" when appropriate. + * - e.g., prefer "cannot parse the expression" to "cannot parse expression". + * - Use the common name for tokens and nodes. + * - e.g., prefer "keyword splat" to "assoc splat" + * - e.g., prefer "embedded document" to "embdoc" + * - Do not capitalize the initial word of the message. + * - Use back ticks around token literals + * - e.g., "Expected a `=>` between the hash key and value" + * - Do not use `.` or other punctuation at the end of the message. + * - Do not use contractions like "can't". Prefer "cannot" to "can not". + * - For tokens that can have multiple meanings, reference the token and its meaning. + * - e.g., "`*` splat argument" is clearer and more complete than "splat argument" or "`*` argument" + * + * ## Error names (PM_ERR_*) + * + * - When appropriate, prefer node name to token name. + * - e.g., prefer "SPLAT" to "STAR" in the context of argument parsing. + * - Prefer token name to common name. + * - e.g., prefer "STAR" to "ASTERISK". + * - Try to order the words in the name from more general to more specific, + * - e.g., "INVALID_NUMBER_DECIMAL" is better than "DECIMAL_INVALID_NUMBER". + * - When in doubt, look for similar patterns and name them so that they are grouped when lexically + * sorted. See PM_ERR_ARGUMENT_NO_FORWARDING_* for an example. + */ +static const char* const diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = { + [PM_ERR_ALIAS_ARGUMENT] = "invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", + [PM_ERR_AMPAMPEQ_MULTI_ASSIGN] = "unexpected `&&=` in a multiple assignment", + [PM_ERR_ARGUMENT_AFTER_BLOCK] = "unexpected argument after a block argument", + [PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES] = "unexpected argument after `...`", + [PM_ERR_ARGUMENT_BARE_HASH] = "unexpected bare hash argument", + [PM_ERR_ARGUMENT_BLOCK_MULTI] = "multiple block arguments; only one block is allowed", + [PM_ERR_ARGUMENT_FORMAL_CLASS] = "invalid formal argument; formal argument cannot be a class variable", + [PM_ERR_ARGUMENT_FORMAL_CONSTANT] = "invalid formal argument; formal argument cannot be a constant", + [PM_ERR_ARGUMENT_FORMAL_GLOBAL] = "invalid formal argument; formal argument cannot be a global variable", + [PM_ERR_ARGUMENT_FORMAL_IVAR] = "invalid formal argument; formal argument cannot be an instance variable", + [PM_ERR_ARGUMENT_FORWARDING_UNBOUND] = "unexpected `...` in an non-parenthesized call", + [PM_ERR_ARGUMENT_NO_FORWARDING_AMP] = "unexpected `&` when the parent method is not forwarding", + [PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = "unexpected `...` when the parent method is not forwarding", + [PM_ERR_ARGUMENT_NO_FORWARDING_STAR] = "unexpected `*` when the parent method is not forwarding", + [PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT] = "unexpected `*` splat argument after a `**` keyword splat argument", + [PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT] = "unexpected `*` splat argument after a `*` splat argument", + [PM_ERR_ARGUMENT_TERM_PAREN] = "expected a `)` to close the arguments", + [PM_ERR_ARGUMENT_UNEXPECTED_BLOCK] = "unexpected `{` after a method call without parenthesis", + [PM_ERR_ARRAY_ELEMENT] = "expected an element for the array", + [PM_ERR_ARRAY_EXPRESSION] = "expected an expression for the array element", + [PM_ERR_ARRAY_EXPRESSION_AFTER_STAR] = "expected an expression after `*` in the array", + [PM_ERR_ARRAY_SEPARATOR] = "expected a `,` separator for the array elements", + [PM_ERR_ARRAY_TERM] = "expected a `]` to close the array", + [PM_ERR_BEGIN_LONELY_ELSE] = "unexpected `else` in `begin` block; a `rescue` clause must precede `else`", + [PM_ERR_BEGIN_TERM] = "expected an `end` to close the `begin` statement", + [PM_ERR_BEGIN_UPCASE_BRACE] = "expected a `{` after `BEGIN`", + [PM_ERR_BEGIN_UPCASE_TERM] = "expected a `}` to close the `BEGIN` statement", + [PM_ERR_BEGIN_UPCASE_TOPLEVEL] = "BEGIN is permitted only at toplevel", + [PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE] = "expected a local variable name in the block parameters", + [PM_ERR_BLOCK_PARAM_PIPE_TERM] = "expected the block parameters to end with `|`", + [PM_ERR_BLOCK_TERM_BRACE] = "expected a block beginning with `{` to end with `}`", + [PM_ERR_BLOCK_TERM_END] = "expected a block beginning with `do` to end with `end`", + [PM_ERR_CANNOT_PARSE_EXPRESSION] = "cannot parse the expression", + [PM_ERR_CANNOT_PARSE_STRING_PART] = "cannot parse the string part", + [PM_ERR_CASE_EXPRESSION_AFTER_CASE] = "expected an expression after `case`", + [PM_ERR_CASE_EXPRESSION_AFTER_WHEN] = "expected an expression after `when`", + [PM_ERR_CASE_MATCH_MISSING_PREDICATE] = "expected a predicate for a case matching statement", + [PM_ERR_CASE_MISSING_CONDITIONS] = "expected a `when` or `in` clause after `case`", + [PM_ERR_CASE_TERM] = "expected an `end` to close the `case` statement", + [PM_ERR_CLASS_IN_METHOD] = "unexpected class definition in a method definition", + [PM_ERR_CLASS_NAME] = "expected a constant name after `class`", + [PM_ERR_CLASS_SUPERCLASS] = "expected a superclass after `<`", + [PM_ERR_CLASS_TERM] = "expected an `end` to close the `class` statement", + [PM_ERR_CLASS_UNEXPECTED_END] = "unexpected `end`, expecting ';' or '\n'", + [PM_ERR_CONDITIONAL_ELSIF_PREDICATE] = "expected a predicate expression for the `elsif` statement", + [PM_ERR_CONDITIONAL_IF_PREDICATE] = "expected a predicate expression for the `if` statement", + [PM_ERR_CONDITIONAL_PREDICATE_TERM] = "expected `then` or `;` or '\n'", + [PM_ERR_CONDITIONAL_TERM] = "expected an `end` to close the conditional clause", + [PM_ERR_CONDITIONAL_TERM_ELSE] = "expected an `end` to close the `else` clause", + [PM_ERR_CONDITIONAL_UNLESS_PREDICATE] = "expected a predicate expression for the `unless` statement", + [PM_ERR_CONDITIONAL_UNTIL_PREDICATE] = "expected a predicate expression for the `until` statement", + [PM_ERR_CONDITIONAL_WHILE_PREDICATE] = "expected a predicate expression for the `while` statement", + [PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT] = "expected a constant after the `::` operator", + [PM_ERR_DEF_ENDLESS] = "could not parse the endless method body", + [PM_ERR_DEF_ENDLESS_SETTER] = "invalid method name; a setter method cannot be defined in an endless method definition", + [PM_ERR_DEF_NAME] = "expected a method name", + [PM_ERR_DEF_NAME_AFTER_RECEIVER] = "expected a method name after the receiver", + [PM_ERR_DEF_PARAMS_TERM] = "expected a delimiter to close the parameters", + [PM_ERR_DEF_PARAMS_TERM_PAREN] = "expected a `)` to close the parameters", + [PM_ERR_DEF_RECEIVER] = "expected a receiver for the method definition", + [PM_ERR_DEF_RECEIVER_TERM] = "expected a `.` or `::` after the receiver in a method definition", + [PM_ERR_DEF_TERM] = "expected an `end` to close the `def` statement", + [PM_ERR_DEFINED_EXPRESSION] = "expected an expression after `defined?`", + [PM_ERR_EMBDOC_TERM] = "could not find a terminator for the embedded document", + [PM_ERR_EMBEXPR_END] = "expected a `}` to close the embedded expression", + [PM_ERR_EMBVAR_INVALID] = "invalid embedded variable", + [PM_ERR_END_UPCASE_BRACE] = "expected a `{` after `END`", + [PM_ERR_END_UPCASE_TERM] = "expected a `}` to close the `END` statement", + [PM_ERR_ESCAPE_INVALID_CONTROL] = "invalid control escape sequence", + [PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT] = "invalid control escape sequence; control cannot be repeated", + [PM_ERR_ESCAPE_INVALID_HEXADECIMAL] = "invalid hexadecimal escape sequence", + [PM_ERR_ESCAPE_INVALID_META] = "invalid meta escape sequence", + [PM_ERR_ESCAPE_INVALID_META_REPEAT] = "invalid meta escape sequence; meta cannot be repeated", + [PM_ERR_ESCAPE_INVALID_UNICODE] = "invalid Unicode escape sequence", + [PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS] = "invalid Unicode escape sequence; Unicode cannot be combined with control or meta flags", + [PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL] = "invalid Unicode escape sequence; multiple codepoints are not allowed in a character literal", + [PM_ERR_ESCAPE_INVALID_UNICODE_LONG] = "invalid Unicode escape sequence; maximum length is 6 digits", + [PM_ERR_ESCAPE_INVALID_UNICODE_TERM] = "invalid Unicode escape sequence; needs closing `}`", + [PM_ERR_EXPECT_ARGUMENT] = "expected an argument", + [PM_ERR_EXPECT_EOL_AFTER_STATEMENT] = "expected a newline or semicolon after the statement", + [PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ] = "expected an expression after `&&=`", + [PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ] = "expected an expression after `||=`", + [PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA] = "expected an expression after `,`", + [PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL] = "expected an expression after `=`", + [PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS] = "expected an expression after `<<`", + [PM_ERR_EXPECT_EXPRESSION_AFTER_LPAREN] = "expected an expression after `(`", + [PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR] = "expected an expression after the operator", + [PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT] = "expected an expression after `*` splat in an argument", + [PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH] = "expected an expression after `**` in a hash", + [PM_ERR_EXPECT_EXPRESSION_AFTER_STAR] = "expected an expression after `*`", + [PM_ERR_EXPECT_IDENT_REQ_PARAMETER] = "expected an identifier for the required parameter", + [PM_ERR_EXPECT_LPAREN_REQ_PARAMETER] = "expected a `(` to start a required parameter", + [PM_ERR_EXPECT_RBRACKET] = "expected a matching `]`", + [PM_ERR_EXPECT_RPAREN] = "expected a matching `)`", + [PM_ERR_EXPECT_RPAREN_AFTER_MULTI] = "expected a `)` after multiple assignment", + [PM_ERR_EXPECT_RPAREN_REQ_PARAMETER] = "expected a `)` to end a required parameter", + [PM_ERR_EXPECT_STRING_CONTENT] = "expected string content after opening string delimiter", + [PM_ERR_EXPECT_WHEN_DELIMITER] = "expected a delimiter after the predicates of a `when` clause", + [PM_ERR_EXPRESSION_BARE_HASH] = "unexpected bare hash in expression", + [PM_ERR_FOR_COLLECTION] = "expected a collection after the `in` in a `for` statement", + [PM_ERR_FOR_INDEX] = "expected an index after `for`", + [PM_ERR_FOR_IN] = "expected an `in` after the index in a `for` statement", + [PM_ERR_FOR_TERM] = "expected an `end` to close the `for` loop", + [PM_ERR_HASH_EXPRESSION_AFTER_LABEL] = "expected an expression after the label in a hash", + [PM_ERR_HASH_KEY] = "expected a key in the hash literal", + [PM_ERR_HASH_ROCKET] = "expected a `=>` between the hash key and value", + [PM_ERR_HASH_TERM] = "expected a `}` to close the hash literal", + [PM_ERR_HASH_VALUE] = "expected a value in the hash literal", + [PM_ERR_HEREDOC_TERM] = "could not find a terminator for the heredoc", + [PM_ERR_INCOMPLETE_QUESTION_MARK] = "incomplete expression at `?`", + [PM_ERR_INCOMPLETE_VARIABLE_CLASS] = "incomplete class variable", + [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = "incomplete instance variable", + [PM_ERR_INVALID_ENCODING_MAGIC_COMMENT] = "unknown or invalid encoding in the magic comment", + [PM_ERR_INVALID_FLOAT_EXPONENT] = "invalid exponent", + [PM_ERR_INVALID_NUMBER_BINARY] = "invalid binary number", + [PM_ERR_INVALID_NUMBER_DECIMAL] = "invalid decimal number", + [PM_ERR_INVALID_NUMBER_HEXADECIMAL] = "invalid hexadecimal number", + [PM_ERR_INVALID_NUMBER_OCTAL] = "invalid octal number", + [PM_ERR_INVALID_NUMBER_UNDERSCORE] = "invalid underscore placement in number", + [PM_ERR_INVALID_PERCENT] = "invalid `%` token", // TODO WHAT? + [PM_ERR_INVALID_TOKEN] = "invalid token", // TODO WHAT? + [PM_ERR_INVALID_VARIABLE_GLOBAL] = "invalid global variable", + [PM_ERR_LAMBDA_OPEN] = "expected a `do` keyword or a `{` to open the lambda block", + [PM_ERR_LAMBDA_TERM_BRACE] = "expected a lambda block beginning with `{` to end with `}`", + [PM_ERR_LAMBDA_TERM_END] = "expected a lambda block beginning with `do` to end with `end`", + [PM_ERR_LIST_I_LOWER_ELEMENT] = "expected a symbol in a `%i` list", + [PM_ERR_LIST_I_LOWER_TERM] = "expected a closing delimiter for the `%i` list", + [PM_ERR_LIST_I_UPPER_ELEMENT] = "expected a symbol in a `%I` list", + [PM_ERR_LIST_I_UPPER_TERM] = "expected a closing delimiter for the `%I` list", + [PM_ERR_LIST_W_LOWER_ELEMENT] = "expected a string in a `%w` list", + [PM_ERR_LIST_W_LOWER_TERM] = "expected a closing delimiter for the `%w` list", + [PM_ERR_LIST_W_UPPER_ELEMENT] = "expected a string in a `%W` list", + [PM_ERR_LIST_W_UPPER_TERM] = "expected a closing delimiter for the `%W` list", + [PM_ERR_MALLOC_FAILED] = "failed to allocate memory", + [PM_ERR_MODULE_IN_METHOD] = "unexpected module definition in a method definition", + [PM_ERR_MODULE_NAME] = "expected a constant name after `module`", + [PM_ERR_MODULE_TERM] = "expected an `end` to close the `module` statement", + [PM_ERR_MULTI_ASSIGN_MULTI_SPLATS] = "multiple splats in multiple assignment", + [PM_ERR_NOT_EXPRESSION] = "expected an expression after `not`", + [PM_ERR_NUMBER_LITERAL_UNDERSCORE] = "number literal ending with a `_`", + [PM_ERR_NUMBERED_PARAMETER_NOT_ALLOWED] = "numbered parameters are not allowed alongside explicit parameters", + [PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE] = "numbered parameter is already used in outer scope", + [PM_ERR_OPERATOR_MULTI_ASSIGN] = "unexpected operator for a multiple assignment", + [PM_ERR_OPERATOR_WRITE_ARGUMENTS] = "unexpected operator after a call with arguments", + [PM_ERR_OPERATOR_WRITE_BLOCK] = "unexpected operator after a call with a block", + [PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI] = "unexpected multiple `**` splat parameters", + [PM_ERR_PARAMETER_BLOCK_MULTI] = "multiple block parameters; only one block is allowed", + [PM_ERR_PARAMETER_CIRCULAR] = "parameter default value references itself", + [PM_ERR_PARAMETER_METHOD_NAME] = "unexpected name for a parameter", + [PM_ERR_PARAMETER_NAME_REPEAT] = "repeated parameter name", + [PM_ERR_PARAMETER_NO_DEFAULT] = "expected a default value for the parameter", + [PM_ERR_PARAMETER_NO_DEFAULT_KW] = "expected a default value for the keyword parameter", + [PM_ERR_PARAMETER_NUMBERED_RESERVED] = "%.2s is reserved for a numbered parameter", + [PM_ERR_PARAMETER_ORDER] = "unexpected parameter order", + [PM_ERR_PARAMETER_SPLAT_MULTI] = "unexpected multiple `*` splat parameters", + [PM_ERR_PARAMETER_STAR] = "unexpected parameter `*`", + [PM_ERR_PARAMETER_UNEXPECTED_FWD] = "unexpected `...` in parameters", + [PM_ERR_PARAMETER_WILD_LOOSE_COMMA] = "unexpected `,` in parameters", + [PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET] = "expected a pattern expression after the `[` operator", + [PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA] = "expected a pattern expression after `,`", + [PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET] = "expected a pattern expression after `=>`", + [PM_ERR_PATTERN_EXPRESSION_AFTER_IN] = "expected a pattern expression after the `in` keyword", + [PM_ERR_PATTERN_EXPRESSION_AFTER_KEY] = "expected a pattern expression after the key", + [PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN] = "expected a pattern expression after the `(` operator", + [PM_ERR_PATTERN_EXPRESSION_AFTER_PIN] = "expected a pattern expression after the `^` pin operator", + [PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE] = "expected a pattern expression after the `|` operator", + [PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE] = "expected a pattern expression after the range operator", + [PM_ERR_PATTERN_HASH_KEY] = "expected a key in the hash pattern", + [PM_ERR_PATTERN_HASH_KEY_LABEL] = "expected a label as the key in the hash pattern", // TODO // THIS // AND // ABOVE // IS WEIRD + [PM_ERR_PATTERN_IDENT_AFTER_HROCKET] = "expected an identifier after the `=>` operator", + [PM_ERR_PATTERN_LABEL_AFTER_COMMA] = "expected a label after the `,` in the hash pattern", + [PM_ERR_PATTERN_REST] = "unexpected rest pattern", + [PM_ERR_PATTERN_TERM_BRACE] = "expected a `}` to close the pattern expression", + [PM_ERR_PATTERN_TERM_BRACKET] = "expected a `]` to close the pattern expression", + [PM_ERR_PATTERN_TERM_PAREN] = "expected a `)` to close the pattern expression", + [PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN] = "unexpected `||=` in a multiple assignment", + [PM_ERR_REGEXP_TERM] = "expected a closing delimiter for the regular expression", + [PM_ERR_RESCUE_EXPRESSION] = "expected a rescued expression", + [PM_ERR_RESCUE_MODIFIER_VALUE] = "expected a value after the `rescue` modifier", + [PM_ERR_RESCUE_TERM] = "expected a closing delimiter for the `rescue` clause", + [PM_ERR_RESCUE_VARIABLE] = "expected an exception variable after `=>` in a rescue statement", + [PM_ERR_RETURN_INVALID] = "invalid `return` in a class or module body", + [PM_ERR_STATEMENT_ALIAS] = "unexpected an `alias` at a non-statement position", + [PM_ERR_STATEMENT_POSTEXE_END] = "unexpected an `END` at a non-statement position", + [PM_ERR_STATEMENT_PREEXE_BEGIN] = "unexpected a `BEGIN` at a non-statement position", + [PM_ERR_STATEMENT_UNDEF] = "unexpected an `undef` at a non-statement position", + [PM_ERR_STRING_CONCATENATION] = "expected a string for concatenation", + [PM_ERR_STRING_INTERPOLATED_TERM] = "expected a closing delimiter for the interpolated string", + [PM_ERR_STRING_LITERAL_TERM] = "expected a closing delimiter for the string literal", + [PM_ERR_SYMBOL_INVALID] = "invalid symbol", // TODO expected symbol? prism.c ~9719 + [PM_ERR_SYMBOL_TERM_DYNAMIC] = "expected a closing delimiter for the dynamic symbol", + [PM_ERR_SYMBOL_TERM_INTERPOLATED] = "expected a closing delimiter for the interpolated symbol", + [PM_ERR_TERNARY_COLON] = "expected a `:` after the true expression of a ternary operator", + [PM_ERR_TERNARY_EXPRESSION_FALSE] = "expected an expression after `:` in the ternary operator", + [PM_ERR_TERNARY_EXPRESSION_TRUE] = "expected an expression after `?` in the ternary operator", + [PM_ERR_UNDEF_ARGUMENT] = "invalid argument being passed to `undef`; expected a bare word, constant, or symbol argument", + [PM_ERR_UNARY_RECEIVER_BANG] = "expected a receiver for unary `!`", + [PM_ERR_UNARY_RECEIVER_MINUS] = "expected a receiver for unary `-`", + [PM_ERR_UNARY_RECEIVER_PLUS] = "expected a receiver for unary `+`", + [PM_ERR_UNARY_RECEIVER_TILDE] = "expected a receiver for unary `~`", + [PM_ERR_UNTIL_TERM] = "expected an `end` to close the `until` statement", + [PM_ERR_VOID_EXPRESSION] = "unexpected void value expression", + [PM_ERR_WHILE_TERM] = "expected an `end` to close the `while` statement", + [PM_ERR_WRITE_TARGET_READONLY] = "immutable variable as a write target", + [PM_ERR_WRITE_TARGET_UNEXPECTED] = "unexpected write target", + [PM_ERR_XSTRING_TERM] = "expected a closing delimiter for the `%x` or backtick string", + [PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS] = "ambiguous first argument; put parentheses or a space even after `-` operator", + [PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS] = "ambiguous first argument; put parentheses or a space even after `+` operator", + [PM_WARN_AMBIGUOUS_PREFIX_STAR] = "ambiguous `*` has been interpreted as an argument prefix", + [PM_WARN_AMBIGUOUS_SLASH] = "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", + [PM_WARN_END_IN_METHOD] = "END in method; use at_exit", +}; + +static const char* +pm_diagnostic_message(pm_diagnostic_id_t diag_id) { + assert(diag_id < PM_DIAGNOSTIC_ID_LEN); + + const char *message = diagnostic_messages[diag_id]; + assert(message); + + return message; +} + +/** + * Append an error to the given list of diagnostic. + */ +bool +pm_diagnostic_list_append(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id) { + pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) calloc(sizeof(pm_diagnostic_t), 1); + if (diagnostic == NULL) return false; + + *diagnostic = (pm_diagnostic_t) { + .start = start, + .end = end, + .message = pm_diagnostic_message(diag_id), + .owned = false + }; + + pm_list_append(list, (pm_list_node_t *) diagnostic); + return true; +} + +/** + * Append a diagnostic to the given list of diagnostics that is using a format + * string for its message. + */ +bool +pm_diagnostic_list_append_format(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id, ...) { + va_list arguments; + va_start(arguments, diag_id); + + const char *format = pm_diagnostic_message(diag_id); + int result = vsnprintf(NULL, 0, format, arguments); + va_end(arguments); + + if (result < 0) { + return false; + } + + pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) calloc(sizeof(pm_diagnostic_t), 1); + if (diagnostic == NULL) { + return false; + } + + size_t length = (size_t) (result + 1); + char *message = (char *) malloc(length); + if (message == NULL) { + free(diagnostic); + return false; + } + + va_start(arguments, diag_id); + vsnprintf(message, length, format, arguments); + va_end(arguments); + + *diagnostic = (pm_diagnostic_t) { + .start = start, + .end = end, + .message = message, + .owned = true + }; + + pm_list_append(list, (pm_list_node_t *) diagnostic); + return true; +} + +/** + * Deallocate the internal state of the given diagnostic list. + */ +void +pm_diagnostic_list_free(pm_list_t *list) { + pm_list_node_t *node, *next; + + for (node = list->head; node != NULL; node = next) { + next = node->next; + pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) node; + + if (diagnostic->owned) free((void *) diagnostic->message); + free(diagnostic); + } +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_big5.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_big5.c new file mode 100644 index 00000000..5e9924b4 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_big5.c @@ -0,0 +1,116 @@ +#include "prism/enc/pm_encoding.h" + +static size_t +pm_encoding_big5_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters. + if (*b < 0x80) { + return 1; + } + + // These are the double byte characters. + if ((n > 1) && (b[0] >= 0xA1 && b[0] <= 0xFE) && (b[1] >= 0x40 && b[1] <= 0xFE)) { + return 2; + } + + return 0; +} + +static size_t +pm_encoding_big5_star_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters. + if (*b < 0x80) { + return 1; + } + + // These are the double byte characters. + if ((n > 1) && (b[0] >= 0x87 && b[0] <= 0xFE) && + ((b[1] >= 0x40 && b[1] <= 0x7E) || (b[1] >= 0xA1 && b[1] <= 0xFE))) { + return 2; + } + + return 0; +} + +static size_t +pm_encoding_big5_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_big5_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_big5_star_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_big5_star_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_big5_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_big5_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_big5_star_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_big5_star_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static bool +pm_encoding_big5_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_big5_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return false; + } +} + +static bool +pm_encoding_big5_star_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_big5_star_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return false; + } +} + +/** Big5 encoding */ +pm_encoding_t pm_encoding_big5 = { + .name = "big5", + .char_width = pm_encoding_big5_char_width, + .alnum_char = pm_encoding_big5_alnum_char, + .alpha_char = pm_encoding_big5_alpha_char, + .isupper_char = pm_encoding_big5_isupper_char, + .multibyte = true +}; + +/** Big5-HKSCS encoding */ +pm_encoding_t pm_encoding_big5_hkscs = { + .name = "big5-hkscs", + .char_width = pm_encoding_big5_star_char_width, + .alnum_char = pm_encoding_big5_star_alnum_char, + .alpha_char = pm_encoding_big5_star_alpha_char, + .isupper_char = pm_encoding_big5_star_isupper_char, + .multibyte = true +}; + +/** Big5-UAO encoding */ +pm_encoding_t pm_encoding_big5_uao = { + .name = "big5-uao", + .char_width = pm_encoding_big5_star_char_width, + .alnum_char = pm_encoding_big5_star_alnum_char, + .alpha_char = pm_encoding_big5_star_alpha_char, + .isupper_char = pm_encoding_big5_star_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp51932.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp51932.c new file mode 100644 index 00000000..75c9fb82 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp51932.c @@ -0,0 +1,57 @@ +#include "prism/enc/pm_encoding.h" + +static size_t +pm_encoding_cp51932_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters. + if (*b < 0x80) { + return 1; + } + + // These are the double byte characters. + if ( + (n > 1) && + ((b[0] >= 0xa1 && b[0] <= 0xfe) || (b[0] == 0x8e)) && + (b[1] >= 0xa1 && b[1] <= 0xfe) + ) { + return 2; + } + + return 0; +} + +static size_t +pm_encoding_cp51932_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_cp51932_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_cp51932_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_cp51932_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static bool +pm_encoding_cp51932_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_cp51932_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return 0; + } +} + +/** cp51932 encoding */ +pm_encoding_t pm_encoding_cp51932 = { + .name = "cp51932", + .char_width = pm_encoding_cp51932_char_width, + .alnum_char = pm_encoding_cp51932_alnum_char, + .alpha_char = pm_encoding_cp51932_alpha_char, + .isupper_char = pm_encoding_cp51932_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp949.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp949.c new file mode 100644 index 00000000..f3b5a50f --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp949.c @@ -0,0 +1,57 @@ +#include "prism/enc/pm_encoding.h" + +static size_t +pm_encoding_cp949_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters + if (*b < 0x81) { + return 1; + } + + // These are the double byte characters + if ( + (n > 1) && + (b[0] >= 0x81 && b[0] <= 0xfe) && + (b[1] >= 0x41 && b[1] <= 0xfe) + ) { + return 2; + } + + return 0; +} + +static size_t +pm_encoding_cp949_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_cp949_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_cp949_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_cp949_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static bool +pm_encoding_cp949_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_cp949_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return 0; + } +} + +/** cp949 encoding */ +pm_encoding_t pm_encoding_cp949 = { + .name = "cp949", + .char_width = pm_encoding_cp949_char_width, + .alnum_char = pm_encoding_cp949_alnum_char, + .alpha_char = pm_encoding_cp949_alpha_char, + .isupper_char = pm_encoding_cp949_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp950.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp950.c new file mode 100644 index 00000000..1b7a0995 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_cp950.c @@ -0,0 +1,57 @@ +#include "prism/enc/pm_encoding.h" + +static size_t +pm_encoding_cp950_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters + if (*b < 0x80) { + return 1; + } + + // These are the double byte characters + if ( + (n > 1) && + ((b[0] >= 0x81 && b[0] <= 0xFE) && + ((b[1] >= 0x40 && b[1] <= 0x7E) || (b[1] >= 0xA1 && b[1] <= 0xFE))) + ) { + return 2; + } + + return 0; +} + +static size_t +pm_encoding_cp950_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_cp950_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_cp950_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_cp950_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static bool +pm_encoding_cp950_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_cp950_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return 0; + } +} + +/** cp950 encoding */ +pm_encoding_t pm_encoding_cp950 = { + .name = "cp950", + .char_width = pm_encoding_cp950_char_width, + .alnum_char = pm_encoding_cp950_alnum_char, + .alpha_char = pm_encoding_cp950_alpha_char, + .isupper_char = pm_encoding_cp950_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_euc_jp.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_euc_jp.c new file mode 100644 index 00000000..c891326a --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_euc_jp.c @@ -0,0 +1,69 @@ +#include "prism/enc/pm_encoding.h" + +static size_t +pm_encoding_euc_jp_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters. + if (*b < 0x80) { + return 1; + } + + // These are the double byte characters. + if ( + (n > 1) && + ( + ((b[0] == 0x8E) && (b[1] >= 0xA1 && b[1] <= 0xFE)) || + ((b[0] >= 0xA1 && b[0] <= 0xFE) && (b[1] >= 0xA1 && b[1] <= 0xFE)) + ) + ) { + return 2; + } + + // These are the triple byte characters. + if ( + (n > 2) && + (b[0] == 0x8F) && + (b[1] >= 0xA1 && b[2] <= 0xFE) && + (b[2] >= 0xA1 && b[2] <= 0xFE) + ) { + return 3; + } + + return 0; +} + +static size_t +pm_encoding_euc_jp_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_euc_jp_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_euc_jp_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_euc_jp_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static bool +pm_encoding_euc_jp_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_euc_jp_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return 0; + } +} + +/** EUC-JP encoding */ +pm_encoding_t pm_encoding_euc_jp = { + .name = "euc-jp", + .char_width = pm_encoding_euc_jp_char_width, + .alnum_char = pm_encoding_euc_jp_alnum_char, + .alpha_char = pm_encoding_euc_jp_alpha_char, + .isupper_char = pm_encoding_euc_jp_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_gbk.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_gbk.c new file mode 100644 index 00000000..0ee98ba3 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_gbk.c @@ -0,0 +1,65 @@ +#include "prism/enc/pm_encoding.h" + +static size_t +pm_encoding_gbk_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters. + if (*b <= 0x80) { + return 1; + } + + // These are the double byte characters. + if ( + (n > 1) && + ( + ((b[0] >= 0xA1 && b[0] <= 0xA9) && (b[1] >= 0xA1 && b[1] <= 0xFE)) || // GBK/1 + ((b[0] >= 0xB0 && b[0] <= 0xF7) && (b[1] >= 0xA1 && b[1] <= 0xFE)) || // GBK/2 + ((b[0] >= 0x81 && b[0] <= 0xA0) && (b[1] >= 0x40 && b[1] <= 0xFE) && (b[1] != 0x7F)) || // GBK/3 + ((b[0] >= 0xAA && b[0] <= 0xFE) && (b[1] >= 0x40 && b[1] <= 0xA0) && (b[1] != 0x7F)) || // GBK/4 + ((b[0] >= 0xA8 && b[0] <= 0xA9) && (b[1] >= 0x40 && b[1] <= 0xA0) && (b[1] != 0x7F)) || // GBK/5 + ((b[0] >= 0xAA && b[0] <= 0xAF) && (b[1] >= 0xA1 && b[1] <= 0xFE)) || // user-defined 1 + ((b[0] >= 0xF8 && b[0] <= 0xFE) && (b[1] >= 0xA1 && b[1] <= 0xFE)) || // user-defined 2 + ((b[0] >= 0xA1 && b[0] <= 0xA7) && (b[1] >= 0x40 && b[1] <= 0xA0) && (b[1] != 0x7F)) // user-defined 3 + ) + ) { + return 2; + } + + return 0; +} + +static size_t +pm_encoding_gbk_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_gbk_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_gbk_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_gbk_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static bool +pm_encoding_gbk_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_gbk_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return false; + } +} + +/** GBK encoding */ +pm_encoding_t pm_encoding_gbk = { + .name = "gbk", + .char_width = pm_encoding_gbk_char_width, + .alnum_char = pm_encoding_gbk_alnum_char, + .alpha_char = pm_encoding_gbk_alpha_char, + .isupper_char = pm_encoding_gbk_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_mac_japanese.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_mac_japanese.c new file mode 100644 index 00000000..a5185f0e --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_mac_japanese.c @@ -0,0 +1,57 @@ +#include "prism/enc/pm_encoding.h" + +static size_t +pm_encoding_mac_japanese_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters. + if (*b < 0x80 || (*b >= 0xA1 && *b <= 0xDF)) { + return 1; + } + + // These are the double byte characters. + if ( + (n > 1) && + ((b[0] >= 0x81 && b[0] <= 0x9F) || (b[0] >= 0xE0 && b[0] <= 0xFC)) && + (b[1] >= 0x40 && b[1] <= 0xFC) + ) { + return 2; + } + + return 0; +} + +static size_t +pm_encoding_mac_japanese_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_mac_japanese_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_mac_japanese_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_mac_japanese_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static bool +pm_encoding_mac_japanese_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_mac_japanese_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return 0; + } +} + +/** MacJapanese encoding */ +pm_encoding_t pm_encoding_mac_japanese = { + .name = "MacJapanese", + .char_width = pm_encoding_mac_japanese_char_width, + .alnum_char = pm_encoding_mac_japanese_alnum_char, + .alpha_char = pm_encoding_mac_japanese_alpha_char, + .isupper_char = pm_encoding_mac_japanese_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_shift_jis.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_shift_jis.c new file mode 100644 index 00000000..f92956e0 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_shift_jis.c @@ -0,0 +1,57 @@ +#include "prism/enc/pm_encoding.h" + +static size_t +pm_encoding_shift_jis_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters. + if (*b < 0x80 || (*b >= 0xA1 && *b <= 0xDF)) { + return 1; + } + + // These are the double byte characters. + if ( + (n > 1) && + ((b[0] >= 0x81 && b[0] <= 0x9F) || (b[0] >= 0xE0 && b[0] <= 0xFC)) && + (b[1] >= 0x40 && b[1] <= 0xFC) + ) { + return 2; + } + + return 0; +} + +static size_t +pm_encoding_shift_jis_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_shift_jis_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_shift_jis_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_shift_jis_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static bool +pm_encoding_shift_jis_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_shift_jis_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return 0; + } +} + +/** Shift_JIS encoding */ +pm_encoding_t pm_encoding_shift_jis = { + .name = "shift_jis", + .char_width = pm_encoding_shift_jis_char_width, + .alnum_char = pm_encoding_shift_jis_alnum_char, + .alpha_char = pm_encoding_shift_jis_alpha_char, + .isupper_char = pm_encoding_shift_jis_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_tables.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_tables.c new file mode 100644 index 00000000..6eede35e --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_tables.c @@ -0,0 +1,2108 @@ +#include "prism/enc/pm_encoding.h" + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ASCII character. + */ +static uint8_t pm_encoding_ascii_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding CP850 character. + */ +static uint8_t pm_encoding_cp850_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding CP852 character. + */ +static uint8_t pm_encoding_cp852_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding CP855 character. + */ +static uint8_t pm_encoding_cp855_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding GB1988 character. + */ +static uint8_t pm_encoding_gb1988_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM437 character. + */ +static uint8_t pm_encoding_ibm437_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM720 character. + */ +static uint8_t pm_encoding_ibm720_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM737 character. + */ +static uint8_t pm_encoding_ibm737_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM775 character. + */ +static uint8_t pm_encoding_ibm775_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM852 character. + */ +static uint8_t pm_encoding_ibm852_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM855 character. + */ +static uint8_t pm_encoding_ibm855_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM857 character. + */ +static uint8_t pm_encoding_ibm857_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM860 character. + */ +static uint8_t pm_encoding_ibm860_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM861 character. + */ +static uint8_t pm_encoding_ibm861_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM862 character. + */ +static uint8_t pm_encoding_ibm862_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM863 character. + */ +static uint8_t pm_encoding_ibm863_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM864 character. + */ +static uint8_t pm_encoding_ibm864_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM865 character. + */ +static uint8_t pm_encoding_ibm865_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM866 character. + */ +static uint8_t pm_encoding_ibm866_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding IBM869 character. + */ +static uint8_t pm_encoding_ibm869_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-1 character. + */ +static uint8_t pm_encoding_iso_8859_1_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-2 character. + */ +static uint8_t pm_encoding_iso_8859_2_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 0, 7, 0, 7, 7, 0, 0, 7, 7, 7, 7, 0, 7, 7, // Ax + 0, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-3 character. + */ +static uint8_t pm_encoding_iso_8859_3_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 0, 0, 0, 0, 7, 0, 0, 7, 7, 7, 7, 0, 0, 7, // Ax + 0, 3, 0, 0, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0, 0, 3, // Bx + 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 0, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-4 character. + */ +static uint8_t pm_encoding_iso_8859_4_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 3, 7, 0, 7, 7, 0, 0, 7, 7, 7, 7, 0, 7, 0, // Ax + 0, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 7, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-5 character. + */ +static uint8_t pm_encoding_iso_8859_5_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, // Ax + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-6 character. + */ +static uint8_t pm_encoding_iso_8859_6_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-7 character. + */ +static uint8_t pm_encoding_iso_8859_7_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, // Bx + 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-8 character. + */ +static uint8_t pm_encoding_iso_8859_8_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-9 character. + */ +static uint8_t pm_encoding_iso_8859_9_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-10 character. + */ +static uint8_t pm_encoding_iso_8859_10_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 0, 7, 7, // Ax + 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-11 character. + */ +static uint8_t pm_encoding_iso_8859_11_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ax + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Bx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-13 character. + */ +static uint8_t pm_encoding_iso_8859_13_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-14 character. + */ +static uint8_t pm_encoding_iso_8859_14_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 3, 0, 7, 3, 7, 0, 7, 0, 7, 3, 7, 0, 0, 7, // Ax + 7, 3, 7, 3, 7, 3, 0, 7, 3, 3, 3, 7, 3, 7, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-15 character. + */ +static uint8_t pm_encoding_iso_8859_15_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 7, 0, 3, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 7, 3, 0, 0, 3, 0, 3, 0, 7, 3, 7, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding ISO-8859-16 character. + */ +static uint8_t pm_encoding_iso_8859_16_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 3, 7, 0, 0, 7, 0, 3, 0, 7, 0, 7, 0, 3, 7, // Ax + 0, 0, 7, 3, 7, 0, 0, 0, 3, 3, 3, 0, 7, 3, 7, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding KOI8-R character. + */ +static uint8_t pm_encoding_koi8_r_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Dx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Ex + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding KOI8-U character. + */ +static uint8_t pm_encoding_koi8_u_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 3, 3, 0, 3, 3, 0, 0, 0, 0, 0, 3, 0, 0, // Ax + 0, 0, 0, 7, 7, 0, 7, 7, 0, 0, 0, 0, 0, 7, 0, 0, // Bx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Dx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Ex + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macCentEuro character. + */ +static uint8_t pm_encoding_mac_cent_euro_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macCroatian character. + */ +static uint8_t pm_encoding_mac_croatian_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + + /** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macCyrillic character. + */ +static uint8_t pm_encoding_mac_cyrillic_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macGreek character. + */ +static uint8_t pm_encoding_mac_greek_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macIceland character. + */ +static uint8_t pm_encoding_mac_iceland_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macRoman character. + */ +static uint8_t pm_encoding_mac_roman_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macRomania character. + */ +static uint8_t pm_encoding_mac_romania_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macThai character. + */ +static uint8_t pm_encoding_mac_thai_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding TIS-620 character. + */ +static uint8_t pm_encoding_tis_620_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ax + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Bx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macTurkish character. + */ +static uint8_t pm_encoding_mac_turkish_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding macUkraine character. + */ +static uint8_t pm_encoding_mac_ukraine_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-1250 character. + */ +static uint8_t pm_encoding_windows_1250_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 7, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 3, 3, 3, // 9x + 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, 0, 0, 0, 0, 7, // Ax + 0, 0, 0, 3, 0, 3, 0, 0, 0, 3, 3, 0, 7, 0, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-1251 character. + */ +static uint8_t pm_encoding_windows_1251_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 7, 7, 0, 3, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 7, // 8x + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 3, 3, 3, // 9x + 0, 7, 3, 7, 0, 7, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax + 0, 0, 7, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 7, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-1252 character. + */ +static uint8_t pm_encoding_windows_1252_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 7, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 7, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-1253 character. + */ +static uint8_t pm_encoding_windows_1253_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, // Bx + 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-1254 character. + */ +static uint8_t pm_encoding_windows_1254_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 7, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-1255 character. + */ +static uint8_t pm_encoding_windows_1255_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-1256 character. + */ +static uint8_t pm_encoding_windows_1256_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-1257 character. + */ +static uint8_t pm_encoding_windows_1257_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-1258 character. + */ +static uint8_t pm_encoding_windows_1258_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding windows-874 character. + */ +static uint8_t pm_encoding_windows_874_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +/** + * Returns the size of the next character in the ASCII encoding. This basically + * means that if the top bit is not set, the character is 1 byte long. + */ +static size_t +pm_encoding_ascii_char_width(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { + return *b < 0x80 ? 1 : 0; +} + +/** + * Return the size of the next character in the ASCII encoding if it is an + * alphabetical character. + */ +size_t +pm_encoding_ascii_alpha_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { + return (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT); +} + +/** + * Return the size of the next character in the ASCII encoding if it is an + * alphanumeric character. + */ +size_t +pm_encoding_ascii_alnum_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { + return (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +/** + * Return true if the next character in the ASCII encoding if it is an uppercase + * character. + */ +bool +pm_encoding_ascii_isupper_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { + return (pm_encoding_ascii_table[*b] & PRISM_ENCODING_UPPERCASE_BIT); +} + +/** + * For a lot of encodings the default is that they are a single byte long no + * matter what the codepoint, so this function is shared between them. + */ +static size_t +pm_encoding_single_char_width(PRISM_ATTRIBUTE_UNUSED const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { + return 1; +} + +/** + * Returns the size of the next character in the KOI-8 encoding. This means + * checking if it's a valid codepoint in KOI-8 and if it is returning 1. + */ +static size_t +pm_encoding_koi8_char_width(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { + return ((*b >= 0x20 && *b <= 0x7E) || (*b >= 0x80)) ? 1 : 0; +} + +#define PRISM_ENCODING_TABLE(name) \ + static size_t pm_encoding_ ##name ## _alpha_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { \ + return (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHABETIC_BIT); \ + } \ + static size_t pm_encoding_ ##name ## _alnum_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { \ + return (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; \ + } \ + static bool pm_encoding_ ##name ## _isupper_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { \ + return (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_UPPERCASE_BIT); \ + } + +PRISM_ENCODING_TABLE(cp850) +PRISM_ENCODING_TABLE(cp852) +PRISM_ENCODING_TABLE(cp855) +PRISM_ENCODING_TABLE(gb1988) +PRISM_ENCODING_TABLE(ibm437) +PRISM_ENCODING_TABLE(ibm720) +PRISM_ENCODING_TABLE(ibm737) +PRISM_ENCODING_TABLE(ibm775) +PRISM_ENCODING_TABLE(ibm852) +PRISM_ENCODING_TABLE(ibm855) +PRISM_ENCODING_TABLE(ibm857) +PRISM_ENCODING_TABLE(ibm860) +PRISM_ENCODING_TABLE(ibm861) +PRISM_ENCODING_TABLE(ibm862) +PRISM_ENCODING_TABLE(ibm863) +PRISM_ENCODING_TABLE(ibm864) +PRISM_ENCODING_TABLE(ibm865) +PRISM_ENCODING_TABLE(ibm866) +PRISM_ENCODING_TABLE(ibm869) +PRISM_ENCODING_TABLE(iso_8859_1) +PRISM_ENCODING_TABLE(iso_8859_2) +PRISM_ENCODING_TABLE(iso_8859_3) +PRISM_ENCODING_TABLE(iso_8859_4) +PRISM_ENCODING_TABLE(iso_8859_5) +PRISM_ENCODING_TABLE(iso_8859_6) +PRISM_ENCODING_TABLE(iso_8859_7) +PRISM_ENCODING_TABLE(iso_8859_8) +PRISM_ENCODING_TABLE(iso_8859_9) +PRISM_ENCODING_TABLE(iso_8859_10) +PRISM_ENCODING_TABLE(iso_8859_11) +PRISM_ENCODING_TABLE(iso_8859_13) +PRISM_ENCODING_TABLE(iso_8859_14) +PRISM_ENCODING_TABLE(iso_8859_15) +PRISM_ENCODING_TABLE(iso_8859_16) +PRISM_ENCODING_TABLE(koi8_r) +PRISM_ENCODING_TABLE(koi8_u) +PRISM_ENCODING_TABLE(mac_cent_euro) +PRISM_ENCODING_TABLE(mac_croatian) +PRISM_ENCODING_TABLE(mac_cyrillic) +PRISM_ENCODING_TABLE(mac_greek) +PRISM_ENCODING_TABLE(mac_iceland) +PRISM_ENCODING_TABLE(mac_roman) +PRISM_ENCODING_TABLE(mac_romania) +PRISM_ENCODING_TABLE(mac_thai) +PRISM_ENCODING_TABLE(mac_turkish) +PRISM_ENCODING_TABLE(mac_ukraine) +PRISM_ENCODING_TABLE(tis_620) +PRISM_ENCODING_TABLE(windows_1250) +PRISM_ENCODING_TABLE(windows_1251) +PRISM_ENCODING_TABLE(windows_1252) +PRISM_ENCODING_TABLE(windows_1253) +PRISM_ENCODING_TABLE(windows_1254) +PRISM_ENCODING_TABLE(windows_1255) +PRISM_ENCODING_TABLE(windows_1256) +PRISM_ENCODING_TABLE(windows_1257) +PRISM_ENCODING_TABLE(windows_1258) +PRISM_ENCODING_TABLE(windows_874) + +#undef PRISM_ENCODING_TABLE + +/** US-ASCII encoding */ +pm_encoding_t pm_encoding_ascii = { + .name = "US-ASCII", + .char_width = pm_encoding_ascii_char_width, + .alnum_char = pm_encoding_ascii_alnum_char, + .alpha_char = pm_encoding_ascii_alpha_char, + .isupper_char = pm_encoding_ascii_isupper_char, + .multibyte = false +}; + +/** ASCII-8BIT encoding */ +pm_encoding_t pm_encoding_ascii_8bit = { + .name = "ASCII-8BIT", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ascii_alnum_char, + .alpha_char = pm_encoding_ascii_alpha_char, + .isupper_char = pm_encoding_ascii_isupper_char, + .multibyte = false +}; + +/** CP850 */ +pm_encoding_t pm_encoding_cp850 = { + .name = "CP850", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_cp850_alnum_char, + .alpha_char = pm_encoding_cp850_alpha_char, + .isupper_char = pm_encoding_cp850_isupper_char, + .multibyte = false +}; + +/** CP852 */ +pm_encoding_t pm_encoding_cp852 = { + .name = "CP852", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_cp852_alnum_char, + .alpha_char = pm_encoding_cp852_alpha_char, + .isupper_char = pm_encoding_cp852_isupper_char, + .multibyte = false +}; + +/** CP855 */ +pm_encoding_t pm_encoding_cp855 = { + .name = "CP855", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_cp855_alnum_char, + .alpha_char = pm_encoding_cp855_alpha_char, + .isupper_char = pm_encoding_cp855_isupper_char, + .multibyte = false +}; + +/** GB1988 */ +pm_encoding_t pm_encoding_gb1988 = { + .name = "GB1988", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_gb1988_alnum_char, + .alpha_char = pm_encoding_gb1988_alpha_char, + .isupper_char = pm_encoding_gb1988_isupper_char, + .multibyte = false +}; + +/** IBM437 */ +pm_encoding_t pm_encoding_ibm437 = { + .name = "IBM437", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm437_alnum_char, + .alpha_char = pm_encoding_ibm437_alpha_char, + .isupper_char = pm_encoding_ibm437_isupper_char, + .multibyte = false +}; + +/** IBM720 */ +pm_encoding_t pm_encoding_ibm720 = { + .name = "IBM720", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm720_alnum_char, + .alpha_char = pm_encoding_ibm720_alpha_char, + .isupper_char = pm_encoding_ibm720_isupper_char, + .multibyte = false +}; + +/** IBM737 */ +pm_encoding_t pm_encoding_ibm737 = { + .name = "IBM737", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm737_alnum_char, + .alpha_char = pm_encoding_ibm737_alpha_char, + .isupper_char = pm_encoding_ibm737_isupper_char, + .multibyte = false +}; + +/** IBM775 */ +pm_encoding_t pm_encoding_ibm775 = { + .name = "IBM775", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm775_alnum_char, + .alpha_char = pm_encoding_ibm775_alpha_char, + .isupper_char = pm_encoding_ibm775_isupper_char, + .multibyte = false +}; + +/** IBM850 */ +pm_encoding_t pm_encoding_ibm852 = { + .name = "IBM852", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm852_alnum_char, + .alpha_char = pm_encoding_ibm852_alpha_char, + .isupper_char = pm_encoding_ibm852_isupper_char, + .multibyte = false +}; + +/** IBM855 */ +pm_encoding_t pm_encoding_ibm855 = { + .name = "IBM855", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm855_alnum_char, + .alpha_char = pm_encoding_ibm855_alpha_char, + .isupper_char = pm_encoding_ibm855_isupper_char, + .multibyte = false +}; + +/** IBM857 */ +pm_encoding_t pm_encoding_ibm857 = { + .name = "IBM857", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm857_alnum_char, + .alpha_char = pm_encoding_ibm857_alpha_char, + .isupper_char = pm_encoding_ibm857_isupper_char, + .multibyte = false +}; + +/** IBM860 */ +pm_encoding_t pm_encoding_ibm860 = { + .name = "IBM860", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm860_alnum_char, + .alpha_char = pm_encoding_ibm860_alpha_char, + .isupper_char = pm_encoding_ibm860_isupper_char, + .multibyte = false +}; + +/** IBM861 */ +pm_encoding_t pm_encoding_ibm861 = { + .name = "IBM861", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm861_alnum_char, + .alpha_char = pm_encoding_ibm861_alpha_char, + .isupper_char = pm_encoding_ibm861_isupper_char, + .multibyte = false +}; + +/** IBM862 */ +pm_encoding_t pm_encoding_ibm862 = { + .name = "IBM862", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm862_alnum_char, + .alpha_char = pm_encoding_ibm862_alpha_char, + .isupper_char = pm_encoding_ibm862_isupper_char, + .multibyte = false +}; + +/** IBM863 */ +pm_encoding_t pm_encoding_ibm863 = { + .name = "IBM863", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm863_alnum_char, + .alpha_char = pm_encoding_ibm863_alpha_char, + .isupper_char = pm_encoding_ibm863_isupper_char, + .multibyte = false +}; + +/** IBM864 */ +pm_encoding_t pm_encoding_ibm864 = { + .name = "IBM864", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm864_alnum_char, + .alpha_char = pm_encoding_ibm864_alpha_char, + .isupper_char = pm_encoding_ibm864_isupper_char, + .multibyte = false +}; + +/** IBM866 */ +pm_encoding_t pm_encoding_ibm865 = { + .name = "IBM865", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm865_alnum_char, + .alpha_char = pm_encoding_ibm865_alpha_char, + .isupper_char = pm_encoding_ibm865_isupper_char, + .multibyte = false +}; + +/** IBM866 */ +pm_encoding_t pm_encoding_ibm866 = { + .name = "IBM866", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm866_alnum_char, + .alpha_char = pm_encoding_ibm866_alpha_char, + .isupper_char = pm_encoding_ibm866_isupper_char, + .multibyte = false +}; + +/** IBM869 */ +pm_encoding_t pm_encoding_ibm869 = { + .name = "IBM869", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_ibm869_alnum_char, + .alpha_char = pm_encoding_ibm869_alpha_char, + .isupper_char = pm_encoding_ibm869_isupper_char, + .multibyte = false +}; + +/** ISO-8859-1 */ +pm_encoding_t pm_encoding_iso_8859_1 = { + .name = "ISO-8859-1", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_1_alnum_char, + .alpha_char = pm_encoding_iso_8859_1_alpha_char, + .isupper_char = pm_encoding_iso_8859_1_isupper_char, + .multibyte = false +}; + +/** ISO-8859-2 */ +pm_encoding_t pm_encoding_iso_8859_2 = { + .name = "ISO-8859-2", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_2_alnum_char, + .alpha_char = pm_encoding_iso_8859_2_alpha_char, + .isupper_char = pm_encoding_iso_8859_2_isupper_char, + .multibyte = false +}; + +/** ISO-8859-3 */ +pm_encoding_t pm_encoding_iso_8859_3 = { + .name = "ISO-8859-3", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_3_alnum_char, + .alpha_char = pm_encoding_iso_8859_3_alpha_char, + .isupper_char = pm_encoding_iso_8859_3_isupper_char, + .multibyte = false +}; + +/** ISO-8859-4 */ +pm_encoding_t pm_encoding_iso_8859_4 = { + .name = "ISO-8859-4", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_4_alnum_char, + .alpha_char = pm_encoding_iso_8859_4_alpha_char, + .isupper_char = pm_encoding_iso_8859_4_isupper_char, + .multibyte = false +}; + +/** ISO-8859-5 */ +pm_encoding_t pm_encoding_iso_8859_5 = { + .name = "ISO-8859-5", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_5_alnum_char, + .alpha_char = pm_encoding_iso_8859_5_alpha_char, + .isupper_char = pm_encoding_iso_8859_5_isupper_char, + .multibyte = false +}; + +/** ISO-8859-6 */ +pm_encoding_t pm_encoding_iso_8859_6 = { + .name = "ISO-8859-6", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_6_alnum_char, + .alpha_char = pm_encoding_iso_8859_6_alpha_char, + .isupper_char = pm_encoding_iso_8859_6_isupper_char, + .multibyte = false +}; + +/** ISO-8859-7 */ +pm_encoding_t pm_encoding_iso_8859_7 = { + .name = "ISO-8859-7", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_7_alnum_char, + .alpha_char = pm_encoding_iso_8859_7_alpha_char, + .isupper_char = pm_encoding_iso_8859_7_isupper_char, + .multibyte = false +}; + +/** ISO-8859-8 */ +pm_encoding_t pm_encoding_iso_8859_8 = { + .name = "ISO-8859-8", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_8_alnum_char, + .alpha_char = pm_encoding_iso_8859_8_alpha_char, + .isupper_char = pm_encoding_iso_8859_8_isupper_char, + .multibyte = false +}; + +/** ISO-8859-9 */ +pm_encoding_t pm_encoding_iso_8859_9 = { + .name = "ISO-8859-9", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_9_alnum_char, + .alpha_char = pm_encoding_iso_8859_9_alpha_char, + .isupper_char = pm_encoding_iso_8859_9_isupper_char, + .multibyte = false +}; + +/** ISO-8859-10 */ +pm_encoding_t pm_encoding_iso_8859_10 = { + .name = "ISO-8859-10", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_10_alnum_char, + .alpha_char = pm_encoding_iso_8859_10_alpha_char, + .isupper_char = pm_encoding_iso_8859_10_isupper_char, + .multibyte = false +}; + +/** ISO-8859-11 */ +pm_encoding_t pm_encoding_iso_8859_11 = { + .name = "ISO-8859-11", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_11_alnum_char, + .alpha_char = pm_encoding_iso_8859_11_alpha_char, + .isupper_char = pm_encoding_iso_8859_11_isupper_char, + .multibyte = false +}; + +/** ISO-8859-13 */ +pm_encoding_t pm_encoding_iso_8859_13 = { + .name = "ISO-8859-13", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_13_alnum_char, + .alpha_char = pm_encoding_iso_8859_13_alpha_char, + .isupper_char = pm_encoding_iso_8859_13_isupper_char, + .multibyte = false +}; + +/** ISO-8859-14 */ +pm_encoding_t pm_encoding_iso_8859_14 = { + .name = "ISO-8859-14", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_14_alnum_char, + .alpha_char = pm_encoding_iso_8859_14_alpha_char, + .isupper_char = pm_encoding_iso_8859_14_isupper_char, + .multibyte = false +}; + +/** ISO-8859-15 */ +pm_encoding_t pm_encoding_iso_8859_15 = { + .name = "ISO-8859-15", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_15_alnum_char, + .alpha_char = pm_encoding_iso_8859_15_alpha_char, + .isupper_char = pm_encoding_iso_8859_15_isupper_char, + .multibyte = false +}; + +/** ISO-8859-16 */ +pm_encoding_t pm_encoding_iso_8859_16 = { + .name = "ISO-8859-16", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_iso_8859_16_alnum_char, + .alpha_char = pm_encoding_iso_8859_16_alpha_char, + .isupper_char = pm_encoding_iso_8859_16_isupper_char, + .multibyte = false +}; + +/** KOI8-R */ +pm_encoding_t pm_encoding_koi8_r = { + .name = "KOI8-R", + .char_width = pm_encoding_koi8_char_width, + .alnum_char = pm_encoding_koi8_r_alnum_char, + .alpha_char = pm_encoding_koi8_r_alpha_char, + .isupper_char = pm_encoding_koi8_r_isupper_char, + .multibyte = false +}; + +/** KOI8-U */ +pm_encoding_t pm_encoding_koi8_u = { + .name = "KOI8-U", + .char_width = pm_encoding_koi8_char_width, + .alnum_char = pm_encoding_koi8_u_alnum_char, + .alpha_char = pm_encoding_koi8_u_alpha_char, + .isupper_char = pm_encoding_koi8_u_isupper_char, + .multibyte = false +}; + +/** macCentEuro */ +pm_encoding_t pm_encoding_mac_cent_euro = { + .name = "macCentEuro", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_cent_euro_alnum_char, + .alpha_char = pm_encoding_mac_cent_euro_alpha_char, + .isupper_char = pm_encoding_mac_cent_euro_isupper_char, + .multibyte = false +}; + +/** macCroatian */ +pm_encoding_t pm_encoding_mac_croatian = { + .name = "macCroatian", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_croatian_alnum_char, + .alpha_char = pm_encoding_mac_croatian_alpha_char, + .isupper_char = pm_encoding_mac_croatian_isupper_char, + .multibyte = false +}; + +/** macCyrillic */ +pm_encoding_t pm_encoding_mac_cyrillic = { + .name = "macCyrillic", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_cyrillic_alnum_char, + .alpha_char = pm_encoding_mac_cyrillic_alpha_char, + .isupper_char = pm_encoding_mac_cyrillic_isupper_char, + .multibyte = false +}; + +/** macGreek */ +pm_encoding_t pm_encoding_mac_greek = { + .name = "macGreek", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_greek_alnum_char, + .alpha_char = pm_encoding_mac_greek_alpha_char, + .isupper_char = pm_encoding_mac_greek_isupper_char, + .multibyte = false +}; + +/** macIceland */ +pm_encoding_t pm_encoding_mac_iceland = { + .name = "macIceland", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_iceland_alnum_char, + .alpha_char = pm_encoding_mac_iceland_alpha_char, + .isupper_char = pm_encoding_mac_iceland_isupper_char, + .multibyte = false +}; + +/** macRoman */ +pm_encoding_t pm_encoding_mac_roman = { + .name = "macRoman", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_roman_alnum_char, + .alpha_char = pm_encoding_mac_roman_alpha_char, + .isupper_char = pm_encoding_mac_roman_isupper_char, + .multibyte = false +}; + +/** macRomania */ +pm_encoding_t pm_encoding_mac_romania = { + .name = "macRomania", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_romania_alnum_char, + .alpha_char = pm_encoding_mac_romania_alpha_char, + .isupper_char = pm_encoding_mac_romania_isupper_char, + .multibyte = false +}; + +/** macThai */ +pm_encoding_t pm_encoding_mac_thai = { + .name = "macThai", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_thai_alnum_char, + .alpha_char = pm_encoding_mac_thai_alpha_char, + .isupper_char = pm_encoding_mac_thai_isupper_char, + .multibyte = false +}; + +/** macTurkish */ +pm_encoding_t pm_encoding_mac_turkish = { + .name = "macTurkish", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_turkish_alnum_char, + .alpha_char = pm_encoding_mac_turkish_alpha_char, + .isupper_char = pm_encoding_mac_turkish_isupper_char, + .multibyte = false +}; + +/** macUkraine */ +pm_encoding_t pm_encoding_mac_ukraine = { + .name = "macUkraine", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_mac_ukraine_alnum_char, + .alpha_char = pm_encoding_mac_ukraine_alpha_char, + .isupper_char = pm_encoding_mac_ukraine_isupper_char, + .multibyte = false +}; + +/** TIS-620 */ +pm_encoding_t pm_encoding_tis_620 = { + .name = "TIS-620", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_tis_620_alnum_char, + .alpha_char = pm_encoding_tis_620_alpha_char, + .isupper_char = pm_encoding_tis_620_isupper_char, + .multibyte = false +}; + +/** Windows-1250 */ +pm_encoding_t pm_encoding_windows_1250 = { + .name = "Windows-1250", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_1250_alnum_char, + .alpha_char = pm_encoding_windows_1250_alpha_char, + .isupper_char = pm_encoding_windows_1250_isupper_char, + .multibyte = false +}; + +/** Windows-1251 */ +pm_encoding_t pm_encoding_windows_1251 = { + .name = "Windows-1251", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_1251_alnum_char, + .alpha_char = pm_encoding_windows_1251_alpha_char, + .isupper_char = pm_encoding_windows_1251_isupper_char, + .multibyte = false +}; + +/** Windows-1252 */ +pm_encoding_t pm_encoding_windows_1252 = { + .name = "Windows-1252", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_1252_alnum_char, + .alpha_char = pm_encoding_windows_1252_alpha_char, + .isupper_char = pm_encoding_windows_1252_isupper_char, + .multibyte = false +}; + +/** Windows-1253 */ +pm_encoding_t pm_encoding_windows_1253 = { + .name = "Windows-1253", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_1253_alnum_char, + .alpha_char = pm_encoding_windows_1253_alpha_char, + .isupper_char = pm_encoding_windows_1253_isupper_char, + .multibyte = false +}; + +/** Windows-1254 */ +pm_encoding_t pm_encoding_windows_1254 = { + .name = "Windows-1254", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_1254_alnum_char, + .alpha_char = pm_encoding_windows_1254_alpha_char, + .isupper_char = pm_encoding_windows_1254_isupper_char, + .multibyte = false +}; + +/** Windows-1255 */ +pm_encoding_t pm_encoding_windows_1255 = { + .name = "Windows-1255", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_1255_alnum_char, + .alpha_char = pm_encoding_windows_1255_alpha_char, + .isupper_char = pm_encoding_windows_1255_isupper_char, + .multibyte = false +}; + +/** Windows-1256 */ +pm_encoding_t pm_encoding_windows_1256 = { + .name = "Windows-1256", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_1256_alnum_char, + .alpha_char = pm_encoding_windows_1256_alpha_char, + .isupper_char = pm_encoding_windows_1256_isupper_char, + .multibyte = false +}; + +/** Windows-1257 */ +pm_encoding_t pm_encoding_windows_1257 = { + .name = "Windows-1257", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_1257_alnum_char, + .alpha_char = pm_encoding_windows_1257_alpha_char, + .isupper_char = pm_encoding_windows_1257_isupper_char, + .multibyte = false +}; + +/** Windows-1258 */ +pm_encoding_t pm_encoding_windows_1258 = { + .name = "Windows-1258", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_1258_alnum_char, + .alpha_char = pm_encoding_windows_1258_alpha_char, + .isupper_char = pm_encoding_windows_1258_isupper_char, + .multibyte = false +}; + +/** Windows-874 */ +pm_encoding_t pm_encoding_windows_874 = { + .name = "Windows-874", + .char_width = pm_encoding_single_char_width, + .alnum_char = pm_encoding_windows_874_alnum_char, + .alpha_char = pm_encoding_windows_874_alpha_char, + .isupper_char = pm_encoding_windows_874_isupper_char, + .multibyte = false +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_unicode.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_unicode.c new file mode 100644 index 00000000..41c1f258 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_unicode.c @@ -0,0 +1,2369 @@ +#include "prism/enc/pm_encoding.h" + +typedef uint32_t pm_unicode_codepoint_t; + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding unicode codepoint. Note that + * this table is different from other encodings where we used a lookup table + * because the indices of those tables are the byte representations, not the + * codepoints themselves. + */ +const uint8_t pm_encoding_unicode_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +#define UNICODE_ALPHA_CODEPOINTS_LENGTH 1450 +static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEPOINTS_LENGTH] = { + 0x100, 0x2C1, + 0x2C6, 0x2D1, + 0x2E0, 0x2E4, + 0x2EC, 0x2EC, + 0x2EE, 0x2EE, + 0x345, 0x345, + 0x370, 0x374, + 0x376, 0x377, + 0x37A, 0x37D, + 0x37F, 0x37F, + 0x386, 0x386, + 0x388, 0x38A, + 0x38C, 0x38C, + 0x38E, 0x3A1, + 0x3A3, 0x3F5, + 0x3F7, 0x481, + 0x48A, 0x52F, + 0x531, 0x556, + 0x559, 0x559, + 0x560, 0x588, + 0x5B0, 0x5BD, + 0x5BF, 0x5BF, + 0x5C1, 0x5C2, + 0x5C4, 0x5C5, + 0x5C7, 0x5C7, + 0x5D0, 0x5EA, + 0x5EF, 0x5F2, + 0x610, 0x61A, + 0x620, 0x657, + 0x659, 0x65F, + 0x66E, 0x6D3, + 0x6D5, 0x6DC, + 0x6E1, 0x6E8, + 0x6ED, 0x6EF, + 0x6FA, 0x6FC, + 0x6FF, 0x6FF, + 0x710, 0x73F, + 0x74D, 0x7B1, + 0x7CA, 0x7EA, + 0x7F4, 0x7F5, + 0x7FA, 0x7FA, + 0x800, 0x817, + 0x81A, 0x82C, + 0x840, 0x858, + 0x860, 0x86A, + 0x870, 0x887, + 0x889, 0x88E, + 0x8A0, 0x8C9, + 0x8D4, 0x8DF, + 0x8E3, 0x8E9, + 0x8F0, 0x93B, + 0x93D, 0x94C, + 0x94E, 0x950, + 0x955, 0x963, + 0x971, 0x983, + 0x985, 0x98C, + 0x98F, 0x990, + 0x993, 0x9A8, + 0x9AA, 0x9B0, + 0x9B2, 0x9B2, + 0x9B6, 0x9B9, + 0x9BD, 0x9C4, + 0x9C7, 0x9C8, + 0x9CB, 0x9CC, + 0x9CE, 0x9CE, + 0x9D7, 0x9D7, + 0x9DC, 0x9DD, + 0x9DF, 0x9E3, + 0x9F0, 0x9F1, + 0x9FC, 0x9FC, + 0xA01, 0xA03, + 0xA05, 0xA0A, + 0xA0F, 0xA10, + 0xA13, 0xA28, + 0xA2A, 0xA30, + 0xA32, 0xA33, + 0xA35, 0xA36, + 0xA38, 0xA39, + 0xA3E, 0xA42, + 0xA47, 0xA48, + 0xA4B, 0xA4C, + 0xA51, 0xA51, + 0xA59, 0xA5C, + 0xA5E, 0xA5E, + 0xA70, 0xA75, + 0xA81, 0xA83, + 0xA85, 0xA8D, + 0xA8F, 0xA91, + 0xA93, 0xAA8, + 0xAAA, 0xAB0, + 0xAB2, 0xAB3, + 0xAB5, 0xAB9, + 0xABD, 0xAC5, + 0xAC7, 0xAC9, + 0xACB, 0xACC, + 0xAD0, 0xAD0, + 0xAE0, 0xAE3, + 0xAF9, 0xAFC, + 0xB01, 0xB03, + 0xB05, 0xB0C, + 0xB0F, 0xB10, + 0xB13, 0xB28, + 0xB2A, 0xB30, + 0xB32, 0xB33, + 0xB35, 0xB39, + 0xB3D, 0xB44, + 0xB47, 0xB48, + 0xB4B, 0xB4C, + 0xB56, 0xB57, + 0xB5C, 0xB5D, + 0xB5F, 0xB63, + 0xB71, 0xB71, + 0xB82, 0xB83, + 0xB85, 0xB8A, + 0xB8E, 0xB90, + 0xB92, 0xB95, + 0xB99, 0xB9A, + 0xB9C, 0xB9C, + 0xB9E, 0xB9F, + 0xBA3, 0xBA4, + 0xBA8, 0xBAA, + 0xBAE, 0xBB9, + 0xBBE, 0xBC2, + 0xBC6, 0xBC8, + 0xBCA, 0xBCC, + 0xBD0, 0xBD0, + 0xBD7, 0xBD7, + 0xC00, 0xC0C, + 0xC0E, 0xC10, + 0xC12, 0xC28, + 0xC2A, 0xC39, + 0xC3D, 0xC44, + 0xC46, 0xC48, + 0xC4A, 0xC4C, + 0xC55, 0xC56, + 0xC58, 0xC5A, + 0xC5D, 0xC5D, + 0xC60, 0xC63, + 0xC80, 0xC83, + 0xC85, 0xC8C, + 0xC8E, 0xC90, + 0xC92, 0xCA8, + 0xCAA, 0xCB3, + 0xCB5, 0xCB9, + 0xCBD, 0xCC4, + 0xCC6, 0xCC8, + 0xCCA, 0xCCC, + 0xCD5, 0xCD6, + 0xCDD, 0xCDE, + 0xCE0, 0xCE3, + 0xCF1, 0xCF3, + 0xD00, 0xD0C, + 0xD0E, 0xD10, + 0xD12, 0xD3A, + 0xD3D, 0xD44, + 0xD46, 0xD48, + 0xD4A, 0xD4C, + 0xD4E, 0xD4E, + 0xD54, 0xD57, + 0xD5F, 0xD63, + 0xD7A, 0xD7F, + 0xD81, 0xD83, + 0xD85, 0xD96, + 0xD9A, 0xDB1, + 0xDB3, 0xDBB, + 0xDBD, 0xDBD, + 0xDC0, 0xDC6, + 0xDCF, 0xDD4, + 0xDD6, 0xDD6, + 0xDD8, 0xDDF, + 0xDF2, 0xDF3, + 0xE01, 0xE3A, + 0xE40, 0xE46, + 0xE4D, 0xE4D, + 0xE81, 0xE82, + 0xE84, 0xE84, + 0xE86, 0xE8A, + 0xE8C, 0xEA3, + 0xEA5, 0xEA5, + 0xEA7, 0xEB9, + 0xEBB, 0xEBD, + 0xEC0, 0xEC4, + 0xEC6, 0xEC6, + 0xECD, 0xECD, + 0xEDC, 0xEDF, + 0xF00, 0xF00, + 0xF40, 0xF47, + 0xF49, 0xF6C, + 0xF71, 0xF83, + 0xF88, 0xF97, + 0xF99, 0xFBC, + 0x1000, 0x1036, + 0x1038, 0x1038, + 0x103B, 0x103F, + 0x1050, 0x108F, + 0x109A, 0x109D, + 0x10A0, 0x10C5, + 0x10C7, 0x10C7, + 0x10CD, 0x10CD, + 0x10D0, 0x10FA, + 0x10FC, 0x1248, + 0x124A, 0x124D, + 0x1250, 0x1256, + 0x1258, 0x1258, + 0x125A, 0x125D, + 0x1260, 0x1288, + 0x128A, 0x128D, + 0x1290, 0x12B0, + 0x12B2, 0x12B5, + 0x12B8, 0x12BE, + 0x12C0, 0x12C0, + 0x12C2, 0x12C5, + 0x12C8, 0x12D6, + 0x12D8, 0x1310, + 0x1312, 0x1315, + 0x1318, 0x135A, + 0x1380, 0x138F, + 0x13A0, 0x13F5, + 0x13F8, 0x13FD, + 0x1401, 0x166C, + 0x166F, 0x167F, + 0x1681, 0x169A, + 0x16A0, 0x16EA, + 0x16EE, 0x16F8, + 0x1700, 0x1713, + 0x171F, 0x1733, + 0x1740, 0x1753, + 0x1760, 0x176C, + 0x176E, 0x1770, + 0x1772, 0x1773, + 0x1780, 0x17B3, + 0x17B6, 0x17C8, + 0x17D7, 0x17D7, + 0x17DC, 0x17DC, + 0x1820, 0x1878, + 0x1880, 0x18AA, + 0x18B0, 0x18F5, + 0x1900, 0x191E, + 0x1920, 0x192B, + 0x1930, 0x1938, + 0x1950, 0x196D, + 0x1970, 0x1974, + 0x1980, 0x19AB, + 0x19B0, 0x19C9, + 0x1A00, 0x1A1B, + 0x1A20, 0x1A5E, + 0x1A61, 0x1A74, + 0x1AA7, 0x1AA7, + 0x1ABF, 0x1AC0, + 0x1ACC, 0x1ACE, + 0x1B00, 0x1B33, + 0x1B35, 0x1B43, + 0x1B45, 0x1B4C, + 0x1B80, 0x1BA9, + 0x1BAC, 0x1BAF, + 0x1BBA, 0x1BE5, + 0x1BE7, 0x1BF1, + 0x1C00, 0x1C36, + 0x1C4D, 0x1C4F, + 0x1C5A, 0x1C7D, + 0x1C80, 0x1C88, + 0x1C90, 0x1CBA, + 0x1CBD, 0x1CBF, + 0x1CE9, 0x1CEC, + 0x1CEE, 0x1CF3, + 0x1CF5, 0x1CF6, + 0x1CFA, 0x1CFA, + 0x1D00, 0x1DBF, + 0x1DE7, 0x1DF4, + 0x1E00, 0x1F15, + 0x1F18, 0x1F1D, + 0x1F20, 0x1F45, + 0x1F48, 0x1F4D, + 0x1F50, 0x1F57, + 0x1F59, 0x1F59, + 0x1F5B, 0x1F5B, + 0x1F5D, 0x1F5D, + 0x1F5F, 0x1F7D, + 0x1F80, 0x1FB4, + 0x1FB6, 0x1FBC, + 0x1FBE, 0x1FBE, + 0x1FC2, 0x1FC4, + 0x1FC6, 0x1FCC, + 0x1FD0, 0x1FD3, + 0x1FD6, 0x1FDB, + 0x1FE0, 0x1FEC, + 0x1FF2, 0x1FF4, + 0x1FF6, 0x1FFC, + 0x2071, 0x2071, + 0x207F, 0x207F, + 0x2090, 0x209C, + 0x2102, 0x2102, + 0x2107, 0x2107, + 0x210A, 0x2113, + 0x2115, 0x2115, + 0x2119, 0x211D, + 0x2124, 0x2124, + 0x2126, 0x2126, + 0x2128, 0x2128, + 0x212A, 0x212D, + 0x212F, 0x2139, + 0x213C, 0x213F, + 0x2145, 0x2149, + 0x214E, 0x214E, + 0x2160, 0x2188, + 0x24B6, 0x24E9, + 0x2C00, 0x2CE4, + 0x2CEB, 0x2CEE, + 0x2CF2, 0x2CF3, + 0x2D00, 0x2D25, + 0x2D27, 0x2D27, + 0x2D2D, 0x2D2D, + 0x2D30, 0x2D67, + 0x2D6F, 0x2D6F, + 0x2D80, 0x2D96, + 0x2DA0, 0x2DA6, + 0x2DA8, 0x2DAE, + 0x2DB0, 0x2DB6, + 0x2DB8, 0x2DBE, + 0x2DC0, 0x2DC6, + 0x2DC8, 0x2DCE, + 0x2DD0, 0x2DD6, + 0x2DD8, 0x2DDE, + 0x2DE0, 0x2DFF, + 0x2E2F, 0x2E2F, + 0x3005, 0x3007, + 0x3021, 0x3029, + 0x3031, 0x3035, + 0x3038, 0x303C, + 0x3041, 0x3096, + 0x309D, 0x309F, + 0x30A1, 0x30FA, + 0x30FC, 0x30FF, + 0x3105, 0x312F, + 0x3131, 0x318E, + 0x31A0, 0x31BF, + 0x31F0, 0x31FF, + 0x3400, 0x4DBF, + 0x4E00, 0xA48C, + 0xA4D0, 0xA4FD, + 0xA500, 0xA60C, + 0xA610, 0xA61F, + 0xA62A, 0xA62B, + 0xA640, 0xA66E, + 0xA674, 0xA67B, + 0xA67F, 0xA6EF, + 0xA717, 0xA71F, + 0xA722, 0xA788, + 0xA78B, 0xA7CA, + 0xA7D0, 0xA7D1, + 0xA7D3, 0xA7D3, + 0xA7D5, 0xA7D9, + 0xA7F2, 0xA805, + 0xA807, 0xA827, + 0xA840, 0xA873, + 0xA880, 0xA8C3, + 0xA8C5, 0xA8C5, + 0xA8F2, 0xA8F7, + 0xA8FB, 0xA8FB, + 0xA8FD, 0xA8FF, + 0xA90A, 0xA92A, + 0xA930, 0xA952, + 0xA960, 0xA97C, + 0xA980, 0xA9B2, + 0xA9B4, 0xA9BF, + 0xA9CF, 0xA9CF, + 0xA9E0, 0xA9EF, + 0xA9FA, 0xA9FE, + 0xAA00, 0xAA36, + 0xAA40, 0xAA4D, + 0xAA60, 0xAA76, + 0xAA7A, 0xAABE, + 0xAAC0, 0xAAC0, + 0xAAC2, 0xAAC2, + 0xAADB, 0xAADD, + 0xAAE0, 0xAAEF, + 0xAAF2, 0xAAF5, + 0xAB01, 0xAB06, + 0xAB09, 0xAB0E, + 0xAB11, 0xAB16, + 0xAB20, 0xAB26, + 0xAB28, 0xAB2E, + 0xAB30, 0xAB5A, + 0xAB5C, 0xAB69, + 0xAB70, 0xABEA, + 0xAC00, 0xD7A3, + 0xD7B0, 0xD7C6, + 0xD7CB, 0xD7FB, + 0xF900, 0xFA6D, + 0xFA70, 0xFAD9, + 0xFB00, 0xFB06, + 0xFB13, 0xFB17, + 0xFB1D, 0xFB28, + 0xFB2A, 0xFB36, + 0xFB38, 0xFB3C, + 0xFB3E, 0xFB3E, + 0xFB40, 0xFB41, + 0xFB43, 0xFB44, + 0xFB46, 0xFBB1, + 0xFBD3, 0xFD3D, + 0xFD50, 0xFD8F, + 0xFD92, 0xFDC7, + 0xFDF0, 0xFDFB, + 0xFE70, 0xFE74, + 0xFE76, 0xFEFC, + 0xFF21, 0xFF3A, + 0xFF41, 0xFF5A, + 0xFF66, 0xFFBE, + 0xFFC2, 0xFFC7, + 0xFFCA, 0xFFCF, + 0xFFD2, 0xFFD7, + 0xFFDA, 0xFFDC, + 0x10000, 0x1000B, + 0x1000D, 0x10026, + 0x10028, 0x1003A, + 0x1003C, 0x1003D, + 0x1003F, 0x1004D, + 0x10050, 0x1005D, + 0x10080, 0x100FA, + 0x10140, 0x10174, + 0x10280, 0x1029C, + 0x102A0, 0x102D0, + 0x10300, 0x1031F, + 0x1032D, 0x1034A, + 0x10350, 0x1037A, + 0x10380, 0x1039D, + 0x103A0, 0x103C3, + 0x103C8, 0x103CF, + 0x103D1, 0x103D5, + 0x10400, 0x1049D, + 0x104B0, 0x104D3, + 0x104D8, 0x104FB, + 0x10500, 0x10527, + 0x10530, 0x10563, + 0x10570, 0x1057A, + 0x1057C, 0x1058A, + 0x1058C, 0x10592, + 0x10594, 0x10595, + 0x10597, 0x105A1, + 0x105A3, 0x105B1, + 0x105B3, 0x105B9, + 0x105BB, 0x105BC, + 0x10600, 0x10736, + 0x10740, 0x10755, + 0x10760, 0x10767, + 0x10780, 0x10785, + 0x10787, 0x107B0, + 0x107B2, 0x107BA, + 0x10800, 0x10805, + 0x10808, 0x10808, + 0x1080A, 0x10835, + 0x10837, 0x10838, + 0x1083C, 0x1083C, + 0x1083F, 0x10855, + 0x10860, 0x10876, + 0x10880, 0x1089E, + 0x108E0, 0x108F2, + 0x108F4, 0x108F5, + 0x10900, 0x10915, + 0x10920, 0x10939, + 0x10980, 0x109B7, + 0x109BE, 0x109BF, + 0x10A00, 0x10A03, + 0x10A05, 0x10A06, + 0x10A0C, 0x10A13, + 0x10A15, 0x10A17, + 0x10A19, 0x10A35, + 0x10A60, 0x10A7C, + 0x10A80, 0x10A9C, + 0x10AC0, 0x10AC7, + 0x10AC9, 0x10AE4, + 0x10B00, 0x10B35, + 0x10B40, 0x10B55, + 0x10B60, 0x10B72, + 0x10B80, 0x10B91, + 0x10C00, 0x10C48, + 0x10C80, 0x10CB2, + 0x10CC0, 0x10CF2, + 0x10D00, 0x10D27, + 0x10E80, 0x10EA9, + 0x10EAB, 0x10EAC, + 0x10EB0, 0x10EB1, + 0x10F00, 0x10F1C, + 0x10F27, 0x10F27, + 0x10F30, 0x10F45, + 0x10F70, 0x10F81, + 0x10FB0, 0x10FC4, + 0x10FE0, 0x10FF6, + 0x11000, 0x11045, + 0x11071, 0x11075, + 0x11080, 0x110B8, + 0x110C2, 0x110C2, + 0x110D0, 0x110E8, + 0x11100, 0x11132, + 0x11144, 0x11147, + 0x11150, 0x11172, + 0x11176, 0x11176, + 0x11180, 0x111BF, + 0x111C1, 0x111C4, + 0x111CE, 0x111CF, + 0x111DA, 0x111DA, + 0x111DC, 0x111DC, + 0x11200, 0x11211, + 0x11213, 0x11234, + 0x11237, 0x11237, + 0x1123E, 0x11241, + 0x11280, 0x11286, + 0x11288, 0x11288, + 0x1128A, 0x1128D, + 0x1128F, 0x1129D, + 0x1129F, 0x112A8, + 0x112B0, 0x112E8, + 0x11300, 0x11303, + 0x11305, 0x1130C, + 0x1130F, 0x11310, + 0x11313, 0x11328, + 0x1132A, 0x11330, + 0x11332, 0x11333, + 0x11335, 0x11339, + 0x1133D, 0x11344, + 0x11347, 0x11348, + 0x1134B, 0x1134C, + 0x11350, 0x11350, + 0x11357, 0x11357, + 0x1135D, 0x11363, + 0x11400, 0x11441, + 0x11443, 0x11445, + 0x11447, 0x1144A, + 0x1145F, 0x11461, + 0x11480, 0x114C1, + 0x114C4, 0x114C5, + 0x114C7, 0x114C7, + 0x11580, 0x115B5, + 0x115B8, 0x115BE, + 0x115D8, 0x115DD, + 0x11600, 0x1163E, + 0x11640, 0x11640, + 0x11644, 0x11644, + 0x11680, 0x116B5, + 0x116B8, 0x116B8, + 0x11700, 0x1171A, + 0x1171D, 0x1172A, + 0x11740, 0x11746, + 0x11800, 0x11838, + 0x118A0, 0x118DF, + 0x118FF, 0x11906, + 0x11909, 0x11909, + 0x1190C, 0x11913, + 0x11915, 0x11916, + 0x11918, 0x11935, + 0x11937, 0x11938, + 0x1193B, 0x1193C, + 0x1193F, 0x11942, + 0x119A0, 0x119A7, + 0x119AA, 0x119D7, + 0x119DA, 0x119DF, + 0x119E1, 0x119E1, + 0x119E3, 0x119E4, + 0x11A00, 0x11A32, + 0x11A35, 0x11A3E, + 0x11A50, 0x11A97, + 0x11A9D, 0x11A9D, + 0x11AB0, 0x11AF8, + 0x11C00, 0x11C08, + 0x11C0A, 0x11C36, + 0x11C38, 0x11C3E, + 0x11C40, 0x11C40, + 0x11C72, 0x11C8F, + 0x11C92, 0x11CA7, + 0x11CA9, 0x11CB6, + 0x11D00, 0x11D06, + 0x11D08, 0x11D09, + 0x11D0B, 0x11D36, + 0x11D3A, 0x11D3A, + 0x11D3C, 0x11D3D, + 0x11D3F, 0x11D41, + 0x11D43, 0x11D43, + 0x11D46, 0x11D47, + 0x11D60, 0x11D65, + 0x11D67, 0x11D68, + 0x11D6A, 0x11D8E, + 0x11D90, 0x11D91, + 0x11D93, 0x11D96, + 0x11D98, 0x11D98, + 0x11EE0, 0x11EF6, + 0x11F00, 0x11F10, + 0x11F12, 0x11F3A, + 0x11F3E, 0x11F40, + 0x11FB0, 0x11FB0, + 0x12000, 0x12399, + 0x12400, 0x1246E, + 0x12480, 0x12543, + 0x12F90, 0x12FF0, + 0x13000, 0x1342F, + 0x13441, 0x13446, + 0x14400, 0x14646, + 0x16800, 0x16A38, + 0x16A40, 0x16A5E, + 0x16A70, 0x16ABE, + 0x16AD0, 0x16AED, + 0x16B00, 0x16B2F, + 0x16B40, 0x16B43, + 0x16B63, 0x16B77, + 0x16B7D, 0x16B8F, + 0x16E40, 0x16E7F, + 0x16F00, 0x16F4A, + 0x16F4F, 0x16F87, + 0x16F8F, 0x16F9F, + 0x16FE0, 0x16FE1, + 0x16FE3, 0x16FE3, + 0x16FF0, 0x16FF1, + 0x17000, 0x187F7, + 0x18800, 0x18CD5, + 0x18D00, 0x18D08, + 0x1AFF0, 0x1AFF3, + 0x1AFF5, 0x1AFFB, + 0x1AFFD, 0x1AFFE, + 0x1B000, 0x1B122, + 0x1B132, 0x1B132, + 0x1B150, 0x1B152, + 0x1B155, 0x1B155, + 0x1B164, 0x1B167, + 0x1B170, 0x1B2FB, + 0x1BC00, 0x1BC6A, + 0x1BC70, 0x1BC7C, + 0x1BC80, 0x1BC88, + 0x1BC90, 0x1BC99, + 0x1BC9E, 0x1BC9E, + 0x1D400, 0x1D454, + 0x1D456, 0x1D49C, + 0x1D49E, 0x1D49F, + 0x1D4A2, 0x1D4A2, + 0x1D4A5, 0x1D4A6, + 0x1D4A9, 0x1D4AC, + 0x1D4AE, 0x1D4B9, + 0x1D4BB, 0x1D4BB, + 0x1D4BD, 0x1D4C3, + 0x1D4C5, 0x1D505, + 0x1D507, 0x1D50A, + 0x1D50D, 0x1D514, + 0x1D516, 0x1D51C, + 0x1D51E, 0x1D539, + 0x1D53B, 0x1D53E, + 0x1D540, 0x1D544, + 0x1D546, 0x1D546, + 0x1D54A, 0x1D550, + 0x1D552, 0x1D6A5, + 0x1D6A8, 0x1D6C0, + 0x1D6C2, 0x1D6DA, + 0x1D6DC, 0x1D6FA, + 0x1D6FC, 0x1D714, + 0x1D716, 0x1D734, + 0x1D736, 0x1D74E, + 0x1D750, 0x1D76E, + 0x1D770, 0x1D788, + 0x1D78A, 0x1D7A8, + 0x1D7AA, 0x1D7C2, + 0x1D7C4, 0x1D7CB, + 0x1DF00, 0x1DF1E, + 0x1DF25, 0x1DF2A, + 0x1E000, 0x1E006, + 0x1E008, 0x1E018, + 0x1E01B, 0x1E021, + 0x1E023, 0x1E024, + 0x1E026, 0x1E02A, + 0x1E030, 0x1E06D, + 0x1E08F, 0x1E08F, + 0x1E100, 0x1E12C, + 0x1E137, 0x1E13D, + 0x1E14E, 0x1E14E, + 0x1E290, 0x1E2AD, + 0x1E2C0, 0x1E2EB, + 0x1E4D0, 0x1E4EB, + 0x1E7E0, 0x1E7E6, + 0x1E7E8, 0x1E7EB, + 0x1E7ED, 0x1E7EE, + 0x1E7F0, 0x1E7FE, + 0x1E800, 0x1E8C4, + 0x1E900, 0x1E943, + 0x1E947, 0x1E947, + 0x1E94B, 0x1E94B, + 0x1EE00, 0x1EE03, + 0x1EE05, 0x1EE1F, + 0x1EE21, 0x1EE22, + 0x1EE24, 0x1EE24, + 0x1EE27, 0x1EE27, + 0x1EE29, 0x1EE32, + 0x1EE34, 0x1EE37, + 0x1EE39, 0x1EE39, + 0x1EE3B, 0x1EE3B, + 0x1EE42, 0x1EE42, + 0x1EE47, 0x1EE47, + 0x1EE49, 0x1EE49, + 0x1EE4B, 0x1EE4B, + 0x1EE4D, 0x1EE4F, + 0x1EE51, 0x1EE52, + 0x1EE54, 0x1EE54, + 0x1EE57, 0x1EE57, + 0x1EE59, 0x1EE59, + 0x1EE5B, 0x1EE5B, + 0x1EE5D, 0x1EE5D, + 0x1EE5F, 0x1EE5F, + 0x1EE61, 0x1EE62, + 0x1EE64, 0x1EE64, + 0x1EE67, 0x1EE6A, + 0x1EE6C, 0x1EE72, + 0x1EE74, 0x1EE77, + 0x1EE79, 0x1EE7C, + 0x1EE7E, 0x1EE7E, + 0x1EE80, 0x1EE89, + 0x1EE8B, 0x1EE9B, + 0x1EEA1, 0x1EEA3, + 0x1EEA5, 0x1EEA9, + 0x1EEAB, 0x1EEBB, + 0x1F130, 0x1F149, + 0x1F150, 0x1F169, + 0x1F170, 0x1F189, + 0x20000, 0x2A6DF, + 0x2A700, 0x2B739, + 0x2B740, 0x2B81D, + 0x2B820, 0x2CEA1, + 0x2CEB0, 0x2EBE0, + 0x2F800, 0x2FA1D, + 0x30000, 0x3134A, + 0x31350, 0x323AF, +}; + +#define UNICODE_ALNUM_CODEPOINTS_LENGTH 1528 +static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEPOINTS_LENGTH] = { + 0x100, 0x2C1, + 0x2C6, 0x2D1, + 0x2E0, 0x2E4, + 0x2EC, 0x2EC, + 0x2EE, 0x2EE, + 0x345, 0x345, + 0x370, 0x374, + 0x376, 0x377, + 0x37A, 0x37D, + 0x37F, 0x37F, + 0x386, 0x386, + 0x388, 0x38A, + 0x38C, 0x38C, + 0x38E, 0x3A1, + 0x3A3, 0x3F5, + 0x3F7, 0x481, + 0x48A, 0x52F, + 0x531, 0x556, + 0x559, 0x559, + 0x560, 0x588, + 0x5B0, 0x5BD, + 0x5BF, 0x5BF, + 0x5C1, 0x5C2, + 0x5C4, 0x5C5, + 0x5C7, 0x5C7, + 0x5D0, 0x5EA, + 0x5EF, 0x5F2, + 0x610, 0x61A, + 0x620, 0x657, + 0x659, 0x669, + 0x66E, 0x6D3, + 0x6D5, 0x6DC, + 0x6E1, 0x6E8, + 0x6ED, 0x6FC, + 0x6FF, 0x6FF, + 0x710, 0x73F, + 0x74D, 0x7B1, + 0x7C0, 0x7EA, + 0x7F4, 0x7F5, + 0x7FA, 0x7FA, + 0x800, 0x817, + 0x81A, 0x82C, + 0x840, 0x858, + 0x860, 0x86A, + 0x870, 0x887, + 0x889, 0x88E, + 0x8A0, 0x8C9, + 0x8D4, 0x8DF, + 0x8E3, 0x8E9, + 0x8F0, 0x93B, + 0x93D, 0x94C, + 0x94E, 0x950, + 0x955, 0x963, + 0x966, 0x96F, + 0x971, 0x983, + 0x985, 0x98C, + 0x98F, 0x990, + 0x993, 0x9A8, + 0x9AA, 0x9B0, + 0x9B2, 0x9B2, + 0x9B6, 0x9B9, + 0x9BD, 0x9C4, + 0x9C7, 0x9C8, + 0x9CB, 0x9CC, + 0x9CE, 0x9CE, + 0x9D7, 0x9D7, + 0x9DC, 0x9DD, + 0x9DF, 0x9E3, + 0x9E6, 0x9F1, + 0x9FC, 0x9FC, + 0xA01, 0xA03, + 0xA05, 0xA0A, + 0xA0F, 0xA10, + 0xA13, 0xA28, + 0xA2A, 0xA30, + 0xA32, 0xA33, + 0xA35, 0xA36, + 0xA38, 0xA39, + 0xA3E, 0xA42, + 0xA47, 0xA48, + 0xA4B, 0xA4C, + 0xA51, 0xA51, + 0xA59, 0xA5C, + 0xA5E, 0xA5E, + 0xA66, 0xA75, + 0xA81, 0xA83, + 0xA85, 0xA8D, + 0xA8F, 0xA91, + 0xA93, 0xAA8, + 0xAAA, 0xAB0, + 0xAB2, 0xAB3, + 0xAB5, 0xAB9, + 0xABD, 0xAC5, + 0xAC7, 0xAC9, + 0xACB, 0xACC, + 0xAD0, 0xAD0, + 0xAE0, 0xAE3, + 0xAE6, 0xAEF, + 0xAF9, 0xAFC, + 0xB01, 0xB03, + 0xB05, 0xB0C, + 0xB0F, 0xB10, + 0xB13, 0xB28, + 0xB2A, 0xB30, + 0xB32, 0xB33, + 0xB35, 0xB39, + 0xB3D, 0xB44, + 0xB47, 0xB48, + 0xB4B, 0xB4C, + 0xB56, 0xB57, + 0xB5C, 0xB5D, + 0xB5F, 0xB63, + 0xB66, 0xB6F, + 0xB71, 0xB71, + 0xB82, 0xB83, + 0xB85, 0xB8A, + 0xB8E, 0xB90, + 0xB92, 0xB95, + 0xB99, 0xB9A, + 0xB9C, 0xB9C, + 0xB9E, 0xB9F, + 0xBA3, 0xBA4, + 0xBA8, 0xBAA, + 0xBAE, 0xBB9, + 0xBBE, 0xBC2, + 0xBC6, 0xBC8, + 0xBCA, 0xBCC, + 0xBD0, 0xBD0, + 0xBD7, 0xBD7, + 0xBE6, 0xBEF, + 0xC00, 0xC0C, + 0xC0E, 0xC10, + 0xC12, 0xC28, + 0xC2A, 0xC39, + 0xC3D, 0xC44, + 0xC46, 0xC48, + 0xC4A, 0xC4C, + 0xC55, 0xC56, + 0xC58, 0xC5A, + 0xC5D, 0xC5D, + 0xC60, 0xC63, + 0xC66, 0xC6F, + 0xC80, 0xC83, + 0xC85, 0xC8C, + 0xC8E, 0xC90, + 0xC92, 0xCA8, + 0xCAA, 0xCB3, + 0xCB5, 0xCB9, + 0xCBD, 0xCC4, + 0xCC6, 0xCC8, + 0xCCA, 0xCCC, + 0xCD5, 0xCD6, + 0xCDD, 0xCDE, + 0xCE0, 0xCE3, + 0xCE6, 0xCEF, + 0xCF1, 0xCF3, + 0xD00, 0xD0C, + 0xD0E, 0xD10, + 0xD12, 0xD3A, + 0xD3D, 0xD44, + 0xD46, 0xD48, + 0xD4A, 0xD4C, + 0xD4E, 0xD4E, + 0xD54, 0xD57, + 0xD5F, 0xD63, + 0xD66, 0xD6F, + 0xD7A, 0xD7F, + 0xD81, 0xD83, + 0xD85, 0xD96, + 0xD9A, 0xDB1, + 0xDB3, 0xDBB, + 0xDBD, 0xDBD, + 0xDC0, 0xDC6, + 0xDCF, 0xDD4, + 0xDD6, 0xDD6, + 0xDD8, 0xDDF, + 0xDE6, 0xDEF, + 0xDF2, 0xDF3, + 0xE01, 0xE3A, + 0xE40, 0xE46, + 0xE4D, 0xE4D, + 0xE50, 0xE59, + 0xE81, 0xE82, + 0xE84, 0xE84, + 0xE86, 0xE8A, + 0xE8C, 0xEA3, + 0xEA5, 0xEA5, + 0xEA7, 0xEB9, + 0xEBB, 0xEBD, + 0xEC0, 0xEC4, + 0xEC6, 0xEC6, + 0xECD, 0xECD, + 0xED0, 0xED9, + 0xEDC, 0xEDF, + 0xF00, 0xF00, + 0xF20, 0xF29, + 0xF40, 0xF47, + 0xF49, 0xF6C, + 0xF71, 0xF83, + 0xF88, 0xF97, + 0xF99, 0xFBC, + 0x1000, 0x1036, + 0x1038, 0x1038, + 0x103B, 0x1049, + 0x1050, 0x109D, + 0x10A0, 0x10C5, + 0x10C7, 0x10C7, + 0x10CD, 0x10CD, + 0x10D0, 0x10FA, + 0x10FC, 0x1248, + 0x124A, 0x124D, + 0x1250, 0x1256, + 0x1258, 0x1258, + 0x125A, 0x125D, + 0x1260, 0x1288, + 0x128A, 0x128D, + 0x1290, 0x12B0, + 0x12B2, 0x12B5, + 0x12B8, 0x12BE, + 0x12C0, 0x12C0, + 0x12C2, 0x12C5, + 0x12C8, 0x12D6, + 0x12D8, 0x1310, + 0x1312, 0x1315, + 0x1318, 0x135A, + 0x1380, 0x138F, + 0x13A0, 0x13F5, + 0x13F8, 0x13FD, + 0x1401, 0x166C, + 0x166F, 0x167F, + 0x1681, 0x169A, + 0x16A0, 0x16EA, + 0x16EE, 0x16F8, + 0x1700, 0x1713, + 0x171F, 0x1733, + 0x1740, 0x1753, + 0x1760, 0x176C, + 0x176E, 0x1770, + 0x1772, 0x1773, + 0x1780, 0x17B3, + 0x17B6, 0x17C8, + 0x17D7, 0x17D7, + 0x17DC, 0x17DC, + 0x17E0, 0x17E9, + 0x1810, 0x1819, + 0x1820, 0x1878, + 0x1880, 0x18AA, + 0x18B0, 0x18F5, + 0x1900, 0x191E, + 0x1920, 0x192B, + 0x1930, 0x1938, + 0x1946, 0x196D, + 0x1970, 0x1974, + 0x1980, 0x19AB, + 0x19B0, 0x19C9, + 0x19D0, 0x19D9, + 0x1A00, 0x1A1B, + 0x1A20, 0x1A5E, + 0x1A61, 0x1A74, + 0x1A80, 0x1A89, + 0x1A90, 0x1A99, + 0x1AA7, 0x1AA7, + 0x1ABF, 0x1AC0, + 0x1ACC, 0x1ACE, + 0x1B00, 0x1B33, + 0x1B35, 0x1B43, + 0x1B45, 0x1B4C, + 0x1B50, 0x1B59, + 0x1B80, 0x1BA9, + 0x1BAC, 0x1BE5, + 0x1BE7, 0x1BF1, + 0x1C00, 0x1C36, + 0x1C40, 0x1C49, + 0x1C4D, 0x1C7D, + 0x1C80, 0x1C88, + 0x1C90, 0x1CBA, + 0x1CBD, 0x1CBF, + 0x1CE9, 0x1CEC, + 0x1CEE, 0x1CF3, + 0x1CF5, 0x1CF6, + 0x1CFA, 0x1CFA, + 0x1D00, 0x1DBF, + 0x1DE7, 0x1DF4, + 0x1E00, 0x1F15, + 0x1F18, 0x1F1D, + 0x1F20, 0x1F45, + 0x1F48, 0x1F4D, + 0x1F50, 0x1F57, + 0x1F59, 0x1F59, + 0x1F5B, 0x1F5B, + 0x1F5D, 0x1F5D, + 0x1F5F, 0x1F7D, + 0x1F80, 0x1FB4, + 0x1FB6, 0x1FBC, + 0x1FBE, 0x1FBE, + 0x1FC2, 0x1FC4, + 0x1FC6, 0x1FCC, + 0x1FD0, 0x1FD3, + 0x1FD6, 0x1FDB, + 0x1FE0, 0x1FEC, + 0x1FF2, 0x1FF4, + 0x1FF6, 0x1FFC, + 0x2071, 0x2071, + 0x207F, 0x207F, + 0x2090, 0x209C, + 0x2102, 0x2102, + 0x2107, 0x2107, + 0x210A, 0x2113, + 0x2115, 0x2115, + 0x2119, 0x211D, + 0x2124, 0x2124, + 0x2126, 0x2126, + 0x2128, 0x2128, + 0x212A, 0x212D, + 0x212F, 0x2139, + 0x213C, 0x213F, + 0x2145, 0x2149, + 0x214E, 0x214E, + 0x2160, 0x2188, + 0x24B6, 0x24E9, + 0x2C00, 0x2CE4, + 0x2CEB, 0x2CEE, + 0x2CF2, 0x2CF3, + 0x2D00, 0x2D25, + 0x2D27, 0x2D27, + 0x2D2D, 0x2D2D, + 0x2D30, 0x2D67, + 0x2D6F, 0x2D6F, + 0x2D80, 0x2D96, + 0x2DA0, 0x2DA6, + 0x2DA8, 0x2DAE, + 0x2DB0, 0x2DB6, + 0x2DB8, 0x2DBE, + 0x2DC0, 0x2DC6, + 0x2DC8, 0x2DCE, + 0x2DD0, 0x2DD6, + 0x2DD8, 0x2DDE, + 0x2DE0, 0x2DFF, + 0x2E2F, 0x2E2F, + 0x3005, 0x3007, + 0x3021, 0x3029, + 0x3031, 0x3035, + 0x3038, 0x303C, + 0x3041, 0x3096, + 0x309D, 0x309F, + 0x30A1, 0x30FA, + 0x30FC, 0x30FF, + 0x3105, 0x312F, + 0x3131, 0x318E, + 0x31A0, 0x31BF, + 0x31F0, 0x31FF, + 0x3400, 0x4DBF, + 0x4E00, 0xA48C, + 0xA4D0, 0xA4FD, + 0xA500, 0xA60C, + 0xA610, 0xA62B, + 0xA640, 0xA66E, + 0xA674, 0xA67B, + 0xA67F, 0xA6EF, + 0xA717, 0xA71F, + 0xA722, 0xA788, + 0xA78B, 0xA7CA, + 0xA7D0, 0xA7D1, + 0xA7D3, 0xA7D3, + 0xA7D5, 0xA7D9, + 0xA7F2, 0xA805, + 0xA807, 0xA827, + 0xA840, 0xA873, + 0xA880, 0xA8C3, + 0xA8C5, 0xA8C5, + 0xA8D0, 0xA8D9, + 0xA8F2, 0xA8F7, + 0xA8FB, 0xA8FB, + 0xA8FD, 0xA92A, + 0xA930, 0xA952, + 0xA960, 0xA97C, + 0xA980, 0xA9B2, + 0xA9B4, 0xA9BF, + 0xA9CF, 0xA9D9, + 0xA9E0, 0xA9FE, + 0xAA00, 0xAA36, + 0xAA40, 0xAA4D, + 0xAA50, 0xAA59, + 0xAA60, 0xAA76, + 0xAA7A, 0xAABE, + 0xAAC0, 0xAAC0, + 0xAAC2, 0xAAC2, + 0xAADB, 0xAADD, + 0xAAE0, 0xAAEF, + 0xAAF2, 0xAAF5, + 0xAB01, 0xAB06, + 0xAB09, 0xAB0E, + 0xAB11, 0xAB16, + 0xAB20, 0xAB26, + 0xAB28, 0xAB2E, + 0xAB30, 0xAB5A, + 0xAB5C, 0xAB69, + 0xAB70, 0xABEA, + 0xABF0, 0xABF9, + 0xAC00, 0xD7A3, + 0xD7B0, 0xD7C6, + 0xD7CB, 0xD7FB, + 0xF900, 0xFA6D, + 0xFA70, 0xFAD9, + 0xFB00, 0xFB06, + 0xFB13, 0xFB17, + 0xFB1D, 0xFB28, + 0xFB2A, 0xFB36, + 0xFB38, 0xFB3C, + 0xFB3E, 0xFB3E, + 0xFB40, 0xFB41, + 0xFB43, 0xFB44, + 0xFB46, 0xFBB1, + 0xFBD3, 0xFD3D, + 0xFD50, 0xFD8F, + 0xFD92, 0xFDC7, + 0xFDF0, 0xFDFB, + 0xFE70, 0xFE74, + 0xFE76, 0xFEFC, + 0xFF10, 0xFF19, + 0xFF21, 0xFF3A, + 0xFF41, 0xFF5A, + 0xFF66, 0xFFBE, + 0xFFC2, 0xFFC7, + 0xFFCA, 0xFFCF, + 0xFFD2, 0xFFD7, + 0xFFDA, 0xFFDC, + 0x10000, 0x1000B, + 0x1000D, 0x10026, + 0x10028, 0x1003A, + 0x1003C, 0x1003D, + 0x1003F, 0x1004D, + 0x10050, 0x1005D, + 0x10080, 0x100FA, + 0x10140, 0x10174, + 0x10280, 0x1029C, + 0x102A0, 0x102D0, + 0x10300, 0x1031F, + 0x1032D, 0x1034A, + 0x10350, 0x1037A, + 0x10380, 0x1039D, + 0x103A0, 0x103C3, + 0x103C8, 0x103CF, + 0x103D1, 0x103D5, + 0x10400, 0x1049D, + 0x104A0, 0x104A9, + 0x104B0, 0x104D3, + 0x104D8, 0x104FB, + 0x10500, 0x10527, + 0x10530, 0x10563, + 0x10570, 0x1057A, + 0x1057C, 0x1058A, + 0x1058C, 0x10592, + 0x10594, 0x10595, + 0x10597, 0x105A1, + 0x105A3, 0x105B1, + 0x105B3, 0x105B9, + 0x105BB, 0x105BC, + 0x10600, 0x10736, + 0x10740, 0x10755, + 0x10760, 0x10767, + 0x10780, 0x10785, + 0x10787, 0x107B0, + 0x107B2, 0x107BA, + 0x10800, 0x10805, + 0x10808, 0x10808, + 0x1080A, 0x10835, + 0x10837, 0x10838, + 0x1083C, 0x1083C, + 0x1083F, 0x10855, + 0x10860, 0x10876, + 0x10880, 0x1089E, + 0x108E0, 0x108F2, + 0x108F4, 0x108F5, + 0x10900, 0x10915, + 0x10920, 0x10939, + 0x10980, 0x109B7, + 0x109BE, 0x109BF, + 0x10A00, 0x10A03, + 0x10A05, 0x10A06, + 0x10A0C, 0x10A13, + 0x10A15, 0x10A17, + 0x10A19, 0x10A35, + 0x10A60, 0x10A7C, + 0x10A80, 0x10A9C, + 0x10AC0, 0x10AC7, + 0x10AC9, 0x10AE4, + 0x10B00, 0x10B35, + 0x10B40, 0x10B55, + 0x10B60, 0x10B72, + 0x10B80, 0x10B91, + 0x10C00, 0x10C48, + 0x10C80, 0x10CB2, + 0x10CC0, 0x10CF2, + 0x10D00, 0x10D27, + 0x10D30, 0x10D39, + 0x10E80, 0x10EA9, + 0x10EAB, 0x10EAC, + 0x10EB0, 0x10EB1, + 0x10F00, 0x10F1C, + 0x10F27, 0x10F27, + 0x10F30, 0x10F45, + 0x10F70, 0x10F81, + 0x10FB0, 0x10FC4, + 0x10FE0, 0x10FF6, + 0x11000, 0x11045, + 0x11066, 0x1106F, + 0x11071, 0x11075, + 0x11080, 0x110B8, + 0x110C2, 0x110C2, + 0x110D0, 0x110E8, + 0x110F0, 0x110F9, + 0x11100, 0x11132, + 0x11136, 0x1113F, + 0x11144, 0x11147, + 0x11150, 0x11172, + 0x11176, 0x11176, + 0x11180, 0x111BF, + 0x111C1, 0x111C4, + 0x111CE, 0x111DA, + 0x111DC, 0x111DC, + 0x11200, 0x11211, + 0x11213, 0x11234, + 0x11237, 0x11237, + 0x1123E, 0x11241, + 0x11280, 0x11286, + 0x11288, 0x11288, + 0x1128A, 0x1128D, + 0x1128F, 0x1129D, + 0x1129F, 0x112A8, + 0x112B0, 0x112E8, + 0x112F0, 0x112F9, + 0x11300, 0x11303, + 0x11305, 0x1130C, + 0x1130F, 0x11310, + 0x11313, 0x11328, + 0x1132A, 0x11330, + 0x11332, 0x11333, + 0x11335, 0x11339, + 0x1133D, 0x11344, + 0x11347, 0x11348, + 0x1134B, 0x1134C, + 0x11350, 0x11350, + 0x11357, 0x11357, + 0x1135D, 0x11363, + 0x11400, 0x11441, + 0x11443, 0x11445, + 0x11447, 0x1144A, + 0x11450, 0x11459, + 0x1145F, 0x11461, + 0x11480, 0x114C1, + 0x114C4, 0x114C5, + 0x114C7, 0x114C7, + 0x114D0, 0x114D9, + 0x11580, 0x115B5, + 0x115B8, 0x115BE, + 0x115D8, 0x115DD, + 0x11600, 0x1163E, + 0x11640, 0x11640, + 0x11644, 0x11644, + 0x11650, 0x11659, + 0x11680, 0x116B5, + 0x116B8, 0x116B8, + 0x116C0, 0x116C9, + 0x11700, 0x1171A, + 0x1171D, 0x1172A, + 0x11730, 0x11739, + 0x11740, 0x11746, + 0x11800, 0x11838, + 0x118A0, 0x118E9, + 0x118FF, 0x11906, + 0x11909, 0x11909, + 0x1190C, 0x11913, + 0x11915, 0x11916, + 0x11918, 0x11935, + 0x11937, 0x11938, + 0x1193B, 0x1193C, + 0x1193F, 0x11942, + 0x11950, 0x11959, + 0x119A0, 0x119A7, + 0x119AA, 0x119D7, + 0x119DA, 0x119DF, + 0x119E1, 0x119E1, + 0x119E3, 0x119E4, + 0x11A00, 0x11A32, + 0x11A35, 0x11A3E, + 0x11A50, 0x11A97, + 0x11A9D, 0x11A9D, + 0x11AB0, 0x11AF8, + 0x11C00, 0x11C08, + 0x11C0A, 0x11C36, + 0x11C38, 0x11C3E, + 0x11C40, 0x11C40, + 0x11C50, 0x11C59, + 0x11C72, 0x11C8F, + 0x11C92, 0x11CA7, + 0x11CA9, 0x11CB6, + 0x11D00, 0x11D06, + 0x11D08, 0x11D09, + 0x11D0B, 0x11D36, + 0x11D3A, 0x11D3A, + 0x11D3C, 0x11D3D, + 0x11D3F, 0x11D41, + 0x11D43, 0x11D43, + 0x11D46, 0x11D47, + 0x11D50, 0x11D59, + 0x11D60, 0x11D65, + 0x11D67, 0x11D68, + 0x11D6A, 0x11D8E, + 0x11D90, 0x11D91, + 0x11D93, 0x11D96, + 0x11D98, 0x11D98, + 0x11DA0, 0x11DA9, + 0x11EE0, 0x11EF6, + 0x11F00, 0x11F10, + 0x11F12, 0x11F3A, + 0x11F3E, 0x11F40, + 0x11F50, 0x11F59, + 0x11FB0, 0x11FB0, + 0x12000, 0x12399, + 0x12400, 0x1246E, + 0x12480, 0x12543, + 0x12F90, 0x12FF0, + 0x13000, 0x1342F, + 0x13441, 0x13446, + 0x14400, 0x14646, + 0x16800, 0x16A38, + 0x16A40, 0x16A5E, + 0x16A60, 0x16A69, + 0x16A70, 0x16ABE, + 0x16AC0, 0x16AC9, + 0x16AD0, 0x16AED, + 0x16B00, 0x16B2F, + 0x16B40, 0x16B43, + 0x16B50, 0x16B59, + 0x16B63, 0x16B77, + 0x16B7D, 0x16B8F, + 0x16E40, 0x16E7F, + 0x16F00, 0x16F4A, + 0x16F4F, 0x16F87, + 0x16F8F, 0x16F9F, + 0x16FE0, 0x16FE1, + 0x16FE3, 0x16FE3, + 0x16FF0, 0x16FF1, + 0x17000, 0x187F7, + 0x18800, 0x18CD5, + 0x18D00, 0x18D08, + 0x1AFF0, 0x1AFF3, + 0x1AFF5, 0x1AFFB, + 0x1AFFD, 0x1AFFE, + 0x1B000, 0x1B122, + 0x1B132, 0x1B132, + 0x1B150, 0x1B152, + 0x1B155, 0x1B155, + 0x1B164, 0x1B167, + 0x1B170, 0x1B2FB, + 0x1BC00, 0x1BC6A, + 0x1BC70, 0x1BC7C, + 0x1BC80, 0x1BC88, + 0x1BC90, 0x1BC99, + 0x1BC9E, 0x1BC9E, + 0x1D400, 0x1D454, + 0x1D456, 0x1D49C, + 0x1D49E, 0x1D49F, + 0x1D4A2, 0x1D4A2, + 0x1D4A5, 0x1D4A6, + 0x1D4A9, 0x1D4AC, + 0x1D4AE, 0x1D4B9, + 0x1D4BB, 0x1D4BB, + 0x1D4BD, 0x1D4C3, + 0x1D4C5, 0x1D505, + 0x1D507, 0x1D50A, + 0x1D50D, 0x1D514, + 0x1D516, 0x1D51C, + 0x1D51E, 0x1D539, + 0x1D53B, 0x1D53E, + 0x1D540, 0x1D544, + 0x1D546, 0x1D546, + 0x1D54A, 0x1D550, + 0x1D552, 0x1D6A5, + 0x1D6A8, 0x1D6C0, + 0x1D6C2, 0x1D6DA, + 0x1D6DC, 0x1D6FA, + 0x1D6FC, 0x1D714, + 0x1D716, 0x1D734, + 0x1D736, 0x1D74E, + 0x1D750, 0x1D76E, + 0x1D770, 0x1D788, + 0x1D78A, 0x1D7A8, + 0x1D7AA, 0x1D7C2, + 0x1D7C4, 0x1D7CB, + 0x1D7CE, 0x1D7FF, + 0x1DF00, 0x1DF1E, + 0x1DF25, 0x1DF2A, + 0x1E000, 0x1E006, + 0x1E008, 0x1E018, + 0x1E01B, 0x1E021, + 0x1E023, 0x1E024, + 0x1E026, 0x1E02A, + 0x1E030, 0x1E06D, + 0x1E08F, 0x1E08F, + 0x1E100, 0x1E12C, + 0x1E137, 0x1E13D, + 0x1E140, 0x1E149, + 0x1E14E, 0x1E14E, + 0x1E290, 0x1E2AD, + 0x1E2C0, 0x1E2EB, + 0x1E2F0, 0x1E2F9, + 0x1E4D0, 0x1E4EB, + 0x1E4F0, 0x1E4F9, + 0x1E7E0, 0x1E7E6, + 0x1E7E8, 0x1E7EB, + 0x1E7ED, 0x1E7EE, + 0x1E7F0, 0x1E7FE, + 0x1E800, 0x1E8C4, + 0x1E900, 0x1E943, + 0x1E947, 0x1E947, + 0x1E94B, 0x1E94B, + 0x1E950, 0x1E959, + 0x1EE00, 0x1EE03, + 0x1EE05, 0x1EE1F, + 0x1EE21, 0x1EE22, + 0x1EE24, 0x1EE24, + 0x1EE27, 0x1EE27, + 0x1EE29, 0x1EE32, + 0x1EE34, 0x1EE37, + 0x1EE39, 0x1EE39, + 0x1EE3B, 0x1EE3B, + 0x1EE42, 0x1EE42, + 0x1EE47, 0x1EE47, + 0x1EE49, 0x1EE49, + 0x1EE4B, 0x1EE4B, + 0x1EE4D, 0x1EE4F, + 0x1EE51, 0x1EE52, + 0x1EE54, 0x1EE54, + 0x1EE57, 0x1EE57, + 0x1EE59, 0x1EE59, + 0x1EE5B, 0x1EE5B, + 0x1EE5D, 0x1EE5D, + 0x1EE5F, 0x1EE5F, + 0x1EE61, 0x1EE62, + 0x1EE64, 0x1EE64, + 0x1EE67, 0x1EE6A, + 0x1EE6C, 0x1EE72, + 0x1EE74, 0x1EE77, + 0x1EE79, 0x1EE7C, + 0x1EE7E, 0x1EE7E, + 0x1EE80, 0x1EE89, + 0x1EE8B, 0x1EE9B, + 0x1EEA1, 0x1EEA3, + 0x1EEA5, 0x1EEA9, + 0x1EEAB, 0x1EEBB, + 0x1F130, 0x1F149, + 0x1F150, 0x1F169, + 0x1F170, 0x1F189, + 0x1FBF0, 0x1FBF9, + 0x20000, 0x2A6DF, + 0x2A700, 0x2B739, + 0x2B740, 0x2B81D, + 0x2B820, 0x2CEA1, + 0x2CEB0, 0x2EBE0, + 0x2F800, 0x2FA1D, + 0x30000, 0x3134A, + 0x31350, 0x323AF, +}; + +#define UNICODE_ISUPPER_CODEPOINTS_LENGTH 1296 +static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_CODEPOINTS_LENGTH] = { + 0x100, 0x100, + 0x102, 0x102, + 0x104, 0x104, + 0x106, 0x106, + 0x108, 0x108, + 0x10A, 0x10A, + 0x10C, 0x10C, + 0x10E, 0x10E, + 0x110, 0x110, + 0x112, 0x112, + 0x114, 0x114, + 0x116, 0x116, + 0x118, 0x118, + 0x11A, 0x11A, + 0x11C, 0x11C, + 0x11E, 0x11E, + 0x120, 0x120, + 0x122, 0x122, + 0x124, 0x124, + 0x126, 0x126, + 0x128, 0x128, + 0x12A, 0x12A, + 0x12C, 0x12C, + 0x12E, 0x12E, + 0x130, 0x130, + 0x132, 0x132, + 0x134, 0x134, + 0x136, 0x136, + 0x139, 0x139, + 0x13B, 0x13B, + 0x13D, 0x13D, + 0x13F, 0x13F, + 0x141, 0x141, + 0x143, 0x143, + 0x145, 0x145, + 0x147, 0x147, + 0x14A, 0x14A, + 0x14C, 0x14C, + 0x14E, 0x14E, + 0x150, 0x150, + 0x152, 0x152, + 0x154, 0x154, + 0x156, 0x156, + 0x158, 0x158, + 0x15A, 0x15A, + 0x15C, 0x15C, + 0x15E, 0x15E, + 0x160, 0x160, + 0x162, 0x162, + 0x164, 0x164, + 0x166, 0x166, + 0x168, 0x168, + 0x16A, 0x16A, + 0x16C, 0x16C, + 0x16E, 0x16E, + 0x170, 0x170, + 0x172, 0x172, + 0x174, 0x174, + 0x176, 0x176, + 0x178, 0x179, + 0x17B, 0x17B, + 0x17D, 0x17D, + 0x181, 0x182, + 0x184, 0x184, + 0x186, 0x187, + 0x189, 0x18B, + 0x18E, 0x191, + 0x193, 0x194, + 0x196, 0x198, + 0x19C, 0x19D, + 0x19F, 0x1A0, + 0x1A2, 0x1A2, + 0x1A4, 0x1A4, + 0x1A6, 0x1A7, + 0x1A9, 0x1A9, + 0x1AC, 0x1AC, + 0x1AE, 0x1AF, + 0x1B1, 0x1B3, + 0x1B5, 0x1B5, + 0x1B7, 0x1B8, + 0x1BC, 0x1BC, + 0x1C4, 0x1C4, + 0x1C7, 0x1C7, + 0x1CA, 0x1CA, + 0x1CD, 0x1CD, + 0x1CF, 0x1CF, + 0x1D1, 0x1D1, + 0x1D3, 0x1D3, + 0x1D5, 0x1D5, + 0x1D7, 0x1D7, + 0x1D9, 0x1D9, + 0x1DB, 0x1DB, + 0x1DE, 0x1DE, + 0x1E0, 0x1E0, + 0x1E2, 0x1E2, + 0x1E4, 0x1E4, + 0x1E6, 0x1E6, + 0x1E8, 0x1E8, + 0x1EA, 0x1EA, + 0x1EC, 0x1EC, + 0x1EE, 0x1EE, + 0x1F1, 0x1F1, + 0x1F4, 0x1F4, + 0x1F6, 0x1F8, + 0x1FA, 0x1FA, + 0x1FC, 0x1FC, + 0x1FE, 0x1FE, + 0x200, 0x200, + 0x202, 0x202, + 0x204, 0x204, + 0x206, 0x206, + 0x208, 0x208, + 0x20A, 0x20A, + 0x20C, 0x20C, + 0x20E, 0x20E, + 0x210, 0x210, + 0x212, 0x212, + 0x214, 0x214, + 0x216, 0x216, + 0x218, 0x218, + 0x21A, 0x21A, + 0x21C, 0x21C, + 0x21E, 0x21E, + 0x220, 0x220, + 0x222, 0x222, + 0x224, 0x224, + 0x226, 0x226, + 0x228, 0x228, + 0x22A, 0x22A, + 0x22C, 0x22C, + 0x22E, 0x22E, + 0x230, 0x230, + 0x232, 0x232, + 0x23A, 0x23B, + 0x23D, 0x23E, + 0x241, 0x241, + 0x243, 0x246, + 0x248, 0x248, + 0x24A, 0x24A, + 0x24C, 0x24C, + 0x24E, 0x24E, + 0x370, 0x370, + 0x372, 0x372, + 0x376, 0x376, + 0x37F, 0x37F, + 0x386, 0x386, + 0x388, 0x38A, + 0x38C, 0x38C, + 0x38E, 0x38F, + 0x391, 0x3A1, + 0x3A3, 0x3AB, + 0x3CF, 0x3CF, + 0x3D2, 0x3D4, + 0x3D8, 0x3D8, + 0x3DA, 0x3DA, + 0x3DC, 0x3DC, + 0x3DE, 0x3DE, + 0x3E0, 0x3E0, + 0x3E2, 0x3E2, + 0x3E4, 0x3E4, + 0x3E6, 0x3E6, + 0x3E8, 0x3E8, + 0x3EA, 0x3EA, + 0x3EC, 0x3EC, + 0x3EE, 0x3EE, + 0x3F4, 0x3F4, + 0x3F7, 0x3F7, + 0x3F9, 0x3FA, + 0x3FD, 0x42F, + 0x460, 0x460, + 0x462, 0x462, + 0x464, 0x464, + 0x466, 0x466, + 0x468, 0x468, + 0x46A, 0x46A, + 0x46C, 0x46C, + 0x46E, 0x46E, + 0x470, 0x470, + 0x472, 0x472, + 0x474, 0x474, + 0x476, 0x476, + 0x478, 0x478, + 0x47A, 0x47A, + 0x47C, 0x47C, + 0x47E, 0x47E, + 0x480, 0x480, + 0x48A, 0x48A, + 0x48C, 0x48C, + 0x48E, 0x48E, + 0x490, 0x490, + 0x492, 0x492, + 0x494, 0x494, + 0x496, 0x496, + 0x498, 0x498, + 0x49A, 0x49A, + 0x49C, 0x49C, + 0x49E, 0x49E, + 0x4A0, 0x4A0, + 0x4A2, 0x4A2, + 0x4A4, 0x4A4, + 0x4A6, 0x4A6, + 0x4A8, 0x4A8, + 0x4AA, 0x4AA, + 0x4AC, 0x4AC, + 0x4AE, 0x4AE, + 0x4B0, 0x4B0, + 0x4B2, 0x4B2, + 0x4B4, 0x4B4, + 0x4B6, 0x4B6, + 0x4B8, 0x4B8, + 0x4BA, 0x4BA, + 0x4BC, 0x4BC, + 0x4BE, 0x4BE, + 0x4C0, 0x4C1, + 0x4C3, 0x4C3, + 0x4C5, 0x4C5, + 0x4C7, 0x4C7, + 0x4C9, 0x4C9, + 0x4CB, 0x4CB, + 0x4CD, 0x4CD, + 0x4D0, 0x4D0, + 0x4D2, 0x4D2, + 0x4D4, 0x4D4, + 0x4D6, 0x4D6, + 0x4D8, 0x4D8, + 0x4DA, 0x4DA, + 0x4DC, 0x4DC, + 0x4DE, 0x4DE, + 0x4E0, 0x4E0, + 0x4E2, 0x4E2, + 0x4E4, 0x4E4, + 0x4E6, 0x4E6, + 0x4E8, 0x4E8, + 0x4EA, 0x4EA, + 0x4EC, 0x4EC, + 0x4EE, 0x4EE, + 0x4F0, 0x4F0, + 0x4F2, 0x4F2, + 0x4F4, 0x4F4, + 0x4F6, 0x4F6, + 0x4F8, 0x4F8, + 0x4FA, 0x4FA, + 0x4FC, 0x4FC, + 0x4FE, 0x4FE, + 0x500, 0x500, + 0x502, 0x502, + 0x504, 0x504, + 0x506, 0x506, + 0x508, 0x508, + 0x50A, 0x50A, + 0x50C, 0x50C, + 0x50E, 0x50E, + 0x510, 0x510, + 0x512, 0x512, + 0x514, 0x514, + 0x516, 0x516, + 0x518, 0x518, + 0x51A, 0x51A, + 0x51C, 0x51C, + 0x51E, 0x51E, + 0x520, 0x520, + 0x522, 0x522, + 0x524, 0x524, + 0x526, 0x526, + 0x528, 0x528, + 0x52A, 0x52A, + 0x52C, 0x52C, + 0x52E, 0x52E, + 0x531, 0x556, + 0x10A0, 0x10C5, + 0x10C7, 0x10C7, + 0x10CD, 0x10CD, + 0x13A0, 0x13F5, + 0x1C90, 0x1CBA, + 0x1CBD, 0x1CBF, + 0x1E00, 0x1E00, + 0x1E02, 0x1E02, + 0x1E04, 0x1E04, + 0x1E06, 0x1E06, + 0x1E08, 0x1E08, + 0x1E0A, 0x1E0A, + 0x1E0C, 0x1E0C, + 0x1E0E, 0x1E0E, + 0x1E10, 0x1E10, + 0x1E12, 0x1E12, + 0x1E14, 0x1E14, + 0x1E16, 0x1E16, + 0x1E18, 0x1E18, + 0x1E1A, 0x1E1A, + 0x1E1C, 0x1E1C, + 0x1E1E, 0x1E1E, + 0x1E20, 0x1E20, + 0x1E22, 0x1E22, + 0x1E24, 0x1E24, + 0x1E26, 0x1E26, + 0x1E28, 0x1E28, + 0x1E2A, 0x1E2A, + 0x1E2C, 0x1E2C, + 0x1E2E, 0x1E2E, + 0x1E30, 0x1E30, + 0x1E32, 0x1E32, + 0x1E34, 0x1E34, + 0x1E36, 0x1E36, + 0x1E38, 0x1E38, + 0x1E3A, 0x1E3A, + 0x1E3C, 0x1E3C, + 0x1E3E, 0x1E3E, + 0x1E40, 0x1E40, + 0x1E42, 0x1E42, + 0x1E44, 0x1E44, + 0x1E46, 0x1E46, + 0x1E48, 0x1E48, + 0x1E4A, 0x1E4A, + 0x1E4C, 0x1E4C, + 0x1E4E, 0x1E4E, + 0x1E50, 0x1E50, + 0x1E52, 0x1E52, + 0x1E54, 0x1E54, + 0x1E56, 0x1E56, + 0x1E58, 0x1E58, + 0x1E5A, 0x1E5A, + 0x1E5C, 0x1E5C, + 0x1E5E, 0x1E5E, + 0x1E60, 0x1E60, + 0x1E62, 0x1E62, + 0x1E64, 0x1E64, + 0x1E66, 0x1E66, + 0x1E68, 0x1E68, + 0x1E6A, 0x1E6A, + 0x1E6C, 0x1E6C, + 0x1E6E, 0x1E6E, + 0x1E70, 0x1E70, + 0x1E72, 0x1E72, + 0x1E74, 0x1E74, + 0x1E76, 0x1E76, + 0x1E78, 0x1E78, + 0x1E7A, 0x1E7A, + 0x1E7C, 0x1E7C, + 0x1E7E, 0x1E7E, + 0x1E80, 0x1E80, + 0x1E82, 0x1E82, + 0x1E84, 0x1E84, + 0x1E86, 0x1E86, + 0x1E88, 0x1E88, + 0x1E8A, 0x1E8A, + 0x1E8C, 0x1E8C, + 0x1E8E, 0x1E8E, + 0x1E90, 0x1E90, + 0x1E92, 0x1E92, + 0x1E94, 0x1E94, + 0x1E9E, 0x1E9E, + 0x1EA0, 0x1EA0, + 0x1EA2, 0x1EA2, + 0x1EA4, 0x1EA4, + 0x1EA6, 0x1EA6, + 0x1EA8, 0x1EA8, + 0x1EAA, 0x1EAA, + 0x1EAC, 0x1EAC, + 0x1EAE, 0x1EAE, + 0x1EB0, 0x1EB0, + 0x1EB2, 0x1EB2, + 0x1EB4, 0x1EB4, + 0x1EB6, 0x1EB6, + 0x1EB8, 0x1EB8, + 0x1EBA, 0x1EBA, + 0x1EBC, 0x1EBC, + 0x1EBE, 0x1EBE, + 0x1EC0, 0x1EC0, + 0x1EC2, 0x1EC2, + 0x1EC4, 0x1EC4, + 0x1EC6, 0x1EC6, + 0x1EC8, 0x1EC8, + 0x1ECA, 0x1ECA, + 0x1ECC, 0x1ECC, + 0x1ECE, 0x1ECE, + 0x1ED0, 0x1ED0, + 0x1ED2, 0x1ED2, + 0x1ED4, 0x1ED4, + 0x1ED6, 0x1ED6, + 0x1ED8, 0x1ED8, + 0x1EDA, 0x1EDA, + 0x1EDC, 0x1EDC, + 0x1EDE, 0x1EDE, + 0x1EE0, 0x1EE0, + 0x1EE2, 0x1EE2, + 0x1EE4, 0x1EE4, + 0x1EE6, 0x1EE6, + 0x1EE8, 0x1EE8, + 0x1EEA, 0x1EEA, + 0x1EEC, 0x1EEC, + 0x1EEE, 0x1EEE, + 0x1EF0, 0x1EF0, + 0x1EF2, 0x1EF2, + 0x1EF4, 0x1EF4, + 0x1EF6, 0x1EF6, + 0x1EF8, 0x1EF8, + 0x1EFA, 0x1EFA, + 0x1EFC, 0x1EFC, + 0x1EFE, 0x1EFE, + 0x1F08, 0x1F0F, + 0x1F18, 0x1F1D, + 0x1F28, 0x1F2F, + 0x1F38, 0x1F3F, + 0x1F48, 0x1F4D, + 0x1F59, 0x1F59, + 0x1F5B, 0x1F5B, + 0x1F5D, 0x1F5D, + 0x1F5F, 0x1F5F, + 0x1F68, 0x1F6F, + 0x1FB8, 0x1FBB, + 0x1FC8, 0x1FCB, + 0x1FD8, 0x1FDB, + 0x1FE8, 0x1FEC, + 0x1FF8, 0x1FFB, + 0x2102, 0x2102, + 0x2107, 0x2107, + 0x210B, 0x210D, + 0x2110, 0x2112, + 0x2115, 0x2115, + 0x2119, 0x211D, + 0x2124, 0x2124, + 0x2126, 0x2126, + 0x2128, 0x2128, + 0x212A, 0x212D, + 0x2130, 0x2133, + 0x213E, 0x213F, + 0x2145, 0x2145, + 0x2160, 0x216F, + 0x2183, 0x2183, + 0x24B6, 0x24CF, + 0x2C00, 0x2C2F, + 0x2C60, 0x2C60, + 0x2C62, 0x2C64, + 0x2C67, 0x2C67, + 0x2C69, 0x2C69, + 0x2C6B, 0x2C6B, + 0x2C6D, 0x2C70, + 0x2C72, 0x2C72, + 0x2C75, 0x2C75, + 0x2C7E, 0x2C80, + 0x2C82, 0x2C82, + 0x2C84, 0x2C84, + 0x2C86, 0x2C86, + 0x2C88, 0x2C88, + 0x2C8A, 0x2C8A, + 0x2C8C, 0x2C8C, + 0x2C8E, 0x2C8E, + 0x2C90, 0x2C90, + 0x2C92, 0x2C92, + 0x2C94, 0x2C94, + 0x2C96, 0x2C96, + 0x2C98, 0x2C98, + 0x2C9A, 0x2C9A, + 0x2C9C, 0x2C9C, + 0x2C9E, 0x2C9E, + 0x2CA0, 0x2CA0, + 0x2CA2, 0x2CA2, + 0x2CA4, 0x2CA4, + 0x2CA6, 0x2CA6, + 0x2CA8, 0x2CA8, + 0x2CAA, 0x2CAA, + 0x2CAC, 0x2CAC, + 0x2CAE, 0x2CAE, + 0x2CB0, 0x2CB0, + 0x2CB2, 0x2CB2, + 0x2CB4, 0x2CB4, + 0x2CB6, 0x2CB6, + 0x2CB8, 0x2CB8, + 0x2CBA, 0x2CBA, + 0x2CBC, 0x2CBC, + 0x2CBE, 0x2CBE, + 0x2CC0, 0x2CC0, + 0x2CC2, 0x2CC2, + 0x2CC4, 0x2CC4, + 0x2CC6, 0x2CC6, + 0x2CC8, 0x2CC8, + 0x2CCA, 0x2CCA, + 0x2CCC, 0x2CCC, + 0x2CCE, 0x2CCE, + 0x2CD0, 0x2CD0, + 0x2CD2, 0x2CD2, + 0x2CD4, 0x2CD4, + 0x2CD6, 0x2CD6, + 0x2CD8, 0x2CD8, + 0x2CDA, 0x2CDA, + 0x2CDC, 0x2CDC, + 0x2CDE, 0x2CDE, + 0x2CE0, 0x2CE0, + 0x2CE2, 0x2CE2, + 0x2CEB, 0x2CEB, + 0x2CED, 0x2CED, + 0x2CF2, 0x2CF2, + 0xA640, 0xA640, + 0xA642, 0xA642, + 0xA644, 0xA644, + 0xA646, 0xA646, + 0xA648, 0xA648, + 0xA64A, 0xA64A, + 0xA64C, 0xA64C, + 0xA64E, 0xA64E, + 0xA650, 0xA650, + 0xA652, 0xA652, + 0xA654, 0xA654, + 0xA656, 0xA656, + 0xA658, 0xA658, + 0xA65A, 0xA65A, + 0xA65C, 0xA65C, + 0xA65E, 0xA65E, + 0xA660, 0xA660, + 0xA662, 0xA662, + 0xA664, 0xA664, + 0xA666, 0xA666, + 0xA668, 0xA668, + 0xA66A, 0xA66A, + 0xA66C, 0xA66C, + 0xA680, 0xA680, + 0xA682, 0xA682, + 0xA684, 0xA684, + 0xA686, 0xA686, + 0xA688, 0xA688, + 0xA68A, 0xA68A, + 0xA68C, 0xA68C, + 0xA68E, 0xA68E, + 0xA690, 0xA690, + 0xA692, 0xA692, + 0xA694, 0xA694, + 0xA696, 0xA696, + 0xA698, 0xA698, + 0xA69A, 0xA69A, + 0xA722, 0xA722, + 0xA724, 0xA724, + 0xA726, 0xA726, + 0xA728, 0xA728, + 0xA72A, 0xA72A, + 0xA72C, 0xA72C, + 0xA72E, 0xA72E, + 0xA732, 0xA732, + 0xA734, 0xA734, + 0xA736, 0xA736, + 0xA738, 0xA738, + 0xA73A, 0xA73A, + 0xA73C, 0xA73C, + 0xA73E, 0xA73E, + 0xA740, 0xA740, + 0xA742, 0xA742, + 0xA744, 0xA744, + 0xA746, 0xA746, + 0xA748, 0xA748, + 0xA74A, 0xA74A, + 0xA74C, 0xA74C, + 0xA74E, 0xA74E, + 0xA750, 0xA750, + 0xA752, 0xA752, + 0xA754, 0xA754, + 0xA756, 0xA756, + 0xA758, 0xA758, + 0xA75A, 0xA75A, + 0xA75C, 0xA75C, + 0xA75E, 0xA75E, + 0xA760, 0xA760, + 0xA762, 0xA762, + 0xA764, 0xA764, + 0xA766, 0xA766, + 0xA768, 0xA768, + 0xA76A, 0xA76A, + 0xA76C, 0xA76C, + 0xA76E, 0xA76E, + 0xA779, 0xA779, + 0xA77B, 0xA77B, + 0xA77D, 0xA77E, + 0xA780, 0xA780, + 0xA782, 0xA782, + 0xA784, 0xA784, + 0xA786, 0xA786, + 0xA78B, 0xA78B, + 0xA78D, 0xA78D, + 0xA790, 0xA790, + 0xA792, 0xA792, + 0xA796, 0xA796, + 0xA798, 0xA798, + 0xA79A, 0xA79A, + 0xA79C, 0xA79C, + 0xA79E, 0xA79E, + 0xA7A0, 0xA7A0, + 0xA7A2, 0xA7A2, + 0xA7A4, 0xA7A4, + 0xA7A6, 0xA7A6, + 0xA7A8, 0xA7A8, + 0xA7AA, 0xA7AE, + 0xA7B0, 0xA7B4, + 0xA7B6, 0xA7B6, + 0xA7B8, 0xA7B8, + 0xA7BA, 0xA7BA, + 0xA7BC, 0xA7BC, + 0xA7BE, 0xA7BE, + 0xA7C0, 0xA7C0, + 0xA7C2, 0xA7C2, + 0xA7C4, 0xA7C7, + 0xA7C9, 0xA7C9, + 0xA7D0, 0xA7D0, + 0xA7D6, 0xA7D6, + 0xA7D8, 0xA7D8, + 0xA7F5, 0xA7F5, + 0xFF21, 0xFF3A, + 0x10400, 0x10427, + 0x104B0, 0x104D3, + 0x10570, 0x1057A, + 0x1057C, 0x1058A, + 0x1058C, 0x10592, + 0x10594, 0x10595, + 0x10C80, 0x10CB2, + 0x118A0, 0x118BF, + 0x16E40, 0x16E5F, + 0x1D400, 0x1D419, + 0x1D434, 0x1D44D, + 0x1D468, 0x1D481, + 0x1D49C, 0x1D49C, + 0x1D49E, 0x1D49F, + 0x1D4A2, 0x1D4A2, + 0x1D4A5, 0x1D4A6, + 0x1D4A9, 0x1D4AC, + 0x1D4AE, 0x1D4B5, + 0x1D4D0, 0x1D4E9, + 0x1D504, 0x1D505, + 0x1D507, 0x1D50A, + 0x1D50D, 0x1D514, + 0x1D516, 0x1D51C, + 0x1D538, 0x1D539, + 0x1D53B, 0x1D53E, + 0x1D540, 0x1D544, + 0x1D546, 0x1D546, + 0x1D54A, 0x1D550, + 0x1D56C, 0x1D585, + 0x1D5A0, 0x1D5B9, + 0x1D5D4, 0x1D5ED, + 0x1D608, 0x1D621, + 0x1D63C, 0x1D655, + 0x1D670, 0x1D689, + 0x1D6A8, 0x1D6C0, + 0x1D6E2, 0x1D6FA, + 0x1D71C, 0x1D734, + 0x1D756, 0x1D76E, + 0x1D790, 0x1D7A8, + 0x1D7CA, 0x1D7CA, + 0x1E900, 0x1E921, + 0x1F130, 0x1F149, + 0x1F150, 0x1F169, + 0x1F170, 0x1F189, +}; + +/** + * Binary search through the given list of codepoints to see if the given + * codepoint is in the list. + */ +static bool +pm_unicode_codepoint_match(pm_unicode_codepoint_t codepoint, const pm_unicode_codepoint_t *codepoints, size_t size) { + size_t start = 0; + size_t end = size; + + while (start < end) { + size_t middle = start + (end - start) / 2; + if ((middle % 2) != 0) middle--; + + if (codepoint >= codepoints[middle] && codepoint <= codepoints[middle + 1]) { + return true; + } + + if (codepoint < codepoints[middle]) { + end = middle; + } else { + start = middle + 2; + } + } + + return false; +} + +/** + * A state transition table for decoding UTF-8. + * + * Copyright (c) 2008-2009 Bjoern Hoehrmann + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +static const uint8_t pm_utf_8_dfa[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 +}; + +/** + * Given a pointer to a string and the number of bytes remaining in the string, + * decode the next UTF-8 codepoint and return it. The number of bytes consumed + * is returned in the width out parameter. + */ +static pm_unicode_codepoint_t +pm_utf_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) { + assert(n >= 1); + size_t maximum = (size_t) n; + + uint32_t codepoint; + uint32_t state = 0; + + for (size_t index = 0; index < 4 && index < maximum; index++) { + uint32_t byte = b[index]; + uint32_t type = pm_utf_8_dfa[byte]; + + codepoint = (state != 0) ? + (byte & 0x3fu) | (codepoint << 6) : + (0xffu >> type) & (byte); + + state = pm_utf_8_dfa[256 + (state * 16) + type]; + if (!state) { + *width = index + 1; + return (pm_unicode_codepoint_t) codepoint; + } + } + + *width = 0; + return 0; +} + +static size_t +pm_encoding_utf_8_char_width(const uint8_t *b, ptrdiff_t n) { + size_t width; + pm_utf_8_codepoint(b, n, &width); + return width; +} + +/** + * Return the size of the next character in the UTF-8 encoding if it is an + * alphabetical character. + */ +size_t +pm_encoding_utf_8_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (*b < 0x80) { + return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) ? 1 : 0; + } + + size_t width; + pm_unicode_codepoint_t codepoint = pm_utf_8_codepoint(b, n, &width); + + if (codepoint <= 0xFF) { + return (pm_encoding_unicode_table[(uint8_t) codepoint] & PRISM_ENCODING_ALPHABETIC_BIT) ? width : 0; + } else { + return pm_unicode_codepoint_match(codepoint, unicode_alpha_codepoints, UNICODE_ALPHA_CODEPOINTS_LENGTH) ? width : 0; + } +} + +/** + * Return the size of the next character in the UTF-8 encoding if it is an + * alphanumeric character. + */ +size_t +pm_encoding_utf_8_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (*b < 0x80) { + return (pm_encoding_unicode_table[*b] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; + } + + size_t width; + pm_unicode_codepoint_t codepoint = pm_utf_8_codepoint(b, n, &width); + + if (codepoint <= 0xFF) { + return (pm_encoding_unicode_table[(uint8_t) codepoint] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? width : 0; + } else { + return pm_unicode_codepoint_match(codepoint, unicode_alnum_codepoints, UNICODE_ALNUM_CODEPOINTS_LENGTH) ? width : 0; + } +} + +/** + * Return true if the next character in the UTF-8 encoding if it is an uppercase + * character. + */ +bool +pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (*b < 0x80) { + return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false; + } + + size_t width; + pm_unicode_codepoint_t codepoint = pm_utf_8_codepoint(b, n, &width); + + if (codepoint <= 0xFF) { + return (pm_encoding_unicode_table[(uint8_t) codepoint] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false; + } else { + return pm_unicode_codepoint_match(codepoint, unicode_isupper_codepoints, UNICODE_ISUPPER_CODEPOINTS_LENGTH) ? true : false; + } +} + +#undef UNICODE_ALPHA_CODEPOINTS_LENGTH +#undef UNICODE_ALNUM_CODEPOINTS_LENGTH +#undef UNICODE_ISUPPER_CODEPOINTS_LENGTH + +/** UTF-8 */ +pm_encoding_t pm_encoding_utf_8 = { + .name = "utf-8", + .char_width = pm_encoding_utf_8_char_width, + .alnum_char = pm_encoding_utf_8_alnum_char, + .alpha_char = pm_encoding_utf_8_alpha_char, + .isupper_char = pm_encoding_utf_8_isupper_char, + .multibyte = true +}; + +/** UTF8-mac */ +pm_encoding_t pm_encoding_utf8_mac = { + .name = "utf8-mac", + .char_width = pm_encoding_utf_8_char_width, + .alnum_char = pm_encoding_utf_8_alnum_char, + .alpha_char = pm_encoding_utf_8_alpha_char, + .isupper_char = pm_encoding_utf_8_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_windows_31j.c b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_windows_31j.c new file mode 100644 index 00000000..848a9efd --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/enc/pm_windows_31j.c @@ -0,0 +1,57 @@ +#include "prism/enc/pm_encoding.h" + +static size_t +pm_encoding_windows_31j_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters. + if (*b < 0x80 || (*b >= 0xA1 && *b <= 0xDF)) { + return 1; + } + + // These are the double byte characters. + if ( + (n > 1) && + ((b[0] >= 0x81 && b[0] <= 0x9F) || (b[0] >= 0xE0 && b[0] <= 0xFC)) && + (b[1] >= 0x40 && b[1] <= 0xFC) + ) { + return 2; + } + + return 0; +} + +static size_t +pm_encoding_windows_31j_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_windows_31j_char_width(b, n) == 1) { + return pm_encoding_ascii_alpha_char(b, n); + } else { + return 0; + } +} + +static size_t +pm_encoding_windows_31j_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_windows_31j_char_width(b, n) == 1) { + return pm_encoding_ascii_alnum_char(b, n); + } else { + return 0; + } +} + +static bool +pm_encoding_windows_31j_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (pm_encoding_windows_31j_char_width(b, n) == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else { + return false; + } +} + +/** Windows-31J */ +pm_encoding_t pm_encoding_windows_31j = { + .name = "windows-31j", + .char_width = pm_encoding_windows_31j_char_width, + .alnum_char = pm_encoding_windows_31j_alnum_char, + .alpha_char = pm_encoding_windows_31j_alpha_char, + .isupper_char = pm_encoding_windows_31j_isupper_char, + .multibyte = true +}; diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/node.c b/crates/prism-sys/vendor/prism-0.18.0/src/node.c new file mode 100644 index 00000000..7e26a7c6 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/node.c @@ -0,0 +1,2736 @@ +/******************************************************************************/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/node.c.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +#line 2 "node.c.erb" +#include "prism/node.h" + +static void +pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize); + +/** + * Calculate the size of the node list in bytes. + */ +static size_t +pm_node_list_memsize(pm_node_list_t *node_list, pm_memsize_t *memsize) { + size_t size = sizeof(pm_node_list_t) + (node_list->capacity * sizeof(pm_node_t *)); + for (size_t index = 0; index < node_list->size; index++) { + pm_node_memsize_node(node_list->nodes[index], memsize); + } + return size; +} + +/** + * Append a new node onto the end of the node list. + */ +void +pm_node_list_append(pm_node_list_t *list, pm_node_t *node) { + if (list->size == list->capacity) { + list->capacity = list->capacity == 0 ? 4 : list->capacity * 2; + list->nodes = (pm_node_t **) realloc(list->nodes, sizeof(pm_node_t *) * list->capacity); + } + list->nodes[list->size++] = node; +} + +PRISM_EXPORTED_FUNCTION void +pm_node_destroy(pm_parser_t *parser, pm_node_t *node); + +/** + * Deallocate the inner memory of a list of nodes. The parser argument is not + * used, but is here for the future possibility of pre-allocating memory pools. + */ +static void +pm_node_list_free(pm_parser_t *parser, pm_node_list_t *list) { + if (list->capacity > 0) { + for (size_t index = 0; index < list->size; index++) { + pm_node_destroy(parser, list->nodes[index]); + } + free(list->nodes); + } +} + +/** + * Deallocate the space for a pm_node_t. Similarly to pm_node_alloc, we're not + * using the parser argument, but it's there to allow for the future possibility + * of pre-allocating larger memory pools. + */ +PRISM_EXPORTED_FUNCTION void +pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { +#line 58 "node.c.erb" + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_alias_global_variable_node_t *cast = (pm_alias_global_variable_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->new_name); + pm_node_destroy(parser, (pm_node_t *)cast->old_name); + break; + } +#line 58 "node.c.erb" + case PM_ALIAS_METHOD_NODE: { + pm_alias_method_node_t *cast = (pm_alias_method_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->new_name); + pm_node_destroy(parser, (pm_node_t *)cast->old_name); + break; + } +#line 58 "node.c.erb" + case PM_ALTERNATION_PATTERN_NODE: { + pm_alternation_pattern_node_t *cast = (pm_alternation_pattern_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->left); + pm_node_destroy(parser, (pm_node_t *)cast->right); + break; + } +#line 58 "node.c.erb" + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->left); + pm_node_destroy(parser, (pm_node_t *)cast->right); + break; + } +#line 58 "node.c.erb" + case PM_ARGUMENTS_NODE: { + pm_arguments_node_t *cast = (pm_arguments_node_t *) node; + pm_node_list_free(parser, &cast->arguments); + break; + } +#line 58 "node.c.erb" + case PM_ARRAY_NODE: { + pm_array_node_t *cast = (pm_array_node_t *) node; + pm_node_list_free(parser, &cast->elements); + break; + } +#line 58 "node.c.erb" + case PM_ARRAY_PATTERN_NODE: { + pm_array_pattern_node_t *cast = (pm_array_pattern_node_t *) node; + if (cast->constant != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->constant); + } + pm_node_list_free(parser, &cast->requireds); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_free(parser, &cast->posts); + break; + } +#line 58 "node.c.erb" + case PM_ASSOC_NODE: { + pm_assoc_node_t *cast = (pm_assoc_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->key); + if (cast->value != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->value); + } + break; + } +#line 58 "node.c.erb" + case PM_ASSOC_SPLAT_NODE: { + pm_assoc_splat_node_t *cast = (pm_assoc_splat_node_t *) node; + if (cast->value != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->value); + } + break; + } +#line 58 "node.c.erb" + case PM_BACK_REFERENCE_READ_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + if (cast->rescue_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rescue_clause); + } + if (cast->else_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->else_clause); + } + if (cast->ensure_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->ensure_clause); + } + break; + } +#line 58 "node.c.erb" + case PM_BLOCK_ARGUMENT_NODE: { + pm_block_argument_node_t *cast = (pm_block_argument_node_t *) node; + if (cast->expression != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->expression); + } + break; + } +#line 58 "node.c.erb" + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_BLOCK_NODE: { + pm_block_node_t *cast = (pm_block_node_t *) node; + pm_constant_id_list_free(&cast->locals); + if (cast->parameters != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parameters); + } + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 58 "node.c.erb" + case PM_BLOCK_PARAMETER_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_BLOCK_PARAMETERS_NODE: { + pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) node; + if (cast->parameters != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parameters); + } + pm_node_list_free(parser, &cast->locals); + break; + } +#line 58 "node.c.erb" + case PM_BREAK_NODE: { + pm_break_node_t *cast = (pm_break_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + break; + } +#line 58 "node.c.erb" + case PM_CALL_AND_WRITE_NODE: { + pm_call_and_write_node_t *cast = (pm_call_and_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + break; + } +#line 58 "node.c.erb" + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_call_operator_write_node_t *cast = (pm_call_operator_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CALL_OR_WRITE_NODE: { + pm_call_or_write_node_t *cast = (pm_call_or_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CAPTURE_PATTERN_NODE: { + pm_capture_pattern_node_t *cast = (pm_capture_pattern_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + pm_node_destroy(parser, (pm_node_t *)cast->target); + break; + } +#line 58 "node.c.erb" + case PM_CASE_MATCH_NODE: { + pm_case_match_node_t *cast = (pm_case_match_node_t *) node; + if (cast->predicate != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + } + pm_node_list_free(parser, &cast->conditions); + if (cast->consequent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->consequent); + } + break; + } +#line 58 "node.c.erb" + case PM_CASE_NODE: { + pm_case_node_t *cast = (pm_case_node_t *) node; + if (cast->predicate != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + } + pm_node_list_free(parser, &cast->conditions); + if (cast->consequent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->consequent); + } + break; + } +#line 58 "node.c.erb" + case PM_CLASS_NODE: { + pm_class_node_t *cast = (pm_class_node_t *) node; + pm_constant_id_list_free(&cast->locals); + pm_node_destroy(parser, (pm_node_t *)cast->constant_path); + if (cast->superclass != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->superclass); + } + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 58 "node.c.erb" + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_class_variable_and_write_node_t *cast = (pm_class_variable_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_class_variable_operator_write_node_t *cast = (pm_class_variable_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_class_variable_or_write_node_t *cast = (pm_class_variable_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CLASS_VARIABLE_READ_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_CLASS_VARIABLE_TARGET_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_AND_WRITE_NODE: { + pm_constant_and_write_node_t *cast = (pm_constant_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_constant_operator_write_node_t *cast = (pm_constant_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_OR_WRITE_NODE: { + pm_constant_or_write_node_t *cast = (pm_constant_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_constant_path_and_write_node_t *cast = (pm_constant_path_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->target); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_PATH_NODE: { + pm_constant_path_node_t *cast = (pm_constant_path_node_t *) node; + if (cast->parent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parent); + } + pm_node_destroy(parser, (pm_node_t *)cast->child); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_constant_path_operator_write_node_t *cast = (pm_constant_path_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->target); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_constant_path_or_write_node_t *cast = (pm_constant_path_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->target); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_constant_path_target_node_t *cast = (pm_constant_path_target_node_t *) node; + if (cast->parent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parent); + } + pm_node_destroy(parser, (pm_node_t *)cast->child); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_constant_path_write_node_t *cast = (pm_constant_path_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->target); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_READ_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_TARGET_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_CONSTANT_WRITE_NODE: { + pm_constant_write_node_t *cast = (pm_constant_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_DEF_NODE: { + pm_def_node_t *cast = (pm_def_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->parameters != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parameters); + } + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + pm_constant_id_list_free(&cast->locals); + break; + } +#line 58 "node.c.erb" + case PM_DEFINED_NODE: { + pm_defined_node_t *cast = (pm_defined_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_EMBEDDED_VARIABLE_NODE: { + pm_embedded_variable_node_t *cast = (pm_embedded_variable_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->variable); + break; + } +#line 58 "node.c.erb" + case PM_ENSURE_NODE: { + pm_ensure_node_t *cast = (pm_ensure_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_FALSE_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_FIND_PATTERN_NODE: { + pm_find_pattern_node_t *cast = (pm_find_pattern_node_t *) node; + if (cast->constant != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->constant); + } + pm_node_destroy(parser, (pm_node_t *)cast->left); + pm_node_list_free(parser, &cast->requireds); + pm_node_destroy(parser, (pm_node_t *)cast->right); + break; + } +#line 58 "node.c.erb" + case PM_FLIP_FLOP_NODE: { + pm_flip_flop_node_t *cast = (pm_flip_flop_node_t *) node; + if (cast->left != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->left); + } + if (cast->right != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->right); + } + break; + } +#line 58 "node.c.erb" + case PM_FLOAT_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_FOR_NODE: { + pm_for_node_t *cast = (pm_for_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->index); + pm_node_destroy(parser, (pm_node_t *)cast->collection); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_FORWARDING_ARGUMENTS_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_FORWARDING_PARAMETER_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_FORWARDING_SUPER_NODE: { + pm_forwarding_super_node_t *cast = (pm_forwarding_super_node_t *) node; + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + break; + } +#line 58 "node.c.erb" + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_global_variable_and_write_node_t *cast = (pm_global_variable_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_global_variable_operator_write_node_t *cast = (pm_global_variable_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_global_variable_or_write_node_t *cast = (pm_global_variable_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_GLOBAL_VARIABLE_READ_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_HASH_NODE: { + pm_hash_node_t *cast = (pm_hash_node_t *) node; + pm_node_list_free(parser, &cast->elements); + break; + } +#line 58 "node.c.erb" + case PM_HASH_PATTERN_NODE: { + pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node; + if (cast->constant != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->constant); + } + pm_node_list_free(parser, &cast->elements); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + break; + } +#line 58 "node.c.erb" + case PM_IF_NODE: { + pm_if_node_t *cast = (pm_if_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + if (cast->consequent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->consequent); + } + break; + } +#line 58 "node.c.erb" + case PM_IMAGINARY_NODE: { + pm_imaginary_node_t *cast = (pm_imaginary_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->numeric); + break; + } +#line 58 "node.c.erb" + case PM_IMPLICIT_NODE: { + pm_implicit_node_t *cast = (pm_implicit_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_IMPLICIT_REST_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_IN_NODE: { + pm_in_node_t *cast = (pm_in_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->pattern); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_INDEX_AND_WRITE_NODE: { + pm_index_and_write_node_t *cast = (pm_index_and_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_index_operator_write_node_t *cast = (pm_index_operator_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_INDEX_OR_WRITE_NODE: { + pm_index_or_write_node_t *cast = (pm_index_or_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_instance_variable_and_write_node_t *cast = (pm_instance_variable_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_instance_variable_operator_write_node_t *cast = (pm_instance_variable_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_instance_variable_or_write_node_t *cast = (pm_instance_variable_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_INSTANCE_VARIABLE_READ_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_INTEGER_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_interpolated_match_last_line_node_t *cast = (pm_interpolated_match_last_line_node_t *) node; + pm_node_list_free(parser, &cast->parts); + break; + } +#line 58 "node.c.erb" + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_interpolated_regular_expression_node_t *cast = (pm_interpolated_regular_expression_node_t *) node; + pm_node_list_free(parser, &cast->parts); + break; + } +#line 58 "node.c.erb" + case PM_INTERPOLATED_STRING_NODE: { + pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; + pm_node_list_free(parser, &cast->parts); + break; + } +#line 58 "node.c.erb" + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; + pm_node_list_free(parser, &cast->parts); + break; + } +#line 58 "node.c.erb" + case PM_INTERPOLATED_X_STRING_NODE: { + pm_interpolated_x_string_node_t *cast = (pm_interpolated_x_string_node_t *) node; + pm_node_list_free(parser, &cast->parts); + break; + } +#line 58 "node.c.erb" + case PM_KEYWORD_HASH_NODE: { + pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; + pm_node_list_free(parser, &cast->elements); + break; + } +#line 58 "node.c.erb" + case PM_KEYWORD_REST_PARAMETER_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_LAMBDA_NODE: { + pm_lambda_node_t *cast = (pm_lambda_node_t *) node; + pm_constant_id_list_free(&cast->locals); + if (cast->parameters != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parameters); + } + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 58 "node.c.erb" + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_local_variable_and_write_node_t *cast = (pm_local_variable_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_local_variable_operator_write_node_t *cast = (pm_local_variable_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_local_variable_or_write_node_t *cast = (pm_local_variable_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_LOCAL_VARIABLE_READ_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_LOCAL_VARIABLE_TARGET_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_MATCH_LAST_LINE_NODE: { + pm_match_last_line_node_t *cast = (pm_match_last_line_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 58 "node.c.erb" + case PM_MATCH_PREDICATE_NODE: { + pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + pm_node_destroy(parser, (pm_node_t *)cast->pattern); + break; + } +#line 58 "node.c.erb" + case PM_MATCH_REQUIRED_NODE: { + pm_match_required_node_t *cast = (pm_match_required_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + pm_node_destroy(parser, (pm_node_t *)cast->pattern); + break; + } +#line 58 "node.c.erb" + case PM_MATCH_WRITE_NODE: { + pm_match_write_node_t *cast = (pm_match_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->call); + pm_node_list_free(parser, &cast->targets); + break; + } +#line 58 "node.c.erb" + case PM_MISSING_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_MODULE_NODE: { + pm_module_node_t *cast = (pm_module_node_t *) node; + pm_constant_id_list_free(&cast->locals); + pm_node_destroy(parser, (pm_node_t *)cast->constant_path); + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 58 "node.c.erb" + case PM_MULTI_TARGET_NODE: { + pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; + pm_node_list_free(parser, &cast->lefts); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_free(parser, &cast->rights); + break; + } +#line 58 "node.c.erb" + case PM_MULTI_WRITE_NODE: { + pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; + pm_node_list_free(parser, &cast->lefts); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_free(parser, &cast->rights); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_NEXT_NODE: { + pm_next_node_t *cast = (pm_next_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + break; + } +#line 58 "node.c.erb" + case PM_NIL_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_NO_KEYWORDS_PARAMETER_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_NUMBERED_REFERENCE_READ_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_optional_keyword_parameter_node_t *cast = (pm_optional_keyword_parameter_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_OPTIONAL_PARAMETER_NODE: { + pm_optional_parameter_node_t *cast = (pm_optional_parameter_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 58 "node.c.erb" + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->left); + pm_node_destroy(parser, (pm_node_t *)cast->right); + break; + } +#line 58 "node.c.erb" + case PM_PARAMETERS_NODE: { + pm_parameters_node_t *cast = (pm_parameters_node_t *) node; + pm_node_list_free(parser, &cast->requireds); + pm_node_list_free(parser, &cast->optionals); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_free(parser, &cast->posts); + pm_node_list_free(parser, &cast->keywords); + if (cast->keyword_rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->keyword_rest); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + break; + } +#line 58 "node.c.erb" + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 58 "node.c.erb" + case PM_PINNED_EXPRESSION_NODE: { + pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->expression); + break; + } +#line 58 "node.c.erb" + case PM_PINNED_VARIABLE_NODE: { + pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->variable); + break; + } +#line 58 "node.c.erb" + case PM_POST_EXECUTION_NODE: { + pm_post_execution_node_t *cast = (pm_post_execution_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_PRE_EXECUTION_NODE: { + pm_pre_execution_node_t *cast = (pm_pre_execution_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_PROGRAM_NODE: { + pm_program_node_t *cast = (pm_program_node_t *) node; + pm_constant_id_list_free(&cast->locals); + pm_node_destroy(parser, (pm_node_t *)cast->statements); + break; + } +#line 58 "node.c.erb" + case PM_RANGE_NODE: { + pm_range_node_t *cast = (pm_range_node_t *) node; + if (cast->left != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->left); + } + if (cast->right != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->right); + } + break; + } +#line 58 "node.c.erb" + case PM_RATIONAL_NODE: { + pm_rational_node_t *cast = (pm_rational_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->numeric); + break; + } +#line 58 "node.c.erb" + case PM_REDO_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_REGULAR_EXPRESSION_NODE: { + pm_regular_expression_node_t *cast = (pm_regular_expression_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 58 "node.c.erb" + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_REQUIRED_PARAMETER_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->expression); + pm_node_destroy(parser, (pm_node_t *)cast->rescue_expression); + break; + } +#line 58 "node.c.erb" + case PM_RESCUE_NODE: { + pm_rescue_node_t *cast = (pm_rescue_node_t *) node; + pm_node_list_free(parser, &cast->exceptions); + if (cast->reference != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->reference); + } + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + if (cast->consequent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->consequent); + } + break; + } +#line 58 "node.c.erb" + case PM_REST_PARAMETER_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_RETRY_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_RETURN_NODE: { + pm_return_node_t *cast = (pm_return_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + break; + } +#line 58 "node.c.erb" + case PM_SELF_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_SINGLETON_CLASS_NODE: { + pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; + pm_constant_id_list_free(&cast->locals); + pm_node_destroy(parser, (pm_node_t *)cast->expression); + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 58 "node.c.erb" + case PM_SOURCE_ENCODING_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_SOURCE_FILE_NODE: { + pm_source_file_node_t *cast = (pm_source_file_node_t *) node; + pm_string_free(&cast->filepath); + break; + } +#line 58 "node.c.erb" + case PM_SOURCE_LINE_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_SPLAT_NODE: { + pm_splat_node_t *cast = (pm_splat_node_t *) node; + if (cast->expression != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->expression); + } + break; + } +#line 58 "node.c.erb" + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + pm_node_list_free(parser, &cast->body); + break; + } +#line 58 "node.c.erb" + case PM_STRING_NODE: { + pm_string_node_t *cast = (pm_string_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 58 "node.c.erb" + case PM_SUPER_NODE: { + pm_super_node_t *cast = (pm_super_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + break; + } +#line 58 "node.c.erb" + case PM_SYMBOL_NODE: { + pm_symbol_node_t *cast = (pm_symbol_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 58 "node.c.erb" + case PM_TRUE_NODE: { + break; + } +#line 58 "node.c.erb" + case PM_UNDEF_NODE: { + pm_undef_node_t *cast = (pm_undef_node_t *) node; + pm_node_list_free(parser, &cast->names); + break; + } +#line 58 "node.c.erb" + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + if (cast->consequent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->consequent); + } + break; + } +#line 58 "node.c.erb" + case PM_UNTIL_NODE: { + pm_until_node_t *cast = (pm_until_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_WHEN_NODE: { + pm_when_node_t *cast = (pm_when_node_t *) node; + pm_node_list_free(parser, &cast->conditions); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_WHILE_NODE: { + pm_while_node_t *cast = (pm_while_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 58 "node.c.erb" + case PM_X_STRING_NODE: { + pm_x_string_node_t *cast = (pm_x_string_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 58 "node.c.erb" + case PM_YIELD_NODE: { + pm_yield_node_t *cast = (pm_yield_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + break; + } +#line 85 "node.c.erb" + default: + assert(false && "unreachable"); + break; + } + free(node); +} + +static void +pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { + memsize->node_count++; + + switch (PM_NODE_TYPE(node)) { + // We do not calculate memsize of a ScopeNode + // as it should never be generated + case PM_SCOPE_NODE: + return; +#line 103 "node.c.erb" + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_alias_global_variable_node_t *cast = (pm_alias_global_variable_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->new_name, memsize); + pm_node_memsize_node((pm_node_t *)cast->old_name, memsize); + break; + } +#line 103 "node.c.erb" + case PM_ALIAS_METHOD_NODE: { + pm_alias_method_node_t *cast = (pm_alias_method_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->new_name, memsize); + pm_node_memsize_node((pm_node_t *)cast->old_name, memsize); + break; + } +#line 103 "node.c.erb" + case PM_ALTERNATION_PATTERN_NODE: { + pm_alternation_pattern_node_t *cast = (pm_alternation_pattern_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->left, memsize); + pm_node_memsize_node((pm_node_t *)cast->right, memsize); + break; + } +#line 103 "node.c.erb" + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->left, memsize); + pm_node_memsize_node((pm_node_t *)cast->right, memsize); + break; + } +#line 103 "node.c.erb" + case PM_ARGUMENTS_NODE: { + pm_arguments_node_t *cast = (pm_arguments_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->arguments, memsize); + break; + } +#line 103 "node.c.erb" + case PM_ARRAY_NODE: { + pm_array_node_t *cast = (pm_array_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->elements, memsize); + break; + } +#line 103 "node.c.erb" + case PM_ARRAY_PATTERN_NODE: { + pm_array_pattern_node_t *cast = (pm_array_pattern_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 2; + if (cast->constant != NULL) { + pm_node_memsize_node((pm_node_t *)cast->constant, memsize); + } + memsize->memsize += pm_node_list_memsize(&cast->requireds, memsize); + if (cast->rest != NULL) { + pm_node_memsize_node((pm_node_t *)cast->rest, memsize); + } + memsize->memsize += pm_node_list_memsize(&cast->posts, memsize); + break; + } +#line 103 "node.c.erb" + case PM_ASSOC_NODE: { + pm_assoc_node_t *cast = (pm_assoc_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->key, memsize); + if (cast->value != NULL) { + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_ASSOC_SPLAT_NODE: { + pm_assoc_splat_node_t *cast = (pm_assoc_splat_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->value != NULL) { + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_BACK_REFERENCE_READ_NODE: { + pm_back_reference_read_node_t *cast = (pm_back_reference_read_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + if (cast->rescue_clause != NULL) { + pm_node_memsize_node((pm_node_t *)cast->rescue_clause, memsize); + } + if (cast->else_clause != NULL) { + pm_node_memsize_node((pm_node_t *)cast->else_clause, memsize); + } + if (cast->ensure_clause != NULL) { + pm_node_memsize_node((pm_node_t *)cast->ensure_clause, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_BLOCK_ARGUMENT_NODE: { + pm_block_argument_node_t *cast = (pm_block_argument_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->expression != NULL) { + pm_node_memsize_node((pm_node_t *)cast->expression, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + pm_block_local_variable_node_t *cast = (pm_block_local_variable_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_BLOCK_NODE: { + pm_block_node_t *cast = (pm_block_node_t *) node; + memsize->memsize += sizeof(*cast); + // Constant id lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_constant_id_list_t) * 1; + memsize->memsize += pm_constant_id_list_memsize(&cast->locals); + if (cast->parameters != NULL) { + pm_node_memsize_node((pm_node_t *)cast->parameters, memsize); + } + if (cast->body != NULL) { + pm_node_memsize_node((pm_node_t *)cast->body, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_BLOCK_PARAMETER_NODE: { + pm_block_parameter_node_t *cast = (pm_block_parameter_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_BLOCK_PARAMETERS_NODE: { + pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + if (cast->parameters != NULL) { + pm_node_memsize_node((pm_node_t *)cast->parameters, memsize); + } + memsize->memsize += pm_node_list_memsize(&cast->locals, memsize); + break; + } +#line 103 "node.c.erb" + case PM_BREAK_NODE: { + pm_break_node_t *cast = (pm_break_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->arguments != NULL) { + pm_node_memsize_node((pm_node_t *)cast->arguments, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_CALL_AND_WRITE_NODE: { + pm_call_and_write_node_t *cast = (pm_call_and_write_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->receiver != NULL) { + pm_node_memsize_node((pm_node_t *)cast->receiver, memsize); + } + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->receiver != NULL) { + pm_node_memsize_node((pm_node_t *)cast->receiver, memsize); + } + if (cast->arguments != NULL) { + pm_node_memsize_node((pm_node_t *)cast->arguments, memsize); + } + if (cast->block != NULL) { + pm_node_memsize_node((pm_node_t *)cast->block, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_call_operator_write_node_t *cast = (pm_call_operator_write_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->receiver != NULL) { + pm_node_memsize_node((pm_node_t *)cast->receiver, memsize); + } + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CALL_OR_WRITE_NODE: { + pm_call_or_write_node_t *cast = (pm_call_or_write_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->receiver != NULL) { + pm_node_memsize_node((pm_node_t *)cast->receiver, memsize); + } + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CAPTURE_PATTERN_NODE: { + pm_capture_pattern_node_t *cast = (pm_capture_pattern_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + pm_node_memsize_node((pm_node_t *)cast->target, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CASE_MATCH_NODE: { + pm_case_match_node_t *cast = (pm_case_match_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + if (cast->predicate != NULL) { + pm_node_memsize_node((pm_node_t *)cast->predicate, memsize); + } + memsize->memsize += pm_node_list_memsize(&cast->conditions, memsize); + if (cast->consequent != NULL) { + pm_node_memsize_node((pm_node_t *)cast->consequent, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_CASE_NODE: { + pm_case_node_t *cast = (pm_case_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + if (cast->predicate != NULL) { + pm_node_memsize_node((pm_node_t *)cast->predicate, memsize); + } + memsize->memsize += pm_node_list_memsize(&cast->conditions, memsize); + if (cast->consequent != NULL) { + pm_node_memsize_node((pm_node_t *)cast->consequent, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_CLASS_NODE: { + pm_class_node_t *cast = (pm_class_node_t *) node; + memsize->memsize += sizeof(*cast); + // Constant id lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_constant_id_list_t) * 1; + memsize->memsize += pm_constant_id_list_memsize(&cast->locals); + pm_node_memsize_node((pm_node_t *)cast->constant_path, memsize); + if (cast->superclass != NULL) { + pm_node_memsize_node((pm_node_t *)cast->superclass, memsize); + } + if (cast->body != NULL) { + pm_node_memsize_node((pm_node_t *)cast->body, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_class_variable_and_write_node_t *cast = (pm_class_variable_and_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_class_variable_operator_write_node_t *cast = (pm_class_variable_operator_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_class_variable_or_write_node_t *cast = (pm_class_variable_or_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CLASS_VARIABLE_READ_NODE: { + pm_class_variable_read_node_t *cast = (pm_class_variable_read_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_CLASS_VARIABLE_TARGET_NODE: { + pm_class_variable_target_node_t *cast = (pm_class_variable_target_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_AND_WRITE_NODE: { + pm_constant_and_write_node_t *cast = (pm_constant_and_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_constant_operator_write_node_t *cast = (pm_constant_operator_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_OR_WRITE_NODE: { + pm_constant_or_write_node_t *cast = (pm_constant_or_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_constant_path_and_write_node_t *cast = (pm_constant_path_and_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->target, memsize); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_PATH_NODE: { + pm_constant_path_node_t *cast = (pm_constant_path_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->parent != NULL) { + pm_node_memsize_node((pm_node_t *)cast->parent, memsize); + } + pm_node_memsize_node((pm_node_t *)cast->child, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_constant_path_operator_write_node_t *cast = (pm_constant_path_operator_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->target, memsize); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_constant_path_or_write_node_t *cast = (pm_constant_path_or_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->target, memsize); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_constant_path_target_node_t *cast = (pm_constant_path_target_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->parent != NULL) { + pm_node_memsize_node((pm_node_t *)cast->parent, memsize); + } + pm_node_memsize_node((pm_node_t *)cast->child, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_constant_path_write_node_t *cast = (pm_constant_path_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->target, memsize); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_READ_NODE: { + pm_constant_read_node_t *cast = (pm_constant_read_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_TARGET_NODE: { + pm_constant_target_node_t *cast = (pm_constant_target_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_CONSTANT_WRITE_NODE: { + pm_constant_write_node_t *cast = (pm_constant_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_DEF_NODE: { + pm_def_node_t *cast = (pm_def_node_t *) node; + memsize->memsize += sizeof(*cast); + // Constant id lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_constant_id_list_t) * 1; + if (cast->receiver != NULL) { + pm_node_memsize_node((pm_node_t *)cast->receiver, memsize); + } + if (cast->parameters != NULL) { + pm_node_memsize_node((pm_node_t *)cast->parameters, memsize); + } + if (cast->body != NULL) { + pm_node_memsize_node((pm_node_t *)cast->body, memsize); + } + memsize->memsize += pm_constant_id_list_memsize(&cast->locals); + break; + } +#line 103 "node.c.erb" + case PM_DEFINED_NODE: { + pm_defined_node_t *cast = (pm_defined_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_EMBEDDED_VARIABLE_NODE: { + pm_embedded_variable_node_t *cast = (pm_embedded_variable_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->variable, memsize); + break; + } +#line 103 "node.c.erb" + case PM_ENSURE_NODE: { + pm_ensure_node_t *cast = (pm_ensure_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_FALSE_NODE: { + pm_false_node_t *cast = (pm_false_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_FIND_PATTERN_NODE: { + pm_find_pattern_node_t *cast = (pm_find_pattern_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + if (cast->constant != NULL) { + pm_node_memsize_node((pm_node_t *)cast->constant, memsize); + } + pm_node_memsize_node((pm_node_t *)cast->left, memsize); + memsize->memsize += pm_node_list_memsize(&cast->requireds, memsize); + pm_node_memsize_node((pm_node_t *)cast->right, memsize); + break; + } +#line 103 "node.c.erb" + case PM_FLIP_FLOP_NODE: { + pm_flip_flop_node_t *cast = (pm_flip_flop_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->left != NULL) { + pm_node_memsize_node((pm_node_t *)cast->left, memsize); + } + if (cast->right != NULL) { + pm_node_memsize_node((pm_node_t *)cast->right, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_FLOAT_NODE: { + pm_float_node_t *cast = (pm_float_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_FOR_NODE: { + pm_for_node_t *cast = (pm_for_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->index, memsize); + pm_node_memsize_node((pm_node_t *)cast->collection, memsize); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_FORWARDING_ARGUMENTS_NODE: { + pm_forwarding_arguments_node_t *cast = (pm_forwarding_arguments_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_FORWARDING_PARAMETER_NODE: { + pm_forwarding_parameter_node_t *cast = (pm_forwarding_parameter_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_FORWARDING_SUPER_NODE: { + pm_forwarding_super_node_t *cast = (pm_forwarding_super_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->block != NULL) { + pm_node_memsize_node((pm_node_t *)cast->block, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_global_variable_and_write_node_t *cast = (pm_global_variable_and_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_global_variable_operator_write_node_t *cast = (pm_global_variable_operator_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_global_variable_or_write_node_t *cast = (pm_global_variable_or_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_GLOBAL_VARIABLE_READ_NODE: { + pm_global_variable_read_node_t *cast = (pm_global_variable_read_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + pm_global_variable_target_node_t *cast = (pm_global_variable_target_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_HASH_NODE: { + pm_hash_node_t *cast = (pm_hash_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->elements, memsize); + break; + } +#line 103 "node.c.erb" + case PM_HASH_PATTERN_NODE: { + pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + if (cast->constant != NULL) { + pm_node_memsize_node((pm_node_t *)cast->constant, memsize); + } + memsize->memsize += pm_node_list_memsize(&cast->elements, memsize); + if (cast->rest != NULL) { + pm_node_memsize_node((pm_node_t *)cast->rest, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_IF_NODE: { + pm_if_node_t *cast = (pm_if_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->predicate, memsize); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + if (cast->consequent != NULL) { + pm_node_memsize_node((pm_node_t *)cast->consequent, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_IMAGINARY_NODE: { + pm_imaginary_node_t *cast = (pm_imaginary_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->numeric, memsize); + break; + } +#line 103 "node.c.erb" + case PM_IMPLICIT_NODE: { + pm_implicit_node_t *cast = (pm_implicit_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_IMPLICIT_REST_NODE: { + pm_implicit_rest_node_t *cast = (pm_implicit_rest_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_IN_NODE: { + pm_in_node_t *cast = (pm_in_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->pattern, memsize); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_INDEX_AND_WRITE_NODE: { + pm_index_and_write_node_t *cast = (pm_index_and_write_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->receiver != NULL) { + pm_node_memsize_node((pm_node_t *)cast->receiver, memsize); + } + if (cast->arguments != NULL) { + pm_node_memsize_node((pm_node_t *)cast->arguments, memsize); + } + if (cast->block != NULL) { + pm_node_memsize_node((pm_node_t *)cast->block, memsize); + } + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_index_operator_write_node_t *cast = (pm_index_operator_write_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->receiver != NULL) { + pm_node_memsize_node((pm_node_t *)cast->receiver, memsize); + } + if (cast->arguments != NULL) { + pm_node_memsize_node((pm_node_t *)cast->arguments, memsize); + } + if (cast->block != NULL) { + pm_node_memsize_node((pm_node_t *)cast->block, memsize); + } + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INDEX_OR_WRITE_NODE: { + pm_index_or_write_node_t *cast = (pm_index_or_write_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->receiver != NULL) { + pm_node_memsize_node((pm_node_t *)cast->receiver, memsize); + } + if (cast->arguments != NULL) { + pm_node_memsize_node((pm_node_t *)cast->arguments, memsize); + } + if (cast->block != NULL) { + pm_node_memsize_node((pm_node_t *)cast->block, memsize); + } + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_instance_variable_and_write_node_t *cast = (pm_instance_variable_and_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_instance_variable_operator_write_node_t *cast = (pm_instance_variable_operator_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_instance_variable_or_write_node_t *cast = (pm_instance_variable_or_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_instance_variable_read_node_t *cast = (pm_instance_variable_read_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + pm_instance_variable_target_node_t *cast = (pm_instance_variable_target_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INTEGER_NODE: { + pm_integer_node_t *cast = (pm_integer_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_interpolated_match_last_line_node_t *cast = (pm_interpolated_match_last_line_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->parts, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_interpolated_regular_expression_node_t *cast = (pm_interpolated_regular_expression_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->parts, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INTERPOLATED_STRING_NODE: { + pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->parts, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->parts, memsize); + break; + } +#line 103 "node.c.erb" + case PM_INTERPOLATED_X_STRING_NODE: { + pm_interpolated_x_string_node_t *cast = (pm_interpolated_x_string_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->parts, memsize); + break; + } +#line 103 "node.c.erb" + case PM_KEYWORD_HASH_NODE: { + pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->elements, memsize); + break; + } +#line 103 "node.c.erb" + case PM_KEYWORD_REST_PARAMETER_NODE: { + pm_keyword_rest_parameter_node_t *cast = (pm_keyword_rest_parameter_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_LAMBDA_NODE: { + pm_lambda_node_t *cast = (pm_lambda_node_t *) node; + memsize->memsize += sizeof(*cast); + // Constant id lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_constant_id_list_t) * 1; + memsize->memsize += pm_constant_id_list_memsize(&cast->locals); + if (cast->parameters != NULL) { + pm_node_memsize_node((pm_node_t *)cast->parameters, memsize); + } + if (cast->body != NULL) { + pm_node_memsize_node((pm_node_t *)cast->body, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_local_variable_and_write_node_t *cast = (pm_local_variable_and_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_local_variable_operator_write_node_t *cast = (pm_local_variable_operator_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_local_variable_or_write_node_t *cast = (pm_local_variable_or_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_LOCAL_VARIABLE_TARGET_NODE: { + pm_local_variable_target_node_t *cast = (pm_local_variable_target_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_MATCH_LAST_LINE_NODE: { + pm_match_last_line_node_t *cast = (pm_match_last_line_node_t *) node; + memsize->memsize += sizeof(*cast); + memsize->memsize += pm_string_memsize(&cast->unescaped); + break; + } +#line 103 "node.c.erb" + case PM_MATCH_PREDICATE_NODE: { + pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + pm_node_memsize_node((pm_node_t *)cast->pattern, memsize); + break; + } +#line 103 "node.c.erb" + case PM_MATCH_REQUIRED_NODE: { + pm_match_required_node_t *cast = (pm_match_required_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + pm_node_memsize_node((pm_node_t *)cast->pattern, memsize); + break; + } +#line 103 "node.c.erb" + case PM_MATCH_WRITE_NODE: { + pm_match_write_node_t *cast = (pm_match_write_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + pm_node_memsize_node((pm_node_t *)cast->call, memsize); + memsize->memsize += pm_node_list_memsize(&cast->targets, memsize); + break; + } +#line 103 "node.c.erb" + case PM_MISSING_NODE: { + pm_missing_node_t *cast = (pm_missing_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_MODULE_NODE: { + pm_module_node_t *cast = (pm_module_node_t *) node; + memsize->memsize += sizeof(*cast); + // Constant id lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_constant_id_list_t) * 1; + memsize->memsize += pm_constant_id_list_memsize(&cast->locals); + pm_node_memsize_node((pm_node_t *)cast->constant_path, memsize); + if (cast->body != NULL) { + pm_node_memsize_node((pm_node_t *)cast->body, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_MULTI_TARGET_NODE: { + pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 2; + memsize->memsize += pm_node_list_memsize(&cast->lefts, memsize); + if (cast->rest != NULL) { + pm_node_memsize_node((pm_node_t *)cast->rest, memsize); + } + memsize->memsize += pm_node_list_memsize(&cast->rights, memsize); + break; + } +#line 103 "node.c.erb" + case PM_MULTI_WRITE_NODE: { + pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 2; + memsize->memsize += pm_node_list_memsize(&cast->lefts, memsize); + if (cast->rest != NULL) { + pm_node_memsize_node((pm_node_t *)cast->rest, memsize); + } + memsize->memsize += pm_node_list_memsize(&cast->rights, memsize); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_NEXT_NODE: { + pm_next_node_t *cast = (pm_next_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->arguments != NULL) { + pm_node_memsize_node((pm_node_t *)cast->arguments, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_NIL_NODE: { + pm_nil_node_t *cast = (pm_nil_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_NO_KEYWORDS_PARAMETER_NODE: { + pm_no_keywords_parameter_node_t *cast = (pm_no_keywords_parameter_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_NUMBERED_REFERENCE_READ_NODE: { + pm_numbered_reference_read_node_t *cast = (pm_numbered_reference_read_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_optional_keyword_parameter_node_t *cast = (pm_optional_keyword_parameter_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_OPTIONAL_PARAMETER_NODE: { + pm_optional_parameter_node_t *cast = (pm_optional_parameter_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->value, memsize); + break; + } +#line 103 "node.c.erb" + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->left, memsize); + pm_node_memsize_node((pm_node_t *)cast->right, memsize); + break; + } +#line 103 "node.c.erb" + case PM_PARAMETERS_NODE: { + pm_parameters_node_t *cast = (pm_parameters_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 4; + memsize->memsize += pm_node_list_memsize(&cast->requireds, memsize); + memsize->memsize += pm_node_list_memsize(&cast->optionals, memsize); + if (cast->rest != NULL) { + pm_node_memsize_node((pm_node_t *)cast->rest, memsize); + } + memsize->memsize += pm_node_list_memsize(&cast->posts, memsize); + memsize->memsize += pm_node_list_memsize(&cast->keywords, memsize); + if (cast->keyword_rest != NULL) { + pm_node_memsize_node((pm_node_t *)cast->keyword_rest, memsize); + } + if (cast->block != NULL) { + pm_node_memsize_node((pm_node_t *)cast->block, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->body != NULL) { + pm_node_memsize_node((pm_node_t *)cast->body, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_PINNED_EXPRESSION_NODE: { + pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->expression, memsize); + break; + } +#line 103 "node.c.erb" + case PM_PINNED_VARIABLE_NODE: { + pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->variable, memsize); + break; + } +#line 103 "node.c.erb" + case PM_POST_EXECUTION_NODE: { + pm_post_execution_node_t *cast = (pm_post_execution_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_PRE_EXECUTION_NODE: { + pm_pre_execution_node_t *cast = (pm_pre_execution_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_PROGRAM_NODE: { + pm_program_node_t *cast = (pm_program_node_t *) node; + memsize->memsize += sizeof(*cast); + // Constant id lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_constant_id_list_t) * 1; + memsize->memsize += pm_constant_id_list_memsize(&cast->locals); + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + break; + } +#line 103 "node.c.erb" + case PM_RANGE_NODE: { + pm_range_node_t *cast = (pm_range_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->left != NULL) { + pm_node_memsize_node((pm_node_t *)cast->left, memsize); + } + if (cast->right != NULL) { + pm_node_memsize_node((pm_node_t *)cast->right, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_RATIONAL_NODE: { + pm_rational_node_t *cast = (pm_rational_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->numeric, memsize); + break; + } +#line 103 "node.c.erb" + case PM_REDO_NODE: { + pm_redo_node_t *cast = (pm_redo_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_REGULAR_EXPRESSION_NODE: { + pm_regular_expression_node_t *cast = (pm_regular_expression_node_t *) node; + memsize->memsize += sizeof(*cast); + memsize->memsize += pm_string_memsize(&cast->unescaped); + break; + } +#line 103 "node.c.erb" + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + pm_required_keyword_parameter_node_t *cast = (pm_required_keyword_parameter_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_REQUIRED_PARAMETER_NODE: { + pm_required_parameter_node_t *cast = (pm_required_parameter_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->expression, memsize); + pm_node_memsize_node((pm_node_t *)cast->rescue_expression, memsize); + break; + } +#line 103 "node.c.erb" + case PM_RESCUE_NODE: { + pm_rescue_node_t *cast = (pm_rescue_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->exceptions, memsize); + if (cast->reference != NULL) { + pm_node_memsize_node((pm_node_t *)cast->reference, memsize); + } + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + if (cast->consequent != NULL) { + pm_node_memsize_node((pm_node_t *)cast->consequent, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_REST_PARAMETER_NODE: { + pm_rest_parameter_node_t *cast = (pm_rest_parameter_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_RETRY_NODE: { + pm_retry_node_t *cast = (pm_retry_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_RETURN_NODE: { + pm_return_node_t *cast = (pm_return_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->arguments != NULL) { + pm_node_memsize_node((pm_node_t *)cast->arguments, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_SELF_NODE: { + pm_self_node_t *cast = (pm_self_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_SINGLETON_CLASS_NODE: { + pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; + memsize->memsize += sizeof(*cast); + // Constant id lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_constant_id_list_t) * 1; + memsize->memsize += pm_constant_id_list_memsize(&cast->locals); + pm_node_memsize_node((pm_node_t *)cast->expression, memsize); + if (cast->body != NULL) { + pm_node_memsize_node((pm_node_t *)cast->body, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_SOURCE_ENCODING_NODE: { + pm_source_encoding_node_t *cast = (pm_source_encoding_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_SOURCE_FILE_NODE: { + pm_source_file_node_t *cast = (pm_source_file_node_t *) node; + memsize->memsize += sizeof(*cast); + memsize->memsize += pm_string_memsize(&cast->filepath); + break; + } +#line 103 "node.c.erb" + case PM_SOURCE_LINE_NODE: { + pm_source_line_node_t *cast = (pm_source_line_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_SPLAT_NODE: { + pm_splat_node_t *cast = (pm_splat_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->expression != NULL) { + pm_node_memsize_node((pm_node_t *)cast->expression, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->body, memsize); + break; + } +#line 103 "node.c.erb" + case PM_STRING_NODE: { + pm_string_node_t *cast = (pm_string_node_t *) node; + memsize->memsize += sizeof(*cast); + memsize->memsize += pm_string_memsize(&cast->unescaped); + break; + } +#line 103 "node.c.erb" + case PM_SUPER_NODE: { + pm_super_node_t *cast = (pm_super_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->arguments != NULL) { + pm_node_memsize_node((pm_node_t *)cast->arguments, memsize); + } + if (cast->block != NULL) { + pm_node_memsize_node((pm_node_t *)cast->block, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_SYMBOL_NODE: { + pm_symbol_node_t *cast = (pm_symbol_node_t *) node; + memsize->memsize += sizeof(*cast); + memsize->memsize += pm_string_memsize(&cast->unescaped); + break; + } +#line 103 "node.c.erb" + case PM_TRUE_NODE: { + pm_true_node_t *cast = (pm_true_node_t *) node; + memsize->memsize += sizeof(*cast); + break; + } +#line 103 "node.c.erb" + case PM_UNDEF_NODE: { + pm_undef_node_t *cast = (pm_undef_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->names, memsize); + break; + } +#line 103 "node.c.erb" + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->predicate, memsize); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + if (cast->consequent != NULL) { + pm_node_memsize_node((pm_node_t *)cast->consequent, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_UNTIL_NODE: { + pm_until_node_t *cast = (pm_until_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->predicate, memsize); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_WHEN_NODE: { + pm_when_node_t *cast = (pm_when_node_t *) node; + memsize->memsize += sizeof(*cast); + // Node lists will add in their own sizes below. + memsize->memsize -= sizeof(pm_node_list_t) * 1; + memsize->memsize += pm_node_list_memsize(&cast->conditions, memsize); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_WHILE_NODE: { + pm_while_node_t *cast = (pm_while_node_t *) node; + memsize->memsize += sizeof(*cast); + pm_node_memsize_node((pm_node_t *)cast->predicate, memsize); + if (cast->statements != NULL) { + pm_node_memsize_node((pm_node_t *)cast->statements, memsize); + } + break; + } +#line 103 "node.c.erb" + case PM_X_STRING_NODE: { + pm_x_string_node_t *cast = (pm_x_string_node_t *) node; + memsize->memsize += sizeof(*cast); + memsize->memsize += pm_string_memsize(&cast->unescaped); + break; + } +#line 103 "node.c.erb" + case PM_YIELD_NODE: { + pm_yield_node_t *cast = (pm_yield_node_t *) node; + memsize->memsize += sizeof(*cast); + if (cast->arguments != NULL) { + pm_node_memsize_node((pm_node_t *)cast->arguments, memsize); + } + break; + } +#line 137 "node.c.erb" + } +} + +/** + * Calculates the memory footprint of a given node. + */ +PRISM_EXPORTED_FUNCTION void +pm_node_memsize(pm_node_t *node, pm_memsize_t *memsize) { + *memsize = (pm_memsize_t) { .memsize = 0, .node_count = 0 }; + pm_node_memsize_node(node, memsize); +} + +/** + * Returns a string representation of the given node type. + */ +PRISM_EXPORTED_FUNCTION const char * +pm_node_type_to_str(pm_node_type_t node_type) +{ + switch (node_type) { + case PM_ALIAS_GLOBAL_VARIABLE_NODE: + return "PM_ALIAS_GLOBAL_VARIABLE_NODE"; + case PM_ALIAS_METHOD_NODE: + return "PM_ALIAS_METHOD_NODE"; + case PM_ALTERNATION_PATTERN_NODE: + return "PM_ALTERNATION_PATTERN_NODE"; + case PM_AND_NODE: + return "PM_AND_NODE"; + case PM_ARGUMENTS_NODE: + return "PM_ARGUMENTS_NODE"; + case PM_ARRAY_NODE: + return "PM_ARRAY_NODE"; + case PM_ARRAY_PATTERN_NODE: + return "PM_ARRAY_PATTERN_NODE"; + case PM_ASSOC_NODE: + return "PM_ASSOC_NODE"; + case PM_ASSOC_SPLAT_NODE: + return "PM_ASSOC_SPLAT_NODE"; + case PM_BACK_REFERENCE_READ_NODE: + return "PM_BACK_REFERENCE_READ_NODE"; + case PM_BEGIN_NODE: + return "PM_BEGIN_NODE"; + case PM_BLOCK_ARGUMENT_NODE: + return "PM_BLOCK_ARGUMENT_NODE"; + case PM_BLOCK_LOCAL_VARIABLE_NODE: + return "PM_BLOCK_LOCAL_VARIABLE_NODE"; + case PM_BLOCK_NODE: + return "PM_BLOCK_NODE"; + case PM_BLOCK_PARAMETER_NODE: + return "PM_BLOCK_PARAMETER_NODE"; + case PM_BLOCK_PARAMETERS_NODE: + return "PM_BLOCK_PARAMETERS_NODE"; + case PM_BREAK_NODE: + return "PM_BREAK_NODE"; + case PM_CALL_AND_WRITE_NODE: + return "PM_CALL_AND_WRITE_NODE"; + case PM_CALL_NODE: + return "PM_CALL_NODE"; + case PM_CALL_OPERATOR_WRITE_NODE: + return "PM_CALL_OPERATOR_WRITE_NODE"; + case PM_CALL_OR_WRITE_NODE: + return "PM_CALL_OR_WRITE_NODE"; + case PM_CAPTURE_PATTERN_NODE: + return "PM_CAPTURE_PATTERN_NODE"; + case PM_CASE_MATCH_NODE: + return "PM_CASE_MATCH_NODE"; + case PM_CASE_NODE: + return "PM_CASE_NODE"; + case PM_CLASS_NODE: + return "PM_CLASS_NODE"; + case PM_CLASS_VARIABLE_AND_WRITE_NODE: + return "PM_CLASS_VARIABLE_AND_WRITE_NODE"; + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: + return "PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE"; + case PM_CLASS_VARIABLE_OR_WRITE_NODE: + return "PM_CLASS_VARIABLE_OR_WRITE_NODE"; + case PM_CLASS_VARIABLE_READ_NODE: + return "PM_CLASS_VARIABLE_READ_NODE"; + case PM_CLASS_VARIABLE_TARGET_NODE: + return "PM_CLASS_VARIABLE_TARGET_NODE"; + case PM_CLASS_VARIABLE_WRITE_NODE: + return "PM_CLASS_VARIABLE_WRITE_NODE"; + case PM_CONSTANT_AND_WRITE_NODE: + return "PM_CONSTANT_AND_WRITE_NODE"; + case PM_CONSTANT_OPERATOR_WRITE_NODE: + return "PM_CONSTANT_OPERATOR_WRITE_NODE"; + case PM_CONSTANT_OR_WRITE_NODE: + return "PM_CONSTANT_OR_WRITE_NODE"; + case PM_CONSTANT_PATH_AND_WRITE_NODE: + return "PM_CONSTANT_PATH_AND_WRITE_NODE"; + case PM_CONSTANT_PATH_NODE: + return "PM_CONSTANT_PATH_NODE"; + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: + return "PM_CONSTANT_PATH_OPERATOR_WRITE_NODE"; + case PM_CONSTANT_PATH_OR_WRITE_NODE: + return "PM_CONSTANT_PATH_OR_WRITE_NODE"; + case PM_CONSTANT_PATH_TARGET_NODE: + return "PM_CONSTANT_PATH_TARGET_NODE"; + case PM_CONSTANT_PATH_WRITE_NODE: + return "PM_CONSTANT_PATH_WRITE_NODE"; + case PM_CONSTANT_READ_NODE: + return "PM_CONSTANT_READ_NODE"; + case PM_CONSTANT_TARGET_NODE: + return "PM_CONSTANT_TARGET_NODE"; + case PM_CONSTANT_WRITE_NODE: + return "PM_CONSTANT_WRITE_NODE"; + case PM_DEF_NODE: + return "PM_DEF_NODE"; + case PM_DEFINED_NODE: + return "PM_DEFINED_NODE"; + case PM_ELSE_NODE: + return "PM_ELSE_NODE"; + case PM_EMBEDDED_STATEMENTS_NODE: + return "PM_EMBEDDED_STATEMENTS_NODE"; + case PM_EMBEDDED_VARIABLE_NODE: + return "PM_EMBEDDED_VARIABLE_NODE"; + case PM_ENSURE_NODE: + return "PM_ENSURE_NODE"; + case PM_FALSE_NODE: + return "PM_FALSE_NODE"; + case PM_FIND_PATTERN_NODE: + return "PM_FIND_PATTERN_NODE"; + case PM_FLIP_FLOP_NODE: + return "PM_FLIP_FLOP_NODE"; + case PM_FLOAT_NODE: + return "PM_FLOAT_NODE"; + case PM_FOR_NODE: + return "PM_FOR_NODE"; + case PM_FORWARDING_ARGUMENTS_NODE: + return "PM_FORWARDING_ARGUMENTS_NODE"; + case PM_FORWARDING_PARAMETER_NODE: + return "PM_FORWARDING_PARAMETER_NODE"; + case PM_FORWARDING_SUPER_NODE: + return "PM_FORWARDING_SUPER_NODE"; + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: + return "PM_GLOBAL_VARIABLE_AND_WRITE_NODE"; + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: + return "PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE"; + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: + return "PM_GLOBAL_VARIABLE_OR_WRITE_NODE"; + case PM_GLOBAL_VARIABLE_READ_NODE: + return "PM_GLOBAL_VARIABLE_READ_NODE"; + case PM_GLOBAL_VARIABLE_TARGET_NODE: + return "PM_GLOBAL_VARIABLE_TARGET_NODE"; + case PM_GLOBAL_VARIABLE_WRITE_NODE: + return "PM_GLOBAL_VARIABLE_WRITE_NODE"; + case PM_HASH_NODE: + return "PM_HASH_NODE"; + case PM_HASH_PATTERN_NODE: + return "PM_HASH_PATTERN_NODE"; + case PM_IF_NODE: + return "PM_IF_NODE"; + case PM_IMAGINARY_NODE: + return "PM_IMAGINARY_NODE"; + case PM_IMPLICIT_NODE: + return "PM_IMPLICIT_NODE"; + case PM_IMPLICIT_REST_NODE: + return "PM_IMPLICIT_REST_NODE"; + case PM_IN_NODE: + return "PM_IN_NODE"; + case PM_INDEX_AND_WRITE_NODE: + return "PM_INDEX_AND_WRITE_NODE"; + case PM_INDEX_OPERATOR_WRITE_NODE: + return "PM_INDEX_OPERATOR_WRITE_NODE"; + case PM_INDEX_OR_WRITE_NODE: + return "PM_INDEX_OR_WRITE_NODE"; + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: + return "PM_INSTANCE_VARIABLE_AND_WRITE_NODE"; + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: + return "PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE"; + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: + return "PM_INSTANCE_VARIABLE_OR_WRITE_NODE"; + case PM_INSTANCE_VARIABLE_READ_NODE: + return "PM_INSTANCE_VARIABLE_READ_NODE"; + case PM_INSTANCE_VARIABLE_TARGET_NODE: + return "PM_INSTANCE_VARIABLE_TARGET_NODE"; + case PM_INSTANCE_VARIABLE_WRITE_NODE: + return "PM_INSTANCE_VARIABLE_WRITE_NODE"; + case PM_INTEGER_NODE: + return "PM_INTEGER_NODE"; + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: + return "PM_INTERPOLATED_MATCH_LAST_LINE_NODE"; + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + return "PM_INTERPOLATED_REGULAR_EXPRESSION_NODE"; + case PM_INTERPOLATED_STRING_NODE: + return "PM_INTERPOLATED_STRING_NODE"; + case PM_INTERPOLATED_SYMBOL_NODE: + return "PM_INTERPOLATED_SYMBOL_NODE"; + case PM_INTERPOLATED_X_STRING_NODE: + return "PM_INTERPOLATED_X_STRING_NODE"; + case PM_KEYWORD_HASH_NODE: + return "PM_KEYWORD_HASH_NODE"; + case PM_KEYWORD_REST_PARAMETER_NODE: + return "PM_KEYWORD_REST_PARAMETER_NODE"; + case PM_LAMBDA_NODE: + return "PM_LAMBDA_NODE"; + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: + return "PM_LOCAL_VARIABLE_AND_WRITE_NODE"; + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: + return "PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE"; + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: + return "PM_LOCAL_VARIABLE_OR_WRITE_NODE"; + case PM_LOCAL_VARIABLE_READ_NODE: + return "PM_LOCAL_VARIABLE_READ_NODE"; + case PM_LOCAL_VARIABLE_TARGET_NODE: + return "PM_LOCAL_VARIABLE_TARGET_NODE"; + case PM_LOCAL_VARIABLE_WRITE_NODE: + return "PM_LOCAL_VARIABLE_WRITE_NODE"; + case PM_MATCH_LAST_LINE_NODE: + return "PM_MATCH_LAST_LINE_NODE"; + case PM_MATCH_PREDICATE_NODE: + return "PM_MATCH_PREDICATE_NODE"; + case PM_MATCH_REQUIRED_NODE: + return "PM_MATCH_REQUIRED_NODE"; + case PM_MATCH_WRITE_NODE: + return "PM_MATCH_WRITE_NODE"; + case PM_MISSING_NODE: + return "PM_MISSING_NODE"; + case PM_MODULE_NODE: + return "PM_MODULE_NODE"; + case PM_MULTI_TARGET_NODE: + return "PM_MULTI_TARGET_NODE"; + case PM_MULTI_WRITE_NODE: + return "PM_MULTI_WRITE_NODE"; + case PM_NEXT_NODE: + return "PM_NEXT_NODE"; + case PM_NIL_NODE: + return "PM_NIL_NODE"; + case PM_NO_KEYWORDS_PARAMETER_NODE: + return "PM_NO_KEYWORDS_PARAMETER_NODE"; + case PM_NUMBERED_REFERENCE_READ_NODE: + return "PM_NUMBERED_REFERENCE_READ_NODE"; + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: + return "PM_OPTIONAL_KEYWORD_PARAMETER_NODE"; + case PM_OPTIONAL_PARAMETER_NODE: + return "PM_OPTIONAL_PARAMETER_NODE"; + case PM_OR_NODE: + return "PM_OR_NODE"; + case PM_PARAMETERS_NODE: + return "PM_PARAMETERS_NODE"; + case PM_PARENTHESES_NODE: + return "PM_PARENTHESES_NODE"; + case PM_PINNED_EXPRESSION_NODE: + return "PM_PINNED_EXPRESSION_NODE"; + case PM_PINNED_VARIABLE_NODE: + return "PM_PINNED_VARIABLE_NODE"; + case PM_POST_EXECUTION_NODE: + return "PM_POST_EXECUTION_NODE"; + case PM_PRE_EXECUTION_NODE: + return "PM_PRE_EXECUTION_NODE"; + case PM_PROGRAM_NODE: + return "PM_PROGRAM_NODE"; + case PM_RANGE_NODE: + return "PM_RANGE_NODE"; + case PM_RATIONAL_NODE: + return "PM_RATIONAL_NODE"; + case PM_REDO_NODE: + return "PM_REDO_NODE"; + case PM_REGULAR_EXPRESSION_NODE: + return "PM_REGULAR_EXPRESSION_NODE"; + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: + return "PM_REQUIRED_KEYWORD_PARAMETER_NODE"; + case PM_REQUIRED_PARAMETER_NODE: + return "PM_REQUIRED_PARAMETER_NODE"; + case PM_RESCUE_MODIFIER_NODE: + return "PM_RESCUE_MODIFIER_NODE"; + case PM_RESCUE_NODE: + return "PM_RESCUE_NODE"; + case PM_REST_PARAMETER_NODE: + return "PM_REST_PARAMETER_NODE"; + case PM_RETRY_NODE: + return "PM_RETRY_NODE"; + case PM_RETURN_NODE: + return "PM_RETURN_NODE"; + case PM_SELF_NODE: + return "PM_SELF_NODE"; + case PM_SINGLETON_CLASS_NODE: + return "PM_SINGLETON_CLASS_NODE"; + case PM_SOURCE_ENCODING_NODE: + return "PM_SOURCE_ENCODING_NODE"; + case PM_SOURCE_FILE_NODE: + return "PM_SOURCE_FILE_NODE"; + case PM_SOURCE_LINE_NODE: + return "PM_SOURCE_LINE_NODE"; + case PM_SPLAT_NODE: + return "PM_SPLAT_NODE"; + case PM_STATEMENTS_NODE: + return "PM_STATEMENTS_NODE"; + case PM_STRING_NODE: + return "PM_STRING_NODE"; + case PM_SUPER_NODE: + return "PM_SUPER_NODE"; + case PM_SYMBOL_NODE: + return "PM_SYMBOL_NODE"; + case PM_TRUE_NODE: + return "PM_TRUE_NODE"; + case PM_UNDEF_NODE: + return "PM_UNDEF_NODE"; + case PM_UNLESS_NODE: + return "PM_UNLESS_NODE"; + case PM_UNTIL_NODE: + return "PM_UNTIL_NODE"; + case PM_WHEN_NODE: + return "PM_WHEN_NODE"; + case PM_WHILE_NODE: + return "PM_WHILE_NODE"; + case PM_X_STRING_NODE: + return "PM_X_STRING_NODE"; + case PM_YIELD_NODE: + return "PM_YIELD_NODE"; + } + return ""; +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/options.c b/crates/prism-sys/vendor/prism-0.18.0/src/options.c new file mode 100644 index 00000000..85d04d62 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/options.c @@ -0,0 +1,189 @@ +#include "prism/options.h" + +/** + * Set the filepath option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_filepath_set(pm_options_t *options, const char *filepath) { + pm_string_constant_init(&options->filepath, filepath, strlen(filepath)); +} + +/** + * Set the encoding option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_encoding_set(pm_options_t *options, const char *encoding) { + pm_string_constant_init(&options->encoding, encoding, strlen(encoding)); +} + +/** + * Set the line option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_line_set(pm_options_t *options, int32_t line) { + options->line = line; +} + +/** + * Set the frozen string literal option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal) { + options->frozen_string_literal = frozen_string_literal; +} + +/** + * Set the suppress warnings option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings) { + options->suppress_warnings = suppress_warnings; +} + +/** + * Allocate and zero out the scopes array on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_scopes_init(pm_options_t *options, size_t scopes_count) { + options->scopes_count = scopes_count; + options->scopes = calloc(scopes_count, sizeof(pm_options_scope_t)); + if (options->scopes == NULL) abort(); +} + +/** + * Return a pointer to the scope at the given index within the given options. + */ +PRISM_EXPORTED_FUNCTION const pm_options_scope_t * +pm_options_scope_get(const pm_options_t *options, size_t index) { + return &options->scopes[index]; +} + +/** + * Create a new options scope struct. This will hold a set of locals that are in + * scope surrounding the code that is being parsed. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count) { + scope->locals_count = locals_count; + scope->locals = calloc(locals_count, sizeof(pm_string_t)); + if (scope->locals == NULL) abort(); +} + +/** + * Return a pointer to the local at the given index within the given scope. + */ +PRISM_EXPORTED_FUNCTION const pm_string_t * +pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index) { + return &scope->locals[index]; +} + +/** + * Free the internal memory associated with the options. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_free(pm_options_t *options) { + pm_string_free(&options->filepath); + pm_string_free(&options->encoding); + + for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) { + pm_options_scope_t *scope = &options->scopes[scope_index]; + + for (size_t local_index = 0; local_index < scope->locals_count; local_index++) { + pm_string_free(&scope->locals[local_index]); + } + + free(scope->locals); + } + + free(options->scopes); +} + +/** + * Read a 32-bit unsigned integer from a pointer. This function is used to read + * the options that are passed into the parser from the Ruby implementation. It + * handles aligned and unaligned reads. + */ +static uint32_t +pm_options_read_u32(const char *data) { + if (((uintptr_t) data) % sizeof(uint32_t) == 0) { + return *((uint32_t *) data); + } else { + uint32_t value; + memcpy(&value, data, sizeof(uint32_t)); + return value; + } +} + +/** + * Read a 32-bit signed integer from a pointer. This function is used to read + * the options that are passed into the parser from the Ruby implementation. It + * handles aligned and unaligned reads. + */ +static int32_t +pm_options_read_s32(const char *data) { + if (((uintptr_t) data) % sizeof(int32_t) == 0) { + return *((int32_t *) data); + } else { + int32_t value; + memcpy(&value, data, sizeof(int32_t)); + return value; + } +} + +/** + * Deserialize an options struct from the given binary string. This is used to + * pass options to the parser from an FFI call so that consumers of the library + * from an FFI perspective don't have to worry about the structure of our + * options structs. Since the source of these calls will be from Ruby + * implementation internals we assume it is from a trusted source. + */ +void +pm_options_read(pm_options_t *options, const char *data) { + options->line = 1; // default + if (data == NULL) return; + + uint32_t filepath_length = pm_options_read_u32(data); + data += 4; + + if (filepath_length > 0) { + pm_string_constant_init(&options->filepath, data, filepath_length); + data += filepath_length; + } + + options->line = pm_options_read_s32(data); + data += 4; + + uint32_t encoding_length = pm_options_read_u32(data); + data += 4; + + if (encoding_length > 0) { + pm_string_constant_init(&options->encoding, data, encoding_length); + data += encoding_length; + } + + options->frozen_string_literal = *data++; + options->suppress_warnings = *data++; + + uint32_t scopes_count = pm_options_read_u32(data); + data += 4; + + if (scopes_count > 0) { + pm_options_scopes_init(options, scopes_count); + + for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) { + uint32_t locals_count = pm_options_read_u32(data); + data += 4; + + pm_options_scope_t *scope = &options->scopes[scope_index]; + pm_options_scope_init(scope, locals_count); + + for (size_t local_index = 0; local_index < locals_count; local_index++) { + uint32_t local_length = pm_options_read_u32(data); + data += 4; + + pm_string_constant_init(&scope->locals[local_index], data, local_length); + data += local_length; + } + } + } +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/pack.c b/crates/prism-sys/vendor/prism-0.18.0/src/pack.c new file mode 100644 index 00000000..d5bfc4d6 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/pack.c @@ -0,0 +1,493 @@ +#include "prism/pack.h" + +#include +#include + +static uintmax_t +strtoumaxc(const char **format); + +PRISM_EXPORTED_FUNCTION pm_pack_result +pm_pack_parse(pm_pack_variant variant, const char **format, const char *format_end, + pm_pack_type *type, pm_pack_signed *signed_type, pm_pack_endian *endian, pm_pack_size *size, + pm_pack_length_type *length_type, uint64_t *length, pm_pack_encoding *encoding) { + + if (*encoding == PM_PACK_ENCODING_START) { + *encoding = PM_PACK_ENCODING_US_ASCII; + } + + if (*format == format_end) { + *type = PM_PACK_END; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + *length_type = PM_PACK_LENGTH_NA; + return PM_PACK_OK; + } + + *length_type = PM_PACK_LENGTH_FIXED; + *length = 1; + bool length_changed_allowed = true; + + char directive = **format; + (*format)++; + switch (directive) { + case ' ': + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + *type = PM_PACK_SPACE; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + *length_type = PM_PACK_LENGTH_NA; + *length = 0; + return PM_PACK_OK; + case '#': + while ((*format < format_end) && (**format != '\n')) { + (*format)++; + } + *type = PM_PACK_COMMENT; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + *length_type = PM_PACK_LENGTH_NA; + *length = 0; + return PM_PACK_OK; + case 'C': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_AGNOSTIC_ENDIAN; + *size = PM_PACK_SIZE_8; + break; + case 'S': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_16; + break; + case 'L': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_32; + break; + case 'Q': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_64; + break; + case 'J': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_P; + break; + case 'c': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_SIGNED; + *endian = PM_PACK_AGNOSTIC_ENDIAN; + *size = PM_PACK_SIZE_8; + break; + case 's': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_SIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_16; + break; + case 'l': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_SIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_32; + break; + case 'q': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_SIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_64; + break; + case 'j': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_SIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_P; + break; + case 'I': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_INT; + break; + case 'i': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_SIGNED; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_INT; + break; + case 'n': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_BIG_ENDIAN; + *size = PM_PACK_SIZE_16; + length_changed_allowed = false; + break; + case 'N': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_BIG_ENDIAN; + *size = PM_PACK_SIZE_32; + length_changed_allowed = false; + break; + case 'v': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_LITTLE_ENDIAN; + *size = PM_PACK_SIZE_16; + length_changed_allowed = false; + break; + case 'V': + *type = PM_PACK_INTEGER; + *signed_type = PM_PACK_UNSIGNED; + *endian = PM_PACK_LITTLE_ENDIAN; + *size = PM_PACK_SIZE_32; + length_changed_allowed = false; + break; + case 'U': + *type = PM_PACK_UTF8; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'w': + *type = PM_PACK_BER; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'D': + case 'd': + *type = PM_PACK_FLOAT; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_64; + break; + case 'F': + case 'f': + *type = PM_PACK_FLOAT; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_NATIVE_ENDIAN; + *size = PM_PACK_SIZE_32; + break; + case 'E': + *type = PM_PACK_FLOAT; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_LITTLE_ENDIAN; + *size = PM_PACK_SIZE_64; + break; + case 'e': + *type = PM_PACK_FLOAT; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_LITTLE_ENDIAN; + *size = PM_PACK_SIZE_32; + break; + case 'G': + *type = PM_PACK_FLOAT; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_BIG_ENDIAN; + *size = PM_PACK_SIZE_64; + break; + case 'g': + *type = PM_PACK_FLOAT; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_BIG_ENDIAN; + *size = PM_PACK_SIZE_32; + break; + case 'A': + *type = PM_PACK_STRING_SPACE_PADDED; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'a': + *type = PM_PACK_STRING_NULL_PADDED; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'Z': + *type = PM_PACK_STRING_NULL_TERMINATED; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'B': + *type = PM_PACK_STRING_MSB; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'b': + *type = PM_PACK_STRING_LSB; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'H': + *type = PM_PACK_STRING_HEX_HIGH; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'h': + *type = PM_PACK_STRING_HEX_LOW; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'u': + *type = PM_PACK_STRING_UU; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'M': + *type = PM_PACK_STRING_MIME; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'm': + *type = PM_PACK_STRING_BASE64; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'P': + *type = PM_PACK_STRING_FIXED; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'p': + *type = PM_PACK_STRING_POINTER; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case '@': + *type = PM_PACK_MOVE; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'X': + *type = PM_PACK_BACK; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case 'x': + *type = PM_PACK_NULL; + *signed_type = PM_PACK_SIGNED_NA; + *endian = PM_PACK_ENDIAN_NA; + *size = PM_PACK_SIZE_NA; + break; + case '%': + return PM_PACK_ERROR_UNSUPPORTED_DIRECTIVE; + default: + return PM_PACK_ERROR_UNKNOWN_DIRECTIVE; + } + + bool explicit_endian = false; + + while (*format < format_end) { + switch (**format) { + case '_': + case '!': + (*format)++; + if (*type != PM_PACK_INTEGER || !length_changed_allowed) { + return PM_PACK_ERROR_BANG_NOT_ALLOWED; + } + switch (*size) { + case PM_PACK_SIZE_SHORT: + case PM_PACK_SIZE_INT: + case PM_PACK_SIZE_LONG: + case PM_PACK_SIZE_LONG_LONG: + break; + case PM_PACK_SIZE_16: + *size = PM_PACK_SIZE_SHORT; + break; + case PM_PACK_SIZE_32: + *size = PM_PACK_SIZE_LONG; + break; + case PM_PACK_SIZE_64: + *size = PM_PACK_SIZE_LONG_LONG; + break; + case PM_PACK_SIZE_P: + break; + default: + return PM_PACK_ERROR_BANG_NOT_ALLOWED; + } + break; + case '<': + (*format)++; + if (explicit_endian) { + return PM_PACK_ERROR_DOUBLE_ENDIAN; + } + *endian = PM_PACK_LITTLE_ENDIAN; + explicit_endian = true; + break; + case '>': + (*format)++; + if (explicit_endian) { + return PM_PACK_ERROR_DOUBLE_ENDIAN; + } + *endian = PM_PACK_BIG_ENDIAN; + explicit_endian = true; + break; + default: + goto exit_modifier_loop; + } + } + +exit_modifier_loop: + + if (variant == PM_PACK_VARIANT_UNPACK && *type == PM_PACK_MOVE) { + *length = 0; + } + + if (*format < format_end) { + if (**format == '*') { + switch (*type) { + case PM_PACK_NULL: + case PM_PACK_BACK: + switch (variant) { + case PM_PACK_VARIANT_PACK: + *length_type = PM_PACK_LENGTH_FIXED; + break; + case PM_PACK_VARIANT_UNPACK: + *length_type = PM_PACK_LENGTH_MAX; + break; + } + *length = 0; + break; + + case PM_PACK_MOVE: + switch (variant) { + case PM_PACK_VARIANT_PACK: + *length_type = PM_PACK_LENGTH_FIXED; + break; + case PM_PACK_VARIANT_UNPACK: + *length_type = PM_PACK_LENGTH_RELATIVE; + break; + } + *length = 0; + break; + + case PM_PACK_STRING_UU: + *length_type = PM_PACK_LENGTH_FIXED; + *length = 0; + break; + + case PM_PACK_STRING_FIXED: + switch (variant) { + case PM_PACK_VARIANT_PACK: + *length_type = PM_PACK_LENGTH_FIXED; + *length = 1; + break; + case PM_PACK_VARIANT_UNPACK: + *length_type = PM_PACK_LENGTH_MAX; + *length = 0; + break; + } + break; + + case PM_PACK_STRING_MIME: + case PM_PACK_STRING_BASE64: + *length_type = PM_PACK_LENGTH_FIXED; + *length = 1; + break; + + default: + *length_type = PM_PACK_LENGTH_MAX; + *length = 0; + break; + } + + (*format)++; + } else if (**format >= '0' && **format <= '9') { + errno = 0; + *length_type = PM_PACK_LENGTH_FIXED; + #if UINTMAX_MAX < UINT64_MAX + #error "prism's design assumes uintmax_t is at least as large as uint64_t" + #endif + uintmax_t length_max = strtoumaxc(format); + if (errno || length_max > UINT64_MAX) { + return PM_PACK_ERROR_LENGTH_TOO_BIG; + } + *length = (uint64_t) length_max; + } + } + + switch (*type) { + case PM_PACK_UTF8: + /* if encoding is US-ASCII, upgrade to UTF-8 */ + if (*encoding == PM_PACK_ENCODING_US_ASCII) { + *encoding = PM_PACK_ENCODING_UTF_8; + } + break; + case PM_PACK_STRING_MIME: + case PM_PACK_STRING_BASE64: + case PM_PACK_STRING_UU: + /* keep US-ASCII (do nothing) */ + break; + default: + /* fall back to BINARY */ + *encoding = PM_PACK_ENCODING_ASCII_8BIT; + break; + } + + return PM_PACK_OK; +} + +PRISM_EXPORTED_FUNCTION size_t +pm_size_to_native(pm_pack_size size) { + switch (size) { + case PM_PACK_SIZE_SHORT: + return sizeof(short); + case PM_PACK_SIZE_INT: + return sizeof(int); + case PM_PACK_SIZE_LONG: + return sizeof(long); + case PM_PACK_SIZE_LONG_LONG: + return sizeof(long long); + case PM_PACK_SIZE_8: + return 1; + case PM_PACK_SIZE_16: + return 2; + case PM_PACK_SIZE_32: + return 4; + case PM_PACK_SIZE_64: + return 8; + case PM_PACK_SIZE_P: + return sizeof(void *); + default: + return 0; + } +} + +static uintmax_t +strtoumaxc(const char **format) { + uintmax_t value = 0; + while (**format >= '0' && **format <= '9') { + if (value > UINTMAX_MAX / 10) { + errno = ERANGE; + } + value = value * 10 + ((uintmax_t) (**format - '0')); + (*format)++; + } + return value; +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/prettyprint.c b/crates/prism-sys/vendor/prism-0.18.0/src/prettyprint.c new file mode 100644 index 00000000..5c487bc6 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/prettyprint.c @@ -0,0 +1,8384 @@ +/******************************************************************************/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/prettyprint.c.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +#include "prism/prettyprint.h" + +static void +prettyprint_source(pm_buffer_t *output_buffer, const uint8_t *source, size_t length) { + for (size_t index = 0; index < length; index++) { + const uint8_t byte = source[index]; + + if ((byte <= 0x06) || (byte >= 0x0E && byte <= 0x1F) || (byte >= 0x7F)) { + pm_buffer_append_format(output_buffer, "\\x%02X", byte); + } else { + switch (byte) { + case '\a': pm_buffer_append_string(output_buffer, "\\a", 2); break; + case '\b': pm_buffer_append_string(output_buffer, "\\b", 2); break; + case '\t': pm_buffer_append_string(output_buffer, "\\t", 2); break; + case '\n': pm_buffer_append_string(output_buffer, "\\n", 2); break; + case '\v': pm_buffer_append_string(output_buffer, "\\v", 2); break; + case '\f': pm_buffer_append_string(output_buffer, "\\f", 2); break; + case '\r': pm_buffer_append_string(output_buffer, "\\r", 2); break; + case '"': pm_buffer_append_string(output_buffer, "\\\"", 2); break; + case '#': { + if (index + 1 < length) { + const uint8_t next_byte = source[index + 1]; + if (next_byte == '{' || next_byte == '@' || next_byte == '$') { + pm_buffer_append_byte(output_buffer, '\\'); + } + } + + pm_buffer_append_byte(output_buffer, '#'); + break; + } + case '\\': pm_buffer_append_string(output_buffer, "\\\\", 2); break; + default: pm_buffer_append_byte(output_buffer, byte); break; + } + } + } +} + +static inline void +prettyprint_location(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_location_t *location) { + pm_line_column_t start = pm_newline_list_line_column(&parser->newline_list, location->start); + pm_line_column_t end = pm_newline_list_line_column(&parser->newline_list, location->end); + pm_buffer_append_format(output_buffer, "(%lu,%lu)-(%lu,%lu)", (unsigned long) (start.line + 1), (unsigned long) start.column, (unsigned long) (end.line + 1), (unsigned long) end.column); +} + +static inline void +prettyprint_constant(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_constant_id_t constant_id) { + pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, constant_id); + pm_buffer_append_format(output_buffer, ":%.*s", (int) constant->length, constant->start); +} + +static void +prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_node_t *node, pm_buffer_t *prefix_buffer) { + switch (PM_NODE_TYPE(node)) { + case PM_SCOPE_NODE: + // We do not need to print a ScopeNode as it's not part of the AST. + return; + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_alias_global_variable_node_t *cast = (pm_alias_global_variable_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AliasGlobalVariableNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // new_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 new_name:", 19); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->new_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // old_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 old_name:", 19); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->old_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_ALIAS_METHOD_NODE: { + pm_alias_method_node_t *cast = (pm_alias_method_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AliasMethodNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // new_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 new_name:", 19); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->new_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // old_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 old_name:", 19); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->old_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_ALTERNATION_PATTERN_NODE: { + pm_alternation_pattern_node_t *cast = (pm_alternation_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AlternationPatternNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 left:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 right:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AndNode (location: ", 21); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 left:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 right:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_ARGUMENTS_NODE: { + pm_arguments_node_t *cast = (pm_arguments_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ArgumentsNode (location: ", 27); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->arguments.size)); + + size_t last_index = cast->arguments.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_keyword_splat", 23); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_ARRAY_NODE: { + pm_array_node_t *cast = (pm_array_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ArrayNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 elements:", 19); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->elements.size)); + + size_t last_index = cast->elements.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->elements.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_splat", 15); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_ARRAY_PATTERN_NODE: { + pm_array_pattern_node_t *cast = (pm_array_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ArrayPatternNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // constant + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 constant:", 19); + if (cast->constant == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // requireds + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 requireds:", 20); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->requireds.size)); + + size_t last_index = cast->requireds.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->requireds.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rest:", 15); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // posts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 posts:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->posts.size)); + + size_t last_index = cast->posts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->posts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_ASSOC_NODE: { + pm_assoc_node_t *cast = (pm_assoc_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AssocNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // key + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 key:", 14); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->key, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + if (cast->value == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_ASSOC_SPLAT_NODE: { + pm_assoc_splat_node_t *cast = (pm_assoc_splat_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AssocSplatNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + if (cast->value == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_BACK_REFERENCE_READ_NODE: { + pm_back_reference_read_node_t *cast = (pm_back_reference_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BackReferenceReadNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BeginNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // begin_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 begin_keyword_loc:", 28); + pm_location_t *location = &cast->begin_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rescue_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rescue_clause:", 24); + if (cast->rescue_clause == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rescue_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // else_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 else_clause:", 22); + if (cast->else_clause == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->else_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // ensure_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ensure_clause:", 24); + if (cast->ensure_clause == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->ensure_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_BLOCK_ARGUMENT_NODE: { + pm_block_argument_node_t *cast = (pm_block_argument_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockArgumentNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 expression:", 21); + if (cast->expression == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + pm_block_local_variable_node_t *cast = (pm_block_local_variable_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockLocalVariableNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_BLOCK_NODE: { + pm_block_node_t *cast = (pm_block_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 locals:", 17); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parameters:", 21); + if (cast->parameters == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 body:", 15); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // numbered_parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 numbered_parameters:", 30); + pm_buffer_append_format(output_buffer, " %d\n", cast->numbered_parameters); + } + + break; + } + case PM_BLOCK_PARAMETER_NODE: { + pm_block_parameter_node_t *cast = (pm_block_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockParameterNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_BLOCK_PARAMETERS_NODE: { + pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockParametersNode (location: ", 33); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parameters:", 21); + if (cast->parameters == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 locals:", 17); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->locals.size)); + + size_t last_index = cast->locals.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->locals.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_BREAK_NODE: { + pm_break_node_t *cast = (pm_break_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BreakNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_CALL_AND_WRITE_NODE: { + pm_call_and_write_node_t *cast = (pm_call_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallAndWriteNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 receiver:", 19); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 call_operator_loc:", 28); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 message_loc:", 22); + pm_location_t *location = &cast->message_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // read_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 read_name:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->read_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // write_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 write_name:", 21); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->write_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 receiver:", 19); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 call_operator_loc:", 28); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 message_loc:", 22); + pm_location_t *location = &cast->message_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 block:", 16); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_call_operator_write_node_t *cast = (pm_call_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallOperatorWriteNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 receiver:", 19); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 call_operator_loc:", 28); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 message_loc:", 22); + pm_location_t *location = &cast->message_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // read_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 read_name:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->read_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // write_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 write_name:", 21); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->write_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator:", 19); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CALL_OR_WRITE_NODE: { + pm_call_or_write_node_t *cast = (pm_call_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallOrWriteNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 receiver:", 19); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 call_operator_loc:", 28); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 message_loc:", 22); + pm_location_t *location = &cast->message_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // read_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 read_name:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->read_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // write_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 write_name:", 21); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->write_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CAPTURE_PATTERN_NODE: { + pm_capture_pattern_node_t *cast = (pm_capture_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CapturePatternNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 target:", 17); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_CASE_MATCH_NODE: { + pm_case_match_node_t *cast = (pm_case_match_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CaseMatchNode (location: ", 27); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 predicate:", 20); + if (cast->predicate == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // conditions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 conditions:", 21); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->conditions.size)); + + size_t last_index = cast->conditions.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->conditions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // consequent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 consequent:", 21); + if (cast->consequent == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->consequent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // case_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 case_keyword_loc:", 27); + pm_location_t *location = &cast->case_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_CASE_NODE: { + pm_case_node_t *cast = (pm_case_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CaseNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 predicate:", 20); + if (cast->predicate == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // conditions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 conditions:", 21); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->conditions.size)); + + size_t last_index = cast->conditions.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->conditions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // consequent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 consequent:", 21); + if (cast->consequent == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->consequent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // case_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 case_keyword_loc:", 27); + pm_location_t *location = &cast->case_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_CLASS_NODE: { + pm_class_node_t *cast = (pm_class_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 locals:", 17); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // class_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 class_keyword_loc:", 28); + pm_location_t *location = &cast->class_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // constant_path + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 constant_path:", 24); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant_path, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // inheritance_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 inheritance_operator_loc:", 35); + pm_location_t *location = &cast->inheritance_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // superclass + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 superclass:", 21); + if (cast->superclass == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->superclass, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 body:", 15); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_class_variable_and_write_node_t *cast = (pm_class_variable_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableAndWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_class_variable_operator_write_node_t *cast = (pm_class_variable_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableOperatorWriteNode (location: ", 44); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator:", 19); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_class_variable_or_write_node_t *cast = (pm_class_variable_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableOrWriteNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CLASS_VARIABLE_READ_NODE: { + pm_class_variable_read_node_t *cast = (pm_class_variable_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableReadNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CLASS_VARIABLE_TARGET_NODE: { + pm_class_variable_target_node_t *cast = (pm_class_variable_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableTargetNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableWriteNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_CONSTANT_AND_WRITE_NODE: { + pm_constant_and_write_node_t *cast = (pm_constant_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantAndWriteNode (location: ", 34); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_constant_operator_write_node_t *cast = (pm_constant_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantOperatorWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator:", 19); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CONSTANT_OR_WRITE_NODE: { + pm_constant_or_write_node_t *cast = (pm_constant_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantOrWriteNode (location: ", 33); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_constant_path_and_write_node_t *cast = (pm_constant_path_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathAndWriteNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 target:", 17); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_PATH_NODE: { + pm_constant_path_node_t *cast = (pm_constant_path_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // parent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parent:", 17); + if (cast->parent == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // child + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 child:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->child, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // delimiter_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 delimiter_loc:", 24); + pm_location_t *location = &cast->delimiter_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_constant_path_operator_write_node_t *cast = (pm_constant_path_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathOperatorWriteNode (location: ", 43); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 target:", 17); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator:", 19); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_constant_path_or_write_node_t *cast = (pm_constant_path_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathOrWriteNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 target:", 17); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_constant_path_target_node_t *cast = (pm_constant_path_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathTargetNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // parent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parent:", 17); + if (cast->parent == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // child + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 child:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->child, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // delimiter_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 delimiter_loc:", 24); + pm_location_t *location = &cast->delimiter_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_constant_path_write_node_t *cast = (pm_constant_path_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathWriteNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 target:", 17); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_READ_NODE: { + pm_constant_read_node_t *cast = (pm_constant_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantReadNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CONSTANT_TARGET_NODE: { + pm_constant_target_node_t *cast = (pm_constant_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantTargetNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CONSTANT_WRITE_NODE: { + pm_constant_write_node_t *cast = (pm_constant_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantWriteNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_DEF_NODE: { + pm_def_node_t *cast = (pm_def_node_t *) node; + pm_buffer_append_string(output_buffer, "@ DefNode (location: ", 21); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 receiver:", 19); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parameters:", 21); + if (cast->parameters == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 body:", 15); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 locals:", 17); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // def_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 def_keyword_loc:", 26); + pm_location_t *location = &cast->def_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lparen_loc:", 21); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rparen_loc:", 21); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // equal_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 equal_loc:", 20); + pm_location_t *location = &cast->equal_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_DEFINED_NODE: { + pm_defined_node_t *cast = (pm_defined_node_t *) node; + pm_buffer_append_string(output_buffer, "@ DefinedNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lparen_loc:", 21); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rparen_loc:", 21); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ElseNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // else_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 else_keyword_loc:", 27); + pm_location_t *location = &cast->else_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) node; + pm_buffer_append_string(output_buffer, "@ EmbeddedStatementsNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: { + pm_embedded_variable_node_t *cast = (pm_embedded_variable_node_t *) node; + pm_buffer_append_string(output_buffer, "@ EmbeddedVariableNode (location: ", 34); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // variable + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 variable:", 19); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->variable, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_ENSURE_NODE: { + pm_ensure_node_t *cast = (pm_ensure_node_t *) node; + pm_buffer_append_string(output_buffer, "@ EnsureNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ensure_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ensure_keyword_loc:", 29); + pm_location_t *location = &cast->ensure_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_FALSE_NODE: { + pm_buffer_append_string(output_buffer, "@ FalseNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_FIND_PATTERN_NODE: { + pm_find_pattern_node_t *cast = (pm_find_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ FindPatternNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // constant + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 constant:", 19); + if (cast->constant == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 left:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // requireds + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 requireds:", 20); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->requireds.size)); + + size_t last_index = cast->requireds.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->requireds.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 right:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_FLIP_FLOP_NODE: { + pm_flip_flop_node_t *cast = (pm_flip_flop_node_t *) node; + pm_buffer_append_string(output_buffer, "@ FlipFlopNode (location: ", 26); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 left:", 15); + if (cast->left == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 right:", 16); + if (cast->right == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_RANGE_FLAGS_EXCLUDE_END) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " exclude_end", 12); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_FLOAT_NODE: { + pm_buffer_append_string(output_buffer, "@ FloatNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_FOR_NODE: { + pm_for_node_t *cast = (pm_for_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ForNode (location: ", 21); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // index + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->index, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // collection + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 collection:", 21); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->collection, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // for_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 for_keyword_loc:", 26); + pm_location_t *location = &cast->for_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // in_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 in_keyword_loc:", 25); + pm_location_t *location = &cast->in_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // do_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 do_keyword_loc:", 25); + pm_location_t *location = &cast->do_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_FORWARDING_ARGUMENTS_NODE: { + pm_buffer_append_string(output_buffer, "@ ForwardingArgumentsNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_FORWARDING_PARAMETER_NODE: { + pm_buffer_append_string(output_buffer, "@ ForwardingParameterNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_FORWARDING_SUPER_NODE: { + pm_forwarding_super_node_t *cast = (pm_forwarding_super_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ForwardingSuperNode (location: ", 33); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 block:", 16); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_global_variable_and_write_node_t *cast = (pm_global_variable_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableAndWriteNode (location: ", 40); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_global_variable_operator_write_node_t *cast = (pm_global_variable_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableOperatorWriteNode (location: ", 45); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator:", 19); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_global_variable_or_write_node_t *cast = (pm_global_variable_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableOrWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_GLOBAL_VARIABLE_READ_NODE: { + pm_global_variable_read_node_t *cast = (pm_global_variable_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableReadNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + pm_global_variable_target_node_t *cast = (pm_global_variable_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableTargetNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableWriteNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_HASH_NODE: { + pm_hash_node_t *cast = (pm_hash_node_t *) node; + pm_buffer_append_string(output_buffer, "@ HashNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 elements:", 19); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->elements.size)); + + size_t last_index = cast->elements.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->elements.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_HASH_PATTERN_NODE: { + pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ HashPatternNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // constant + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 constant:", 19); + if (cast->constant == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 elements:", 19); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->elements.size)); + + size_t last_index = cast->elements.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->elements.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rest:", 15); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_IF_NODE: { + pm_if_node_t *cast = (pm_if_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IfNode (location: ", 20); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // if_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 if_keyword_loc:", 25); + pm_location_t *location = &cast->if_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 predicate:", 20); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 then_keyword_loc:", 27); + pm_location_t *location = &cast->then_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // consequent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 consequent:", 21); + if (cast->consequent == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->consequent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_IMAGINARY_NODE: { + pm_imaginary_node_t *cast = (pm_imaginary_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ImaginaryNode (location: ", 27); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // numeric + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 numeric:", 18); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->numeric, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_IMPLICIT_NODE: { + pm_implicit_node_t *cast = (pm_implicit_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ImplicitNode (location: ", 26); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_IMPLICIT_REST_NODE: { + pm_buffer_append_string(output_buffer, "@ ImplicitRestNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_IN_NODE: { + pm_in_node_t *cast = (pm_in_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InNode (location: ", 20); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // pattern + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 pattern:", 18); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->pattern, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // in_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 in_loc:", 17); + pm_location_t *location = &cast->in_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // then_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 then_loc:", 19); + pm_location_t *location = &cast->then_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_INDEX_AND_WRITE_NODE: { + pm_index_and_write_node_t *cast = (pm_index_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IndexAndWriteNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 receiver:", 19); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 call_operator_loc:", 28); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 block:", 16); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_index_operator_write_node_t *cast = (pm_index_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IndexOperatorWriteNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 receiver:", 19); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 call_operator_loc:", 28); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 block:", 16); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator:", 19); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INDEX_OR_WRITE_NODE: { + pm_index_or_write_node_t *cast = (pm_index_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IndexOrWriteNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 receiver:", 19); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 call_operator_loc:", 28); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 block:", 16); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_instance_variable_and_write_node_t *cast = (pm_instance_variable_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableAndWriteNode (location: ", 42); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_instance_variable_operator_write_node_t *cast = (pm_instance_variable_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableOperatorWriteNode (location: ", 47); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator:", 19); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_instance_variable_or_write_node_t *cast = (pm_instance_variable_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableOrWriteNode (location: ", 41); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_instance_variable_read_node_t *cast = (pm_instance_variable_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableReadNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + pm_instance_variable_target_node_t *cast = (pm_instance_variable_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableTargetNode (location: ", 40); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_INTEGER_NODE: { + pm_integer_node_t *cast = (pm_integer_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IntegerNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_BINARY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " binary", 7); + found = true; + } + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_OCTAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " octal", 6); + found = true; + } + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_DECIMAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " decimal", 8); + found = true; + } + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_HEXADECIMAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " hexadecimal", 12); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_interpolated_match_last_line_node_t *cast = (pm_interpolated_match_last_line_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedMatchLastLineNode (location: ", 43); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parts:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_case", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EXTENDED) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " extended", 9); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multi_line", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " once", 5); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " euc_jp", 7); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ascii_8bit", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " windows_31j", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " utf_8", 6); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_interpolated_regular_expression_node_t *cast = (pm_interpolated_regular_expression_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedRegularExpressionNode (location: ", 47); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parts:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_case", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EXTENDED) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " extended", 9); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multi_line", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " once", 5); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " euc_jp", 7); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ascii_8bit", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " windows_31j", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " utf_8", 6); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INTERPOLATED_STRING_NODE: { + pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedStringNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parts:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedSymbolNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parts:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_INTERPOLATED_X_STRING_NODE: { + pm_interpolated_x_string_node_t *cast = (pm_interpolated_x_string_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedXStringNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parts:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_KEYWORD_HASH_NODE: { + pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; + pm_buffer_append_string(output_buffer, "@ KeywordHashNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 elements:", 19); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->elements.size)); + + size_t last_index = cast->elements.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->elements.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_KEYWORD_REST_PARAMETER_NODE: { + pm_keyword_rest_parameter_node_t *cast = (pm_keyword_rest_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ KeywordRestParameterNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_LAMBDA_NODE: { + pm_lambda_node_t *cast = (pm_lambda_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LambdaNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 locals:", 17); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parameters:", 21); + if (cast->parameters == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 body:", 15); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // numbered_parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 numbered_parameters:", 30); + pm_buffer_append_format(output_buffer, " %d\n", cast->numbered_parameters); + } + + break; + } + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_local_variable_and_write_node_t *cast = (pm_local_variable_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableAndWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 depth:", 16); + pm_buffer_append_format(output_buffer, " %d\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_local_variable_operator_write_node_t *cast = (pm_local_variable_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableOperatorWriteNode (location: ", 44); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator:", 19); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 depth:", 16); + pm_buffer_append_format(output_buffer, " %d\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_local_variable_or_write_node_t *cast = (pm_local_variable_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableOrWriteNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 depth:", 16); + pm_buffer_append_format(output_buffer, " %d\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableReadNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 depth:", 16); + pm_buffer_append_format(output_buffer, " %d\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_TARGET_NODE: { + pm_local_variable_target_node_t *cast = (pm_local_variable_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableTargetNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 depth:", 16); + pm_buffer_append_format(output_buffer, " %d\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableWriteNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 depth:", 16); + pm_buffer_append_format(output_buffer, " %d\n", cast->depth); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_MATCH_LAST_LINE_NODE: { + pm_match_last_line_node_t *cast = (pm_match_last_line_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MatchLastLineNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 content_loc:", 22); + pm_location_t *location = &cast->content_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 unescaped:", 20); + pm_buffer_append_string(output_buffer, " \"", 2); + prettyprint_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_case", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EXTENDED) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " extended", 9); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multi_line", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " once", 5); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " euc_jp", 7); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ascii_8bit", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " windows_31j", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " utf_8", 6); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_MATCH_PREDICATE_NODE: { + pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MatchPredicateNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // pattern + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 pattern:", 18); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->pattern, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_MATCH_REQUIRED_NODE: { + pm_match_required_node_t *cast = (pm_match_required_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MatchRequiredNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // pattern + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 pattern:", 18); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->pattern, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_MATCH_WRITE_NODE: { + pm_match_write_node_t *cast = (pm_match_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MatchWriteNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // call + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 call:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->call, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // targets + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 targets:", 18); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->targets.size)); + + size_t last_index = cast->targets.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->targets.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_MISSING_NODE: { + pm_buffer_append_string(output_buffer, "@ MissingNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_MODULE_NODE: { + pm_module_node_t *cast = (pm_module_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ModuleNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 locals:", 17); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // module_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 module_keyword_loc:", 29); + pm_location_t *location = &cast->module_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // constant_path + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 constant_path:", 24); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant_path, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 body:", 15); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_MULTI_TARGET_NODE: { + pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MultiTargetNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // lefts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lefts:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->lefts.size)); + + size_t last_index = cast->lefts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->lefts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rest:", 15); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rights + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rights:", 17); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->rights.size)); + + size_t last_index = cast->rights.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rights.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lparen_loc:", 21); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 rparen_loc:", 21); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_MULTI_WRITE_NODE: { + pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MultiWriteNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // lefts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lefts:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->lefts.size)); + + size_t last_index = cast->lefts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->lefts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rest:", 15); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rights + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rights:", 17); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->rights.size)); + + size_t last_index = cast->rights.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rights.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lparen_loc:", 21); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rparen_loc:", 21); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_NEXT_NODE: { + pm_next_node_t *cast = (pm_next_node_t *) node; + pm_buffer_append_string(output_buffer, "@ NextNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_NIL_NODE: { + pm_buffer_append_string(output_buffer, "@ NilNode (location: ", 21); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_NO_KEYWORDS_PARAMETER_NODE: { + pm_no_keywords_parameter_node_t *cast = (pm_no_keywords_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ NoKeywordsParameterNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_NUMBERED_REFERENCE_READ_NODE: { + pm_numbered_reference_read_node_t *cast = (pm_numbered_reference_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ NumberedReferenceReadNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // number + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 number:", 17); + pm_buffer_append_format(output_buffer, " %d\n", cast->number); + } + + break; + } + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_optional_keyword_parameter_node_t *cast = (pm_optional_keyword_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ OptionalKeywordParameterNode (location: ", 42); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_OPTIONAL_PARAMETER_NODE: { + pm_optional_parameter_node_t *cast = (pm_optional_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ OptionalParameterNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 value:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + pm_buffer_append_string(output_buffer, "@ OrNode (location: ", 20); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 left:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 right:", 16); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_PARAMETERS_NODE: { + pm_parameters_node_t *cast = (pm_parameters_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ParametersNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // requireds + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 requireds:", 20); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->requireds.size)); + + size_t last_index = cast->requireds.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->requireds.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // optionals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 optionals:", 20); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->optionals.size)); + + size_t last_index = cast->optionals.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->optionals.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rest:", 15); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // posts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 posts:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->posts.size)); + + size_t last_index = cast->posts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->posts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keywords + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keywords:", 19); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->keywords.size)); + + size_t last_index = cast->keywords.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->keywords.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_rest:", 23); + if (cast->keyword_rest == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->keyword_rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 block:", 16); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ParenthesesNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 body:", 15); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_PINNED_EXPRESSION_NODE: { + pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node; + pm_buffer_append_string(output_buffer, "@ PinnedExpressionNode (location: ", 34); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 expression:", 21); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lparen_loc:", 21); + pm_location_t *location = &cast->lparen_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 rparen_loc:", 21); + pm_location_t *location = &cast->rparen_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_PINNED_VARIABLE_NODE: { + pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node; + pm_buffer_append_string(output_buffer, "@ PinnedVariableNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // variable + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 variable:", 19); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->variable, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_POST_EXECUTION_NODE: { + pm_post_execution_node_t *cast = (pm_post_execution_node_t *) node; + pm_buffer_append_string(output_buffer, "@ PostExecutionNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_PRE_EXECUTION_NODE: { + pm_pre_execution_node_t *cast = (pm_pre_execution_node_t *) node; + pm_buffer_append_string(output_buffer, "@ PreExecutionNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_PROGRAM_NODE: { + pm_program_node_t *cast = (pm_program_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ProgramNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 locals:", 17); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 statements:", 21); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_RANGE_NODE: { + pm_range_node_t *cast = (pm_range_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RangeNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 left:", 15); + if (cast->left == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 right:", 16); + if (cast->right == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_RANGE_FLAGS_EXCLUDE_END) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " exclude_end", 12); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_RATIONAL_NODE: { + pm_rational_node_t *cast = (pm_rational_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RationalNode (location: ", 26); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // numeric + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 numeric:", 18); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->numeric, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_REDO_NODE: { + pm_buffer_append_string(output_buffer, "@ RedoNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_REGULAR_EXPRESSION_NODE: { + pm_regular_expression_node_t *cast = (pm_regular_expression_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RegularExpressionNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 content_loc:", 22); + pm_location_t *location = &cast->content_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 unescaped:", 20); + pm_buffer_append_string(output_buffer, " \"", 2); + prettyprint_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_case", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EXTENDED) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " extended", 9); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multi_line", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " once", 5); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " euc_jp", 7); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ascii_8bit", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " windows_31j", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " utf_8", 6); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + pm_required_keyword_parameter_node_t *cast = (pm_required_keyword_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RequiredKeywordParameterNode (location: ", 42); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_REQUIRED_PARAMETER_NODE: { + pm_required_parameter_node_t *cast = (pm_required_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RequiredParameterNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RescueModifierNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 expression:", 21); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // rescue_expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 rescue_expression:", 28); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rescue_expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_RESCUE_NODE: { + pm_rescue_node_t *cast = (pm_rescue_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RescueNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // exceptions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 exceptions:", 21); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->exceptions.size)); + + size_t last_index = cast->exceptions.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->exceptions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // reference + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 reference:", 20); + if (cast->reference == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->reference, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // consequent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 consequent:", 21); + if (cast->consequent == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->consequent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_REST_PARAMETER_NODE: { + pm_rest_parameter_node_t *cast = (pm_rest_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RestParameterNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name:", 15); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 name_loc:", 19); + pm_location_t *location = &cast->name_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_RETRY_NODE: { + pm_buffer_append_string(output_buffer, "@ RetryNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_RETURN_NODE: { + pm_return_node_t *cast = (pm_return_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ReturnNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_SELF_NODE: { + pm_buffer_append_string(output_buffer, "@ SelfNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_SINGLETON_CLASS_NODE: { + pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SingletonClassNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 locals:", 17); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // class_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 class_keyword_loc:", 28); + pm_location_t *location = &cast->class_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 expression:", 21); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 body:", 15); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_SOURCE_ENCODING_NODE: { + pm_buffer_append_string(output_buffer, "@ SourceEncodingNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_SOURCE_FILE_NODE: { + pm_source_file_node_t *cast = (pm_source_file_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SourceFileNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // filepath + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 filepath:", 19); + pm_buffer_append_string(output_buffer, " \"", 2); + prettyprint_source(output_buffer, pm_string_source(&cast->filepath), pm_string_length(&cast->filepath)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_SOURCE_LINE_NODE: { + pm_buffer_append_string(output_buffer, "@ SourceLineNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_SPLAT_NODE: { + pm_splat_node_t *cast = (pm_splat_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SplatNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 operator_loc:", 23); + pm_location_t *location = &cast->operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 expression:", 21); + if (cast->expression == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + pm_buffer_append_string(output_buffer, "@ StatementsNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 body:", 15); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->body.size)); + + size_t last_index = cast->body.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_STRING_NODE: { + pm_string_node_t *cast = (pm_string_node_t *) node; + pm_buffer_append_string(output_buffer, "@ StringNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_STRING_FLAGS_FROZEN) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " frozen", 7); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 content_loc:", 22); + pm_location_t *location = &cast->content_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 unescaped:", 20); + pm_buffer_append_string(output_buffer, " \"", 2); + prettyprint_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_SUPER_NODE: { + pm_super_node_t *cast = (pm_super_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SuperNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lparen_loc:", 21); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 rparen_loc:", 21); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 block:", 16); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_SYMBOL_NODE: { + pm_symbol_node_t *cast = (pm_symbol_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SymbolNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // value_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 value_loc:", 20); + pm_location_t *location = &cast->value_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 unescaped:", 20); + pm_buffer_append_string(output_buffer, " \"", 2); + prettyprint_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_TRUE_NODE: { + pm_buffer_append_string(output_buffer, "@ TrueNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_UNDEF_NODE: { + pm_undef_node_t *cast = (pm_undef_node_t *) node; + pm_buffer_append_string(output_buffer, "@ UndefNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // names + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 names:", 16); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->names.size)); + + size_t last_index = cast->names.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->names.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + pm_buffer_append_string(output_buffer, "@ UnlessNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 predicate:", 20); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 then_keyword_loc:", 27); + pm_location_t *location = &cast->then_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // consequent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 consequent:", 21); + if (cast->consequent == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->consequent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 end_keyword_loc:", 26); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + case PM_UNTIL_NODE: { + pm_until_node_t *cast = (pm_until_node_t *) node; + pm_buffer_append_string(output_buffer, "@ UntilNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 predicate:", 20); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " begin_modifier", 15); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_WHEN_NODE: { + pm_when_node_t *cast = (pm_when_node_t *) node; + pm_buffer_append_string(output_buffer, "@ WhenNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // conditions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 conditions:", 21); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->conditions.size)); + + size_t last_index = cast->conditions.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + + if (index == last_index - 1) { + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, " ", 4); + } else { + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ", 10); + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + } + + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->conditions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_WHILE_NODE: { + pm_while_node_t *cast = (pm_while_node_t *) node; + pm_buffer_append_string(output_buffer, "@ WhileNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 predicate:", 20); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 statements:", 21); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // flags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 flags:", 16); + bool found = false; + if (cast->base.flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " begin_modifier", 15); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " \xe2\x88\x85", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_X_STRING_NODE: { + pm_x_string_node_t *cast = (pm_x_string_node_t *) node; + pm_buffer_append_string(output_buffer, "@ XStringNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 opening_loc:", 22); + pm_location_t *location = &cast->opening_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 content_loc:", 22); + pm_location_t *location = &cast->content_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 closing_loc:", 22); + pm_location_t *location = &cast->closing_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 unescaped:", 20); + pm_buffer_append_string(output_buffer, " \"", 2); + prettyprint_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_YIELD_NODE: { + pm_yield_node_t *cast = (pm_yield_node_t *) node; + pm_buffer_append_string(output_buffer, "@ YieldNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 keyword_loc:", 22); + pm_location_t *location = &cast->keyword_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lparen_loc:", 21); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 arguments:", 20); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "\xe2\x94\x82 ", 6); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 rparen_loc:", 21); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " \xe2\x88\x85\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + prettyprint_source(output_buffer, location->start, (size_t) (location->end - location->start)); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + break; + } + } +} + +/** + * Pretty-prints the AST represented by the given node to the given buffer. + */ +PRISM_EXPORTED_FUNCTION void +pm_prettyprint(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_node_t *node) { + pm_buffer_t prefix_buffer = { 0 }; + prettyprint_node(output_buffer, parser, node, &prefix_buffer); + pm_buffer_free(&prefix_buffer); +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/prism.c b/crates/prism-sys/vendor/prism-0.18.0/src/prism.c new file mode 100644 index 00000000..1e02752e --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/prism.c @@ -0,0 +1,17287 @@ +#include "prism.h" + +/** + * The prism version and the serialization format. + */ +const char * +pm_version(void) { + return PRISM_VERSION; +} + +/** + * In heredocs, tabs automatically complete up to the next 8 spaces. This is + * defined in CRuby as TAB_WIDTH. + */ +#define PM_TAB_WHITESPACE_SIZE 8 + +#ifndef PM_DEBUG_LOGGING +/** + * Debugging logging will provide you with additional debugging functions as + * well as automatically replace some functions with their debugging + * counterparts. + */ +#define PM_DEBUG_LOGGING 0 +#endif + +#if PM_DEBUG_LOGGING + +/******************************************************************************/ +/* Debugging */ +/******************************************************************************/ + +PRISM_ATTRIBUTE_UNUSED static const char * +debug_context(pm_context_t context) { + switch (context) { + case PM_CONTEXT_BEGIN: return "BEGIN"; + case PM_CONTEXT_CLASS: return "CLASS"; + case PM_CONTEXT_CASE_IN: return "CASE_IN"; + case PM_CONTEXT_CASE_WHEN: return "CASE_WHEN"; + case PM_CONTEXT_DEF: return "DEF"; + case PM_CONTEXT_DEF_PARAMS: return "DEF_PARAMS"; + case PM_CONTEXT_DEFAULT_PARAMS: return "DEFAULT_PARAMS"; + case PM_CONTEXT_ENSURE: return "ENSURE"; + case PM_CONTEXT_ELSE: return "ELSE"; + case PM_CONTEXT_ELSIF: return "ELSIF"; + case PM_CONTEXT_EMBEXPR: return "EMBEXPR"; + case PM_CONTEXT_BLOCK_BRACES: return "BLOCK_BRACES"; + case PM_CONTEXT_BLOCK_KEYWORDS: return "BLOCK_KEYWORDS"; + case PM_CONTEXT_FOR: return "FOR"; + case PM_CONTEXT_FOR_INDEX: return "FOR_INDEX"; + case PM_CONTEXT_IF: return "IF"; + case PM_CONTEXT_MAIN: return "MAIN"; + case PM_CONTEXT_MODULE: return "MODULE"; + case PM_CONTEXT_PARENS: return "PARENS"; + case PM_CONTEXT_POSTEXE: return "POSTEXE"; + case PM_CONTEXT_PREDICATE: return "PREDICATE"; + case PM_CONTEXT_PREEXE: return "PREEXE"; + case PM_CONTEXT_RESCUE: return "RESCUE"; + case PM_CONTEXT_RESCUE_ELSE: return "RESCUE_ELSE"; + case PM_CONTEXT_SCLASS: return "SCLASS"; + case PM_CONTEXT_UNLESS: return "UNLESS"; + case PM_CONTEXT_UNTIL: return "UNTIL"; + case PM_CONTEXT_WHILE: return "WHILE"; + case PM_CONTEXT_LAMBDA_BRACES: return "LAMBDA_BRACES"; + case PM_CONTEXT_LAMBDA_DO_END: return "LAMBDA_DO_END"; + } + return NULL; +} + +PRISM_ATTRIBUTE_UNUSED static void +debug_contexts(pm_parser_t *parser) { + pm_context_node_t *context_node = parser->current_context; + fprintf(stderr, "CONTEXTS: "); + + if (context_node != NULL) { + while (context_node != NULL) { + fprintf(stderr, "%s", debug_context(context_node->context)); + context_node = context_node->prev; + if (context_node != NULL) { + fprintf(stderr, " <- "); + } + } + } else { + fprintf(stderr, "NONE"); + } + + fprintf(stderr, "\n"); +} + +PRISM_ATTRIBUTE_UNUSED static void +debug_node(const pm_parser_t *parser, const pm_node_t *node) { + pm_buffer_t output_buffer = { 0 }; + pm_prettyprint(&output_buffer, parser, node); + + fprintf(stderr, "%.*s", (int) output_buffer.length, output_buffer.value); + pm_buffer_free(&output_buffer); +} + +PRISM_ATTRIBUTE_UNUSED static void +debug_lex_mode(pm_parser_t *parser) { + pm_lex_mode_t *lex_mode = parser->lex_modes.current; + bool first = true; + + while (lex_mode != NULL) { + if (first) { + first = false; + } else { + fprintf(stderr, " <- "); + } + + switch (lex_mode->mode) { + case PM_LEX_DEFAULT: fprintf(stderr, "DEFAULT"); break; + case PM_LEX_EMBEXPR: fprintf(stderr, "EMBEXPR"); break; + case PM_LEX_EMBVAR: fprintf(stderr, "EMBVAR"); break; + case PM_LEX_HEREDOC: fprintf(stderr, "HEREDOC"); break; + case PM_LEX_LIST: fprintf(stderr, "LIST (terminator=%c, interpolation=%d)", lex_mode->as.list.terminator, lex_mode->as.list.interpolation); break; + case PM_LEX_REGEXP: fprintf(stderr, "REGEXP (terminator=%c)", lex_mode->as.regexp.terminator); break; + case PM_LEX_STRING: fprintf(stderr, "STRING (terminator=%c, interpolation=%d)", lex_mode->as.string.terminator, lex_mode->as.string.interpolation); break; + } + + lex_mode = lex_mode->prev; + } + + fprintf(stderr, "\n"); +} + +PRISM_ATTRIBUTE_UNUSED static void +debug_state(pm_parser_t *parser) { + fprintf(stderr, "STATE: "); + bool first = true; + + if (parser->lex_state == PM_LEX_STATE_NONE) { + fprintf(stderr, "NONE\n"); + return; + } + +#define CHECK_STATE(state) \ + if (parser->lex_state & state) { \ + if (!first) fprintf(stderr, "|"); \ + fprintf(stderr, "%s", #state); \ + first = false; \ + } + + CHECK_STATE(PM_LEX_STATE_BEG) + CHECK_STATE(PM_LEX_STATE_END) + CHECK_STATE(PM_LEX_STATE_ENDARG) + CHECK_STATE(PM_LEX_STATE_ENDFN) + CHECK_STATE(PM_LEX_STATE_ARG) + CHECK_STATE(PM_LEX_STATE_CMDARG) + CHECK_STATE(PM_LEX_STATE_MID) + CHECK_STATE(PM_LEX_STATE_FNAME) + CHECK_STATE(PM_LEX_STATE_DOT) + CHECK_STATE(PM_LEX_STATE_CLASS) + CHECK_STATE(PM_LEX_STATE_LABEL) + CHECK_STATE(PM_LEX_STATE_LABELED) + CHECK_STATE(PM_LEX_STATE_FITEM) + +#undef CHECK_STATE + + fprintf(stderr, "\n"); +} + +PRISM_ATTRIBUTE_UNUSED static void +debug_token(pm_token_t * token) { + fprintf(stderr, "%s: \"%.*s\"\n", pm_token_type_to_str(token->type), (int) (token->end - token->start), token->start); +} + +#endif + +// Macros for min/max. +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +/******************************************************************************/ +/* Lex mode manipulations */ +/******************************************************************************/ + +/** + * Returns the incrementor character that should be used to increment the + * nesting count if one is possible. + */ +static inline uint8_t +lex_mode_incrementor(const uint8_t start) { + switch (start) { + case '(': + case '[': + case '{': + case '<': + return start; + default: + return '\0'; + } +} + +/** + * Returns the matching character that should be used to terminate a list + * beginning with the given character. + */ +static inline uint8_t +lex_mode_terminator(const uint8_t start) { + switch (start) { + case '(': + return ')'; + case '[': + return ']'; + case '{': + return '}'; + case '<': + return '>'; + default: + return start; + } +} + +/** + * Push a new lex state onto the stack. If we're still within the pre-allocated + * space of the lex state stack, then we'll just use a new slot. Otherwise we'll + * allocate a new pointer and use that. + */ +static bool +lex_mode_push(pm_parser_t *parser, pm_lex_mode_t lex_mode) { + lex_mode.prev = parser->lex_modes.current; + parser->lex_modes.index++; + + if (parser->lex_modes.index > PM_LEX_STACK_SIZE - 1) { + parser->lex_modes.current = (pm_lex_mode_t *) malloc(sizeof(pm_lex_mode_t)); + if (parser->lex_modes.current == NULL) return false; + + *parser->lex_modes.current = lex_mode; + } else { + parser->lex_modes.stack[parser->lex_modes.index] = lex_mode; + parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index]; + } + + return true; +} + +/** + * Push on a new list lex mode. + */ +static inline bool +lex_mode_push_list(pm_parser_t *parser, bool interpolation, uint8_t delimiter) { + uint8_t incrementor = lex_mode_incrementor(delimiter); + uint8_t terminator = lex_mode_terminator(delimiter); + + pm_lex_mode_t lex_mode = { + .mode = PM_LEX_LIST, + .as.list = { + .nesting = 0, + .interpolation = interpolation, + .incrementor = incrementor, + .terminator = terminator + } + }; + + // These are the places where we need to split up the content of the list. + // We'll use strpbrk to find the first of these characters. + uint8_t *breakpoints = lex_mode.as.list.breakpoints; + memcpy(breakpoints, "\\ \t\f\r\v\n\0\0\0", sizeof(lex_mode.as.list.breakpoints)); + + // Now we'll add the terminator to the list of breakpoints. + size_t index = 7; + breakpoints[index++] = terminator; + + // If interpolation is allowed, then we're going to check for the # + // character. Otherwise we'll only look for escapes and the terminator. + if (interpolation) { + breakpoints[index++] = '#'; + } + + // If there is an incrementor, then we'll check for that as well. + if (incrementor != '\0') { + breakpoints[index++] = incrementor; + } + + return lex_mode_push(parser, lex_mode); +} + +/** + * Push on a new list lex mode that is only used for compatibility. This is + * called when we're at the end of the file. We want the parser to be able to + * perform its normal error tolerance. + */ +static inline bool +lex_mode_push_list_eof(pm_parser_t *parser) { + return lex_mode_push_list(parser, false, '\0'); +} + +/** + * Push on a new regexp lex mode. + */ +static inline bool +lex_mode_push_regexp(pm_parser_t *parser, uint8_t incrementor, uint8_t terminator) { + pm_lex_mode_t lex_mode = { + .mode = PM_LEX_REGEXP, + .as.regexp = { + .nesting = 0, + .incrementor = incrementor, + .terminator = terminator + } + }; + + // These are the places where we need to split up the content of the + // regular expression. We'll use strpbrk to find the first of these + // characters. + uint8_t *breakpoints = lex_mode.as.regexp.breakpoints; + memcpy(breakpoints, "\n\\#\0\0", sizeof(lex_mode.as.regexp.breakpoints)); + + // First we'll add the terminator. + breakpoints[3] = terminator; + + // Next, if there is an incrementor, then we'll check for that as well. + if (incrementor != '\0') { + breakpoints[4] = incrementor; + } + + return lex_mode_push(parser, lex_mode); +} + +/** + * Push on a new string lex mode. + */ +static inline bool +lex_mode_push_string(pm_parser_t *parser, bool interpolation, bool label_allowed, uint8_t incrementor, uint8_t terminator) { + pm_lex_mode_t lex_mode = { + .mode = PM_LEX_STRING, + .as.string = { + .nesting = 0, + .interpolation = interpolation, + .label_allowed = label_allowed, + .incrementor = incrementor, + .terminator = terminator + } + }; + + // These are the places where we need to split up the content of the + // string. We'll use strpbrk to find the first of these characters. + uint8_t *breakpoints = lex_mode.as.string.breakpoints; + memcpy(breakpoints, "\n\\\0\0\0", sizeof(lex_mode.as.string.breakpoints)); + + // Now add in the terminator. + size_t index = 2; + breakpoints[index++] = terminator; + + // If interpolation is allowed, then we're going to check for the # + // character. Otherwise we'll only look for escapes and the terminator. + if (interpolation) { + breakpoints[index++] = '#'; + } + + // If we have an incrementor, then we'll add that in as a breakpoint as + // well. + if (incrementor != '\0') { + breakpoints[index++] = incrementor; + } + + return lex_mode_push(parser, lex_mode); +} + +/** + * Push on a new string lex mode that is only used for compatibility. This is + * called when we're at the end of the file. We want the parser to be able to + * perform its normal error tolerance. + */ +static inline bool +lex_mode_push_string_eof(pm_parser_t *parser) { + return lex_mode_push_string(parser, false, false, '\0', '\0'); +} + +/** + * Pop the current lex state off the stack. If we're within the pre-allocated + * space of the lex state stack, then we'll just decrement the index. Otherwise + * we'll free the current pointer and use the previous pointer. + */ +static void +lex_mode_pop(pm_parser_t *parser) { + if (parser->lex_modes.index == 0) { + parser->lex_modes.current->mode = PM_LEX_DEFAULT; + } else if (parser->lex_modes.index < PM_LEX_STACK_SIZE) { + parser->lex_modes.index--; + parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index]; + } else { + parser->lex_modes.index--; + pm_lex_mode_t *prev = parser->lex_modes.current->prev; + free(parser->lex_modes.current); + parser->lex_modes.current = prev; + } +} + +/** + * This is the equivalent of IS_lex_state is CRuby. + */ +static inline bool +lex_state_p(pm_parser_t *parser, pm_lex_state_t state) { + return parser->lex_state & state; +} + +typedef enum { + PM_IGNORED_NEWLINE_NONE = 0, + PM_IGNORED_NEWLINE_ALL, + PM_IGNORED_NEWLINE_PATTERN +} pm_ignored_newline_type_t; + +static inline pm_ignored_newline_type_t +lex_state_ignored_p(pm_parser_t *parser) { + bool ignored = lex_state_p(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_CLASS | PM_LEX_STATE_FNAME | PM_LEX_STATE_DOT) && !lex_state_p(parser, PM_LEX_STATE_LABELED); + + if (ignored) { + return PM_IGNORED_NEWLINE_ALL; + } else if ((parser->lex_state & ~((unsigned int) PM_LEX_STATE_LABEL)) == (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)) { + return PM_IGNORED_NEWLINE_PATTERN; + } else { + return PM_IGNORED_NEWLINE_NONE; + } +} + +static inline bool +lex_state_beg_p(pm_parser_t *parser) { + return lex_state_p(parser, PM_LEX_STATE_BEG_ANY) || ((parser->lex_state & (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)) == (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)); +} + +static inline bool +lex_state_arg_p(pm_parser_t *parser) { + return lex_state_p(parser, PM_LEX_STATE_ARG_ANY); +} + +static inline bool +lex_state_spcarg_p(pm_parser_t *parser, bool space_seen) { + if (parser->current.end >= parser->end) { + return false; + } + return lex_state_arg_p(parser) && space_seen && !pm_char_is_whitespace(*parser->current.end); +} + +static inline bool +lex_state_end_p(pm_parser_t *parser) { + return lex_state_p(parser, PM_LEX_STATE_END_ANY); +} + +/** + * This is the equivalent of IS_AFTER_OPERATOR in CRuby. + */ +static inline bool +lex_state_operator_p(pm_parser_t *parser) { + return lex_state_p(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_DOT); +} + +/** + * Set the state of the lexer. This is defined as a function to be able to put a + * breakpoint in it. + */ +static inline void +lex_state_set(pm_parser_t *parser, pm_lex_state_t state) { + parser->lex_state = state; +} + +#if PM_DEBUG_LOGGING +static inline void +debug_lex_state_set(pm_parser_t *parser, pm_lex_state_t state, char const * caller_name, int line_number) { + fprintf(stderr, "Caller: %s:%d\nPrevious: ", caller_name, line_number); + debug_state(parser); + lex_state_set(parser, state); + fprintf(stderr, "Now: "); + debug_state(parser); + fprintf(stderr, "\n"); +} + +#define lex_state_set(parser, state) debug_lex_state_set(parser, state, __func__, __LINE__) +#endif + +/******************************************************************************/ +/* Diagnostic-related functions */ +/******************************************************************************/ + +/** + * Append an error to the list of errors on the parser. + */ +static inline void +pm_parser_err(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id) { + pm_diagnostic_list_append(&parser->error_list, start, end, diag_id); +} + +/** + * Append an error to the list of errors on the parser using a format string. + */ +#define PM_PARSER_ERR_FORMAT(parser, start, end, diag_id, ...) pm_diagnostic_list_append_format(&parser->error_list, start, end, diag_id, __VA_ARGS__) + +/** + * Append an error to the list of errors on the parser using the location of the + * current token. + */ +static inline void +pm_parser_err_current(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { + pm_parser_err(parser, parser->current.start, parser->current.end, diag_id); +} + +/** + * Append an error to the list of errors on the parser using the given location + * using a format string. + */ +#define PM_PARSER_ERR_LOCATION_FORMAT(parser, location, diag_id, ...) pm_diagnostic_list_append_format(&parser->error_list, (location)->start, (location)->end, diag_id, __VA_ARGS__) + +/** + * Append an error to the list of errors on the parser using the location of the + * given node. + */ +static inline void +pm_parser_err_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) { + pm_parser_err(parser, node->location.start, node->location.end, diag_id); +} + +/** + * Append an error to the list of errors on the parser using the location of the + * given node and a format string. + */ +#define PM_PARSER_ERR_NODE_FORMAT(parser, node, diag_id, ...) pm_diagnostic_list_append_format(&parser->error_list, node->location.start, node->location.end, diag_id, __VA_ARGS__) + +/** + * Append an error to the list of errors on the parser using the location of the + * previous token. + */ +static inline void +pm_parser_err_previous(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { + pm_parser_err(parser, parser->previous.start, parser->previous.end, diag_id); +} + +/** + * Append an error to the list of errors on the parser using the location of the + * given token. + */ +static inline void +pm_parser_err_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) { + pm_parser_err(parser, token->start, token->end, diag_id); +} + +/** + * Append an error to the list of errors on the parser using the location of the + * given token and a format string. + */ +#define PM_PARSER_ERR_TOKEN_FORMAT(parser, token, diag_id, ...) pm_diagnostic_list_append_format(&parser->error_list, token->start, token->end, diag_id, __VA_ARGS__) + +/** + * Append a warning to the list of warnings on the parser. + */ +static inline void +pm_parser_warn(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id) { + if (!parser->suppress_warnings) { + pm_diagnostic_list_append(&parser->warning_list, start, end, diag_id); + } +} + +/** + * Append a warning to the list of warnings on the parser using the location of + * the given token. + */ +static inline void +pm_parser_warn_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) { + pm_parser_warn(parser, token->start, token->end, diag_id); +} + +/******************************************************************************/ +/* Node-related functions */ +/******************************************************************************/ + +/** + * Retrieve the constant pool id for the given location. + */ +static inline pm_constant_id_t +pm_parser_constant_id_location(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + return pm_constant_pool_insert_shared(&parser->constant_pool, start, (size_t) (end - start)); +} + +/** + * Retrieve the constant pool id for the given string. + */ +static inline pm_constant_id_t +pm_parser_constant_id_owned(pm_parser_t *parser, const uint8_t *start, size_t length) { + return pm_constant_pool_insert_owned(&parser->constant_pool, start, length); +} + +/** + * Retrieve the constant pool id for the given static literal C string. + */ +static inline pm_constant_id_t +pm_parser_constant_id_constant(pm_parser_t *parser, const char *start, size_t length) { + return pm_constant_pool_insert_constant(&parser->constant_pool, (const uint8_t *) start, length); +} + +/** + * Retrieve the constant pool id for the given token. + */ +static inline pm_constant_id_t +pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) { + return pm_parser_constant_id_location(parser, token->start, token->end); +} + +/** + * Retrieve the constant pool id for the given token. If the token is not + * provided, then return 0. + */ +static inline pm_constant_id_t +pm_parser_optional_constant_id_token(pm_parser_t *parser, const pm_token_t *token) { + return token->type == PM_TOKEN_NOT_PROVIDED ? 0 : pm_parser_constant_id_token(parser, token); +} + +/** + * Check whether or not the given node is value expression. + * If the node is value node, it returns NULL. + * If not, it returns the pointer to the node to be inspected as "void expression". + */ +static pm_node_t* +pm_check_value_expression(pm_node_t *node) { + pm_node_t* void_node = NULL; + + while (node != NULL) { + switch (PM_NODE_TYPE(node)) { + case PM_RETURN_NODE: + case PM_BREAK_NODE: + case PM_NEXT_NODE: + case PM_REDO_NODE: + case PM_RETRY_NODE: + case PM_MATCH_REQUIRED_NODE: + return void_node != NULL ? void_node : node; + case PM_MATCH_PREDICATE_NODE: + return NULL; + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + node = (pm_node_t *) cast->statements; + break; + } + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + node = (pm_node_t *) cast->body; + break; + } + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + node = cast->body.nodes[cast->body.size - 1]; + break; + } + case PM_IF_NODE: { + pm_if_node_t *cast = (pm_if_node_t *) node; + if (cast->statements == NULL || cast->consequent == NULL) { + return NULL; + } + pm_node_t *vn = pm_check_value_expression((pm_node_t *) cast->statements); + if (vn == NULL) { + return NULL; + } + if (void_node == NULL) { + void_node = vn; + } + node = cast->consequent; + break; + } + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + if (cast->statements == NULL || cast->consequent == NULL) { + return NULL; + } + pm_node_t *vn = pm_check_value_expression((pm_node_t *) cast->statements); + if (vn == NULL) { + return NULL; + } + if (void_node == NULL) { + void_node = vn; + } + node = (pm_node_t *) cast->consequent; + break; + } + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + node = (pm_node_t *) cast->statements; + break; + } + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + node = cast->left; + break; + } + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + node = cast->left; + break; + } + default: + return NULL; + } + } + + return NULL; +} + +static inline void +pm_assert_value_expression(pm_parser_t *parser, pm_node_t *node) { + pm_node_t *void_node = pm_check_value_expression(node); + if (void_node != NULL) { + pm_parser_err_node(parser, void_node, PM_ERR_VOID_EXPRESSION); + } +} + +/** + * The predicate of conditional nodes can change what would otherwise be regular + * nodes into specialized nodes. For example: + * + * if foo .. bar => RangeNode becomes FlipFlopNode + * if foo and bar .. baz => RangeNode becomes FlipFlopNode + * if /foo/ => RegularExpressionNode becomes MatchLastLineNode + * if /foo #{bar}/ => InterpolatedRegularExpressionNode becomes InterpolatedMatchLastLineNode + */ +static void +pm_conditional_predicate(pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + pm_conditional_predicate(cast->left); + pm_conditional_predicate(cast->right); + break; + } + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + pm_conditional_predicate(cast->left); + pm_conditional_predicate(cast->right); + break; + } + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + + if ((cast->body != NULL) && PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE)) { + pm_statements_node_t *statements = (pm_statements_node_t *) cast->body; + if (statements->body.size == 1) pm_conditional_predicate(statements->body.nodes[0]); + } + + break; + } + case PM_RANGE_NODE: { + pm_range_node_t *cast = (pm_range_node_t *) node; + if (cast->left) { + pm_conditional_predicate(cast->left); + } + if (cast->right) { + pm_conditional_predicate(cast->right); + } + + // Here we change the range node into a flip flop node. We can do + // this since the nodes are exactly the same except for the type. + // We're only asserting against the size when we should probably + // assert against the entire layout, but we'll assume tests will + // catch this. + assert(sizeof(pm_range_node_t) == sizeof(pm_flip_flop_node_t)); + node->type = PM_FLIP_FLOP_NODE; + + break; + } + case PM_REGULAR_EXPRESSION_NODE: + // Here we change the regular expression node into a match last line + // node. We can do this since the nodes are exactly the same except + // for the type. + assert(sizeof(pm_regular_expression_node_t) == sizeof(pm_match_last_line_node_t)); + node->type = PM_MATCH_LAST_LINE_NODE; + break; + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + // Here we change the interpolated regular expression node into an + // interpolated match last line node. We can do this since the nodes + // are exactly the same except for the type. + assert(sizeof(pm_interpolated_regular_expression_node_t) == sizeof(pm_interpolated_match_last_line_node_t)); + node->type = PM_INTERPOLATED_MATCH_LAST_LINE_NODE; + break; + default: + break; + } +} + +/** + * In a lot of places in the tree you can have tokens that are not provided but + * that do not cause an error. For example, in a method call without + * parentheses. In these cases we set the token to the "not provided" type. For + * example: + * + * pm_token_t token; + * not_provided(&token, parser->previous.end); + */ +static inline pm_token_t +not_provided(pm_parser_t *parser) { + return (pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }; +} + +#define PM_LOCATION_NULL_VALUE(parser) ((pm_location_t) { .start = parser->start, .end = parser->start }) +#define PM_LOCATION_TOKEN_VALUE(token) ((pm_location_t) { .start = (token)->start, .end = (token)->end }) +#define PM_LOCATION_NODE_VALUE(node) ((pm_location_t) { .start = (node)->location.start, .end = (node)->location.end }) +#define PM_LOCATION_NODE_BASE_VALUE(node) ((pm_location_t) { .start = (node)->base.location.start, .end = (node)->base.location.end }) +#define PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE ((pm_location_t) { .start = NULL, .end = NULL }) +#define PM_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == PM_TOKEN_NOT_PROVIDED ? PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE : PM_LOCATION_TOKEN_VALUE(token)) + +/** + * This is a special out parameter to the parse_arguments_list function that + * includes opening and closing parentheses in addition to the arguments since + * it's so common. It is handy to use when passing argument information to one + * of the call node creation functions. + */ +typedef struct { + /** The optional location of the opening parenthesis or bracket. */ + pm_location_t opening_loc; + + /** The lazily-allocated optional arguments node. */ + pm_arguments_node_t *arguments; + + /** The optional location of the closing parenthesis or bracket. */ + pm_location_t closing_loc; + + /** The optional block attached to the call. */ + pm_node_t *block; +} pm_arguments_t; + +/** + * Retrieve the end location of a `pm_arguments_t` object. + */ +static inline const uint8_t * +pm_arguments_end(pm_arguments_t *arguments) { + if (arguments->block != NULL) { + const uint8_t *end = arguments->block->location.end; + if (arguments->closing_loc.start != NULL && arguments->closing_loc.end > end) { + end = arguments->closing_loc.end; + } + return end; + } + if (arguments->closing_loc.start != NULL) { + return arguments->closing_loc.end; + } + if (arguments->arguments != NULL) { + return arguments->arguments->base.location.end; + } + return arguments->closing_loc.end; +} + +/** + * Check that we're not about to attempt to attach a brace block to a call that + * has arguments without parentheses. + */ +static void +pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_block_node_t *block) { + // First, check that we have arguments and that we don't have a closing + // location for them. + if (arguments->arguments == NULL || arguments->closing_loc.start != NULL) { + return; + } + + // Next, check that we don't have a single parentheses argument. This would + // look like: + // + // foo (1) {} + // + // In this case, it's actually okay for the block to be attached to the + // call, even though it looks like it's attached to the argument. + if (arguments->arguments->arguments.size == 1 && PM_NODE_TYPE_P(arguments->arguments->arguments.nodes[0], PM_PARENTHESES_NODE)) { + return; + } + + // If we didn't hit a case before this check, then at this point we need to + // add a syntax error. + pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_UNEXPECTED_BLOCK); +} + +/******************************************************************************/ +/* Node creation functions */ +/******************************************************************************/ + +/** + * Parse the decimal number represented by the range of bytes. returns + * UINT32_MAX if the number fails to parse. This function assumes that the range + * of bytes has already been validated to contain only decimal digits. + */ +static uint32_t +parse_decimal_number(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + ptrdiff_t diff = end - start; + assert(diff > 0 && ((unsigned long) diff < SIZE_MAX)); + size_t length = (size_t) diff; + + char *digits = calloc(length + 1, sizeof(char)); + memcpy(digits, start, length); + digits[length] = '\0'; + + char *endptr; + errno = 0; + unsigned long value = strtoul(digits, &endptr, 10); + + if ((digits == endptr) || (*endptr != '\0') || (errno == ERANGE)) { + pm_parser_err(parser, start, end, PM_ERR_INVALID_NUMBER_DECIMAL); + value = UINT32_MAX; + } + + free(digits); + + if (value > UINT32_MAX) { + pm_parser_err(parser, start, end, PM_ERR_INVALID_NUMBER_DECIMAL); + value = UINT32_MAX; + } + + return (uint32_t) value; +} + +/** + * When you have an encoding flag on a regular expression, it takes precedence + * over all of the previously set encoding flags. So we need to mask off any + * previously set encoding flags before setting the new one. + */ +#define PM_REGULAR_EXPRESSION_ENCODING_MASK ~(PM_REGULAR_EXPRESSION_FLAGS_EUC_JP | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J | PM_REGULAR_EXPRESSION_FLAGS_UTF_8) + +/** + * Parse out the options for a regular expression. + */ +static inline pm_node_flags_t +pm_regular_expression_flags_create(const pm_token_t *closing) { + pm_node_flags_t flags = 0; + + if (closing->type == PM_TOKEN_REGEXP_END) { + for (const uint8_t *flag = closing->start + 1; flag < closing->end; flag++) { + switch (*flag) { + case 'i': flags |= PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE; break; + case 'm': flags |= PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE; break; + case 'x': flags |= PM_REGULAR_EXPRESSION_FLAGS_EXTENDED; break; + case 'o': flags |= PM_REGULAR_EXPRESSION_FLAGS_ONCE; break; + + case 'e': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_EUC_JP); break; + case 'n': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT); break; + case 's': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J); break; + case 'u': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_UTF_8); break; + + default: assert(false && "unreachable"); + } + } + } + + return flags; +} + +#undef PM_REGULAR_EXPRESSION_ENCODING_MASK + +static pm_statements_node_t * +pm_statements_node_create(pm_parser_t *parser); + +static void +pm_statements_node_body_append(pm_statements_node_t *node, pm_node_t *statement); + +static size_t +pm_statements_node_body_length(pm_statements_node_t *node); + +/** + * This function is here to allow us a place to extend in the future when we + * implement our own arena allocation. + */ +static inline void * +pm_alloc_node(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { + void *memory = calloc(1, size); + if (memory == NULL) { + fprintf(stderr, "Failed to allocate %zu bytes\n", size); + abort(); + } + return memory; +} + +#define PM_ALLOC_NODE(parser, type) (type *) pm_alloc_node(parser, sizeof(type)) + +/** + * Allocate a new MissingNode node. + */ +static pm_missing_node_t * +pm_missing_node_create(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + pm_missing_node_t *node = PM_ALLOC_NODE(parser, pm_missing_node_t); + *node = (pm_missing_node_t) {{ .type = PM_MISSING_NODE, .location = { .start = start, .end = end } }}; + return node; +} + +/** + * Allocate and initialize a new AliasGlobalVariableNode node. + */ +static pm_alias_global_variable_node_t * +pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) { + assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS); + pm_alias_global_variable_node_t *node = PM_ALLOC_NODE(parser, pm_alias_global_variable_node_t); + + *node = (pm_alias_global_variable_node_t) { + { + .type = PM_ALIAS_GLOBAL_VARIABLE_NODE, + .location = { + .start = keyword->start, + .end = old_name->location.end + }, + }, + .new_name = new_name, + .old_name = old_name, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) + }; + + return node; +} + +/** + * Allocate and initialize a new AliasMethodNode node. + */ +static pm_alias_method_node_t * +pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) { + assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS); + pm_alias_method_node_t *node = PM_ALLOC_NODE(parser, pm_alias_method_node_t); + + *node = (pm_alias_method_node_t) { + { + .type = PM_ALIAS_METHOD_NODE, + .location = { + .start = keyword->start, + .end = old_name->location.end + }, + }, + .new_name = new_name, + .old_name = old_name, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) + }; + + return node; +} + +/** + * Allocate a new AlternationPatternNode node. + */ +static pm_alternation_pattern_node_t * +pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node_t *right, const pm_token_t *operator) { + pm_alternation_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_alternation_pattern_node_t); + + *node = (pm_alternation_pattern_node_t) { + { + .type = PM_ALTERNATION_PATTERN_NODE, + .location = { + .start = left->location.start, + .end = right->location.end + }, + }, + .left = left, + .right = right, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new and node. + */ +static pm_and_node_t * +pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) { + pm_assert_value_expression(parser, left); + + pm_and_node_t *node = PM_ALLOC_NODE(parser, pm_and_node_t); + + *node = (pm_and_node_t) { + { + .type = PM_AND_NODE, + .location = { + .start = left->location.start, + .end = right->location.end + }, + }, + .left = left, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .right = right + }; + + return node; +} + +/** + * Allocate an initialize a new arguments node. + */ +static pm_arguments_node_t * +pm_arguments_node_create(pm_parser_t *parser) { + pm_arguments_node_t *node = PM_ALLOC_NODE(parser, pm_arguments_node_t); + + *node = (pm_arguments_node_t) { + { + .type = PM_ARGUMENTS_NODE, + .location = PM_LOCATION_NULL_VALUE(parser) + }, + .arguments = { 0 } + }; + + return node; +} + +/** + * Return the size of the given arguments node. + */ +static size_t +pm_arguments_node_size(pm_arguments_node_t *node) { + return node->arguments.size; +} + +/** + * Append an argument to an arguments node. + */ +static void +pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argument) { + if (pm_arguments_node_size(node) == 0) { + node->base.location.start = argument->location.start; + } + + node->base.location.end = argument->location.end; + pm_node_list_append(&node->arguments, argument); +} + +/** + * Allocate and initialize a new ArrayNode node. + */ +static pm_array_node_t * +pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) { + pm_array_node_t *node = PM_ALLOC_NODE(parser, pm_array_node_t); + + *node = (pm_array_node_t) { + { + .type = PM_ARRAY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(opening) + }, + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .elements = { 0 } + }; + + return node; +} + +/** + * Return the size of the given array node. + */ +static inline size_t +pm_array_node_size(pm_array_node_t *node) { + return node->elements.size; +} + +/** + * Append an argument to an array node. + */ +static inline void +pm_array_node_elements_append(pm_array_node_t *node, pm_node_t *element) { + if (!node->elements.size && !node->opening_loc.start) { + node->base.location.start = element->location.start; + } + + pm_node_list_append(&node->elements, element); + node->base.location.end = element->location.end; + + // If the element is not a static literal, then the array is not a static + // literal. Turn that flag off. + if (PM_NODE_TYPE_P(element, PM_ARRAY_NODE) || PM_NODE_TYPE_P(element, PM_HASH_NODE) || PM_NODE_TYPE_P(element, PM_RANGE_NODE) || (element->flags & PM_NODE_FLAG_STATIC_LITERAL) == 0) { + node->base.flags &= (pm_node_flags_t) ~PM_NODE_FLAG_STATIC_LITERAL; + } + + if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) { + node->base.flags |= PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT; + } +} + +/** + * Set the closing token and end location of an array node. + */ +static void +pm_array_node_close_set(pm_array_node_t *node, const pm_token_t *closing) { + assert(closing->type == PM_TOKEN_BRACKET_RIGHT || closing->type == PM_TOKEN_STRING_END || closing->type == PM_TOKEN_MISSING || closing->type == PM_TOKEN_NOT_PROVIDED); + node->base.location.end = closing->end; + node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing); +} + +/** + * Allocate and initialize a new array pattern node. The node list given in the + * nodes parameter is guaranteed to have at least two nodes. + */ +static pm_array_pattern_node_t * +pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *nodes) { + pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + { + .type = PM_ARRAY_PATTERN_NODE, + .location = { + .start = nodes->nodes[0]->location.start, + .end = nodes->nodes[nodes->size - 1]->location.end + }, + }, + .constant = NULL, + .rest = NULL, + .requireds = { 0 }, + .posts = { 0 }, + .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + // For now we're going to just copy over each pointer manually. This could be + // much more efficient, as we could instead resize the node list. + bool found_rest = false; + for (size_t index = 0; index < nodes->size; index++) { + pm_node_t *child = nodes->nodes[index]; + + if (!found_rest && (PM_NODE_TYPE_P(child, PM_SPLAT_NODE) || PM_NODE_TYPE_P(child, PM_IMPLICIT_REST_NODE))) { + node->rest = child; + found_rest = true; + } else if (found_rest) { + pm_node_list_append(&node->posts, child); + } else { + pm_node_list_append(&node->requireds, child); + } + } + + return node; +} + +/** + * Allocate and initialize a new array pattern node from a single rest node. + */ +static pm_array_pattern_node_t * +pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) { + pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + { + .type = PM_ARRAY_PATTERN_NODE, + .location = rest->location, + }, + .constant = NULL, + .rest = rest, + .requireds = { 0 }, + .posts = { 0 }, + .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +/** + * Allocate and initialize a new array pattern node from a constant and opening + * and closing tokens. + */ +static pm_array_pattern_node_t * +pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, const pm_token_t *opening, const pm_token_t *closing) { + pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + { + .type = PM_ARRAY_PATTERN_NODE, + .location = { + .start = constant->location.start, + .end = closing->end + }, + }, + .constant = constant, + .rest = NULL, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), + .requireds = { 0 }, + .posts = { 0 } + }; + + return node; +} + +/** + * Allocate and initialize a new array pattern node from an opening and closing + * token. + */ +static pm_array_pattern_node_t * +pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) { + pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + { + .type = PM_ARRAY_PATTERN_NODE, + .location = { + .start = opening->start, + .end = closing->end + }, + }, + .constant = NULL, + .rest = NULL, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), + .requireds = { 0 }, + .posts = { 0 } + }; + + return node; +} + +static inline void +pm_array_pattern_node_requireds_append(pm_array_pattern_node_t *node, pm_node_t *inner) { + pm_node_list_append(&node->requireds, inner); +} + +/** + * Allocate and initialize a new assoc node. + */ +static pm_assoc_node_t * +pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *operator, pm_node_t *value) { + pm_assoc_node_t *node = PM_ALLOC_NODE(parser, pm_assoc_node_t); + const uint8_t *end; + + if (value != NULL) { + end = value->location.end; + } else if (operator->type != PM_TOKEN_NOT_PROVIDED) { + end = operator->end; + } else { + end = key->location.end; + } + + // If the key and value of this assoc node are both static literals, then + // we can mark this node as a static literal. + pm_node_flags_t flags = 0; + if (value && !PM_NODE_TYPE_P(value, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(value, PM_HASH_NODE) && !PM_NODE_TYPE_P(value, PM_RANGE_NODE)) { + flags = key->flags & value->flags & PM_NODE_FLAG_STATIC_LITERAL; + } + + *node = (pm_assoc_node_t) { + { + .type = PM_ASSOC_NODE, + .flags = flags, + .location = { + .start = key->location.start, + .end = end + }, + }, + .key = key, + .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new assoc splat node. + */ +static pm_assoc_splat_node_t * +pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token_t *operator) { + assert(operator->type == PM_TOKEN_USTAR_STAR); + pm_assoc_splat_node_t *node = PM_ALLOC_NODE(parser, pm_assoc_splat_node_t); + + *node = (pm_assoc_splat_node_t) { + { + .type = PM_ASSOC_SPLAT_NODE, + .location = { + .start = operator->start, + .end = value == NULL ? operator->end : value->location.end + }, + }, + .value = value, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate a new BackReferenceReadNode node. + */ +static pm_back_reference_read_node_t * +pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) { + assert(name->type == PM_TOKEN_BACK_REFERENCE); + pm_back_reference_read_node_t *node = PM_ALLOC_NODE(parser, pm_back_reference_read_node_t); + + *node = (pm_back_reference_read_node_t) { + { + .type = PM_BACK_REFERENCE_READ_NODE, + .location = PM_LOCATION_TOKEN_VALUE(name), + }, + .name = pm_parser_constant_id_token(parser, name) + }; + + return node; +} + +/** + * Allocate and initialize new a begin node. + */ +static pm_begin_node_t * +pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_statements_node_t *statements) { + pm_begin_node_t *node = PM_ALLOC_NODE(parser, pm_begin_node_t); + + *node = (pm_begin_node_t) { + { + .type = PM_BEGIN_NODE, + .location = { + .start = begin_keyword->start, + .end = statements == NULL ? begin_keyword->end : statements->base.location.end + }, + }, + .begin_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(begin_keyword), + .statements = statements, + .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +/** + * Set the rescue clause, optionally start, and end location of a begin node. + */ +static void +pm_begin_node_rescue_clause_set(pm_begin_node_t *node, pm_rescue_node_t *rescue_clause) { + // If the begin keyword doesn't exist, we set the start on the begin_node + if (!node->begin_keyword_loc.start) { + node->base.location.start = rescue_clause->base.location.start; + } + node->base.location.end = rescue_clause->base.location.end; + node->rescue_clause = rescue_clause; +} + +/** + * Set the else clause and end location of a begin node. + */ +static void +pm_begin_node_else_clause_set(pm_begin_node_t *node, pm_else_node_t *else_clause) { + node->base.location.end = else_clause->base.location.end; + node->else_clause = else_clause; +} + +/** + * Set the ensure clause and end location of a begin node. + */ +static void +pm_begin_node_ensure_clause_set(pm_begin_node_t *node, pm_ensure_node_t *ensure_clause) { + node->base.location.end = ensure_clause->base.location.end; + node->ensure_clause = ensure_clause; +} + +/** + * Set the end keyword and end location of a begin node. + */ +static void +pm_begin_node_end_keyword_set(pm_begin_node_t *node, const pm_token_t *end_keyword) { + assert(end_keyword->type == PM_TOKEN_KEYWORD_END || end_keyword->type == PM_TOKEN_MISSING); + + node->base.location.end = end_keyword->end; + node->end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword); +} + +/** + * Allocate and initialize a new BlockArgumentNode node. + */ +static pm_block_argument_node_t * +pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) { + pm_block_argument_node_t *node = PM_ALLOC_NODE(parser, pm_block_argument_node_t); + + *node = (pm_block_argument_node_t) { + { + .type = PM_BLOCK_ARGUMENT_NODE, + .location = { + .start = operator->start, + .end = expression == NULL ? operator->end : expression->location.end + }, + }, + .expression = expression, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new BlockNode node. + */ +static pm_block_node_t * +pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *opening, pm_block_parameters_node_t *parameters, pm_node_t *body, const pm_token_t *closing, uint32_t numbered_parameters) { + pm_block_node_t *node = PM_ALLOC_NODE(parser, pm_block_node_t); + + *node = (pm_block_node_t) { + { + .type = PM_BLOCK_NODE, + .location = { .start = opening->start, .end = closing->end }, + }, + .locals = *locals, + .parameters = parameters, + .body = body, + .numbered_parameters = numbered_parameters, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +/** + * Allocate and initialize a new BlockParameterNode node. + */ +static pm_block_parameter_node_t * +pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator) { + assert(operator->type == PM_TOKEN_NOT_PROVIDED || operator->type == PM_TOKEN_UAMPERSAND || operator->type == PM_TOKEN_AMPERSAND); + pm_block_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_block_parameter_node_t); + + *node = (pm_block_parameter_node_t) { + { + .type = PM_BLOCK_PARAMETER_NODE, + .location = { + .start = operator->start, + .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end) + }, + }, + .name = pm_parser_optional_constant_id_token(parser, name), + .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new BlockParametersNode node. + */ +static pm_block_parameters_node_t * +pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *parameters, const pm_token_t *opening) { + pm_block_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_block_parameters_node_t); + + const uint8_t *start; + if (opening->type != PM_TOKEN_NOT_PROVIDED) { + start = opening->start; + } else if (parameters != NULL) { + start = parameters->base.location.start; + } else { + start = NULL; + } + + const uint8_t *end; + if (parameters != NULL) { + end = parameters->base.location.end; + } else if (opening->type != PM_TOKEN_NOT_PROVIDED) { + end = opening->end; + } else { + end = NULL; + } + + *node = (pm_block_parameters_node_t) { + { + .type = PM_BLOCK_PARAMETERS_NODE, + .location = { + .start = start, + .end = end + } + }, + .parameters = parameters, + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .locals = { 0 } + }; + + return node; +} + +/** + * Set the closing location of a BlockParametersNode node. + */ +static void +pm_block_parameters_node_closing_set(pm_block_parameters_node_t *node, const pm_token_t *closing) { + assert(closing->type == PM_TOKEN_PIPE || closing->type == PM_TOKEN_PARENTHESIS_RIGHT || closing->type == PM_TOKEN_MISSING); + + node->base.location.end = closing->end; + node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing); +} + +/** + * Allocate and initialize a new BlockLocalVariableNode node. + */ +static pm_block_local_variable_node_t * +pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) { + assert(name->type == PM_TOKEN_IDENTIFIER || name->type == PM_TOKEN_MISSING); + pm_block_local_variable_node_t *node = PM_ALLOC_NODE(parser, pm_block_local_variable_node_t); + + *node = (pm_block_local_variable_node_t) { + { + .type = PM_BLOCK_LOCAL_VARIABLE_NODE, + .location = PM_LOCATION_TOKEN_VALUE(name), + }, + .name = pm_parser_constant_id_token(parser, name) + }; + + return node; +} + +/** + * Append a new block-local variable to a BlockParametersNode node. + */ +static void +pm_block_parameters_node_append_local(pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) { + pm_node_list_append(&node->locals, (pm_node_t *) local); + + if (node->base.location.start == NULL) node->base.location.start = local->base.location.start; + node->base.location.end = local->base.location.end; +} + +/** + * Allocate and initialize a new BreakNode node. + */ +static pm_break_node_t * +pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) { + assert(keyword->type == PM_TOKEN_KEYWORD_BREAK); + pm_break_node_t *node = PM_ALLOC_NODE(parser, pm_break_node_t); + + *node = (pm_break_node_t) { + { + .type = PM_BREAK_NODE, + .location = { + .start = keyword->start, + .end = (arguments == NULL ? keyword->end : arguments->base.location.end) + }, + }, + .arguments = arguments, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) + }; + + return node; +} + +/** + * Allocate and initialize a new CallNode node. This sets everything to NULL or + * PM_TOKEN_NOT_PROVIDED as appropriate such that its values can be overridden + * in the various specializations of this function. + */ +static pm_call_node_t * +pm_call_node_create(pm_parser_t *parser) { + pm_call_node_t *node = PM_ALLOC_NODE(parser, pm_call_node_t); + + *node = (pm_call_node_t) { + { + .type = PM_CALL_NODE, + .location = PM_LOCATION_NULL_VALUE(parser), + }, + .receiver = NULL, + .call_operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .message_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .arguments = NULL, + .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .block = NULL, + .name = 0 + }; + + return node; +} + +/** + * Allocate and initialize a new CallNode node from an aref or an aset + * expression. + */ +static pm_call_node_t * +pm_call_node_aref_create(pm_parser_t *parser, pm_node_t *receiver, pm_arguments_t *arguments) { + pm_assert_value_expression(parser, receiver); + + pm_call_node_t *node = pm_call_node_create(parser); + + node->base.location.start = receiver->location.start; + node->base.location.end = pm_arguments_end(arguments); + + node->receiver = receiver; + node->message_loc.start = arguments->opening_loc.start; + node->message_loc.end = arguments->closing_loc.end; + + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + node->block = arguments->block; + + node->name = pm_parser_constant_id_constant(parser, "[]", 2); + return node; +} + +/** + * Allocate and initialize a new CallNode node from a binary expression. + */ +static pm_call_node_t * +pm_call_node_binary_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_node_t *argument) { + pm_assert_value_expression(parser, receiver); + pm_assert_value_expression(parser, argument); + + pm_call_node_t *node = pm_call_node_create(parser); + + node->base.location.start = MIN(receiver->location.start, argument->location.start); + node->base.location.end = MAX(receiver->location.end, argument->location.end); + + node->receiver = receiver; + node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator); + + pm_arguments_node_t *arguments = pm_arguments_node_create(parser); + pm_arguments_node_arguments_append(arguments, argument); + node->arguments = arguments; + + node->name = pm_parser_constant_id_token(parser, operator); + return node; +} + +/** + * Allocate and initialize a new CallNode node from a call expression. + */ +static pm_call_node_t * +pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_token_t *message, pm_arguments_t *arguments) { + pm_assert_value_expression(parser, receiver); + + pm_call_node_t *node = pm_call_node_create(parser); + + node->base.location.start = receiver->location.start; + const uint8_t *end = pm_arguments_end(arguments); + if (end == NULL) { + end = message->end; + } + node->base.location.end = end; + + node->receiver = receiver; + node->call_operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator); + node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message); + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + node->block = arguments->block; + + if (operator->type == PM_TOKEN_AMPERSAND_DOT) { + node->base.flags |= PM_CALL_NODE_FLAGS_SAFE_NAVIGATION; + } + + node->name = pm_parser_constant_id_token(parser, message); + return node; +} + +/** + * Allocate and initialize a new CallNode node from a call to a method name + * without a receiver that could not have been a local variable read. + */ +static pm_call_node_t * +pm_call_node_fcall_create(pm_parser_t *parser, pm_token_t *message, pm_arguments_t *arguments) { + pm_call_node_t *node = pm_call_node_create(parser); + + node->base.location.start = message->start; + node->base.location.end = pm_arguments_end(arguments); + + node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message); + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + node->block = arguments->block; + + node->name = pm_parser_constant_id_token(parser, message); + return node; +} + +/** + * Allocate and initialize a new CallNode node from a not expression. + */ +static pm_call_node_t * +pm_call_node_not_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *message, pm_arguments_t *arguments) { + pm_assert_value_expression(parser, receiver); + + pm_call_node_t *node = pm_call_node_create(parser); + + node->base.location.start = message->start; + if (arguments->closing_loc.start != NULL) { + node->base.location.end = arguments->closing_loc.end; + } else { + node->base.location.end = receiver->location.end; + } + + node->receiver = receiver; + node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message); + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + + node->name = pm_parser_constant_id_constant(parser, "!", 1); + return node; +} + +/** + * Allocate and initialize a new CallNode node from a call shorthand expression. + */ +static pm_call_node_t * +pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_arguments_t *arguments) { + pm_assert_value_expression(parser, receiver); + + pm_call_node_t *node = pm_call_node_create(parser); + + node->base.location.start = receiver->location.start; + node->base.location.end = pm_arguments_end(arguments); + + node->receiver = receiver; + node->call_operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator); + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + node->block = arguments->block; + + if (operator->type == PM_TOKEN_AMPERSAND_DOT) { + node->base.flags |= PM_CALL_NODE_FLAGS_SAFE_NAVIGATION; + } + + node->name = pm_parser_constant_id_constant(parser, "call", 4); + return node; +} + +/** + * Allocate and initialize a new CallNode node from a unary operator expression. + */ +static pm_call_node_t * +pm_call_node_unary_create(pm_parser_t *parser, pm_token_t *operator, pm_node_t *receiver, const char *name) { + pm_assert_value_expression(parser, receiver); + + pm_call_node_t *node = pm_call_node_create(parser); + + node->base.location.start = operator->start; + node->base.location.end = receiver->location.end; + + node->receiver = receiver; + node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator); + + node->name = pm_parser_constant_id_constant(parser, name, strlen(name)); + return node; +} + +/** + * Allocate and initialize a new CallNode node from a call to a method name + * without a receiver that could also have been a local variable read. + */ +static pm_call_node_t * +pm_call_node_variable_call_create(pm_parser_t *parser, pm_token_t *message) { + pm_call_node_t *node = pm_call_node_create(parser); + + node->base.location = PM_LOCATION_TOKEN_VALUE(message); + node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message); + + node->name = pm_parser_constant_id_token(parser, message); + return node; +} + +/** + * Returns whether or not this call node is a "vcall" (a call to a method name + * without a receiver that could also have been a local variable read). + */ +static inline bool +pm_call_node_variable_call_p(pm_call_node_t *node) { + return node->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL; +} + +/** + * Returns whether or not this call is to the [] method in the index form without a block (as + * opposed to `foo.[]` and `foo[] { }`). + */ +static inline bool +pm_call_node_index_p(pm_call_node_t *node) { + return ( + (node->call_operator_loc.start == NULL) && + (node->message_loc.start != NULL) && + (node->message_loc.start[0] == '[') && + (node->message_loc.end[-1] == ']') && + (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) + ); +} + +/** + * Returns whether or not this call can be used on the left-hand side of an + * operator assignment. + */ +static inline bool +pm_call_node_writable_p(pm_call_node_t *node) { + return ( + (node->message_loc.start != NULL) && + (node->message_loc.end[-1] != '!') && + (node->message_loc.end[-1] != '?') && + (node->opening_loc.start == NULL) && + (node->arguments == NULL) && + (node->block == NULL) + ); +} + +/** + * Initialize the read name by reading the write name and chopping off the '='. + */ +static void +pm_call_write_read_name_init(pm_parser_t *parser, pm_constant_id_t *read_name, pm_constant_id_t *write_name) { + pm_constant_t *write_constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *write_name); + + if (write_constant->length > 0) { + size_t length = write_constant->length - 1; + + void *memory = malloc(length); + memcpy(memory, write_constant->start, length); + + *read_name = pm_constant_pool_insert_owned(&parser->constant_pool, (uint8_t *) memory, length); + } else { + // We can get here if the message was missing because of a syntax error. + *read_name = pm_parser_constant_id_constant(parser, "", 0); + } +} + +/** + * Allocate and initialize a new CallAndWriteNode node. + */ +static pm_call_and_write_node_t * +pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(target->block == NULL); + assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + pm_call_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_call_and_write_node_t); + + *node = (pm_call_and_write_node_t) { + { + .type = PM_CALL_AND_WRITE_NODE, + .flags = target->base.flags, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .message_loc = target->message_loc, + .read_name = 0, + .write_name = target->name, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + pm_call_write_read_name_init(parser, &node->read_name, &node->write_name); + + // Here we're going to free the target, since it is no longer necessary. + // However, we don't want to call `pm_node_destroy` because we want to keep + // around all of its children since we just reused them. + free(target); + + return node; +} + +/** + * Allocate and initialize a new IndexAndWriteNode node. + */ +static pm_index_and_write_node_t * +pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + pm_index_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_and_write_node_t); + + *node = (pm_index_and_write_node_t) { + { + .type = PM_INDEX_AND_WRITE_NODE, + .flags = target->base.flags, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .opening_loc = target->opening_loc, + .arguments = target->arguments, + .closing_loc = target->closing_loc, + .block = target->block, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + // Here we're going to free the target, since it is no longer necessary. + // However, we don't want to call `pm_node_destroy` because we want to keep + // around all of its children since we just reused them. + free(target); + + return node; +} + +/** + * Allocate a new CallOperatorWriteNode node. + */ +static pm_call_operator_write_node_t * +pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(target->block == NULL); + pm_call_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_call_operator_write_node_t); + + *node = (pm_call_operator_write_node_t) { + { + .type = PM_CALL_OPERATOR_WRITE_NODE, + .flags = target->base.flags, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .message_loc = target->message_loc, + .read_name = 0, + .write_name = target->name, + .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + pm_call_write_read_name_init(parser, &node->read_name, &node->write_name); + + // Here we're going to free the target, since it is no longer necessary. + // However, we don't want to call `pm_node_destroy` because we want to keep + // around all of its children since we just reused them. + free(target); + + return node; +} + +/** + * Allocate a new IndexOperatorWriteNode node. + */ +static pm_index_operator_write_node_t * +pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_index_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_operator_write_node_t); + + *node = (pm_index_operator_write_node_t) { + { + .type = PM_INDEX_OPERATOR_WRITE_NODE, + .flags = target->base.flags, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .opening_loc = target->opening_loc, + .arguments = target->arguments, + .closing_loc = target->closing_loc, + .block = target->block, + .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + // Here we're going to free the target, since it is no longer necessary. + // However, we don't want to call `pm_node_destroy` because we want to keep + // around all of its children since we just reused them. + free(target); + + return node; +} + +/** + * Allocate and initialize a new CallOrWriteNode node. + */ +static pm_call_or_write_node_t * +pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(target->block == NULL); + assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); + pm_call_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_call_or_write_node_t); + + *node = (pm_call_or_write_node_t) { + { + .type = PM_CALL_OR_WRITE_NODE, + .flags = target->base.flags, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .message_loc = target->message_loc, + .read_name = 0, + .write_name = target->name, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + pm_call_write_read_name_init(parser, &node->read_name, &node->write_name); + + // Here we're going to free the target, since it is no longer necessary. + // However, we don't want to call `pm_node_destroy` because we want to keep + // around all of its children since we just reused them. + free(target); + + return node; +} + +/** + * Allocate and initialize a new IndexOrWriteNode node. + */ +static pm_index_or_write_node_t * +pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); + pm_index_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_or_write_node_t); + + *node = (pm_index_or_write_node_t) { + { + .type = PM_INDEX_OR_WRITE_NODE, + .flags = target->base.flags, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .opening_loc = target->opening_loc, + .arguments = target->arguments, + .closing_loc = target->closing_loc, + .block = target->block, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + // Here we're going to free the target, since it is no longer necessary. + // However, we don't want to call `pm_node_destroy` because we want to keep + // around all of its children since we just reused them. + free(target); + + return node; +} + +/** + * Allocate and initialize a new CapturePatternNode node. + */ +static pm_capture_pattern_node_t * +pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *target, const pm_token_t *operator) { + pm_capture_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_capture_pattern_node_t); + + *node = (pm_capture_pattern_node_t) { + { + .type = PM_CAPTURE_PATTERN_NODE, + .location = { + .start = value->location.start, + .end = target->location.end + }, + }, + .value = value, + .target = target, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new CaseNode node. + */ +static pm_case_node_t * +pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) { + pm_case_node_t *node = PM_ALLOC_NODE(parser, pm_case_node_t); + + *node = (pm_case_node_t) { + { + .type = PM_CASE_NODE, + .location = { + .start = case_keyword->start, + .end = end_keyword->end + }, + }, + .predicate = predicate, + .consequent = NULL, + .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword), + .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword), + .conditions = { 0 } + }; + + return node; +} + +/** + * Append a new condition to a CaseNode node. + */ +static void +pm_case_node_condition_append(pm_case_node_t *node, pm_node_t *condition) { + assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE)); + + pm_node_list_append(&node->conditions, condition); + node->base.location.end = condition->location.end; +} + +/** + * Set the consequent of a CaseNode node. + */ +static void +pm_case_node_consequent_set(pm_case_node_t *node, pm_else_node_t *consequent) { + node->consequent = consequent; + node->base.location.end = consequent->base.location.end; +} + +/** + * Set the end location for a CaseNode node. + */ +static void +pm_case_node_end_keyword_loc_set(pm_case_node_t *node, const pm_token_t *end_keyword) { + node->base.location.end = end_keyword->end; + node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword); +} + +/** + * Allocate and initialize a new CaseMatchNode node. + */ +static pm_case_match_node_t * +pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) { + pm_case_match_node_t *node = PM_ALLOC_NODE(parser, pm_case_match_node_t); + + *node = (pm_case_match_node_t) { + { + .type = PM_CASE_MATCH_NODE, + .location = { + .start = case_keyword->start, + .end = end_keyword->end + }, + }, + .predicate = predicate, + .consequent = NULL, + .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword), + .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword), + .conditions = { 0 } + }; + + return node; +} + +/** + * Append a new condition to a CaseMatchNode node. + */ +static void +pm_case_match_node_condition_append(pm_case_match_node_t *node, pm_node_t *condition) { + assert(PM_NODE_TYPE_P(condition, PM_IN_NODE)); + + pm_node_list_append(&node->conditions, condition); + node->base.location.end = condition->location.end; +} + +/** + * Set the consequent of a CaseMatchNode node. + */ +static void +pm_case_match_node_consequent_set(pm_case_match_node_t *node, pm_else_node_t *consequent) { + node->consequent = consequent; + node->base.location.end = consequent->base.location.end; +} + +/** + * Set the end location for a CaseMatchNode node. + */ +static void +pm_case_match_node_end_keyword_loc_set(pm_case_match_node_t *node, const pm_token_t *end_keyword) { + node->base.location.end = end_keyword->end; + node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword); +} + +/** + * Allocate a new ClassNode node. + */ +static pm_class_node_t * +pm_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *class_keyword, pm_node_t *constant_path, const pm_token_t *name, const pm_token_t *inheritance_operator, pm_node_t *superclass, pm_node_t *body, const pm_token_t *end_keyword) { + pm_class_node_t *node = PM_ALLOC_NODE(parser, pm_class_node_t); + + *node = (pm_class_node_t) { + { + .type = PM_CLASS_NODE, + .location = { .start = class_keyword->start, .end = end_keyword->end }, + }, + .locals = *locals, + .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword), + .constant_path = constant_path, + .inheritance_operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(inheritance_operator), + .superclass = superclass, + .body = body, + .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword), + .name = pm_parser_constant_id_token(parser, name) + }; + + return node; +} + +/** + * Allocate and initialize a new ClassVariableAndWriteNode node. + */ +static pm_class_variable_and_write_node_t * +pm_class_variable_and_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + pm_class_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_and_write_node_t); + + *node = (pm_class_variable_and_write_node_t) { + { + .type = PM_CLASS_VARIABLE_AND_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new ClassVariableOperatorWriteNode node. + */ +static pm_class_variable_operator_write_node_t * +pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_class_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_operator_write_node_t); + + *node = (pm_class_variable_operator_write_node_t) { + { + .type = PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +/** + * Allocate and initialize a new ClassVariableOrWriteNode node. + */ +static pm_class_variable_or_write_node_t * +pm_class_variable_or_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); + pm_class_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_or_write_node_t); + + *node = (pm_class_variable_or_write_node_t) { + { + .type = PM_CLASS_VARIABLE_OR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new ClassVariableReadNode node. + */ +static pm_class_variable_read_node_t * +pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_CLASS_VARIABLE); + pm_class_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_read_node_t); + + *node = (pm_class_variable_read_node_t) { + { + .type = PM_CLASS_VARIABLE_READ_NODE, + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .name = pm_parser_constant_id_token(parser, token) + }; + + return node; +} + +/** + * Initialize a new ClassVariableWriteNode node from a ClassVariableRead node. + */ +static pm_class_variable_write_node_t * +pm_class_variable_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) { + pm_class_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_write_node_t); + + *node = (pm_class_variable_write_node_t) { + { + .type = PM_CLASS_VARIABLE_WRITE_NODE, + .location = { + .start = read_node->base.location.start, + .end = value->location.end + }, + }, + .name = read_node->name, + .name_loc = PM_LOCATION_NODE_VALUE((pm_node_t *) read_node), + .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new ConstantPathAndWriteNode node. + */ +static pm_constant_path_and_write_node_t * +pm_constant_path_and_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + pm_constant_path_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_and_write_node_t); + + *node = (pm_constant_path_and_write_node_t) { + { + .type = PM_CONSTANT_PATH_AND_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new ConstantPathOperatorWriteNode node. + */ +static pm_constant_path_operator_write_node_t * +pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_constant_path_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_operator_write_node_t); + + *node = (pm_constant_path_operator_write_node_t) { + { + .type = PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +/** + * Allocate and initialize a new ConstantPathOrWriteNode node. + */ +static pm_constant_path_or_write_node_t * +pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); + pm_constant_path_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_or_write_node_t); + + *node = (pm_constant_path_or_write_node_t) { + { + .type = PM_CONSTANT_PATH_OR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new ConstantPathNode node. + */ +static pm_constant_path_node_t * +pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_token_t *delimiter, pm_node_t *child) { + pm_assert_value_expression(parser, parent); + + pm_constant_path_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_node_t); + + *node = (pm_constant_path_node_t) { + { + .type = PM_CONSTANT_PATH_NODE, + .location = { + .start = parent == NULL ? delimiter->start : parent->location.start, + .end = child->location.end + }, + }, + .parent = parent, + .child = child, + .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter) + }; + + return node; +} + +/** + * Allocate a new ConstantPathWriteNode node. + */ +static pm_constant_path_write_node_t * +pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_constant_path_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_write_node_t); + + *node = (pm_constant_path_write_node_t) { + { + .type = PM_CONSTANT_PATH_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + }, + }, + .target = target, + .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new ConstantAndWriteNode node. + */ +static pm_constant_and_write_node_t * +pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + pm_constant_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_and_write_node_t); + + *node = (pm_constant_and_write_node_t) { + { + .type = PM_CONSTANT_AND_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new ConstantOperatorWriteNode node. + */ +static pm_constant_operator_write_node_t * +pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_constant_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_operator_write_node_t); + + *node = (pm_constant_operator_write_node_t) { + { + .type = PM_CONSTANT_OPERATOR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +/** + * Allocate and initialize a new ConstantOrWriteNode node. + */ +static pm_constant_or_write_node_t * +pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); + pm_constant_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_or_write_node_t); + + *node = (pm_constant_or_write_node_t) { + { + .type = PM_CONSTANT_OR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new ConstantReadNode node. + */ +static pm_constant_read_node_t * +pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) { + assert(name->type == PM_TOKEN_CONSTANT || name->type == PM_TOKEN_MISSING); + pm_constant_read_node_t *node = PM_ALLOC_NODE(parser, pm_constant_read_node_t); + + *node = (pm_constant_read_node_t) { + { + .type = PM_CONSTANT_READ_NODE, + .location = PM_LOCATION_TOKEN_VALUE(name) + }, + .name = pm_parser_constant_id_token(parser, name) + }; + + return node; +} + +/** + * Allocate a new ConstantWriteNode node. + */ +static pm_constant_write_node_t * +pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_constant_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_write_node_t); + + *node = (pm_constant_write_node_t) { + { + .type = PM_CONSTANT_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new DefNode node. + */ +static pm_def_node_t * +pm_def_node_create( + pm_parser_t *parser, + const pm_token_t *name, + pm_node_t *receiver, + pm_parameters_node_t *parameters, + pm_node_t *body, + pm_constant_id_list_t *locals, + const pm_token_t *def_keyword, + const pm_token_t *operator, + const pm_token_t *lparen, + const pm_token_t *rparen, + const pm_token_t *equal, + const pm_token_t *end_keyword +) { + pm_def_node_t *node = PM_ALLOC_NODE(parser, pm_def_node_t); + const uint8_t *end; + + if (end_keyword->type == PM_TOKEN_NOT_PROVIDED) { + end = body->location.end; + } else { + end = end_keyword->end; + } + + *node = (pm_def_node_t) { + { + .type = PM_DEF_NODE, + .location = { .start = def_keyword->start, .end = end }, + }, + .name = pm_parser_constant_id_token(parser, name), + .name_loc = PM_LOCATION_TOKEN_VALUE(name), + .receiver = receiver, + .parameters = parameters, + .body = body, + .locals = *locals, + .def_keyword_loc = PM_LOCATION_TOKEN_VALUE(def_keyword), + .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen), + .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen), + .equal_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(equal), + .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +/** + * Allocate a new DefinedNode node. + */ +static pm_defined_node_t * +pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t *value, const pm_token_t *rparen, const pm_location_t *keyword_loc) { + pm_defined_node_t *node = PM_ALLOC_NODE(parser, pm_defined_node_t); + + *node = (pm_defined_node_t) { + { + .type = PM_DEFINED_NODE, + .location = { + .start = keyword_loc->start, + .end = (rparen->type == PM_TOKEN_NOT_PROVIDED ? value->location.end : rparen->end) + }, + }, + .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen), + .value = value, + .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen), + .keyword_loc = *keyword_loc + }; + + return node; +} + +/** + * Allocate and initialize a new ElseNode node. + */ +static pm_else_node_t * +pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) { + pm_else_node_t *node = PM_ALLOC_NODE(parser, pm_else_node_t); + const uint8_t *end = NULL; + if ((end_keyword->type == PM_TOKEN_NOT_PROVIDED) && (statements != NULL)) { + end = statements->base.location.end; + } else { + end = end_keyword->end; + } + + *node = (pm_else_node_t) { + { + .type = PM_ELSE_NODE, + .location = { + .start = else_keyword->start, + .end = end, + }, + }, + .else_keyword_loc = PM_LOCATION_TOKEN_VALUE(else_keyword), + .statements = statements, + .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +/** + * Allocate and initialize a new EmbeddedStatementsNode node. + */ +static pm_embedded_statements_node_t * +pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) { + pm_embedded_statements_node_t *node = PM_ALLOC_NODE(parser, pm_embedded_statements_node_t); + + *node = (pm_embedded_statements_node_t) { + { + .type = PM_EMBEDDED_STATEMENTS_NODE, + .location = { + .start = opening->start, + .end = closing->end + } + }, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .statements = statements, + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +/** + * Allocate and initialize a new EmbeddedVariableNode node. + */ +static pm_embedded_variable_node_t * +pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) { + pm_embedded_variable_node_t *node = PM_ALLOC_NODE(parser, pm_embedded_variable_node_t); + + *node = (pm_embedded_variable_node_t) { + { + .type = PM_EMBEDDED_VARIABLE_NODE, + .location = { + .start = operator->start, + .end = variable->location.end + } + }, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .variable = variable + }; + + return node; +} + +/** + * Allocate a new EnsureNode node. + */ +static pm_ensure_node_t * +pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) { + pm_ensure_node_t *node = PM_ALLOC_NODE(parser, pm_ensure_node_t); + + *node = (pm_ensure_node_t) { + { + .type = PM_ENSURE_NODE, + .location = { + .start = ensure_keyword->start, + .end = end_keyword->end + }, + }, + .ensure_keyword_loc = PM_LOCATION_TOKEN_VALUE(ensure_keyword), + .statements = statements, + .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +/** + * Allocate and initialize a new FalseNode node. + */ +static pm_false_node_t * +pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_KEYWORD_FALSE); + pm_false_node_t *node = PM_ALLOC_NODE(parser, pm_false_node_t); + + *node = (pm_false_node_t) {{ + .type = PM_FALSE_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate and initialize a new find pattern node. The node list given in the + * nodes parameter is guaranteed to have at least two nodes. + */ +static pm_find_pattern_node_t * +pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { + pm_find_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_find_pattern_node_t); + + pm_node_t *left = nodes->nodes[0]; + pm_node_t *right; + + if (nodes->size == 1) { + right = (pm_node_t *) pm_missing_node_create(parser, left->location.end, left->location.end); + } else { + right = nodes->nodes[nodes->size - 1]; + } + + *node = (pm_find_pattern_node_t) { + { + .type = PM_FIND_PATTERN_NODE, + .location = { + .start = left->location.start, + .end = right->location.end, + }, + }, + .constant = NULL, + .left = left, + .right = right, + .requireds = { 0 }, + .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + // For now we're going to just copy over each pointer manually. This could be + // much more efficient, as we could instead resize the node list to only point + // to 1...-1. + for (size_t index = 1; index < nodes->size - 1; index++) { + pm_node_list_append(&node->requireds, nodes->nodes[index]); + } + + return node; +} + +/** + * Allocate and initialize a new FloatNode node. + */ +static pm_float_node_t * +pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_FLOAT); + pm_float_node_t *node = PM_ALLOC_NODE(parser, pm_float_node_t); + + *node = (pm_float_node_t) {{ + .type = PM_FLOAT_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate and initialize a new FloatNode node from a FLOAT_IMAGINARY token. + */ +static pm_imaginary_node_t * +pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_FLOAT_IMAGINARY); + + pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + { + .type = PM_IMAGINARY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .numeric = (pm_node_t *) pm_float_node_create(parser, &((pm_token_t) { + .type = PM_TOKEN_FLOAT, + .start = token->start, + .end = token->end - 1 + })) + }; + + return node; +} + +/** + * Allocate and initialize a new FloatNode node from a FLOAT_RATIONAL token. + */ +static pm_rational_node_t * +pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_FLOAT_RATIONAL); + + pm_rational_node_t *node = PM_ALLOC_NODE(parser, pm_rational_node_t); + *node = (pm_rational_node_t) { + { + .type = PM_RATIONAL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .numeric = (pm_node_t *) pm_float_node_create(parser, &((pm_token_t) { + .type = PM_TOKEN_FLOAT, + .start = token->start, + .end = token->end - 1 + })) + }; + + return node; +} + +/** + * Allocate and initialize a new FloatNode node from a FLOAT_RATIONAL_IMAGINARY + * token. + */ +static pm_imaginary_node_t * +pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_FLOAT_RATIONAL_IMAGINARY); + + pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + { + .type = PM_IMAGINARY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .numeric = (pm_node_t *) pm_float_node_rational_create(parser, &((pm_token_t) { + .type = PM_TOKEN_FLOAT_RATIONAL, + .start = token->start, + .end = token->end - 1 + })) + }; + + return node; +} + +/** + * Allocate and initialize a new ForNode node. + */ +static pm_for_node_t * +pm_for_node_create( + pm_parser_t *parser, + pm_node_t *index, + pm_node_t *collection, + pm_statements_node_t *statements, + const pm_token_t *for_keyword, + const pm_token_t *in_keyword, + const pm_token_t *do_keyword, + const pm_token_t *end_keyword +) { + pm_for_node_t *node = PM_ALLOC_NODE(parser, pm_for_node_t); + + *node = (pm_for_node_t) { + { + .type = PM_FOR_NODE, + .location = { + .start = for_keyword->start, + .end = end_keyword->end + }, + }, + .index = index, + .collection = collection, + .statements = statements, + .for_keyword_loc = PM_LOCATION_TOKEN_VALUE(for_keyword), + .in_keyword_loc = PM_LOCATION_TOKEN_VALUE(in_keyword), + .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword), + .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +/** + * Allocate and initialize a new ForwardingArgumentsNode node. + */ +static pm_forwarding_arguments_node_t * +pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_UDOT_DOT_DOT); + pm_forwarding_arguments_node_t *node = PM_ALLOC_NODE(parser, pm_forwarding_arguments_node_t); + *node = (pm_forwarding_arguments_node_t) {{ .type = PM_FORWARDING_ARGUMENTS_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +/** + * Allocate and initialize a new ForwardingParameterNode node. + */ +static pm_forwarding_parameter_node_t * +pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_UDOT_DOT_DOT); + pm_forwarding_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_forwarding_parameter_node_t); + *node = (pm_forwarding_parameter_node_t) {{ .type = PM_FORWARDING_PARAMETER_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +/** + * Allocate and initialize a new ForwardingSuper node. + */ +static pm_forwarding_super_node_t * +pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm_arguments_t *arguments) { + assert(arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_NODE)); + assert(token->type == PM_TOKEN_KEYWORD_SUPER); + pm_forwarding_super_node_t *node = PM_ALLOC_NODE(parser, pm_forwarding_super_node_t); + + pm_block_node_t *block = NULL; + if (arguments->block != NULL) { + block = (pm_block_node_t *) arguments->block; + } + + *node = (pm_forwarding_super_node_t) { + { + .type = PM_FORWARDING_SUPER_NODE, + .location = { + .start = token->start, + .end = block != NULL ? block->base.location.end : token->end + }, + }, + .block = block + }; + + return node; +} + +/** + * Allocate and initialize a new hash pattern node from an opening and closing + * token. + */ +static pm_hash_pattern_node_t * +pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) { + pm_hash_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_hash_pattern_node_t); + + *node = (pm_hash_pattern_node_t) { + { + .type = PM_HASH_PATTERN_NODE, + .location = { + .start = opening->start, + .end = closing->end + }, + }, + .constant = NULL, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), + .elements = { 0 }, + .rest = NULL + }; + + return node; +} + +/** + * Allocate and initialize a new hash pattern node. + */ +static pm_hash_pattern_node_t * +pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *elements, pm_node_t *rest) { + pm_hash_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_hash_pattern_node_t); + + const uint8_t *start; + const uint8_t *end; + + if (elements->size > 0) { + if (rest) { + start = elements->nodes[0]->location.start; + end = rest->location.end; + } else { + start = elements->nodes[0]->location.start; + end = elements->nodes[elements->size - 1]->location.end; + } + } else { + assert(rest != NULL); + start = rest->location.start; + end = rest->location.end; + } + + *node = (pm_hash_pattern_node_t) { + { + .type = PM_HASH_PATTERN_NODE, + .location = { + .start = start, + .end = end + }, + }, + .constant = NULL, + .elements = { 0 }, + .rest = rest, + .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + for (size_t index = 0; index < elements->size; index++) { + pm_node_t *element = elements->nodes[index]; + pm_node_list_append(&node->elements, element); + } + + return node; +} + +/** + * Retrieve the name from a node that will become a global variable write node. + */ +static pm_constant_id_t +pm_global_variable_write_name(pm_parser_t *parser, const pm_node_t *target) { + switch (PM_NODE_TYPE(target)) { + case PM_GLOBAL_VARIABLE_READ_NODE: + return ((pm_global_variable_read_node_t *) target)->name; + case PM_BACK_REFERENCE_READ_NODE: + return ((pm_back_reference_read_node_t *) target)->name; + case PM_NUMBERED_REFERENCE_READ_NODE: + // This will only ever happen in the event of a syntax error, but we + // still need to provide something for the node. + return pm_parser_constant_id_location(parser, target->location.start, target->location.end); + default: + assert(false && "unreachable"); + return (pm_constant_id_t) -1; + } +} + +/** + * Allocate and initialize a new GlobalVariableAndWriteNode node. + */ +static pm_global_variable_and_write_node_t * +pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + pm_global_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_and_write_node_t); + + *node = (pm_global_variable_and_write_node_t) { + { + .type = PM_GLOBAL_VARIABLE_AND_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name = pm_global_variable_write_name(parser, target), + .name_loc = target->location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new GlobalVariableOperatorWriteNode node. + */ +static pm_global_variable_operator_write_node_t * +pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_global_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_operator_write_node_t); + + *node = (pm_global_variable_operator_write_node_t) { + { + .type = PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name = pm_global_variable_write_name(parser, target), + .name_loc = target->location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +/** + * Allocate and initialize a new GlobalVariableOrWriteNode node. + */ +static pm_global_variable_or_write_node_t * +pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); + pm_global_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_or_write_node_t); + + *node = (pm_global_variable_or_write_node_t) { + { + .type = PM_GLOBAL_VARIABLE_OR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name = pm_global_variable_write_name(parser, target), + .name_loc = target->location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate a new GlobalVariableReadNode node. + */ +static pm_global_variable_read_node_t * +pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) { + pm_global_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_read_node_t); + + *node = (pm_global_variable_read_node_t) { + { + .type = PM_GLOBAL_VARIABLE_READ_NODE, + .location = PM_LOCATION_TOKEN_VALUE(name), + }, + .name = pm_parser_constant_id_token(parser, name) + }; + + return node; +} + +/** + * Allocate a new GlobalVariableWriteNode node. + */ +static pm_global_variable_write_node_t * +pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_global_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_write_node_t); + + *node = (pm_global_variable_write_node_t) { + { + .type = PM_GLOBAL_VARIABLE_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + }, + }, + .name = pm_global_variable_write_name(parser, target), + .name_loc = PM_LOCATION_NODE_VALUE(target), + .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate a new HashNode node. + */ +static pm_hash_node_t * +pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) { + assert(opening != NULL); + pm_hash_node_t *node = PM_ALLOC_NODE(parser, pm_hash_node_t); + + *node = (pm_hash_node_t) { + { + .type = PM_HASH_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(opening) + }, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_NULL_VALUE(parser), + .elements = { 0 } + }; + + return node; +} + +/** + * Append a new element to a hash node. + */ +static inline void +pm_hash_node_elements_append(pm_hash_node_t *hash, pm_node_t *element) { + pm_node_list_append(&hash->elements, element); + + // If the element is not a static literal, then the hash is not a static + // literal. Turn that flag off. + if ((element->flags & PM_NODE_FLAG_STATIC_LITERAL) == 0) { + hash->base.flags &= (pm_node_flags_t) ~PM_NODE_FLAG_STATIC_LITERAL; + } +} + +static inline void +pm_hash_node_closing_loc_set(pm_hash_node_t *hash, pm_token_t *token) { + hash->base.location.end = token->end; + hash->closing_loc = PM_LOCATION_TOKEN_VALUE(token); +} + +/** + * Allocate a new IfNode node. + */ +static pm_if_node_t * +pm_if_node_create(pm_parser_t *parser, + const pm_token_t *if_keyword, + pm_node_t *predicate, + const pm_token_t *then_keyword, + pm_statements_node_t *statements, + pm_node_t *consequent, + const pm_token_t *end_keyword +) { + pm_conditional_predicate(predicate); + pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t); + + const uint8_t *end; + if (end_keyword->type != PM_TOKEN_NOT_PROVIDED) { + end = end_keyword->end; + } else if (consequent != NULL) { + end = consequent->location.end; + } else if (pm_statements_node_body_length(statements) != 0) { + end = statements->base.location.end; + } else { + end = predicate->location.end; + } + + *node = (pm_if_node_t) { + { + .type = PM_IF_NODE, + .flags = PM_NODE_FLAG_NEWLINE, + .location = { + .start = if_keyword->start, + .end = end + }, + }, + .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), + .predicate = predicate, + .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword), + .statements = statements, + .consequent = consequent, + .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +/** + * Allocate and initialize new IfNode node in the modifier form. + */ +static pm_if_node_t * +pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *if_keyword, pm_node_t *predicate) { + pm_conditional_predicate(predicate); + pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t); + + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(statements, statement); + + *node = (pm_if_node_t) { + { + .type = PM_IF_NODE, + .flags = PM_NODE_FLAG_NEWLINE, + .location = { + .start = statement->location.start, + .end = predicate->location.end + }, + }, + .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), + .predicate = predicate, + .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .statements = statements, + .consequent = NULL, + .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +/** + * Allocate and initialize an if node from a ternary expression. + */ +static pm_if_node_t * +pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_token_t *qmark, pm_node_t *true_expression, const pm_token_t *colon, pm_node_t *false_expression) { + pm_assert_value_expression(parser, predicate); + pm_conditional_predicate(predicate); + + pm_statements_node_t *if_statements = pm_statements_node_create(parser); + pm_statements_node_body_append(if_statements, true_expression); + + pm_statements_node_t *else_statements = pm_statements_node_create(parser); + pm_statements_node_body_append(else_statements, false_expression); + + pm_token_t end_keyword = not_provided(parser); + pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, &end_keyword); + + pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t); + + *node = (pm_if_node_t) { + { + .type = PM_IF_NODE, + .flags = PM_NODE_FLAG_NEWLINE, + .location = { + .start = predicate->location.start, + .end = false_expression->location.end, + }, + }, + .if_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .predicate = predicate, + .then_keyword_loc = PM_LOCATION_TOKEN_VALUE(qmark), + .statements = if_statements, + .consequent = (pm_node_t *)else_node, + .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; + +} + +static inline void +pm_if_node_end_keyword_loc_set(pm_if_node_t *node, const pm_token_t *keyword) { + node->base.location.end = keyword->end; + node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword); +} + +static inline void +pm_else_node_end_keyword_loc_set(pm_else_node_t *node, const pm_token_t *keyword) { + node->base.location.end = keyword->end; + node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword); +} + +/** + * Allocate and initialize a new ImplicitNode node. + */ +static pm_implicit_node_t * +pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) { + pm_implicit_node_t *node = PM_ALLOC_NODE(parser, pm_implicit_node_t); + + *node = (pm_implicit_node_t) { + { + .type = PM_IMPLICIT_NODE, + .location = value->location + }, + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new ImplicitRestNode node. + */ +static pm_implicit_rest_node_t * +pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_COMMA); + + pm_implicit_rest_node_t *node = PM_ALLOC_NODE(parser, pm_implicit_rest_node_t); + + *node = (pm_implicit_rest_node_t) { + { + .type = PM_IMPLICIT_REST_NODE, + .location = PM_LOCATION_TOKEN_VALUE(token) + } + }; + + return node; +} + +/** + * Allocate and initialize a new IntegerNode node. + */ +static pm_integer_node_t * +pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) { + assert(token->type == PM_TOKEN_INTEGER); + pm_integer_node_t *node = PM_ALLOC_NODE(parser, pm_integer_node_t); + + *node = (pm_integer_node_t) {{ + .type = PM_INTEGER_NODE, + .flags = base | PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate and initialize a new IntegerNode node from an INTEGER_IMAGINARY + * token. + */ +static pm_imaginary_node_t * +pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) { + assert(token->type == PM_TOKEN_INTEGER_IMAGINARY); + + pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + { + .type = PM_IMAGINARY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .numeric = (pm_node_t *) pm_integer_node_create(parser, base, &((pm_token_t) { + .type = PM_TOKEN_INTEGER, + .start = token->start, + .end = token->end - 1 + })) + }; + + return node; +} + +/** + * Allocate and initialize a new IntegerNode node from an INTEGER_RATIONAL + * token. + */ +static pm_rational_node_t * +pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) { + assert(token->type == PM_TOKEN_INTEGER_RATIONAL); + + pm_rational_node_t *node = PM_ALLOC_NODE(parser, pm_rational_node_t); + *node = (pm_rational_node_t) { + { + .type = PM_RATIONAL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .numeric = (pm_node_t *) pm_integer_node_create(parser, base, &((pm_token_t) { + .type = PM_TOKEN_INTEGER, + .start = token->start, + .end = token->end - 1 + })) + }; + + return node; +} + +/** + * Allocate and initialize a new IntegerNode node from an + * INTEGER_RATIONAL_IMAGINARY token. + */ +static pm_imaginary_node_t * +pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) { + assert(token->type == PM_TOKEN_INTEGER_RATIONAL_IMAGINARY); + + pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + { + .type = PM_IMAGINARY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .numeric = (pm_node_t *) pm_integer_node_rational_create(parser, base, &((pm_token_t) { + .type = PM_TOKEN_INTEGER_RATIONAL, + .start = token->start, + .end = token->end - 1 + })) + }; + + return node; +} + +/** + * Allocate and initialize a new InNode node. + */ +static pm_in_node_t * +pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t *statements, const pm_token_t *in_keyword, const pm_token_t *then_keyword) { + pm_in_node_t *node = PM_ALLOC_NODE(parser, pm_in_node_t); + + const uint8_t *end; + if (statements != NULL) { + end = statements->base.location.end; + } else if (then_keyword->type != PM_TOKEN_NOT_PROVIDED) { + end = then_keyword->end; + } else { + end = pattern->location.end; + } + + *node = (pm_in_node_t) { + { + .type = PM_IN_NODE, + .location = { + .start = in_keyword->start, + .end = end + }, + }, + .pattern = pattern, + .statements = statements, + .in_loc = PM_LOCATION_TOKEN_VALUE(in_keyword), + .then_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword) + }; + + return node; +} + +/** + * Allocate and initialize a new InstanceVariableAndWriteNode node. + */ +static pm_instance_variable_and_write_node_t * +pm_instance_variable_and_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + pm_instance_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_and_write_node_t); + + *node = (pm_instance_variable_and_write_node_t) { + { + .type = PM_INSTANCE_VARIABLE_AND_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new InstanceVariableOperatorWriteNode node. + */ +static pm_instance_variable_operator_write_node_t * +pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_instance_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_operator_write_node_t); + + *node = (pm_instance_variable_operator_write_node_t) { + { + .type = PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +/** + * Allocate and initialize a new InstanceVariableOrWriteNode node. + */ +static pm_instance_variable_or_write_node_t * +pm_instance_variable_or_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { + assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); + pm_instance_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_or_write_node_t); + + *node = (pm_instance_variable_or_write_node_t) { + { + .type = PM_INSTANCE_VARIABLE_OR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new InstanceVariableReadNode node. + */ +static pm_instance_variable_read_node_t * +pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_INSTANCE_VARIABLE); + pm_instance_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_read_node_t); + + *node = (pm_instance_variable_read_node_t) { + { + .type = PM_INSTANCE_VARIABLE_READ_NODE, + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .name = pm_parser_constant_id_token(parser, token) + }; + + return node; +} + +/** + * Initialize a new InstanceVariableWriteNode node from an InstanceVariableRead + * node. + */ +static pm_instance_variable_write_node_t * +pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) { + pm_instance_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_write_node_t); + *node = (pm_instance_variable_write_node_t) { + { + .type = PM_INSTANCE_VARIABLE_WRITE_NODE, + .location = { + .start = read_node->base.location.start, + .end = value->location.end + } + }, + .name = read_node->name, + .name_loc = PM_LOCATION_NODE_BASE_VALUE(read_node), + .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate a new InterpolatedRegularExpressionNode node. + */ +static pm_interpolated_regular_expression_node_t * +pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening) { + pm_interpolated_regular_expression_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_regular_expression_node_t); + + *node = (pm_interpolated_regular_expression_node_t) { + { + .type = PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, + .location = { + .start = opening->start, + .end = NULL, + }, + }, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_TOKEN_VALUE(opening), + .parts = { 0 } + }; + + return node; +} + +static inline void +pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expression_node_t *node, pm_node_t *part) { + if (node->base.location.start > part->location.start) { + node->base.location.start = part->location.start; + } + if (node->base.location.end < part->location.end) { + node->base.location.end = part->location.end; + } + pm_node_list_append(&node->parts, part); +} + +static inline void +pm_interpolated_regular_expression_node_closing_set(pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) { + node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing); + node->base.location.end = closing->end; + node->base.flags |= pm_regular_expression_flags_create(closing); +} + +/** + * Allocate and initialize a new InterpolatedStringNode node. + */ +static pm_interpolated_string_node_t * +pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) { + pm_interpolated_string_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_string_node_t); + + *node = (pm_interpolated_string_node_t) { + { + .type = PM_INTERPOLATED_STRING_NODE, + .location = { + .start = opening->start, + .end = closing->end, + }, + }, + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .parts = { 0 } + }; + + if (parts != NULL) { + node->parts = *parts; + } + + return node; +} + +/** + * Append a part to an InterpolatedStringNode node. + */ +static inline void +pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_t *part) { + if (node->parts.size == 0 && node->opening_loc.start == NULL) { + node->base.location.start = part->location.start; + } + + pm_node_list_append(&node->parts, part); + node->base.location.end = part->location.end; +} + +/** + * Set the closing token of the given InterpolatedStringNode node. + */ +static void +pm_interpolated_string_node_closing_set(pm_interpolated_string_node_t *node, const pm_token_t *closing) { + node->closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing); + node->base.location.end = closing->end; +} + +/** + * Allocate and initialize a new InterpolatedSymbolNode node. + */ +static pm_interpolated_symbol_node_t * +pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) { + pm_interpolated_symbol_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_symbol_node_t); + + *node = (pm_interpolated_symbol_node_t) { + { + .type = PM_INTERPOLATED_SYMBOL_NODE, + .location = { + .start = opening->start, + .end = closing->end, + }, + }, + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .parts = { 0 } + }; + + if (parts != NULL) { + node->parts = *parts; + } + + return node; +} + +static inline void +pm_interpolated_symbol_node_append(pm_interpolated_symbol_node_t *node, pm_node_t *part) { + if (node->parts.size == 0 && node->opening_loc.start == NULL) { + node->base.location.start = part->location.start; + } + + pm_node_list_append(&node->parts, part); + node->base.location.end = part->location.end; +} + +/** + * Allocate a new InterpolatedXStringNode node. + */ +static pm_interpolated_x_string_node_t * +pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) { + pm_interpolated_x_string_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_x_string_node_t); + + *node = (pm_interpolated_x_string_node_t) { + { + .type = PM_INTERPOLATED_X_STRING_NODE, + .location = { + .start = opening->start, + .end = closing->end + }, + }, + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .parts = { 0 } + }; + + return node; +} + +static inline void +pm_interpolated_xstring_node_append(pm_interpolated_x_string_node_t *node, pm_node_t *part) { + pm_node_list_append(&node->parts, part); + node->base.location.end = part->location.end; +} + +static inline void +pm_interpolated_xstring_node_closing_set(pm_interpolated_x_string_node_t *node, const pm_token_t *closing) { + node->closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing); + node->base.location.end = closing->end; +} + +/** + * Allocate a new KeywordHashNode node. + */ +static pm_keyword_hash_node_t * +pm_keyword_hash_node_create(pm_parser_t *parser) { + pm_keyword_hash_node_t *node = PM_ALLOC_NODE(parser, pm_keyword_hash_node_t); + + *node = (pm_keyword_hash_node_t) { + .base = { + .type = PM_KEYWORD_HASH_NODE, + .location = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }, + .elements = { 0 } + }; + + return node; +} + +/** + * Append an element to a KeywordHashNode node. + */ +static void +pm_keyword_hash_node_elements_append(pm_keyword_hash_node_t *hash, pm_node_t *element) { + pm_node_list_append(&hash->elements, element); + if (hash->base.location.start == NULL) { + hash->base.location.start = element->location.start; + } + hash->base.location.end = element->location.end; +} + +/** + * Allocate and initialize a new RequiredKeywordParameterNode node. + */ +static pm_required_keyword_parameter_node_t * +pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name) { + pm_required_keyword_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_required_keyword_parameter_node_t); + + *node = (pm_required_keyword_parameter_node_t) { + { + .type = PM_REQUIRED_KEYWORD_PARAMETER_NODE, + .location = { + .start = name->start, + .end = name->end + }, + }, + .name = pm_parser_constant_id_location(parser, name->start, name->end - 1), + .name_loc = PM_LOCATION_TOKEN_VALUE(name), + }; + + return node; +} + +/** + * Allocate a new OptionalKeywordParameterNode node. + */ +static pm_optional_keyword_parameter_node_t * +pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, pm_node_t *value) { + pm_optional_keyword_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_optional_keyword_parameter_node_t); + + *node = (pm_optional_keyword_parameter_node_t) { + { + .type = PM_OPTIONAL_KEYWORD_PARAMETER_NODE, + .location = { + .start = name->start, + .end = value->location.end + }, + }, + .name = pm_parser_constant_id_location(parser, name->start, name->end - 1), + .name_loc = PM_LOCATION_TOKEN_VALUE(name), + .value = value + }; + + return node; +} + +/** + * Allocate a new KeywordRestParameterNode node. + */ +static pm_keyword_rest_parameter_node_t * +pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) { + pm_keyword_rest_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_keyword_rest_parameter_node_t); + + *node = (pm_keyword_rest_parameter_node_t) { + { + .type = PM_KEYWORD_REST_PARAMETER_NODE, + .location = { + .start = operator->start, + .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end) + }, + }, + .name = pm_parser_optional_constant_id_token(parser, name), + .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate a new LambdaNode node. + */ +static pm_lambda_node_t * +pm_lambda_node_create( + pm_parser_t *parser, + pm_constant_id_list_t *locals, + const pm_token_t *operator, + const pm_token_t *opening, + const pm_token_t *closing, + pm_block_parameters_node_t *parameters, + pm_node_t *body, + uint32_t numbered_parameters +) { + pm_lambda_node_t *node = PM_ALLOC_NODE(parser, pm_lambda_node_t); + + *node = (pm_lambda_node_t) { + { + .type = PM_LAMBDA_NODE, + .location = { + .start = operator->start, + .end = closing->end + }, + }, + .locals = *locals, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), + .parameters = parameters, + .body = body, + .numbered_parameters = numbered_parameters + }; + + return node; +} + +/** + * Allocate and initialize a new LocalVariableAndWriteNode node. + */ +static pm_local_variable_and_write_node_t * +pm_local_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) { + assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_CALL_NODE)); + assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + pm_local_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_and_write_node_t); + + *node = (pm_local_variable_and_write_node_t) { + { + .type = PM_LOCAL_VARIABLE_AND_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .name = name, + .depth = depth + }; + + return node; +} + +/** + * Allocate and initialize a new LocalVariableOperatorWriteNode node. + */ +static pm_local_variable_operator_write_node_t * +pm_local_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) { + pm_local_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_operator_write_node_t); + + *node = (pm_local_variable_operator_write_node_t) { + { + .type = PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .name = name, + .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .depth = depth + }; + + return node; +} + +/** + * Allocate and initialize a new LocalVariableOrWriteNode node. + */ +static pm_local_variable_or_write_node_t * +pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) { + assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_CALL_NODE)); + assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); + pm_local_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_or_write_node_t); + + *node = (pm_local_variable_or_write_node_t) { + { + .type = PM_LOCAL_VARIABLE_OR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .name = name, + .depth = depth + }; + + return node; +} + +/** + * Allocate a new LocalVariableReadNode node. + */ +static pm_local_variable_read_node_t * +pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) { + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name); + + if (parser->current_param_name == name_id) { + pm_parser_err_token(parser, name, PM_ERR_PARAMETER_CIRCULAR); + } + + pm_local_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_read_node_t); + + *node = (pm_local_variable_read_node_t) { + { + .type = PM_LOCAL_VARIABLE_READ_NODE, + .location = PM_LOCATION_TOKEN_VALUE(name) + }, + .name = name_id, + .depth = depth + }; + + return node; +} + +/** + * Allocate and initialize a new LocalVariableWriteNode node. + */ +static pm_local_variable_write_node_t * +pm_local_variable_write_node_create(pm_parser_t *parser, pm_constant_id_t name, uint32_t depth, pm_node_t *value, const pm_location_t *name_loc, const pm_token_t *operator) { + pm_local_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_write_node_t); + + *node = (pm_local_variable_write_node_t) { + { + .type = PM_LOCAL_VARIABLE_WRITE_NODE, + .location = { + .start = name_loc->start, + .end = value->location.end + } + }, + .name = name, + .depth = depth, + .value = value, + .name_loc = *name_loc, + .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Returns true if the given bounds comprise a numbered parameter (i.e., they + * are of the form /^_\d$/). + */ +static inline bool +pm_token_is_numbered_parameter(const uint8_t *start, const uint8_t *end) { + return (end - start == 2) && (start[0] == '_') && (start[1] != '0') && (pm_char_is_decimal_digit(start[1])); +} + +/** + * Ensure the given bounds do not comprise a numbered parameter. If they do, add + * an appropriate error message to the parser. + */ +static inline void +pm_refute_numbered_parameter(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + if (pm_token_is_numbered_parameter(start, end)) { + PM_PARSER_ERR_FORMAT(parser, start, end, PM_ERR_PARAMETER_NUMBERED_RESERVED, start); + } +} + +/** + * Allocate and initialize a new LocalVariableTargetNode node with the given + * name and depth. + */ +static pm_local_variable_target_node_t * +pm_local_variable_target_node_create_values(pm_parser_t *parser, const pm_location_t *location, pm_constant_id_t name, uint32_t depth) { + pm_local_variable_target_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_target_node_t); + + *node = (pm_local_variable_target_node_t) { + { + .type = PM_LOCAL_VARIABLE_TARGET_NODE, + .location = *location + }, + .name = name, + .depth = depth + }; + + return node; +} + +/** + * Allocate and initialize a new LocalVariableTargetNode node. + */ +static pm_local_variable_target_node_t * +pm_local_variable_target_node_create(pm_parser_t *parser, const pm_token_t *name) { + pm_refute_numbered_parameter(parser, name->start, name->end); + + return pm_local_variable_target_node_create_values( + parser, + &(pm_location_t) { .start = name->start, .end = name->end }, + pm_parser_constant_id_token(parser, name), + 0 + ); +} + +/** + * Allocate and initialize a new LocalVariableTargetNode node with the given depth. + */ +static pm_local_variable_target_node_t * +pm_local_variable_target_node_create_depth(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) { + pm_refute_numbered_parameter(parser, name->start, name->end); + + return pm_local_variable_target_node_create_values( + parser, + &(pm_location_t) { .start = name->start, .end = name->end }, + pm_parser_constant_id_token(parser, name), + depth + ); +} + +/** + * Allocate and initialize a new MatchPredicateNode node. + */ +static pm_match_predicate_node_t * +pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) { + pm_assert_value_expression(parser, value); + + pm_match_predicate_node_t *node = PM_ALLOC_NODE(parser, pm_match_predicate_node_t); + + *node = (pm_match_predicate_node_t) { + { + .type = PM_MATCH_PREDICATE_NODE, + .location = { + .start = value->location.start, + .end = pattern->location.end + } + }, + .value = value, + .pattern = pattern, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new MatchRequiredNode node. + */ +static pm_match_required_node_t * +pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) { + pm_assert_value_expression(parser, value); + + pm_match_required_node_t *node = PM_ALLOC_NODE(parser, pm_match_required_node_t); + + *node = (pm_match_required_node_t) { + { + .type = PM_MATCH_REQUIRED_NODE, + .location = { + .start = value->location.start, + .end = pattern->location.end + } + }, + .value = value, + .pattern = pattern, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new MatchWriteNode node. + */ +static pm_match_write_node_t * +pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) { + pm_match_write_node_t *node = PM_ALLOC_NODE(parser, pm_match_write_node_t); + + *node = (pm_match_write_node_t) { + { + .type = PM_MATCH_WRITE_NODE, + .location = call->base.location + }, + .call = call, + .targets = { 0 } + }; + + return node; +} + +/** + * Allocate a new ModuleNode node. + */ +static pm_module_node_t * +pm_module_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *module_keyword, pm_node_t *constant_path, const pm_token_t *name, pm_node_t *body, const pm_token_t *end_keyword) { + pm_module_node_t *node = PM_ALLOC_NODE(parser, pm_module_node_t); + + *node = (pm_module_node_t) { + { + .type = PM_MODULE_NODE, + .location = { + .start = module_keyword->start, + .end = end_keyword->end + } + }, + .locals = (locals == NULL ? ((pm_constant_id_list_t) { .ids = NULL, .size = 0, .capacity = 0 }) : *locals), + .module_keyword_loc = PM_LOCATION_TOKEN_VALUE(module_keyword), + .constant_path = constant_path, + .body = body, + .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword), + .name = pm_parser_constant_id_token(parser, name) + }; + + return node; +} + +/** + * Allocate and initialize new MultiTargetNode node. + */ +static pm_multi_target_node_t * +pm_multi_target_node_create(pm_parser_t *parser) { + pm_multi_target_node_t *node = PM_ALLOC_NODE(parser, pm_multi_target_node_t); + + *node = (pm_multi_target_node_t) { + { + .type = PM_MULTI_TARGET_NODE, + .location = { .start = NULL, .end = NULL } + }, + .lefts = { 0 }, + .rest = NULL, + .rights = { 0 }, + .lparen_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .rparen_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +/** + * Append a target to a MultiTargetNode node. + */ +static void +pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t *node, pm_node_t *target) { + if (PM_NODE_TYPE_P(target, PM_SPLAT_NODE) || PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) { + if (node->rest == NULL) { + node->rest = target; + } else { + pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS); + pm_node_list_append(&node->rights, target); + } + } else if (node->rest == NULL) { + pm_node_list_append(&node->lefts, target); + } else { + pm_node_list_append(&node->rights, target); + } + + if (node->base.location.start == NULL || (node->base.location.start > target->location.start)) { + node->base.location.start = target->location.start; + } + + if (node->base.location.end == NULL || (node->base.location.end < target->location.end)) { + node->base.location.end = target->location.end; + } +} + +/** + * Set the opening of a MultiTargetNode node. + */ +static void +pm_multi_target_node_opening_set(pm_multi_target_node_t *node, const pm_token_t *lparen) { + node->base.location.start = lparen->start; + node->lparen_loc = PM_LOCATION_TOKEN_VALUE(lparen); +} + +/** + * Set the closing of a MultiTargetNode node. + */ +static void +pm_multi_target_node_closing_set(pm_multi_target_node_t *node, const pm_token_t *rparen) { + node->base.location.end = rparen->end; + node->rparen_loc = PM_LOCATION_TOKEN_VALUE(rparen); +} + +/** + * Allocate a new MultiWriteNode node. + */ +static pm_multi_write_node_t * +pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, const pm_token_t *operator, pm_node_t *value) { + pm_multi_write_node_t *node = PM_ALLOC_NODE(parser, pm_multi_write_node_t); + + *node = (pm_multi_write_node_t) { + { + .type = PM_MULTI_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .lefts = target->lefts, + .rest = target->rest, + .rights = target->rights, + .lparen_loc = target->lparen_loc, + .rparen_loc = target->rparen_loc, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + // Explicitly do not call pm_node_destroy here because we want to keep + // around all of the information within the MultiWriteNode node. + free(target); + + return node; +} + +/** + * Allocate and initialize a new NextNode node. + */ +static pm_next_node_t * +pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) { + assert(keyword->type == PM_TOKEN_KEYWORD_NEXT); + pm_next_node_t *node = PM_ALLOC_NODE(parser, pm_next_node_t); + + *node = (pm_next_node_t) { + { + .type = PM_NEXT_NODE, + .location = { + .start = keyword->start, + .end = (arguments == NULL ? keyword->end : arguments->base.location.end) + } + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .arguments = arguments + }; + + return node; +} + +/** + * Allocate and initialize a new NilNode node. + */ +static pm_nil_node_t * +pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_KEYWORD_NIL); + pm_nil_node_t *node = PM_ALLOC_NODE(parser, pm_nil_node_t); + + *node = (pm_nil_node_t) {{ + .type = PM_NIL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate and initialize a new NoKeywordsParameterNode node. + */ +static pm_no_keywords_parameter_node_t * +pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) { + assert(operator->type == PM_TOKEN_USTAR_STAR || operator->type == PM_TOKEN_STAR_STAR); + assert(keyword->type == PM_TOKEN_KEYWORD_NIL); + pm_no_keywords_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_no_keywords_parameter_node_t); + + *node = (pm_no_keywords_parameter_node_t) { + { + .type = PM_NO_KEYWORDS_PARAMETER_NODE, + .location = { + .start = operator->start, + .end = keyword->end + } + }, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) + }; + + return node; +} + +/** + * Allocate a new NthReferenceReadNode node. + */ +static pm_numbered_reference_read_node_t * +pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) { + assert(name->type == PM_TOKEN_NUMBERED_REFERENCE); + pm_numbered_reference_read_node_t *node = PM_ALLOC_NODE(parser, pm_numbered_reference_read_node_t); + + *node = (pm_numbered_reference_read_node_t) { + { + .type = PM_NUMBERED_REFERENCE_READ_NODE, + .location = PM_LOCATION_TOKEN_VALUE(name), + }, + .number = parse_decimal_number(parser, name->start + 1, name->end) + }; + + return node; +} + +/** + * Allocate a new OptionalParameterNode node. + */ +static pm_optional_parameter_node_t * +pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator, pm_node_t *value) { + pm_optional_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_optional_parameter_node_t); + + *node = (pm_optional_parameter_node_t) { + { + .type = PM_OPTIONAL_PARAMETER_NODE, + .location = { + .start = name->start, + .end = value->location.end + } + }, + .name = pm_parser_constant_id_token(parser, name), + .name_loc = PM_LOCATION_TOKEN_VALUE(name), + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Allocate and initialize a new OrNode node. + */ +static pm_or_node_t * +pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) { + pm_assert_value_expression(parser, left); + + pm_or_node_t *node = PM_ALLOC_NODE(parser, pm_or_node_t); + + *node = (pm_or_node_t) { + { + .type = PM_OR_NODE, + .location = { + .start = left->location.start, + .end = right->location.end + } + }, + .left = left, + .right = right, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new ParametersNode node. + */ +static pm_parameters_node_t * +pm_parameters_node_create(pm_parser_t *parser) { + pm_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_parameters_node_t); + + *node = (pm_parameters_node_t) { + { + .type = PM_PARAMETERS_NODE, + .location = PM_LOCATION_TOKEN_VALUE(&parser->current) + }, + .rest = NULL, + .keyword_rest = NULL, + .block = NULL, + .requireds = { 0 }, + .optionals = { 0 }, + .posts = { 0 }, + .keywords = { 0 } + }; + + return node; +} + +/** + * Set the location properly for the parameters node. + */ +static void +pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param) { + if (params->base.location.start == NULL) { + params->base.location.start = param->location.start; + } else { + params->base.location.start = params->base.location.start < param->location.start ? params->base.location.start : param->location.start; + } + + if (params->base.location.end == NULL) { + params->base.location.end = param->location.end; + } else { + params->base.location.end = params->base.location.end > param->location.end ? params->base.location.end : param->location.end; + } +} + +/** + * Append a required parameter to a ParametersNode node. + */ +static void +pm_parameters_node_requireds_append(pm_parameters_node_t *params, pm_node_t *param) { + pm_parameters_node_location_set(params, param); + pm_node_list_append(¶ms->requireds, param); +} + +/** + * Append an optional parameter to a ParametersNode node. + */ +static void +pm_parameters_node_optionals_append(pm_parameters_node_t *params, pm_optional_parameter_node_t *param) { + pm_parameters_node_location_set(params, (pm_node_t *) param); + pm_node_list_append(¶ms->optionals, (pm_node_t *) param); +} + +/** + * Append a post optional arguments parameter to a ParametersNode node. + */ +static void +pm_parameters_node_posts_append(pm_parameters_node_t *params, pm_node_t *param) { + pm_parameters_node_location_set(params, param); + pm_node_list_append(¶ms->posts, param); +} + +/** + * Set the rest parameter on a ParametersNode node. + */ +static void +pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) { + pm_parameters_node_location_set(params, param); + params->rest = param; +} + +/** + * Append a keyword parameter to a ParametersNode node. + */ +static void +pm_parameters_node_keywords_append(pm_parameters_node_t *params, pm_node_t *param) { + pm_parameters_node_location_set(params, param); + pm_node_list_append(¶ms->keywords, param); +} + +/** + * Set the keyword rest parameter on a ParametersNode node. + */ +static void +pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *param) { + assert(params->keyword_rest == NULL); + pm_parameters_node_location_set(params, param); + params->keyword_rest = param; +} + +/** + * Set the block parameter on a ParametersNode node. + */ +static void +pm_parameters_node_block_set(pm_parameters_node_t *params, pm_block_parameter_node_t *param) { + assert(params->block == NULL); + pm_parameters_node_location_set(params, (pm_node_t *) param); + params->block = param; +} + +/** + * Allocate a new ProgramNode node. + */ +static pm_program_node_t * +pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) { + pm_program_node_t *node = PM_ALLOC_NODE(parser, pm_program_node_t); + + *node = (pm_program_node_t) { + { + .type = PM_PROGRAM_NODE, + .location = { + .start = statements == NULL ? parser->start : statements->base.location.start, + .end = statements == NULL ? parser->end : statements->base.location.end + } + }, + .locals = *locals, + .statements = statements + }; + + return node; +} + +/** + * Allocate and initialize new ParenthesesNode node. + */ +static pm_parentheses_node_t * +pm_parentheses_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_node_t *body, const pm_token_t *closing) { + pm_parentheses_node_t *node = PM_ALLOC_NODE(parser, pm_parentheses_node_t); + + *node = (pm_parentheses_node_t) { + { + .type = PM_PARENTHESES_NODE, + .location = { + .start = opening->start, + .end = closing->end + } + }, + .body = body, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +/** + * Allocate and initialize a new PinnedExpressionNode node. + */ +static pm_pinned_expression_node_t * +pm_pinned_expression_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *operator, const pm_token_t *lparen, const pm_token_t *rparen) { + pm_pinned_expression_node_t *node = PM_ALLOC_NODE(parser, pm_pinned_expression_node_t); + + *node = (pm_pinned_expression_node_t) { + { + .type = PM_PINNED_EXPRESSION_NODE, + .location = { + .start = operator->start, + .end = rparen->end + } + }, + .expression = expression, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .lparen_loc = PM_LOCATION_TOKEN_VALUE(lparen), + .rparen_loc = PM_LOCATION_TOKEN_VALUE(rparen) + }; + + return node; +} + +/** + * Allocate and initialize a new PinnedVariableNode node. + */ +static pm_pinned_variable_node_t * +pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) { + pm_pinned_variable_node_t *node = PM_ALLOC_NODE(parser, pm_pinned_variable_node_t); + + *node = (pm_pinned_variable_node_t) { + { + .type = PM_PINNED_VARIABLE_NODE, + .location = { + .start = operator->start, + .end = variable->location.end + } + }, + .variable = variable, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new PostExecutionNode node. + */ +static pm_post_execution_node_t * +pm_post_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) { + pm_post_execution_node_t *node = PM_ALLOC_NODE(parser, pm_post_execution_node_t); + + *node = (pm_post_execution_node_t) { + { + .type = PM_POST_EXECUTION_NODE, + .location = { + .start = keyword->start, + .end = closing->end + } + }, + .statements = statements, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +/** + * Allocate and initialize a new PreExecutionNode node. + */ +static pm_pre_execution_node_t * +pm_pre_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) { + pm_pre_execution_node_t *node = PM_ALLOC_NODE(parser, pm_pre_execution_node_t); + + *node = (pm_pre_execution_node_t) { + { + .type = PM_PRE_EXECUTION_NODE, + .location = { + .start = keyword->start, + .end = closing->end + } + }, + .statements = statements, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +/** + * Allocate and initialize new RangeNode node. + */ +static pm_range_node_t * +pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) { + pm_assert_value_expression(parser, left); + pm_assert_value_expression(parser, right); + + pm_range_node_t *node = PM_ALLOC_NODE(parser, pm_range_node_t); + pm_node_flags_t flags = 0; + + // Indicate that this node an exclusive range if the operator is `...`. + if (operator->type == PM_TOKEN_DOT_DOT_DOT || operator->type == PM_TOKEN_UDOT_DOT_DOT) { + flags |= PM_RANGE_FLAGS_EXCLUDE_END; + } + + // Indicate that this node is a static literal (i.e., can be compiled with + // a putobject in CRuby) if the left and right are implicit nil, explicit + // nil, or integers. + if ( + (left == NULL || PM_NODE_TYPE_P(left, PM_NIL_NODE) || PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) && + (right == NULL || PM_NODE_TYPE_P(right, PM_NIL_NODE) || PM_NODE_TYPE_P(right, PM_INTEGER_NODE)) + ) { + flags |= PM_NODE_FLAG_STATIC_LITERAL; + } + + *node = (pm_range_node_t) { + { + .type = PM_RANGE_NODE, + .flags = flags, + .location = { + .start = (left == NULL ? operator->start : left->location.start), + .end = (right == NULL ? operator->end : right->location.end) + } + }, + .left = left, + .right = right, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new RedoNode node. + */ +static pm_redo_node_t * +pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_KEYWORD_REDO); + pm_redo_node_t *node = PM_ALLOC_NODE(parser, pm_redo_node_t); + + *node = (pm_redo_node_t) {{ .type = PM_REDO_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +/** + * Allocate a new initialize a new RegularExpressionNode node with the given + * unescaped string. + */ +static pm_regular_expression_node_t * +pm_regular_expression_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *unescaped) { + pm_regular_expression_node_t *node = PM_ALLOC_NODE(parser, pm_regular_expression_node_t); + + *node = (pm_regular_expression_node_t) { + { + .type = PM_REGULAR_EXPRESSION_NODE, + .flags = pm_regular_expression_flags_create(closing) | PM_NODE_FLAG_STATIC_LITERAL, + .location = { + .start = MIN(opening->start, closing->start), + .end = MAX(opening->end, closing->end) + } + }, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .content_loc = PM_LOCATION_TOKEN_VALUE(content), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), + .unescaped = *unescaped + }; + + return node; +} + +/** + * Allocate a new initialize a new RegularExpressionNode node. + */ +static inline pm_regular_expression_node_t * +pm_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) { + return pm_regular_expression_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY); +} + +/** + * Allocate a new RequiredParameterNode node. + */ +static pm_required_parameter_node_t * +pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) { + pm_required_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_required_parameter_node_t); + + *node = (pm_required_parameter_node_t) { + { + .type = PM_REQUIRED_PARAMETER_NODE, + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .name = pm_parser_constant_id_token(parser, token) + }; + + return node; +} + +/** + * Allocate a new RescueModifierNode node. + */ +static pm_rescue_modifier_node_t * +pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *keyword, pm_node_t *rescue_expression) { + pm_rescue_modifier_node_t *node = PM_ALLOC_NODE(parser, pm_rescue_modifier_node_t); + + *node = (pm_rescue_modifier_node_t) { + { + .type = PM_RESCUE_MODIFIER_NODE, + .location = { + .start = expression->location.start, + .end = rescue_expression->location.end + } + }, + .expression = expression, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .rescue_expression = rescue_expression + }; + + return node; +} + +/** + * Allocate and initiliaze a new RescueNode node. + */ +static pm_rescue_node_t * +pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) { + pm_rescue_node_t *node = PM_ALLOC_NODE(parser, pm_rescue_node_t); + + *node = (pm_rescue_node_t) { + { + .type = PM_RESCUE_NODE, + .location = PM_LOCATION_TOKEN_VALUE(keyword) + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .reference = NULL, + .statements = NULL, + .consequent = NULL, + .exceptions = { 0 } + }; + + return node; +} + +static inline void +pm_rescue_node_operator_set(pm_rescue_node_t *node, const pm_token_t *operator) { + node->operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator); +} + +/** + * Set the reference of a rescue node, and update the location of the node. + */ +static void +pm_rescue_node_reference_set(pm_rescue_node_t *node, pm_node_t *reference) { + node->reference = reference; + node->base.location.end = reference->location.end; +} + +/** + * Set the statements of a rescue node, and update the location of the node. + */ +static void +pm_rescue_node_statements_set(pm_rescue_node_t *node, pm_statements_node_t *statements) { + node->statements = statements; + if (pm_statements_node_body_length(statements) > 0) { + node->base.location.end = statements->base.location.end; + } +} + +/** + * Set the consequent of a rescue node, and update the location. + */ +static void +pm_rescue_node_consequent_set(pm_rescue_node_t *node, pm_rescue_node_t *consequent) { + node->consequent = consequent; + node->base.location.end = consequent->base.location.end; +} + +/** + * Append an exception node to a rescue node, and update the location. + */ +static void +pm_rescue_node_exceptions_append(pm_rescue_node_t *node, pm_node_t *exception) { + pm_node_list_append(&node->exceptions, exception); + node->base.location.end = exception->location.end; +} + +/** + * Allocate a new RestParameterNode node. + */ +static pm_rest_parameter_node_t * +pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) { + pm_rest_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_rest_parameter_node_t); + + *node = (pm_rest_parameter_node_t) { + { + .type = PM_REST_PARAMETER_NODE, + .location = { + .start = operator->start, + .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end) + } + }, + .name = pm_parser_optional_constant_id_token(parser, name), + .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +/** + * Allocate and initialize a new RetryNode node. + */ +static pm_retry_node_t * +pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_KEYWORD_RETRY); + pm_retry_node_t *node = PM_ALLOC_NODE(parser, pm_retry_node_t); + + *node = (pm_retry_node_t) {{ .type = PM_RETRY_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +/** + * Allocate a new ReturnNode node. + */ +static pm_return_node_t * +pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) { + pm_return_node_t *node = PM_ALLOC_NODE(parser, pm_return_node_t); + + *node = (pm_return_node_t) { + { + .type = PM_RETURN_NODE, + .location = { + .start = keyword->start, + .end = (arguments == NULL ? keyword->end : arguments->base.location.end) + } + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .arguments = arguments + }; + + return node; +} + +/** + * Allocate and initialize a new SelfNode node. + */ +static pm_self_node_t * +pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_KEYWORD_SELF); + pm_self_node_t *node = PM_ALLOC_NODE(parser, pm_self_node_t); + + *node = (pm_self_node_t) {{ + .type = PM_SELF_NODE, + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate a new SingletonClassNode node. + */ +static pm_singleton_class_node_t * +pm_singleton_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *class_keyword, const pm_token_t *operator, pm_node_t *expression, pm_node_t *body, const pm_token_t *end_keyword) { + pm_singleton_class_node_t *node = PM_ALLOC_NODE(parser, pm_singleton_class_node_t); + + *node = (pm_singleton_class_node_t) { + { + .type = PM_SINGLETON_CLASS_NODE, + .location = { + .start = class_keyword->start, + .end = end_keyword->end + } + }, + .locals = *locals, + .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword), + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .expression = expression, + .body = body, + .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +/** + * Allocate and initialize a new SourceEncodingNode node. + */ +static pm_source_encoding_node_t * +pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_KEYWORD___ENCODING__); + pm_source_encoding_node_t *node = PM_ALLOC_NODE(parser, pm_source_encoding_node_t); + + *node = (pm_source_encoding_node_t) {{ + .type = PM_SOURCE_ENCODING_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate and initialize a new SourceFileNode node. + */ +static pm_source_file_node_t* +pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) { + pm_source_file_node_t *node = PM_ALLOC_NODE(parser, pm_source_file_node_t); + assert(file_keyword->type == PM_TOKEN_KEYWORD___FILE__); + + *node = (pm_source_file_node_t) { + { + .type = PM_SOURCE_FILE_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(file_keyword), + }, + .filepath = parser->filepath_string, + }; + + return node; +} + +/** + * Allocate and initialize a new SourceLineNode node. + */ +static pm_source_line_node_t * +pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_KEYWORD___LINE__); + pm_source_line_node_t *node = PM_ALLOC_NODE(parser, pm_source_line_node_t); + + *node = (pm_source_line_node_t) {{ + .type = PM_SOURCE_LINE_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate a new SplatNode node. + */ +static pm_splat_node_t * +pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) { + pm_splat_node_t *node = PM_ALLOC_NODE(parser, pm_splat_node_t); + + *node = (pm_splat_node_t) { + { + .type = PM_SPLAT_NODE, + .location = { + .start = operator->start, + .end = (expression == NULL ? operator->end : expression->location.end) + } + }, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .expression = expression + }; + + return node; +} + +/** + * Allocate and initialize a new StatementsNode node. + */ +static pm_statements_node_t * +pm_statements_node_create(pm_parser_t *parser) { + pm_statements_node_t *node = PM_ALLOC_NODE(parser, pm_statements_node_t); + + *node = (pm_statements_node_t) { + { + .type = PM_STATEMENTS_NODE, + .location = PM_LOCATION_NULL_VALUE(parser) + }, + .body = { 0 } + }; + + return node; +} + +/** + * Get the length of the given StatementsNode node's body. + */ +static size_t +pm_statements_node_body_length(pm_statements_node_t *node) { + return node && node->body.size; +} + +/** + * Set the location of the given StatementsNode. + */ +static void +pm_statements_node_location_set(pm_statements_node_t *node, const uint8_t *start, const uint8_t *end) { + node->base.location = (pm_location_t) { .start = start, .end = end }; +} + +/** + * Append a new node to the given StatementsNode node's body. + */ +static void +pm_statements_node_body_append(pm_statements_node_t *node, pm_node_t *statement) { + if (pm_statements_node_body_length(node) == 0 || statement->location.start < node->base.location.start) { + node->base.location.start = statement->location.start; + } + if (statement->location.end > node->base.location.end) { + node->base.location.end = statement->location.end; + } + + pm_node_list_append(&node->body, statement); + + // Every statement gets marked as a place where a newline can occur. + statement->flags |= PM_NODE_FLAG_NEWLINE; +} + +/** + * Allocate a new StringNode node with the current string on the parser. + */ +static inline pm_string_node_t * +pm_string_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *string) { + pm_string_node_t *node = PM_ALLOC_NODE(parser, pm_string_node_t); + pm_node_flags_t flags = 0; + + if (parser->frozen_string_literal) { + flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN; + } + + *node = (pm_string_node_t) { + { + .type = PM_STRING_NODE, + .flags = flags, + .location = { + .start = (opening->type == PM_TOKEN_NOT_PROVIDED ? content->start : opening->start), + .end = (closing->type == PM_TOKEN_NOT_PROVIDED ? content->end : closing->end) + } + }, + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .content_loc = PM_LOCATION_TOKEN_VALUE(content), + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .unescaped = *string + }; + + return node; +} + +/** + * Allocate a new StringNode node. + */ +static pm_string_node_t * +pm_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) { + return pm_string_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY); +} + +/** + * Allocate a new StringNode node and create it using the current string on the + * parser. + */ +static pm_string_node_t * +pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) { + pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, &parser->current_string); + parser->current_string = PM_STRING_EMPTY; + return node; +} + +/** + * Allocate and initialize a new SuperNode node. + */ +static pm_super_node_t * +pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_t *arguments) { + assert(keyword->type == PM_TOKEN_KEYWORD_SUPER); + pm_super_node_t *node = PM_ALLOC_NODE(parser, pm_super_node_t); + + const uint8_t *end = pm_arguments_end(arguments); + if (end == NULL) { + assert(false && "unreachable"); + } + + *node = (pm_super_node_t) { + { + .type = PM_SUPER_NODE, + .location = { + .start = keyword->start, + .end = end, + } + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .lparen_loc = arguments->opening_loc, + .arguments = arguments->arguments, + .rparen_loc = arguments->closing_loc, + .block = arguments->block + }; + + return node; +} + +/** + * Allocate and initialize a new SymbolNode node with the given unescaped + * string. + */ +static pm_symbol_node_t * +pm_symbol_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing, const pm_string_t *unescaped) { + pm_symbol_node_t *node = PM_ALLOC_NODE(parser, pm_symbol_node_t); + + *node = (pm_symbol_node_t) { + { + .type = PM_SYMBOL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = { + .start = (opening->type == PM_TOKEN_NOT_PROVIDED ? value->start : opening->start), + .end = (closing->type == PM_TOKEN_NOT_PROVIDED ? value->end : closing->end) + } + }, + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .value_loc = PM_LOCATION_TOKEN_VALUE(value), + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .unescaped = *unescaped + }; + + return node; +} + +/** + * Allocate and initialize a new SymbolNode node. + */ +static inline pm_symbol_node_t * +pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) { + return pm_symbol_node_create_unescaped(parser, opening, value, closing, &PM_STRING_EMPTY); +} + +/** + * Allocate and initialize a new SymbolNode node with the current string. + */ +static pm_symbol_node_t * +pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) { + pm_symbol_node_t *node = pm_symbol_node_create_unescaped(parser, opening, value, closing, &parser->current_string); + parser->current_string = PM_STRING_EMPTY; + return node; +} + +/** + * Allocate and initialize a new SymbolNode node from a label. + */ +static pm_symbol_node_t * +pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) { + pm_symbol_node_t *node; + + switch (token->type) { + case PM_TOKEN_LABEL: { + pm_token_t opening = not_provided(parser); + pm_token_t closing = { .type = PM_TOKEN_LABEL_END, .start = token->end - 1, .end = token->end }; + + pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end - 1 }; + node = pm_symbol_node_create(parser, &opening, &label, &closing); + + assert((label.end - label.start) >= 0); + pm_string_shared_init(&node->unescaped, label.start, label.end); + break; + } + case PM_TOKEN_MISSING: { + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + + pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end }; + node = pm_symbol_node_create(parser, &opening, &label, &closing); + break; + } + default: + assert(false && "unreachable"); + node = NULL; + break; + } + + return node; +} + +/** + * Check if the given node is a label in a hash. + */ +static bool +pm_symbol_node_label_p(pm_node_t *node) { + const uint8_t *end = NULL; + + switch (PM_NODE_TYPE(node)) { + case PM_SYMBOL_NODE: + end = ((pm_symbol_node_t *) node)->closing_loc.end; + break; + case PM_INTERPOLATED_SYMBOL_NODE: + end = ((pm_interpolated_symbol_node_t *) node)->closing_loc.end; + break; + default: + return false; + } + + return (end != NULL) && (end[-1] == ':'); +} + +/** + * Convert the given StringNode node to a SymbolNode node. + */ +static pm_symbol_node_t * +pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t *opening, const pm_token_t *closing) { + pm_symbol_node_t *new_node = PM_ALLOC_NODE(parser, pm_symbol_node_t); + + *new_node = (pm_symbol_node_t) { + { + .type = PM_SYMBOL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = { + .start = opening->start, + .end = closing->end + } + }, + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .value_loc = node->content_loc, + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .unescaped = node->unescaped + }; + + // We are explicitly _not_ using pm_node_destroy here because we don't want + // to trash the unescaped string. We could instead copy the string if we + // know that it is owned, but we're taking the fast path for now. + free(node); + + return new_node; +} + +/** + * Convert the given SymbolNode node to a StringNode node. + */ +static pm_string_node_t * +pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { + pm_string_node_t *new_node = PM_ALLOC_NODE(parser, pm_string_node_t); + pm_node_flags_t flags = 0; + + if (parser->frozen_string_literal) { + flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN; + } + + *new_node = (pm_string_node_t) { + { + .type = PM_STRING_NODE, + .flags = flags, + .location = node->base.location + }, + .opening_loc = node->opening_loc, + .content_loc = node->value_loc, + .closing_loc = node->closing_loc, + .unescaped = node->unescaped + }; + + // We are explicitly _not_ using pm_node_destroy here because we don't want + // to trash the unescaped string. We could instead copy the string if we + // know that it is owned, but we're taking the fast path for now. + free(node); + + return new_node; +} + +/** + * Allocate and initialize a new TrueNode node. + */ +static pm_true_node_t * +pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_KEYWORD_TRUE); + pm_true_node_t *node = PM_ALLOC_NODE(parser, pm_true_node_t); + + *node = (pm_true_node_t) {{ + .type = PM_TRUE_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate and initialize a new UndefNode node. + */ +static pm_undef_node_t * +pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) { + assert(token->type == PM_TOKEN_KEYWORD_UNDEF); + pm_undef_node_t *node = PM_ALLOC_NODE(parser, pm_undef_node_t); + + *node = (pm_undef_node_t) { + { + .type = PM_UNDEF_NODE, + .location = PM_LOCATION_TOKEN_VALUE(token), + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(token), + .names = { 0 } + }; + + return node; +} + +/** + * Append a name to an undef node. + */ +static void +pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) { + node->base.location.end = name->location.end; + pm_node_list_append(&node->names, name); +} + +/** + * Allocate a new UnlessNode node. + */ +static pm_unless_node_t * +pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, const pm_token_t *then_keyword, pm_statements_node_t *statements) { + pm_conditional_predicate(predicate); + pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t); + + const uint8_t *end; + if (statements != NULL) { + end = statements->base.location.end; + } else { + end = predicate->location.end; + } + + *node = (pm_unless_node_t) { + { + .type = PM_UNLESS_NODE, + .flags = PM_NODE_FLAG_NEWLINE, + .location = { + .start = keyword->start, + .end = end + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .predicate = predicate, + .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword), + .statements = statements, + .consequent = NULL, + .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +/** + * Allocate and initialize new UnlessNode node in the modifier form. + */ +static pm_unless_node_t * +pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) { + pm_conditional_predicate(predicate); + pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t); + + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(statements, statement); + + *node = (pm_unless_node_t) { + { + .type = PM_UNLESS_NODE, + .flags = PM_NODE_FLAG_NEWLINE, + .location = { + .start = statement->location.start, + .end = predicate->location.end + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(unless_keyword), + .predicate = predicate, + .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .statements = statements, + .consequent = NULL, + .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +static inline void +pm_unless_node_end_keyword_loc_set(pm_unless_node_t *node, const pm_token_t *end_keyword) { + node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword); + node->base.location.end = end_keyword->end; +} + +/** + * Allocate a new UntilNode node. + */ +static pm_until_node_t * +pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) { + pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t); + + *node = (pm_until_node_t) { + { + .type = PM_UNTIL_NODE, + .flags = flags, + .location = { + .start = keyword->start, + .end = closing->end, + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .predicate = predicate, + .statements = statements + }; + + return node; +} + +/** + * Allocate a new UntilNode node. + */ +static pm_until_node_t * +pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) { + pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t); + + *node = (pm_until_node_t) { + { + .type = PM_UNTIL_NODE, + .flags = flags, + .location = { + .start = statements->base.location.start, + .end = predicate->location.end, + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .predicate = predicate, + .statements = statements + }; + + return node; +} + +/** + * Allocate and initialize a new WhenNode node. + */ +static pm_when_node_t * +pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) { + pm_when_node_t *node = PM_ALLOC_NODE(parser, pm_when_node_t); + + *node = (pm_when_node_t) { + { + .type = PM_WHEN_NODE, + .location = { + .start = keyword->start, + .end = NULL + } + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .statements = NULL, + .conditions = { 0 } + }; + + return node; +} + +/** + * Append a new condition to a when node. + */ +static void +pm_when_node_conditions_append(pm_when_node_t *node, pm_node_t *condition) { + node->base.location.end = condition->location.end; + pm_node_list_append(&node->conditions, condition); +} + +/** + * Set the statements list of a when node. + */ +static void +pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statements) { + if (statements->base.location.end > node->base.location.end) { + node->base.location.end = statements->base.location.end; + } + + node->statements = statements; +} + +/** + * Allocate a new WhileNode node. + */ +static pm_while_node_t * +pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) { + pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t); + + *node = (pm_while_node_t) { + { + .type = PM_WHILE_NODE, + .flags = flags, + .location = { + .start = keyword->start, + .end = closing->end + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .predicate = predicate, + .statements = statements + }; + + return node; +} + +/** + * Allocate a new WhileNode node. + */ +static pm_while_node_t * +pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) { + pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t); + + *node = (pm_while_node_t) { + { + .type = PM_WHILE_NODE, + .flags = flags, + .location = { + .start = statements->base.location.start, + .end = predicate->location.end + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .predicate = predicate, + .statements = statements + }; + + return node; +} + +/** + * Allocate and initialize a new XStringNode node with the given unescaped + * string. + */ +static pm_x_string_node_t * +pm_xstring_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *unescaped) { + pm_x_string_node_t *node = PM_ALLOC_NODE(parser, pm_x_string_node_t); + + *node = (pm_x_string_node_t) { + { + .type = PM_X_STRING_NODE, + .location = { + .start = opening->start, + .end = closing->end + }, + }, + .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), + .content_loc = PM_LOCATION_TOKEN_VALUE(content), + .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), + .unescaped = *unescaped + }; + + return node; +} + +/** + * Allocate and initialize a new XStringNode node. + */ +static inline pm_x_string_node_t * +pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) { + return pm_xstring_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY); +} + +/** + * Allocate a new YieldNode node. + */ +static pm_yield_node_t * +pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_location_t *lparen_loc, pm_arguments_node_t *arguments, const pm_location_t *rparen_loc) { + pm_yield_node_t *node = PM_ALLOC_NODE(parser, pm_yield_node_t); + + const uint8_t *end; + if (rparen_loc->start != NULL) { + end = rparen_loc->end; + } else if (arguments != NULL) { + end = arguments->base.location.end; + } else if (lparen_loc->start != NULL) { + end = lparen_loc->end; + } else { + end = keyword->end; + } + + *node = (pm_yield_node_t) { + { + .type = PM_YIELD_NODE, + .location = { + .start = keyword->start, + .end = end + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .lparen_loc = *lparen_loc, + .arguments = arguments, + .rparen_loc = *rparen_loc + }; + + return node; +} + +#undef PM_ALLOC_NODE + +/******************************************************************************/ +/* Scope-related functions */ +/******************************************************************************/ + +/** + * Allocate and initialize a new scope. Push it onto the scope stack. + */ +static bool +pm_parser_scope_push(pm_parser_t *parser, bool closed) { + pm_scope_t *scope = (pm_scope_t *) malloc(sizeof(pm_scope_t)); + if (scope == NULL) return false; + + *scope = (pm_scope_t) { + .previous = parser->current_scope, + .closed = closed, + .explicit_params = false, + .numbered_parameters = 0, + .transparent = false + }; + + pm_constant_id_list_init(&scope->locals); + parser->current_scope = scope; + + return true; +} + +/** + * Allocate and initialize a new scope. Push it onto the scope stack. + */ +static bool +pm_parser_scope_push_transparent(pm_parser_t *parser) { + pm_scope_t *scope = (pm_scope_t *) malloc(sizeof(pm_scope_t)); + if (scope == NULL) return false; + + *scope = (pm_scope_t) { + .previous = parser->current_scope, + .closed = false, + .explicit_params = false, + .numbered_parameters = 0, + .transparent = true + }; + + parser->current_scope = scope; + + return true; +} + +/** + * Check if any of the currently visible scopes contain a local variable + * described by the given constant id. + */ +static int +pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant_id) { + pm_scope_t *scope = parser->current_scope; + int depth = 0; + + while (scope != NULL) { + if (!scope->transparent && pm_constant_id_list_includes(&scope->locals, constant_id)) return depth; + if (scope->closed) break; + + scope = scope->previous; + depth++; + } + + return -1; +} + +/** + * Check if any of the currently visible scopes contain a local variable + * described by the given token. This function implicitly inserts a constant + * into the constant pool. + */ +static inline int +pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) { + return pm_parser_local_depth_constant_id(parser, pm_parser_constant_id_token(parser, token)); +} + +/** + * Add a constant id to the local table of the current scope. + */ +static inline void +pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id) { + pm_scope_t *scope = parser->current_scope; + while (scope && scope->transparent) scope = scope->previous; + + assert(scope != NULL); + if (!pm_constant_id_list_includes(&scope->locals, constant_id)) { + pm_constant_id_list_append(&scope->locals, constant_id); + } +} + +/** + * Set the numbered_parameters value of the current scope. + */ +static inline void +pm_parser_numbered_parameters_set(pm_parser_t *parser, uint32_t numbered_parameters) { + pm_scope_t *scope = parser->current_scope; + while (scope && scope->transparent) scope = scope->previous; + + assert(scope != NULL); + scope->numbered_parameters = numbered_parameters; +} + +/** + * Add a local variable from a location to the current scope. + */ +static pm_constant_id_t +pm_parser_local_add_location(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, start, end); + if (constant_id != 0) pm_parser_local_add(parser, constant_id); + return constant_id; +} + +/** + * Add a local variable from a token to the current scope. + */ +static inline void +pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token) { + pm_parser_local_add_location(parser, token->start, token->end); +} + +/** + * Add a local variable from an owned string to the current scope. + */ +static pm_constant_id_t +pm_parser_local_add_owned(pm_parser_t *parser, const uint8_t *start, size_t length) { + pm_constant_id_t constant_id = pm_parser_constant_id_owned(parser, start, length); + if (constant_id != 0) pm_parser_local_add(parser, constant_id); + return constant_id; +} + +/** + * Add a parameter name to the current scope and check whether the name of the + * parameter is unique or not. + */ +static void +pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) { + // We want to check whether the parameter name is a numbered parameter or + // not. + pm_refute_numbered_parameter(parser, name->start, name->end); + + // We want to ignore any parameter name that starts with an underscore. + if ((name->start < name->end) && (*name->start == '_')) return; + + // Otherwise we'll fetch the constant id for the parameter name and check + // whether it's already in the current scope. + pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name); + + if (pm_constant_id_list_includes(&parser->current_scope->locals, constant_id)) { + pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_REPEAT); + } +} + +/** + * Pop the current scope off the scope stack. Note that we specifically do not + * free the associated constant list because we assume that we have already + * transferred ownership of the list to the AST somewhere. + */ +static void +pm_parser_scope_pop(pm_parser_t *parser) { + pm_scope_t *scope = parser->current_scope; + parser->current_scope = scope->previous; + free(scope); +} + +/******************************************************************************/ +/* Basic character checks */ +/******************************************************************************/ + +/** + * This function is used extremely frequently to lex all of the identifiers in a + * source file, so it's important that it be as fast as possible. For this + * reason we have the encoding_changed boolean to check if we need to go through + * the function pointer or can just directly use the UTF-8 functions. + */ +static inline size_t +char_is_identifier_start(pm_parser_t *parser, const uint8_t *b) { + if (parser->encoding_changed) { + size_t width; + if ((width = parser->encoding.alpha_char(b, parser->end - b)) != 0) { + return width; + } else if (*b == '_') { + return 1; + } else if (*b >= 0x80) { + return parser->encoding.char_width(b, parser->end - b); + } else { + return 0; + } + } else if (*b < 0x80) { + return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT ? 1 : 0) || (*b == '_'); + } else { + return (size_t) (pm_encoding_utf_8_alpha_char(b, parser->end - b) || 1u); + } +} + +/** + * Similar to char_is_identifier but this function assumes that the encoding + * has not been changed. + */ +static inline size_t +char_is_identifier_utf8(const uint8_t *b, const uint8_t *end) { + if (*b < 0x80) { + return (*b == '_') || (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT ? 1 : 0); + } else { + return (size_t) (pm_encoding_utf_8_alnum_char(b, end - b) || 1u); + } +} + +/** + * Like the above, this function is also used extremely frequently to lex all of + * the identifiers in a source file once the first character has been found. So + * it's important that it be as fast as possible. + */ +static inline size_t +char_is_identifier(pm_parser_t *parser, const uint8_t *b) { + if (parser->encoding_changed) { + size_t width; + if ((width = parser->encoding.alnum_char(b, parser->end - b)) != 0) { + return width; + } else if (*b == '_') { + return 1; + } else if (*b >= 0x80) { + return parser->encoding.char_width(b, parser->end - b); + } else { + return 0; + } + } + return char_is_identifier_utf8(b, parser->end); +} + +// Here we're defining a perfect hash for the characters that are allowed in +// global names. This is used to quickly check the next character after a $ to +// see if it's a valid character for a global name. +#define BIT(c, idx) (((c) / 32 - 1 == idx) ? (1U << ((c) % 32)) : 0) +#define PUNCT(idx) ( \ + BIT('~', idx) | BIT('*', idx) | BIT('$', idx) | BIT('?', idx) | \ + BIT('!', idx) | BIT('@', idx) | BIT('/', idx) | BIT('\\', idx) | \ + BIT(';', idx) | BIT(',', idx) | BIT('.', idx) | BIT('=', idx) | \ + BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \ + BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \ + BIT('0', idx)) + +const unsigned int pm_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { PUNCT(0), PUNCT(1), PUNCT(2) }; + +#undef BIT +#undef PUNCT + +static inline bool +char_is_global_name_punctuation(const uint8_t b) { + const unsigned int i = (const unsigned int) b; + if (i <= 0x20 || 0x7e < i) return false; + + return (pm_global_name_punctuation_hash[(i - 0x20) / 32] >> (i % 32)) & 1; +} + +static inline bool +token_is_setter_name(pm_token_t *token) { + return ( + (token->type == PM_TOKEN_IDENTIFIER) && + (token->end - token->start >= 2) && + (token->end[-1] == '=') + ); +} + +/******************************************************************************/ +/* Stack helpers */ +/******************************************************************************/ + +static inline void +pm_accepts_block_stack_push(pm_parser_t *parser, bool value) { + // Use the negation of the value to prevent stack overflow. + pm_state_stack_push(&parser->accepts_block_stack, !value); +} + +static inline void +pm_accepts_block_stack_pop(pm_parser_t *parser) { + pm_state_stack_pop(&parser->accepts_block_stack); +} + +static inline bool +pm_accepts_block_stack_p(pm_parser_t *parser) { + return !pm_state_stack_p(&parser->accepts_block_stack); +} + +static inline void +pm_do_loop_stack_push(pm_parser_t *parser, bool value) { + pm_state_stack_push(&parser->do_loop_stack, value); +} + +static inline void +pm_do_loop_stack_pop(pm_parser_t *parser) { + pm_state_stack_pop(&parser->do_loop_stack); +} + +static inline bool +pm_do_loop_stack_p(pm_parser_t *parser) { + return pm_state_stack_p(&parser->do_loop_stack); +} + +/******************************************************************************/ +/* Lexer check helpers */ +/******************************************************************************/ + +/** + * Get the next character in the source starting from +cursor+. If that position + * is beyond the end of the source then return '\0'. + */ +static inline uint8_t +peek_at(pm_parser_t *parser, const uint8_t *cursor) { + if (cursor < parser->end) { + return *cursor; + } else { + return '\0'; + } +} + +/** + * Get the next character in the source starting from parser->current.end and + * adding the given offset. If that position is beyond the end of the source + * then return '\0'. + */ +static inline uint8_t +peek_offset(pm_parser_t *parser, ptrdiff_t offset) { + return peek_at(parser, parser->current.end + offset); +} + +/** + * Get the next character in the source starting from parser->current.end. If + * that position is beyond the end of the source then return '\0'. + */ +static inline uint8_t +peek(pm_parser_t *parser) { + return peek_at(parser, parser->current.end); +} + +/** + * If the character to be read matches the given value, then returns true and + * advanced the current pointer. + */ +static inline bool +match(pm_parser_t *parser, uint8_t value) { + if (peek(parser) == value) { + parser->current.end++; + return true; + } + return false; +} + +/** + * Return the length of the line ending string starting at +cursor+, or 0 if it + * is not a line ending. This function is intended to be CRLF/LF agnostic. + */ +static inline size_t +match_eol_at(pm_parser_t *parser, const uint8_t *cursor) { + if (peek_at(parser, cursor) == '\n') { + return 1; + } + if (peek_at(parser, cursor) == '\r' && peek_at(parser, cursor + 1) == '\n') { + return 2; + } + return 0; +} + +/** + * Return the length of the line ending string starting at + * `parser->current.end + offset`, or 0 if it is not a line ending. This + * function is intended to be CRLF/LF agnostic. + */ +static inline size_t +match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) { + return match_eol_at(parser, parser->current.end + offset); +} + +/** + * Return the length of the line ending string starting at parser->current.end, + * or 0 if it is not a line ending. This function is intended to be CRLF/LF + * agnostic. + */ +static inline size_t +match_eol(pm_parser_t *parser) { + return match_eol_at(parser, parser->current.end); +} + +/** + * Skip to the next newline character or NUL byte. + */ +static inline const uint8_t * +next_newline(const uint8_t *cursor, ptrdiff_t length) { + assert(length >= 0); + + // Note that it's okay for us to use memchr here to look for \n because none + // of the encodings that we support have \n as a component of a multi-byte + // character. + return memchr(cursor, '\n', (size_t) length); +} + +/** + * Here we're going to check if this is a "magic" comment, and perform whatever + * actions are necessary for it here. + */ +static bool +parser_lex_magic_comment_encoding_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + size_t width = (size_t) (end - start); + + // First, we're going to call out to a user-defined callback if one was + // provided. If they return an encoding struct that we can use, then we'll + // use that here. + if (parser->encoding_decode_callback != NULL) { + pm_encoding_t *encoding = parser->encoding_decode_callback(parser, start, width); + + if (encoding != NULL) { + parser->encoding = *encoding; + return true; + } + } + + // Next, we're going to check for UTF-8. This is the most common encoding. + // utf-8 can contain extra information at the end about the platform it is + // encoded on, such as utf-8-mac or utf-8-unix. We'll ignore those suffixes. + if ((start + 5 <= end) && (pm_strncasecmp(start, (const uint8_t *) "utf-8", 5) == 0)) { + // We need to explicitly handle utf-8-hfs, as that one needs to switch + // over to being utf8-mac. + if (width == 9 && (pm_strncasecmp(start + 5, (const uint8_t *) "-hfs", 4) == 0)) { + parser->encoding = pm_encoding_utf8_mac; + parser->encoding_changed = true; + if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser); + return true; + } + + // We don't need to do anything here because the default encoding is + // already UTF-8. We'll just return. + return true; + } + + // Next, we're going to loop through each of the encodings that we handle + // explicitly. If we found one that we understand, we'll use that value. +#define ENCODING1(value, prebuilt) \ + if (width == sizeof(value) - 1 && start + width <= end && pm_strncasecmp(start, (const uint8_t *) value, width) == 0) { \ + parser->encoding = prebuilt; \ + parser->encoding_changed = true; \ + if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser); \ + return true; \ + } + + // A convenience macros for comparing two aliases for the same encoding. +#define ENCODING2(value1, value2, prebuilt) ENCODING1(value1, prebuilt) ENCODING1(value2, prebuilt) + + if (width >= 3) { + switch (*start) { + case 'A': case 'a': + ENCODING1("ASCII", pm_encoding_ascii); + ENCODING1("ASCII-8BIT", pm_encoding_ascii_8bit); + ENCODING1("ANSI_X3.4-1968", pm_encoding_ascii); + break; + case 'B': case 'b': + ENCODING1("BINARY", pm_encoding_ascii_8bit); + ENCODING1("Big5", pm_encoding_big5); + ENCODING1("Big5-HKSCS", pm_encoding_big5_hkscs); + ENCODING1("Big5-UAO", pm_encoding_big5_uao); + break; + case 'C': case 'c': + ENCODING1("CP437", pm_encoding_ibm437); + ENCODING1("CP720", pm_encoding_ibm720); + ENCODING1("CP737", pm_encoding_ibm737); + ENCODING1("CP775", pm_encoding_ibm775); + ENCODING1("CP850", pm_encoding_cp850); + ENCODING1("CP852", pm_encoding_cp852); + ENCODING1("CP855", pm_encoding_cp855); + ENCODING1("CP857", pm_encoding_ibm857); + ENCODING1("CP860", pm_encoding_ibm860); + ENCODING1("CP861", pm_encoding_ibm861); + ENCODING1("CP862", pm_encoding_ibm862); + ENCODING1("CP864", pm_encoding_ibm864); + ENCODING1("CP865", pm_encoding_ibm865); + ENCODING1("CP866", pm_encoding_ibm866); + ENCODING1("CP869", pm_encoding_ibm869); + ENCODING1("CP874", pm_encoding_windows_874); + ENCODING1("CP878", pm_encoding_koi8_r); + ENCODING1("CP863", pm_encoding_ibm863); + ENCODING2("CP932", "csWindows31J", pm_encoding_windows_31j); + ENCODING1("CP936", pm_encoding_gbk); + ENCODING1("CP949", pm_encoding_cp949); + ENCODING1("CP950", pm_encoding_cp950); + ENCODING1("CP1250", pm_encoding_windows_1250); + ENCODING1("CP1251", pm_encoding_windows_1251); + ENCODING1("CP1252", pm_encoding_windows_1252); + ENCODING1("CP1253", pm_encoding_windows_1253); + ENCODING1("CP1254", pm_encoding_windows_1254); + ENCODING1("CP1255", pm_encoding_windows_1255); + ENCODING1("CP1256", pm_encoding_windows_1256); + ENCODING1("CP1257", pm_encoding_windows_1257); + ENCODING1("CP1258", pm_encoding_windows_1258); + ENCODING1("CP51932", pm_encoding_cp51932); + ENCODING1("CP65001", pm_encoding_utf_8); + break; + case 'E': case 'e': + ENCODING2("EUC-JP", "eucJP", pm_encoding_euc_jp); + ENCODING1("external", pm_encoding_utf_8); + break; + case 'F': case 'f': + ENCODING1("filesystem", pm_encoding_utf_8); + break; + case 'G': case 'g': + ENCODING1("GB1988", pm_encoding_gb1988); + ENCODING1("GBK", pm_encoding_gbk); + break; + case 'I': case 'i': + ENCODING1("IBM437", pm_encoding_ibm437); + ENCODING1("IBM720", pm_encoding_ibm720); + ENCODING1("IBM737", pm_encoding_ibm737); + ENCODING1("IBM775", pm_encoding_ibm775); + ENCODING1("IBM850", pm_encoding_cp850); + ENCODING1("IBM852", pm_encoding_ibm852); + ENCODING1("IBM855", pm_encoding_ibm855); + ENCODING1("IBM857", pm_encoding_ibm857); + ENCODING1("IBM860", pm_encoding_ibm860); + ENCODING1("IBM861", pm_encoding_ibm861); + ENCODING1("IBM862", pm_encoding_ibm862); + ENCODING1("IBM863", pm_encoding_ibm863); + ENCODING1("IBM864", pm_encoding_ibm864); + ENCODING1("IBM865", pm_encoding_ibm865); + ENCODING1("IBM866", pm_encoding_ibm866); + ENCODING1("IBM869", pm_encoding_ibm869); + ENCODING2("ISO-8859-1", "ISO8859-1", pm_encoding_iso_8859_1); + ENCODING2("ISO-8859-2", "ISO8859-2", pm_encoding_iso_8859_2); + ENCODING2("ISO-8859-3", "ISO8859-3", pm_encoding_iso_8859_3); + ENCODING2("ISO-8859-4", "ISO8859-4", pm_encoding_iso_8859_4); + ENCODING2("ISO-8859-5", "ISO8859-5", pm_encoding_iso_8859_5); + ENCODING2("ISO-8859-6", "ISO8859-6", pm_encoding_iso_8859_6); + ENCODING2("ISO-8859-7", "ISO8859-7", pm_encoding_iso_8859_7); + ENCODING2("ISO-8859-8", "ISO8859-8", pm_encoding_iso_8859_8); + ENCODING2("ISO-8859-9", "ISO8859-9", pm_encoding_iso_8859_9); + ENCODING2("ISO-8859-10", "ISO8859-10", pm_encoding_iso_8859_10); + ENCODING2("ISO-8859-11", "ISO8859-11", pm_encoding_iso_8859_11); + ENCODING2("ISO-8859-13", "ISO8859-13", pm_encoding_iso_8859_13); + ENCODING2("ISO-8859-14", "ISO8859-14", pm_encoding_iso_8859_14); + ENCODING2("ISO-8859-15", "ISO8859-15", pm_encoding_iso_8859_15); + ENCODING2("ISO-8859-16", "ISO8859-16", pm_encoding_iso_8859_16); + break; + case 'K': case 'k': + ENCODING1("KOI8-R", pm_encoding_koi8_r); + ENCODING1("KOI8-U", pm_encoding_koi8_u); + break; + case 'L': case 'l': + ENCODING1("locale", pm_encoding_utf_8); + break; + case 'M': case 'm': + ENCODING1("macCentEuro", pm_encoding_mac_cent_euro); + ENCODING1("macCroatian", pm_encoding_mac_croatian); + ENCODING1("macCyrillic", pm_encoding_mac_cyrillic); + ENCODING1("macGreek", pm_encoding_mac_greek); + ENCODING1("macIceland", pm_encoding_mac_iceland); + ENCODING1("MacJapanese", pm_encoding_mac_japanese); + ENCODING1("MacJapan", pm_encoding_mac_japanese); + ENCODING1("macRoman", pm_encoding_mac_roman); + ENCODING1("macRomania", pm_encoding_mac_romania); + ENCODING1("macThai", pm_encoding_mac_thai); + ENCODING1("macTurkish", pm_encoding_mac_turkish); + ENCODING1("macUkraine", pm_encoding_mac_ukraine); + break; + case 'P': case 'p': + ENCODING1("PCK", pm_encoding_windows_31j); + break; + case 'S': case 's': + ENCODING1("Shift_JIS", pm_encoding_shift_jis); + ENCODING1("SJIS", pm_encoding_windows_31j); + break; + case 'T': case 't': + ENCODING1("TIS-620", pm_encoding_tis_620); + break; + case 'U': case 'u': + ENCODING1("US-ASCII", pm_encoding_ascii); + ENCODING2("UTF8-MAC", "UTF-8-HFS", pm_encoding_utf8_mac); + break; + case 'W': case 'w': + ENCODING1("Windows-31J", pm_encoding_windows_31j); + ENCODING1("Windows-874", pm_encoding_windows_874); + ENCODING1("Windows-1250", pm_encoding_windows_1250); + ENCODING1("Windows-1251", pm_encoding_windows_1251); + ENCODING1("Windows-1252", pm_encoding_windows_1252); + ENCODING1("Windows-1253", pm_encoding_windows_1253); + ENCODING1("Windows-1254", pm_encoding_windows_1254); + ENCODING1("Windows-1255", pm_encoding_windows_1255); + ENCODING1("Windows-1256", pm_encoding_windows_1256); + ENCODING1("Windows-1257", pm_encoding_windows_1257); + ENCODING1("Windows-1258", pm_encoding_windows_1258); + break; + case '6': + ENCODING1("646", pm_encoding_ascii); + break; + } + } + +#undef ENCODING2 +#undef ENCODING1 + + return false; +} + +/** + * Look for a specific pattern of "coding" and potentially set the encoding on + * the parser. + */ +static void +parser_lex_magic_comment_encoding(pm_parser_t *parser) { + const uint8_t *cursor = parser->current.start + 1; + const uint8_t *end = parser->current.end; + + bool separator = false; + while (true) { + if (end - cursor <= 6) return; + switch (cursor[6]) { + case 'C': case 'c': cursor += 6; continue; + case 'O': case 'o': cursor += 5; continue; + case 'D': case 'd': cursor += 4; continue; + case 'I': case 'i': cursor += 3; continue; + case 'N': case 'n': cursor += 2; continue; + case 'G': case 'g': cursor += 1; continue; + case '=': case ':': + separator = true; + cursor += 6; + break; + default: + cursor += 6; + if (pm_char_is_whitespace(*cursor)) break; + continue; + } + if (pm_strncasecmp(cursor - 6, (const uint8_t *) "coding", 6) == 0) break; + separator = false; + } + + while (true) { + do { + if (++cursor >= end) return; + } while (pm_char_is_whitespace(*cursor)); + + if (separator) break; + if (*cursor != '=' && *cursor != ':') return; + + separator = true; + cursor++; + } + + const uint8_t *value_start = cursor; + while ((*cursor == '-' || *cursor == '_' || parser->encoding.alnum_char(cursor, 1)) && ++cursor < end); + + if (!parser_lex_magic_comment_encoding_value(parser, value_start, cursor)) { + // If we were unable to parse the encoding value, then we've got an + // issue because we didn't understand the encoding that the user was + // trying to use. In this case we'll keep using the default encoding but + // add an error to the parser to indicate an unsuccessful parse. + pm_parser_err(parser, value_start, cursor, PM_ERR_INVALID_ENCODING_MAGIC_COMMENT); + } +} + +/** + * Check if this is a magic comment that includes the frozen_string_literal + * pragma. If it does, set that field on the parser. + */ +static void +parser_lex_magic_comment_frozen_string_literal_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + if (start + 4 <= end && pm_strncasecmp(start, (const uint8_t *) "true", 4) == 0) { + parser->frozen_string_literal = true; + } +} + +static inline bool +pm_char_is_magic_comment_key_delimiter(const uint8_t b) { + return b == '\'' || b == '"' || b == ':' || b == ';'; +} + +/** + * Find an emacs magic comment marker (-*-) within the given bounds. If one is + * found, it returns a pointer to the start of the marker. Otherwise it returns + * NULL. + */ +static inline const uint8_t * +parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor, const uint8_t *end) { + while ((cursor + 3 <= end) && (cursor = pm_memchr(cursor, '-', (size_t) (end - cursor), parser->encoding_changed, &parser->encoding)) != NULL) { + if (cursor + 3 <= end && cursor[1] == '*' && cursor[2] == '-') { + return cursor; + } + cursor++; + } + return NULL; +} + +/** + * Parse the current token on the parser to see if it's a magic comment and + * potentially perform some action based on that. A regular expression that this + * function is effectively matching is: + * + * %r"([^\\s\'\":;]+)\\s*:\\s*(\"(?:\\\\.|[^\"])*\"|[^\"\\s;]+)[\\s;]*" + * + * It returns true if it consumes the entire comment. Otherwise it returns + * false. + */ +static inline bool +parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) { + bool result = true; + + const uint8_t *start = parser->current.start + 1; + const uint8_t *end = parser->current.end; + if (end - start <= 7) return false; + + const uint8_t *cursor; + bool indicator = false; + + if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) { + start = cursor + 3; + + if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) { + end = cursor; + indicator = true; + } else { + // If we have a start marker but not an end marker, then we cannot + // have a magic comment. + return false; + } + } + + cursor = start; + while (cursor < end) { + while (cursor < end && (pm_char_is_magic_comment_key_delimiter(*cursor) || pm_char_is_whitespace(*cursor))) cursor++; + + const uint8_t *key_start = cursor; + while (cursor < end && (!pm_char_is_magic_comment_key_delimiter(*cursor) && !pm_char_is_whitespace(*cursor))) cursor++; + + const uint8_t *key_end = cursor; + while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++; + if (cursor == end) break; + + if (*cursor == ':') { + cursor++; + } else { + if (!indicator) return false; + continue; + } + + while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++; + if (cursor == end) break; + + const uint8_t *value_start; + const uint8_t *value_end; + + if (*cursor == '"') { + value_start = ++cursor; + for (; cursor < end && *cursor != '"'; cursor++) { + if (*cursor == '\\' && (cursor + 1 < end)) cursor++; + } + value_end = cursor; + } else { + value_start = cursor; + while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++; + value_end = cursor; + } + + if (indicator) { + while (cursor < end && (*cursor == ';' || pm_char_is_whitespace(*cursor))) cursor++; + } else { + while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++; + if (cursor != end) return false; + } + + // Here, we need to do some processing on the key to swap out dashes for + // underscores. We only need to do this if there _is_ a dash in the key. + pm_string_t key; + const size_t key_length = (size_t) (key_end - key_start); + const uint8_t *dash = pm_memchr(key_start, '-', (size_t) key_length, parser->encoding_changed, &parser->encoding); + + if (dash == NULL) { + pm_string_shared_init(&key, key_start, key_end); + } else { + size_t width = (size_t) (key_end - key_start); + uint8_t *buffer = malloc(width); + if (buffer == NULL) break; + + memcpy(buffer, key_start, width); + buffer[dash - key_start] = '_'; + + while ((dash = pm_memchr(dash + 1, '-', (size_t) (key_end - dash - 1), parser->encoding_changed, &parser->encoding)) != NULL) { + buffer[dash - key_start] = '_'; + } + + pm_string_owned_init(&key, buffer, width); + } + + // Finally, we can start checking the key against the list of known + // magic comment keys, and potentially change state based on that. + const uint8_t *key_source = pm_string_source(&key); + + // We only want to attempt to compare against encoding comments if it's + // the first line in the file (or the second in the case of a shebang). + if (parser->current.start == parser->encoding_comment_start) { + if ( + (key_length == 8 && pm_strncasecmp(key_source, (const uint8_t *) "encoding", 8) == 0) || + (key_length == 6 && pm_strncasecmp(key_source, (const uint8_t *) "coding", 6) == 0) + ) { + result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end); + } + } + + // We only want to handle frozen string literal comments if it's before + // any semantic tokens have been seen. + if (!semantic_token_seen) { + if (key_length == 21 && pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) { + parser_lex_magic_comment_frozen_string_literal_value(parser, value_start, value_end); + } + } + + // When we're done, we want to free the string in case we had to + // allocate memory for it. + pm_string_free(&key); + + // Allocate a new magic comment node to append to the parser's list. + pm_magic_comment_t *magic_comment; + if ((magic_comment = (pm_magic_comment_t *) calloc(sizeof(pm_magic_comment_t), 1)) != NULL) { + magic_comment->key_start = key_start; + magic_comment->value_start = value_start; + magic_comment->key_length = (uint32_t) key_length; + magic_comment->value_length = (uint32_t) (value_end - value_start); + pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment); + } + } + + return result; +} + +/******************************************************************************/ +/* Context manipulations */ +/******************************************************************************/ + +static bool +context_terminator(pm_context_t context, pm_token_t *token) { + switch (context) { + case PM_CONTEXT_MAIN: + case PM_CONTEXT_DEF_PARAMS: + return token->type == PM_TOKEN_EOF; + case PM_CONTEXT_DEFAULT_PARAMS: + return token->type == PM_TOKEN_COMMA || token->type == PM_TOKEN_PARENTHESIS_RIGHT; + case PM_CONTEXT_PREEXE: + case PM_CONTEXT_POSTEXE: + return token->type == PM_TOKEN_BRACE_RIGHT; + case PM_CONTEXT_MODULE: + case PM_CONTEXT_CLASS: + case PM_CONTEXT_SCLASS: + case PM_CONTEXT_LAMBDA_DO_END: + case PM_CONTEXT_DEF: + case PM_CONTEXT_BLOCK_KEYWORDS: + return token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ENSURE; + case PM_CONTEXT_WHILE: + case PM_CONTEXT_UNTIL: + case PM_CONTEXT_ELSE: + case PM_CONTEXT_FOR: + case PM_CONTEXT_ENSURE: + case PM_CONTEXT_ENSURE_DEF: + return token->type == PM_TOKEN_KEYWORD_END; + case PM_CONTEXT_FOR_INDEX: + return token->type == PM_TOKEN_KEYWORD_IN; + case PM_CONTEXT_CASE_WHEN: + return token->type == PM_TOKEN_KEYWORD_WHEN || token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_ELSE; + case PM_CONTEXT_CASE_IN: + return token->type == PM_TOKEN_KEYWORD_IN || token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_ELSE; + case PM_CONTEXT_IF: + case PM_CONTEXT_ELSIF: + return token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_ELSIF || token->type == PM_TOKEN_KEYWORD_END; + case PM_CONTEXT_UNLESS: + return token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END; + case PM_CONTEXT_EMBEXPR: + return token->type == PM_TOKEN_EMBEXPR_END; + case PM_CONTEXT_BLOCK_BRACES: + return token->type == PM_TOKEN_BRACE_RIGHT; + case PM_CONTEXT_PARENS: + return token->type == PM_TOKEN_PARENTHESIS_RIGHT; + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_RESCUE: + case PM_CONTEXT_RESCUE_DEF: + return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END; + case PM_CONTEXT_RESCUE_ELSE: + case PM_CONTEXT_RESCUE_ELSE_DEF: + return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_END; + case PM_CONTEXT_LAMBDA_BRACES: + return token->type == PM_TOKEN_BRACE_RIGHT; + case PM_CONTEXT_PREDICATE: + return token->type == PM_TOKEN_KEYWORD_THEN || token->type == PM_TOKEN_NEWLINE || token->type == PM_TOKEN_SEMICOLON; + } + + return false; +} + +static bool +context_recoverable(pm_parser_t *parser, pm_token_t *token) { + pm_context_node_t *context_node = parser->current_context; + + while (context_node != NULL) { + if (context_terminator(context_node->context, token)) return true; + context_node = context_node->prev; + } + + return false; +} + +static bool +context_push(pm_parser_t *parser, pm_context_t context) { + pm_context_node_t *context_node = (pm_context_node_t *) malloc(sizeof(pm_context_node_t)); + if (context_node == NULL) return false; + + *context_node = (pm_context_node_t) { .context = context, .prev = NULL }; + + if (parser->current_context == NULL) { + parser->current_context = context_node; + } else { + context_node->prev = parser->current_context; + parser->current_context = context_node; + } + + return true; +} + +static void +context_pop(pm_parser_t *parser) { + pm_context_node_t *prev = parser->current_context->prev; + free(parser->current_context); + parser->current_context = prev; +} + +static bool +context_p(pm_parser_t *parser, pm_context_t context) { + pm_context_node_t *context_node = parser->current_context; + + while (context_node != NULL) { + if (context_node->context == context) return true; + context_node = context_node->prev; + } + + return false; +} + +static bool +context_def_p(pm_parser_t *parser) { + pm_context_node_t *context_node = parser->current_context; + + while (context_node != NULL) { + switch (context_node->context) { + case PM_CONTEXT_DEF: + case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_ENSURE_DEF: + case PM_CONTEXT_RESCUE_DEF: + case PM_CONTEXT_RESCUE_ELSE_DEF: + return true; + case PM_CONTEXT_CLASS: + case PM_CONTEXT_MODULE: + case PM_CONTEXT_SCLASS: + return false; + default: + context_node = context_node->prev; + } + } + + return false; +} + +/******************************************************************************/ +/* Specific token lexers */ +/******************************************************************************/ + +static void +pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *invalid) { + if (invalid != NULL) { + pm_parser_err(parser, invalid, invalid + 1, PM_ERR_INVALID_NUMBER_UNDERSCORE); + } +} + +static size_t +pm_strspn_binary_number_validate(pm_parser_t *parser, const uint8_t *string) { + const uint8_t *invalid = NULL; + size_t length = pm_strspn_binary_number(string, parser->end - string, &invalid); + pm_strspn_number_validate(parser, invalid); + return length; +} + +static size_t +pm_strspn_octal_number_validate(pm_parser_t *parser, const uint8_t *string) { + const uint8_t *invalid = NULL; + size_t length = pm_strspn_octal_number(string, parser->end - string, &invalid); + pm_strspn_number_validate(parser, invalid); + return length; +} + +static size_t +pm_strspn_decimal_number_validate(pm_parser_t *parser, const uint8_t *string) { + const uint8_t *invalid = NULL; + size_t length = pm_strspn_decimal_number(string, parser->end - string, &invalid); + pm_strspn_number_validate(parser, invalid); + return length; +} + +static size_t +pm_strspn_hexadecimal_number_validate(pm_parser_t *parser, const uint8_t *string) { + const uint8_t *invalid = NULL; + size_t length = pm_strspn_hexadecimal_number(string, parser->end - string, &invalid); + pm_strspn_number_validate(parser, invalid); + return length; +} + +static pm_token_type_t +lex_optional_float_suffix(pm_parser_t *parser, bool* seen_e) { + pm_token_type_t type = PM_TOKEN_INTEGER; + + // Here we're going to attempt to parse the optional decimal portion of a + // float. If it's not there, then it's okay and we'll just continue on. + if (peek(parser) == '.') { + if (pm_char_is_decimal_digit(peek_offset(parser, 1))) { + parser->current.end += 2; + parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end); + type = PM_TOKEN_FLOAT; + } else { + // If we had a . and then something else, then it's not a float suffix on + // a number it's a method call or something else. + return type; + } + } + + // Here we're going to attempt to parse the optional exponent portion of a + // float. If it's not there, it's okay and we'll just continue on. + if (match(parser, 'e') || match(parser, 'E')) { + (void) (match(parser, '+') || match(parser, '-')); + *seen_e = true; + + if (pm_char_is_decimal_digit(peek(parser))) { + parser->current.end++; + parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end); + type = PM_TOKEN_FLOAT; + } else { + pm_parser_err_current(parser, PM_ERR_INVALID_FLOAT_EXPONENT); + type = PM_TOKEN_FLOAT; + } + } + + return type; +} + +static pm_token_type_t +lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { + pm_token_type_t type = PM_TOKEN_INTEGER; + *seen_e = false; + + if (peek_offset(parser, -1) == '0') { + switch (*parser->current.end) { + // 0d1111 is a decimal number + case 'd': + case 'D': + parser->current.end++; + if (pm_char_is_decimal_digit(peek(parser))) { + parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end); + } else { + pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_DECIMAL); + } + + break; + + // 0b1111 is a binary number + case 'b': + case 'B': + parser->current.end++; + if (pm_char_is_binary_digit(peek(parser))) { + parser->current.end += pm_strspn_binary_number_validate(parser, parser->current.end); + } else { + pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY); + } + + parser->integer_base = PM_INTEGER_BASE_FLAGS_BINARY; + break; + + // 0o1111 is an octal number + case 'o': + case 'O': + parser->current.end++; + if (pm_char_is_octal_digit(peek(parser))) { + parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end); + } else { + pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL); + } + + parser->integer_base = PM_INTEGER_BASE_FLAGS_OCTAL; + break; + + // 01111 is an octal number + case '_': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end); + parser->integer_base = PM_INTEGER_BASE_FLAGS_OCTAL; + break; + + // 0x1111 is a hexadecimal number + case 'x': + case 'X': + parser->current.end++; + if (pm_char_is_hexadecimal_digit(peek(parser))) { + parser->current.end += pm_strspn_hexadecimal_number_validate(parser, parser->current.end); + } else { + pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL); + } + + parser->integer_base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL; + break; + + // 0.xxx is a float + case '.': { + type = lex_optional_float_suffix(parser, seen_e); + break; + } + + // 0exxx is a float + case 'e': + case 'E': { + type = lex_optional_float_suffix(parser, seen_e); + break; + } + } + } else { + // If it didn't start with a 0, then we'll lex as far as we can into a + // decimal number. + parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end); + + // Afterward, we'll lex as far as we can into an optional float suffix. + type = lex_optional_float_suffix(parser, seen_e); + } + + return type; +} + +static pm_token_type_t +lex_numeric(pm_parser_t *parser) { + pm_token_type_t type = PM_TOKEN_INTEGER; + parser->integer_base = PM_INTEGER_BASE_FLAGS_DECIMAL; + + if (parser->current.end < parser->end) { + bool seen_e = false; + type = lex_numeric_prefix(parser, &seen_e); + + const uint8_t *end = parser->current.end; + pm_token_type_t suffix_type = type; + + if (type == PM_TOKEN_INTEGER) { + if (match(parser, 'r')) { + suffix_type = PM_TOKEN_INTEGER_RATIONAL; + + if (match(parser, 'i')) { + suffix_type = PM_TOKEN_INTEGER_RATIONAL_IMAGINARY; + } + } else if (match(parser, 'i')) { + suffix_type = PM_TOKEN_INTEGER_IMAGINARY; + } + } else { + if (!seen_e && match(parser, 'r')) { + suffix_type = PM_TOKEN_FLOAT_RATIONAL; + + if (match(parser, 'i')) { + suffix_type = PM_TOKEN_FLOAT_RATIONAL_IMAGINARY; + } + } else if (match(parser, 'i')) { + suffix_type = PM_TOKEN_FLOAT_IMAGINARY; + } + } + + const uint8_t b = peek(parser); + if (b != '\0' && (b >= 0x80 || ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) || b == '_')) { + parser->current.end = end; + } else { + type = suffix_type; + } + } + + return type; +} + +static pm_token_type_t +lex_global_variable(pm_parser_t *parser) { + if (parser->current.end >= parser->end) { + pm_parser_err_current(parser, PM_ERR_INVALID_VARIABLE_GLOBAL); + return PM_TOKEN_GLOBAL_VARIABLE; + } + + switch (*parser->current.end) { + case '~': // $~: match-data + case '*': // $*: argv + case '$': // $$: pid + case '?': // $?: last status + case '!': // $!: error string + case '@': // $@: error position + case '/': // $/: input record separator + case '\\': // $\: output record separator + case ';': // $;: field separator + case ',': // $,: output field separator + case '.': // $.: last read line number + case '=': // $=: ignorecase + case ':': // $:: load path + case '<': // $<: reading filename + case '>': // $>: default output handle + case '\"': // $": already loaded files + parser->current.end++; + return PM_TOKEN_GLOBAL_VARIABLE; + + case '&': // $&: last match + case '`': // $`: string before last match + case '\'': // $': string after last match + case '+': // $+: string matches last paren. + parser->current.end++; + return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_BACK_REFERENCE; + + case '0': { + parser->current.end++; + size_t width; + + if (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) { + do { + parser->current.end += width; + } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0); + + // $0 isn't allowed to be followed by anything. + pm_parser_err_current(parser, PM_ERR_INVALID_VARIABLE_GLOBAL); + } + + return PM_TOKEN_GLOBAL_VARIABLE; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + parser->current.end += pm_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end); + return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_NUMBERED_REFERENCE; + + case '-': + parser->current.end++; + /* fallthrough */ + default: { + size_t width; + + if ((width = char_is_identifier(parser, parser->current.end)) > 0) { + do { + parser->current.end += width; + } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0); + } else { + // If we get here, then we have a $ followed by something that isn't + // recognized as a global variable. + pm_parser_err_current(parser, PM_ERR_INVALID_VARIABLE_GLOBAL); + } + + return PM_TOKEN_GLOBAL_VARIABLE; + } + } +} + +/** + * This function checks if the current token matches a keyword. If it does, it + * returns the token type. Otherwise, it returns PM_TOKEN_EOF. The arguments are as follows: + * + * * `parser` - the parser object + * * `current_start` - pointer to the start of the current token + * * `value` - the literal string that we're checking for + * * `vlen` - the length of the token + * * `state` - the state that we should transition to if the token matches + * * `type` - the expected token type + * * `modifier_type` - the expected modifier token type + */ +static inline pm_token_type_t +lex_keyword(pm_parser_t *parser, const uint8_t *current_start, const char *value, size_t vlen, pm_lex_state_t state, pm_token_type_t type, pm_token_type_t modifier_type) { + if (memcmp(current_start, value, vlen) == 0) { + pm_lex_state_t last_state = parser->lex_state; + + if (parser->lex_state & PM_LEX_STATE_FNAME) { + lex_state_set(parser, PM_LEX_STATE_ENDFN); + } else { + lex_state_set(parser, state); + if (state == PM_LEX_STATE_BEG) { + parser->command_start = true; + } + + if ((modifier_type != PM_TOKEN_EOF) && !(last_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) { + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + return modifier_type; + } + } + + return type; + } + + return PM_TOKEN_EOF; +} + +static pm_token_type_t +lex_identifier(pm_parser_t *parser, bool previous_command_start) { + // Lex as far as we can into the current identifier. + size_t width; + const uint8_t *end = parser->end; + const uint8_t *current_start = parser->current.start; + const uint8_t *current_end = parser->current.end; + bool encoding_changed = parser->encoding_changed; + + if (encoding_changed) { + while (current_end < end && (width = char_is_identifier(parser, current_end)) > 0) { + current_end += width; + } + } else { + while (current_end < end && (width = char_is_identifier_utf8(current_end, end)) > 0) { + current_end += width; + } + } + parser->current.end = current_end; + + // Now cache the length of the identifier so that we can quickly compare it + // against known keywords. + width = (size_t) (current_end - current_start); + + if (current_end < end) { + if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) { + // First we'll attempt to extend the identifier by a ! or ?. Then we'll + // check if we're returning the defined? keyword or just an identifier. + width++; + + if ( + ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) && + (peek(parser) == ':') && (peek_offset(parser, 1) != ':') + ) { + // If we're in a position where we can accept a : at the end of an + // identifier, then we'll optionally accept it. + lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED); + (void) match(parser, ':'); + return PM_TOKEN_LABEL; + } + + if (parser->lex_state != PM_LEX_STATE_DOT) { + if (width == 8 && (lex_keyword(parser, current_start, "defined?", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF) != PM_TOKEN_EOF)) { + return PM_TOKEN_KEYWORD_DEFINED; + } + } + + return PM_TOKEN_METHOD_NAME; + } + + if (lex_state_p(parser, PM_LEX_STATE_FNAME) && peek_offset(parser, 1) != '~' && peek_offset(parser, 1) != '>' && (peek_offset(parser, 1) != '=' || peek_offset(parser, 2) == '>') && match(parser, '=')) { + // If we're in a position where we can accept a = at the end of an + // identifier, then we'll optionally accept it. + return PM_TOKEN_IDENTIFIER; + } + + if ( + ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) && + peek(parser) == ':' && peek_offset(parser, 1) != ':' + ) { + // If we're in a position where we can accept a : at the end of an + // identifier, then we'll optionally accept it. + lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED); + (void) match(parser, ':'); + return PM_TOKEN_LABEL; + } + } + + if (parser->lex_state != PM_LEX_STATE_DOT) { + pm_token_type_t type; + switch (width) { + case 2: + if (lex_keyword(parser, current_start, "do", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF) != PM_TOKEN_EOF) { + if (pm_do_loop_stack_p(parser)) { + return PM_TOKEN_KEYWORD_DO_LOOP; + } + return PM_TOKEN_KEYWORD_DO; + } + + if ((type = lex_keyword(parser, current_start, "if", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "in", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "or", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_OR, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + break; + case 3: + if ((type = lex_keyword(parser, current_start, "and", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_AND, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "def", width, PM_LEX_STATE_FNAME, PM_TOKEN_KEYWORD_DEF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "end", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "END", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END_UPCASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "for", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_FOR, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "nil", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_NIL, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "not", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_NOT, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + break; + case 4: + if ((type = lex_keyword(parser, current_start, "case", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_CASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "else", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "next", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_NEXT, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "redo", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_REDO, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "self", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_SELF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "then", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "true", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_TRUE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "when", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + break; + case 5: + if ((type = lex_keyword(parser, current_start, "alias", width, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_ALIAS, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "begin", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_BEGIN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "BEGIN", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_BEGIN_UPCASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "break", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_BREAK, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "class", width, PM_LEX_STATE_CLASS, PM_TOKEN_KEYWORD_CLASS, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "elsif", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "false", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_FALSE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "retry", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_RETRY, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "super", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_SUPER, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "undef", width, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_UNDEF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "until", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNTIL, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "while", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHILE, PM_TOKEN_KEYWORD_WHILE_MODIFIER)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "yield", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_YIELD, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + break; + case 6: + if ((type = lex_keyword(parser, current_start, "ensure", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "module", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_MODULE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "rescue", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "return", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RETURN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "unless", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNLESS, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) != PM_TOKEN_EOF) return type; + break; + case 8: + if ((type = lex_keyword(parser, current_start, "__LINE__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___LINE__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, current_start, "__FILE__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___FILE__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + break; + case 12: + if ((type = lex_keyword(parser, current_start, "__ENCODING__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___ENCODING__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type; + break; + } + } + + if (encoding_changed) { + return parser->encoding.isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER; + } + return pm_encoding_utf_8_isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER; +} + +/** + * Returns true if the current token that the parser is considering is at the + * beginning of a line or the beginning of the source. + */ +static bool +current_token_starts_line(pm_parser_t *parser) { + return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n'); +} + +/** + * When we hit a # while lexing something like a string, we need to potentially + * handle interpolation. This function performs that check. It returns a token + * type representing what it found. Those cases are: + * + * * PM_TOKEN_NOT_PROVIDED - No interpolation was found at this point. The + * caller should keep lexing. + * * PM_TOKEN_STRING_CONTENT - No interpolation was found at this point. The + * caller should return this token type. + * * PM_TOKEN_EMBEXPR_BEGIN - An embedded expression was found. The caller + * should return this token type. + * * PM_TOKEN_EMBVAR - An embedded variable was found. The caller should return + * this token type. + */ +static pm_token_type_t +lex_interpolation(pm_parser_t *parser, const uint8_t *pound) { + // If there is no content following this #, then we're at the end of + // the string and we can safely return string content. + if (pound + 1 >= parser->end) { + parser->current.end = pound + 1; + return PM_TOKEN_STRING_CONTENT; + } + + // Now we'll check against the character the follows the #. If it constitutes + // valid interplation, we'll handle that, otherwise we'll return + // PM_TOKEN_NOT_PROVIDED. + switch (pound[1]) { + case '@': { + // In this case we may have hit an embedded instance or class variable. + if (pound + 2 >= parser->end) { + parser->current.end = pound + 1; + return PM_TOKEN_STRING_CONTENT; + } + + // If we're looking at a @ and there's another @, then we'll skip past the + // second @. + const uint8_t *variable = pound + 2; + if (*variable == '@' && pound + 3 < parser->end) variable++; + + if (char_is_identifier_start(parser, variable)) { + // At this point we're sure that we've either hit an embedded instance + // or class variable. In this case we'll first need to check if we've + // already consumed content. + if (pound > parser->current.start) { + parser->current.end = pound; + return PM_TOKEN_STRING_CONTENT; + } + + // Otherwise we need to return the embedded variable token + // and then switch to the embedded variable lex mode. + lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR }); + parser->current.end = pound + 1; + return PM_TOKEN_EMBVAR; + } + + // If we didn't get an valid interpolation, then this is just regular + // string content. This is like if we get "#@-". In this case the caller + // should keep lexing. + parser->current.end = pound + 1; + return PM_TOKEN_NOT_PROVIDED; + } + case '$': + // In this case we may have hit an embedded global variable. If there's + // not enough room, then we'll just return string content. + if (pound + 2 >= parser->end) { + parser->current.end = pound + 1; + return PM_TOKEN_STRING_CONTENT; + } + + // This is the character that we're going to check to see if it is the + // start of an identifier that would indicate that this is a global + // variable. + const uint8_t *check = pound + 2; + + if (pound[2] == '-') { + if (pound + 3 >= parser->end) { + parser->current.end = pound + 2; + return PM_TOKEN_STRING_CONTENT; + } + + check++; + } + + // If the character that we're going to check is the start of an + // identifier, or we don't have a - and the character is a decimal number + // or a global name punctuation character, then we've hit an embedded + // global variable. + if ( + char_is_identifier_start(parser, check) || + (pound[2] != '-' && (pm_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2]))) + ) { + // In this case we've hit an embedded global variable. First check to + // see if we've already consumed content. If we have, then we need to + // return that content as string content first. + if (pound > parser->current.start) { + parser->current.end = pound; + return PM_TOKEN_STRING_CONTENT; + } + + // Otherwise, we need to return the embedded variable token and switch + // to the embedded variable lex mode. + lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR }); + parser->current.end = pound + 1; + return PM_TOKEN_EMBVAR; + } + + // In this case we've hit a #$ that does not indicate a global variable. + // In this case we'll continue lexing past it. + parser->current.end = pound + 1; + return PM_TOKEN_NOT_PROVIDED; + case '{': + // In this case it's the start of an embedded expression. If we have + // already consumed content, then we need to return that content as string + // content first. + if (pound > parser->current.start) { + parser->current.end = pound; + return PM_TOKEN_STRING_CONTENT; + } + + parser->enclosure_nesting++; + + // Otherwise we'll skip past the #{ and begin lexing the embedded + // expression. + lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBEXPR }); + parser->current.end = pound + 2; + parser->command_start = true; + pm_do_loop_stack_push(parser, false); + return PM_TOKEN_EMBEXPR_BEGIN; + default: + // In this case we've hit a # that doesn't constitute interpolation. We'll + // mark that by returning the not provided token type. This tells the + // consumer to keep lexing forward. + parser->current.end = pound + 1; + return PM_TOKEN_NOT_PROVIDED; + } +} + +static const uint8_t PM_ESCAPE_FLAG_NONE = 0x0; +static const uint8_t PM_ESCAPE_FLAG_CONTROL = 0x1; +static const uint8_t PM_ESCAPE_FLAG_META = 0x2; +static const uint8_t PM_ESCAPE_FLAG_SINGLE = 0x4; +static const uint8_t PM_ESCAPE_FLAG_REGEXP = 0x8; + +/** + * This is a lookup table for whether or not an ASCII character is printable. + */ +static const bool ascii_printable_chars[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 +}; + +static inline bool +char_is_ascii_printable(const uint8_t b) { + return (b < 0x80) && ascii_printable_chars[b]; +} + +/** + * Return the value that a hexadecimal digit character represents. For example, + * transform 'a' into 10, 'b' into 11, etc. + */ +static inline uint8_t +escape_hexadecimal_digit(const uint8_t value) { + return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9); +} + +/** + * Scan the 4 digits of a Unicode escape into the value. Returns the number of + * digits scanned. This function assumes that the characters have already been + * validated. + */ +static inline uint32_t +escape_unicode(const uint8_t *string, size_t length) { + uint32_t value = 0; + for (size_t index = 0; index < length; index++) { + if (index != 0) value <<= 4; + value |= escape_hexadecimal_digit(string[index]); + } + return value; +} + +/** + * Escape a single character value based on the given flags. + */ +static inline uint8_t +escape_byte(uint8_t value, const uint8_t flags) { + if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x1f; + if (flags & PM_ESCAPE_FLAG_META) value |= 0x80; + return value; +} + +/** + * Write a unicode codepoint to the given buffer. + */ +static inline void +escape_write_unicode(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t *start, const uint8_t *end, uint32_t value) { + if (value <= 0x7F) { // 0xxxxxxx + pm_buffer_append_byte(buffer, (uint8_t) value); + } else if (value <= 0x7FF) { // 110xxxxx 10xxxxxx + pm_buffer_append_byte(buffer, (uint8_t) (0xC0 | (value >> 6))); + pm_buffer_append_byte(buffer, (uint8_t) (0x80 | (value & 0x3F))); + } else if (value <= 0xFFFF) { // 1110xxxx 10xxxxxx 10xxxxxx + pm_buffer_append_byte(buffer, (uint8_t) (0xE0 | (value >> 12))); + pm_buffer_append_byte(buffer, (uint8_t) (0x80 | ((value >> 6) & 0x3F))); + pm_buffer_append_byte(buffer, (uint8_t) (0x80 | (value & 0x3F))); + } else if (value <= 0x10FFFF) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + pm_buffer_append_byte(buffer, (uint8_t) (0xF0 | (value >> 18))); + pm_buffer_append_byte(buffer, (uint8_t) (0x80 | ((value >> 12) & 0x3F))); + pm_buffer_append_byte(buffer, (uint8_t) (0x80 | ((value >> 6) & 0x3F))); + pm_buffer_append_byte(buffer, (uint8_t) (0x80 | (value & 0x3F))); + } else { + pm_parser_err(parser, start, end, PM_ERR_ESCAPE_INVALID_UNICODE); + pm_buffer_append_byte(buffer, 0xEF); + pm_buffer_append_byte(buffer, 0xBF); + pm_buffer_append_byte(buffer, 0xBD); + } +} + +/** + * The regular expression engine doesn't support the same escape sequences as + * Ruby does. So first we have to read the escape sequence, and then we have to + * format it like the regular expression engine expects it. For example, in Ruby + * if we have: + * + * /\M-\C-?/ + * + * then the first byte is actually 255, so we have to rewrite this as: + * + * /\xFF/ + * + * Note that in this case there is a literal \ byte in the regular expression + * source so that the regular expression engine will perform its own unescaping. + */ +static inline void +escape_write_byte(pm_buffer_t *buffer, uint8_t flags, uint8_t byte) { + if (flags & PM_ESCAPE_FLAG_REGEXP) { + pm_buffer_append_bytes(buffer, (const uint8_t *) "\\x", 2); + + uint8_t byte1 = (uint8_t) ((byte >> 4) & 0xF); + uint8_t byte2 = (uint8_t) (byte & 0xF); + + if (byte1 >= 0xA) { + pm_buffer_append_byte(buffer, (uint8_t) ((byte1 - 0xA) + 'A')); + } else { + pm_buffer_append_byte(buffer, (uint8_t) (byte1 + '0')); + } + + if (byte2 >= 0xA) { + pm_buffer_append_byte(buffer, (uint8_t) (byte2 - 0xA + 'A')); + } else { + pm_buffer_append_byte(buffer, (uint8_t) (byte2 + '0')); + } + } else { + pm_buffer_append_byte(buffer, byte); + } +} + +/** + * Read the value of an escape into the buffer. + */ +static void +escape_read(pm_parser_t *parser, pm_buffer_t *buffer, uint8_t flags) { + switch (peek(parser)) { + case '\\': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\\'); + return; + } + case '\'': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\''); + return; + } + case 'a': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\a'); + return; + } + case 'b': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\b'); + return; + } + case 'e': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\033'); + return; + } + case 'f': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\f'); + return; + } + case 'n': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\n'); + return; + } + case 'r': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\r'); + return; + } + case 's': { + parser->current.end++; + pm_buffer_append_byte(buffer, ' '); + return; + } + case 't': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\t'); + return; + } + case 'v': { + parser->current.end++; + pm_buffer_append_byte(buffer, '\v'); + return; + } + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { + uint8_t value = (uint8_t) (*parser->current.end - '0'); + parser->current.end++; + + if (pm_char_is_octal_digit(peek(parser))) { + value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0')); + parser->current.end++; + + if (pm_char_is_octal_digit(peek(parser))) { + value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0')); + parser->current.end++; + } + } + + pm_buffer_append_byte(buffer, value); + return; + } + case 'x': { + const uint8_t *start = parser->current.end - 1; + + parser->current.end++; + uint8_t byte = peek(parser); + + if (pm_char_is_hexadecimal_digit(byte)) { + uint8_t value = escape_hexadecimal_digit(byte); + parser->current.end++; + + byte = peek(parser); + if (pm_char_is_hexadecimal_digit(byte)) { + value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(byte)); + parser->current.end++; + } + + if (flags & PM_ESCAPE_FLAG_REGEXP) { + pm_buffer_append_bytes(buffer, start, (size_t) (parser->current.end - start)); + } else { + pm_buffer_append_byte(buffer, value); + } + } else { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL); + } + + return; + } + case 'u': { + const uint8_t *start = parser->current.end - 1; + parser->current.end++; + + if ( + (parser->current.end + 4 <= parser->end) && + pm_char_is_hexadecimal_digit(parser->current.end[0]) && + pm_char_is_hexadecimal_digit(parser->current.end[1]) && + pm_char_is_hexadecimal_digit(parser->current.end[2]) && + pm_char_is_hexadecimal_digit(parser->current.end[3]) + ) { + uint32_t value = escape_unicode(parser->current.end, 4); + + if (flags & PM_ESCAPE_FLAG_REGEXP) { + pm_buffer_append_bytes(buffer, start, (size_t) (parser->current.end + 4 - start)); + } else { + escape_write_unicode(parser, buffer, start, parser->current.end + 4, value); + } + + parser->current.end += 4; + } else if (peek(parser) == '{') { + const uint8_t *unicode_codepoints_start = parser->current.end - 2; + + parser->current.end++; + parser->current.end += pm_strspn_whitespace(parser->current.end, parser->end - parser->current.end); + + const uint8_t *extra_codepoints_start = NULL; + int codepoints_count = 0; + + while ((parser->current.end < parser->end) && (*parser->current.end != '}')) { + const uint8_t *unicode_start = parser->current.end; + size_t hexadecimal_length = pm_strspn_hexadecimal_digit(parser->current.end, parser->end - parser->current.end); + + if (hexadecimal_length > 6) { + // \u{nnnn} character literal allows only 1-6 hexadecimal digits + pm_parser_err(parser, unicode_start, unicode_start + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE_LONG); + } else if (hexadecimal_length == 0) { + // there are not hexadecimal characters + pm_parser_err(parser, unicode_start, unicode_start + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE); + return; + } + + parser->current.end += hexadecimal_length; + codepoints_count++; + if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count == 2) { + extra_codepoints_start = unicode_start; + } + + if (!(flags & PM_ESCAPE_FLAG_REGEXP)) { + uint32_t value = escape_unicode(unicode_start, hexadecimal_length); + escape_write_unicode(parser, buffer, unicode_start, parser->current.end, value); + } + + parser->current.end += pm_strspn_whitespace(parser->current.end, parser->end - parser->current.end); + } + + // ?\u{nnnn} character literal should contain only one codepoint and cannot be like ?\u{nnnn mmmm} + if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count > 1) { + pm_parser_err(parser, extra_codepoints_start, parser->current.end - 1, PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL); + } + + if (peek(parser) == '}') { + parser->current.end++; + } else { + pm_parser_err(parser, unicode_codepoints_start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_TERM); + } + + if (flags & PM_ESCAPE_FLAG_REGEXP) { + pm_buffer_append_bytes(buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start)); + } + } else { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE); + } + + return; + } + case 'c': { + parser->current.end++; + if (parser->current.end == parser->end) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + return; + } + + uint8_t peeked = peek(parser); + switch (peeked) { + case '?': { + parser->current.end++; + escape_write_byte(buffer, flags, escape_byte(0x7f, flags)); + return; + } + case '\\': + if (flags & PM_ESCAPE_FLAG_CONTROL) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT); + return; + } + parser->current.end++; + escape_read(parser, buffer, flags | PM_ESCAPE_FLAG_CONTROL); + return; + default: { + if (!char_is_ascii_printable(peeked)) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + return; + } + + parser->current.end++; + escape_write_byte(buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL)); + return; + } + } + } + case 'C': { + parser->current.end++; + if (peek(parser) != '-') { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + return; + } + + parser->current.end++; + if (parser->current.end == parser->end) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + return; + } + + uint8_t peeked = peek(parser); + switch (peeked) { + case '?': { + parser->current.end++; + escape_write_byte(buffer, flags, escape_byte(0x7f, flags)); + return; + } + case '\\': + if (flags & PM_ESCAPE_FLAG_CONTROL) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT); + return; + } + parser->current.end++; + escape_read(parser, buffer, flags | PM_ESCAPE_FLAG_CONTROL); + return; + default: { + if (!char_is_ascii_printable(peeked)) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + return; + } + + parser->current.end++; + escape_write_byte(buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL)); + return; + } + } + } + case 'M': { + parser->current.end++; + if (peek(parser) != '-') { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META); + return; + } + + parser->current.end++; + if (parser->current.end == parser->end) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META); + return; + } + + uint8_t peeked = peek(parser); + if (peeked == '\\') { + if (flags & PM_ESCAPE_FLAG_META) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT); + return; + } + parser->current.end++; + escape_read(parser, buffer, flags | PM_ESCAPE_FLAG_META); + return; + } + + if (!char_is_ascii_printable(peeked)) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META); + return; + } + + parser->current.end++; + escape_write_byte(buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META)); + return; + } + case '\r': { + if (peek_offset(parser, 1) == '\n') { + parser->current.end += 2; + pm_buffer_append_byte(buffer, '\n'); + return; + } + } + /* fallthrough */ + default: { + if (parser->current.end < parser->end) { + pm_buffer_append_byte(buffer, *parser->current.end++); + } + return; + } + } +} + +/** + * This function is responsible for lexing either a character literal or the ? + * operator. The supported character literals are described below. + * + * \\a bell, ASCII 07h (BEL) + * \\b backspace, ASCII 08h (BS) + * \t horizontal tab, ASCII 09h (TAB) + * \\n newline (line feed), ASCII 0Ah (LF) + * \v vertical tab, ASCII 0Bh (VT) + * \f form feed, ASCII 0Ch (FF) + * \r carriage return, ASCII 0Dh (CR) + * \\e escape, ASCII 1Bh (ESC) + * \s space, ASCII 20h (SPC) + * \\ backslash + * \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7]) + * \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F]) + * \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F]) + * \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F]) + * \cx or \C-x control character, where x is an ASCII printable character + * \M-x meta character, where x is an ASCII printable character + * \M-\C-x meta control character, where x is an ASCII printable character + * \M-\cx same as above + * \\c\M-x same as above + * \\c? or \C-? delete, ASCII 7Fh (DEL) + */ +static pm_token_type_t +lex_question_mark(pm_parser_t *parser) { + if (lex_state_end_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_BEG); + return PM_TOKEN_QUESTION_MARK; + } + + if (parser->current.end >= parser->end) { + pm_parser_err_current(parser, PM_ERR_INCOMPLETE_QUESTION_MARK); + pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end); + return PM_TOKEN_CHARACTER_LITERAL; + } + + if (pm_char_is_whitespace(*parser->current.end)) { + lex_state_set(parser, PM_LEX_STATE_BEG); + return PM_TOKEN_QUESTION_MARK; + } + + lex_state_set(parser, PM_LEX_STATE_BEG); + + if (match(parser, '\\')) { + lex_state_set(parser, PM_LEX_STATE_END); + + pm_buffer_t buffer; + pm_buffer_init_capacity(&buffer, 3); + + escape_read(parser, &buffer, PM_ESCAPE_FLAG_SINGLE); + pm_string_owned_init(&parser->current_string, (uint8_t *) buffer.value, buffer.length); + + return PM_TOKEN_CHARACTER_LITERAL; + } else { + size_t encoding_width = parser->encoding.char_width(parser->current.end, parser->end - parser->current.end); + + // Ternary operators can have a ? immediately followed by an identifier which starts with + // an underscore. We check for this case + if ( + !(parser->encoding.alnum_char(parser->current.end, parser->end - parser->current.end) || + peek(parser) == '_') || + ( + (parser->current.end + encoding_width >= parser->end) || + !char_is_identifier(parser, parser->current.end + encoding_width) + ) + ) { + lex_state_set(parser, PM_LEX_STATE_END); + parser->current.end += encoding_width; + pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end); + return PM_TOKEN_CHARACTER_LITERAL; + } + } + + return PM_TOKEN_QUESTION_MARK; +} + +/** + * Lex a variable that starts with an @ sign (either an instance or class + * variable). + */ +static pm_token_type_t +lex_at_variable(pm_parser_t *parser) { + pm_token_type_t type = match(parser, '@') ? PM_TOKEN_CLASS_VARIABLE : PM_TOKEN_INSTANCE_VARIABLE; + size_t width; + + if (parser->current.end < parser->end && (width = char_is_identifier_start(parser, parser->current.end)) > 0) { + parser->current.end += width; + + while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) { + parser->current.end += width; + } + } else if (type == PM_TOKEN_CLASS_VARIABLE) { + pm_parser_err_current(parser, PM_ERR_INCOMPLETE_VARIABLE_CLASS); + } else { + pm_parser_err_current(parser, PM_ERR_INCOMPLETE_VARIABLE_INSTANCE); + } + + // If we're lexing an embedded variable, then we need to pop back into the + // parent lex context. + if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) { + lex_mode_pop(parser); + } + + return type; +} + +/** + * Optionally call out to the lex callback if one is provided. + */ +static inline void +parser_lex_callback(pm_parser_t *parser) { + if (parser->lex_callback) { + parser->lex_callback->callback(parser->lex_callback->data, parser, &parser->current); + } +} + +/** + * Return a new comment node of the specified type. + */ +static inline pm_comment_t * +parser_comment(pm_parser_t *parser, pm_comment_type_t type) { + pm_comment_t *comment = (pm_comment_t *) calloc(sizeof(pm_comment_t), 1); + if (comment == NULL) return NULL; + + *comment = (pm_comment_t) { + .type = type, + .start = parser->current.start, + .end = parser->current.end + }; + + return comment; +} + +/** + * Lex out embedded documentation, and return when we have either hit the end of + * the file or the end of the embedded documentation. This calls the callback + * manually because only the lexer should see these tokens, not the parser. + */ +static pm_token_type_t +lex_embdoc(pm_parser_t *parser) { + // First, lex out the EMBDOC_BEGIN token. + const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end); + + if (newline == NULL) { + parser->current.end = parser->end; + } else { + pm_newline_list_append(&parser->newline_list, newline); + parser->current.end = newline + 1; + } + + parser->current.type = PM_TOKEN_EMBDOC_BEGIN; + parser_lex_callback(parser); + + // Now, create a comment that is going to be attached to the parser. + pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC); + if (comment == NULL) return PM_TOKEN_EOF; + + // Now, loop until we find the end of the embedded documentation or the end of + // the file. + while (parser->current.end + 4 <= parser->end) { + parser->current.start = parser->current.end; + + // If we've hit the end of the embedded documentation then we'll return that + // token here. + if (memcmp(parser->current.end, "=end", 4) == 0 && + (parser->current.end + 4 == parser->end || pm_char_is_whitespace(parser->current.end[4]))) { + const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end); + + if (newline == NULL) { + parser->current.end = parser->end; + } else { + pm_newline_list_append(&parser->newline_list, newline); + parser->current.end = newline + 1; + } + + parser->current.type = PM_TOKEN_EMBDOC_END; + parser_lex_callback(parser); + + comment->end = parser->current.end; + pm_list_append(&parser->comment_list, (pm_list_node_t *) comment); + + return PM_TOKEN_EMBDOC_END; + } + + // Otherwise, we'll parse until the end of the line and return a line of + // embedded documentation. + const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end); + + if (newline == NULL) { + parser->current.end = parser->end; + } else { + pm_newline_list_append(&parser->newline_list, newline); + parser->current.end = newline + 1; + } + + parser->current.type = PM_TOKEN_EMBDOC_LINE; + parser_lex_callback(parser); + } + + pm_parser_err_current(parser, PM_ERR_EMBDOC_TERM); + + comment->end = parser->current.end; + pm_list_append(&parser->comment_list, (pm_list_node_t *) comment); + + return PM_TOKEN_EOF; +} + +/** + * Set the current type to an ignored newline and then call the lex callback. + * This happens in a couple places depending on whether or not we have already + * lexed a comment. + */ +static inline void +parser_lex_ignored_newline(pm_parser_t *parser) { + parser->current.type = PM_TOKEN_IGNORED_NEWLINE; + parser_lex_callback(parser); +} + +/** + * This function will be called when a newline is encountered. In some newlines, + * we need to check if there is a heredoc or heredocs that we have already lexed + * the body of that we need to now skip past. That will be indicated by the + * heredoc_end field on the parser. + * + * If it is set, then we need to skip past the heredoc body and then clear the + * heredoc_end field. + */ +static inline void +parser_flush_heredoc_end(pm_parser_t *parser) { + assert(parser->heredoc_end <= parser->end); + parser->next_start = parser->heredoc_end; + parser->heredoc_end = NULL; +} + +/** + * When we're lexing certain types (strings, symbols, lists, etc.) we have + * string content associated with the tokens. For example: + * + * "foo" + * + * In this case, the string content is foo. Since there is no escaping, there's + * no need to track additional information and the token can be returned as + * normal. However, if we have escape sequences: + * + * "foo\n" + * + * then the bytes in the string are "f", "o", "o", "\", "n", but we want to + * provide out consumers with the string content "f", "o", "o", "\n". In these + * cases, when we find the first escape sequence, we initialize a pm_buffer_t + * to keep track of the string content. Then in the parser, it will + * automatically attach the string content to the node that it belongs to. + */ +typedef struct { + /** + * The buffer that we're using to keep track of the string content. It will + * only be initialized if we receive an escape sequence. + */ + pm_buffer_t buffer; + + /** + * The cursor into the source string that points to how far we have + * currently copied into the buffer. + */ + const uint8_t *cursor; +} pm_token_buffer_t; + +/** + * Push the given byte into the token buffer. + */ +static inline void +pm_token_buffer_push(pm_token_buffer_t *token_buffer, uint8_t byte) { + pm_buffer_append_byte(&token_buffer->buffer, byte); +} + +/** + * When we're about to return from lexing the current token and we know for sure + * that we have found an escape sequence, this function is called to copy the + * + * contents of the token buffer into the current string on the parser so that it + * can be attached to the correct node. + */ +static inline void +pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) { + pm_string_owned_init(&parser->current_string, (uint8_t *) token_buffer->buffer.value, token_buffer->buffer.length); +} + +/** + * When we're about to return from lexing the current token, we need to flush + * all of the content that we have pushed into the buffer into the current + * string. If we haven't pushed anything into the buffer, this means that we + * never found an escape sequence, so we can directly reference the bounds of + * the current string. Either way, at the return of this function it is expected + * + * that parser->current_string is established in such a way that it can be + * attached to a node. + */ +static void +pm_token_buffer_flush(pm_parser_t *parser, pm_token_buffer_t *token_buffer) { + if (token_buffer->cursor == NULL) { + pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end); + } else { + pm_buffer_append_bytes(&token_buffer->buffer, token_buffer->cursor, (size_t) (parser->current.end - token_buffer->cursor)); + pm_token_buffer_copy(parser, token_buffer); + } +} + +/** + * When we've found an escape sequence, we need to copy everything up to this + * point into the buffer because we're about to provide a string that has + * different content than a direct slice of the source. + * + * + * It is expected that the parser's current token end will be pointing at one + * byte past the backslash that starts the escape sequence. + */ +static void +pm_token_buffer_escape(pm_parser_t *parser, pm_token_buffer_t *token_buffer) { + const uint8_t *start; + if (token_buffer->cursor == NULL) { + pm_buffer_init_capacity(&token_buffer->buffer, 16); + start = parser->current.start; + } else { + start = token_buffer->cursor; + } + + const uint8_t *end = parser->current.end - 1; + pm_buffer_append_bytes(&token_buffer->buffer, start, (size_t) (end - start)); +} + +/** + * Effectively the same thing as pm_strspn_inline_whitespace, but in the case of + * a tilde heredoc expands out tab characters to the nearest tab boundaries. + */ +static inline size_t +pm_heredoc_strspn_inline_whitespace(pm_parser_t *parser, const uint8_t **cursor, pm_heredoc_indent_t indent) { + size_t whitespace = 0; + + switch (indent) { + case PM_HEREDOC_INDENT_NONE: + // Do nothing, we can't match a terminator with + // indentation and there's no need to calculate common + // whitespace. + break; + case PM_HEREDOC_INDENT_DASH: + // Skip past inline whitespace. + *cursor += pm_strspn_inline_whitespace(*cursor, parser->end - *cursor); + break; + case PM_HEREDOC_INDENT_TILDE: + // Skip past inline whitespace and calculate common + // whitespace. + while (*cursor < parser->end && pm_char_is_inline_whitespace(**cursor)) { + if (**cursor == '\t') { + whitespace = (whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE; + } else { + whitespace++; + } + (*cursor)++; + } + + break; + } + + return whitespace; +} + +/** + * This is a convenience macro that will set the current token type, call the + * lex callback, and then return from the parser_lex function. + */ +#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return + +/** + * Called when the parser requires a new token. The parser maintains a moving + * window of two tokens at a time: parser.previous and parser.current. This + * function will move the current token into the previous token and then + * lex a new token into the current token. + */ +static void +parser_lex(pm_parser_t *parser) { + assert(parser->current.end <= parser->end); + parser->previous = parser->current; + + // This value mirrors cmd_state from CRuby. + bool previous_command_start = parser->command_start; + parser->command_start = false; + + // This is used to communicate to the newline lexing function that we've + // already seen a comment. + bool lexed_comment = false; + + // Here we cache the current value of the semantic token seen flag. This is + // used to reset it in case we find a token that shouldn't flip this flag. + unsigned int semantic_token_seen = parser->semantic_token_seen; + parser->semantic_token_seen = true; + + switch (parser->lex_modes.current->mode) { + case PM_LEX_DEFAULT: + case PM_LEX_EMBEXPR: + case PM_LEX_EMBVAR: + + // We have a specific named label here because we are going to jump back to + // this location in the event that we have lexed a token that should not be + // returned to the parser. This includes comments, ignored newlines, and + // invalid tokens of some form. + lex_next_token: { + // If we have the special next_start pointer set, then we're going to jump + // to that location and start lexing from there. + if (parser->next_start != NULL) { + parser->current.end = parser->next_start; + parser->next_start = NULL; + } + + // This value mirrors space_seen from CRuby. It tracks whether or not + // space has been eaten before the start of the next token. + bool space_seen = false; + + // First, we're going to skip past any whitespace at the front of the next + // token. + bool chomping = true; + while (parser->current.end < parser->end && chomping) { + switch (*parser->current.end) { + case ' ': + case '\t': + case '\f': + case '\v': + parser->current.end++; + space_seen = true; + break; + case '\r': + if (match_eol_offset(parser, 1)) { + chomping = false; + } else { + parser->current.end++; + space_seen = true; + } + break; + case '\\': { + size_t eol_length = match_eol_offset(parser, 1); + if (eol_length) { + if (parser->heredoc_end) { + parser->current.end = parser->heredoc_end; + parser->heredoc_end = NULL; + } else { + parser->current.end += eol_length + 1; + pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + space_seen = true; + } + } else if (pm_char_is_inline_whitespace(*parser->current.end)) { + parser->current.end += 2; + } else { + chomping = false; + } + + break; + } + default: + chomping = false; + break; + } + } + + // Next, we'll set to start of this token to be the current end. + parser->current.start = parser->current.end; + + // We'll check if we're at the end of the file. If we are, then we + // need to return the EOF token. + if (parser->current.end >= parser->end) { + LEX(PM_TOKEN_EOF); + } + + // Finally, we'll check the current character to determine the next + // token. + switch (*parser->current.end++) { + case '\0': // NUL or end of script + case '\004': // ^D + case '\032': // ^Z + parser->current.end--; + LEX(PM_TOKEN_EOF); + + case '#': { // comments + const uint8_t *ending = next_newline(parser->current.end, parser->end - parser->current.end); + parser->current.end = ending == NULL ? parser->end : ending; + + // If we found a comment while lexing, then we're going to + // add it to the list of comments in the file and keep + // lexing. + pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE); + pm_list_append(&parser->comment_list, (pm_list_node_t *) comment); + + if (ending) parser->current.end++; + parser->current.type = PM_TOKEN_COMMENT; + parser_lex_callback(parser); + + // Here, parse the comment to see if it's a magic comment + // and potentially change state on the parser. + if (!parser_lex_magic_comment(parser, semantic_token_seen) && (parser->current.start == parser->encoding_comment_start)) { + ptrdiff_t length = parser->current.end - parser->current.start; + + // If we didn't find a magic comment within the first + // pass and we're at the start of the file, then we need + // to do another pass to potentially find other patterns + // for encoding comments. + if (length >= 10) parser_lex_magic_comment_encoding(parser); + } + + lexed_comment = true; + } + /* fallthrough */ + case '\r': + case '\n': { + parser->semantic_token_seen = semantic_token_seen & 0x1; + size_t eol_length = match_eol_at(parser, parser->current.end - 1); + + if (eol_length) { + // The only way you can have carriage returns in this + // particular loop is if you have a carriage return + // followed by a newline. In that case we'll just skip + // over the carriage return and continue lexing, in + // order to make it so that the newline token + // encapsulates both the carriage return and the + // newline. Note that we need to check that we haven't + // already lexed a comment here because that falls + // through into here as well. + if (!lexed_comment) { + parser->current.end += eol_length - 1; // skip CR + } + + if (parser->heredoc_end == NULL) { + pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + } + } + + if (parser->heredoc_end) { + parser_flush_heredoc_end(parser); + } + + // If this is an ignored newline, then we can continue lexing after + // calling the callback with the ignored newline token. + switch (lex_state_ignored_p(parser)) { + case PM_IGNORED_NEWLINE_NONE: + break; + case PM_IGNORED_NEWLINE_PATTERN: + if (parser->pattern_matching_newlines || parser->in_keyword_arg) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->command_start = true; + parser->current.type = PM_TOKEN_NEWLINE; + return; + } + /* fallthrough */ + case PM_IGNORED_NEWLINE_ALL: + if (!lexed_comment) parser_lex_ignored_newline(parser); + lexed_comment = false; + goto lex_next_token; + } + + // Here we need to look ahead and see if there is a call operator + // (either . or &.) that starts the next line. If there is, then this + // is going to become an ignored newline and we're going to instead + // return the call operator. + const uint8_t *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start; + next_content += pm_strspn_inline_whitespace(next_content, parser->end - next_content); + + if (next_content < parser->end) { + // If we hit a comment after a newline, then we're going to check + // if it's ignored or if it's followed by a method call ('.'). + // If it is, then we're going to call the + // callback with an ignored newline and then continue lexing. + // Otherwise we'll return a regular newline. + if (next_content[0] == '#') { + // Here we look for a "." or "&." following a "\n". + const uint8_t *following = next_newline(next_content, parser->end - next_content); + + while (following && (following + 1 < parser->end)) { + following++; + following += pm_strspn_inline_whitespace(following, parser->end - following); + + // If this is not followed by a comment, then we can break out + // of this loop. + if (peek_at(parser, following) != '#') break; + + // If there is a comment, then we need to find the end of the + // comment and continue searching from there. + following = next_newline(following, parser->end - following); + } + + // If the lex state was ignored, or we hit a '.' or a '&.', + // we will lex the ignored newline + if ( + lex_state_ignored_p(parser) || + (following && ( + (peek_at(parser, following) == '.') || + (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.') + )) + ) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lexed_comment = false; + goto lex_next_token; + } + } + + // If we hit a . after a newline, then we're in a call chain and + // we need to return the call operator. + if (next_content[0] == '.') { + // To match ripper, we need to emit an ignored newline even though + // its a real newline in the case that we have a beginless range + // on a subsequent line. + if (peek_at(parser, next_content + 1) == '.') { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->command_start = true; + parser->current.type = PM_TOKEN_NEWLINE; + return; + } + + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, PM_LEX_STATE_DOT); + parser->current.start = next_content; + parser->current.end = next_content + 1; + parser->next_start = NULL; + LEX(PM_TOKEN_DOT); + } + + // If we hit a &. after a newline, then we're in a call chain and + // we need to return the call operator. + if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '.') { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, PM_LEX_STATE_DOT); + parser->current.start = next_content; + parser->current.end = next_content + 2; + parser->next_start = NULL; + LEX(PM_TOKEN_AMPERSAND_DOT); + } + } + + // At this point we know this is a regular newline, and we can set the + // necessary state and return the token. + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->command_start = true; + parser->current.type = PM_TOKEN_NEWLINE; + if (!lexed_comment) parser_lex_callback(parser); + return; + } + + // , + case ',': + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + LEX(PM_TOKEN_COMMA); + + // ( + case '(': { + pm_token_type_t type = PM_TOKEN_PARENTHESIS_LEFT; + + if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (PM_LEX_STATE_END | PM_LEX_STATE_LABEL))) { + type = PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES; + } + + parser->enclosure_nesting++; + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + pm_do_loop_stack_push(parser, false); + LEX(type); + } + + // ) + case ')': + parser->enclosure_nesting--; + lex_state_set(parser, PM_LEX_STATE_ENDFN); + pm_do_loop_stack_pop(parser); + LEX(PM_TOKEN_PARENTHESIS_RIGHT); + + // ; + case ';': + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->command_start = true; + LEX(PM_TOKEN_SEMICOLON); + + // [ [] []= + case '[': + parser->enclosure_nesting++; + pm_token_type_t type = PM_TOKEN_BRACKET_LEFT; + + if (lex_state_operator_p(parser)) { + if (match(parser, ']')) { + parser->enclosure_nesting--; + lex_state_set(parser, PM_LEX_STATE_ARG); + LEX(match(parser, '=') ? PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : PM_TOKEN_BRACKET_LEFT_RIGHT); + } + + lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABEL); + LEX(type); + } + + if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, PM_LEX_STATE_LABELED)))) { + type = PM_TOKEN_BRACKET_LEFT_ARRAY; + } + + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + pm_do_loop_stack_push(parser, false); + LEX(type); + + // ] + case ']': + parser->enclosure_nesting--; + lex_state_set(parser, PM_LEX_STATE_END); + pm_do_loop_stack_pop(parser); + LEX(PM_TOKEN_BRACKET_RIGHT); + + // { + case '{': { + pm_token_type_t type = PM_TOKEN_BRACE_LEFT; + + if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) { + // This { begins a lambda + parser->command_start = true; + lex_state_set(parser, PM_LEX_STATE_BEG); + type = PM_TOKEN_LAMBDA_BEGIN; + } else if (lex_state_p(parser, PM_LEX_STATE_LABELED)) { + // This { begins a hash literal + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + } else if (lex_state_p(parser, PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_END | PM_LEX_STATE_ENDFN)) { + // This { begins a block + parser->command_start = true; + lex_state_set(parser, PM_LEX_STATE_BEG); + } else if (lex_state_p(parser, PM_LEX_STATE_ENDARG)) { + // This { begins a block on a command + parser->command_start = true; + lex_state_set(parser, PM_LEX_STATE_BEG); + } else { + // This { begins a hash literal + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + } + + parser->enclosure_nesting++; + parser->brace_nesting++; + pm_do_loop_stack_push(parser, false); + + LEX(type); + } + + // } + case '}': + parser->enclosure_nesting--; + pm_do_loop_stack_pop(parser); + + if ((parser->lex_modes.current->mode == PM_LEX_EMBEXPR) && (parser->brace_nesting == 0)) { + lex_mode_pop(parser); + LEX(PM_TOKEN_EMBEXPR_END); + } + + parser->brace_nesting--; + lex_state_set(parser, PM_LEX_STATE_END); + LEX(PM_TOKEN_BRACE_RIGHT); + + // * ** **= *= + case '*': { + if (match(parser, '*')) { + if (match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_STAR_STAR_EQUAL); + } + + pm_token_type_t type = PM_TOKEN_STAR_STAR; + + if (lex_state_spcarg_p(parser, space_seen) || lex_state_beg_p(parser)) { + type = PM_TOKEN_USTAR_STAR; + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + LEX(type); + } + + if (match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_STAR_EQUAL); + } + + pm_token_type_t type = PM_TOKEN_STAR; + + if (lex_state_spcarg_p(parser, space_seen)) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR); + type = PM_TOKEN_USTAR; + } else if (lex_state_beg_p(parser)) { + type = PM_TOKEN_USTAR; + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + LEX(type); + } + + // ! != !~ !@ + case '!': + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + if (match(parser, '@')) { + LEX(PM_TOKEN_BANG); + } + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + if (match(parser, '=')) { + LEX(PM_TOKEN_BANG_EQUAL); + } + + if (match(parser, '~')) { + LEX(PM_TOKEN_BANG_TILDE); + } + + LEX(PM_TOKEN_BANG); + + // = => =~ == === =begin + case '=': + if (current_token_starts_line(parser) && (parser->current.end + 5 <= parser->end) && memcmp(parser->current.end, "begin", 5) == 0 && pm_char_is_whitespace(peek_offset(parser, 5))) { + pm_token_type_t type = lex_embdoc(parser); + + if (type == PM_TOKEN_EOF) { + LEX(type); + } + + goto lex_next_token; + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + if (match(parser, '>')) { + LEX(PM_TOKEN_EQUAL_GREATER); + } + + if (match(parser, '~')) { + LEX(PM_TOKEN_EQUAL_TILDE); + } + + if (match(parser, '=')) { + LEX(match(parser, '=') ? PM_TOKEN_EQUAL_EQUAL_EQUAL : PM_TOKEN_EQUAL_EQUAL); + } + + LEX(PM_TOKEN_EQUAL); + + // < << <<= <= <=> + case '<': + if (match(parser, '<')) { + if ( + !lex_state_p(parser, PM_LEX_STATE_DOT | PM_LEX_STATE_CLASS) && + !lex_state_end_p(parser) && + (!lex_state_p(parser, PM_LEX_STATE_ARG_ANY) || lex_state_p(parser, PM_LEX_STATE_LABELED) || space_seen) + ) { + const uint8_t *end = parser->current.end; + + pm_heredoc_quote_t quote = PM_HEREDOC_QUOTE_NONE; + pm_heredoc_indent_t indent = PM_HEREDOC_INDENT_NONE; + + if (match(parser, '-')) { + indent = PM_HEREDOC_INDENT_DASH; + } + else if (match(parser, '~')) { + indent = PM_HEREDOC_INDENT_TILDE; + } + + if (match(parser, '`')) { + quote = PM_HEREDOC_QUOTE_BACKTICK; + } + else if (match(parser, '"')) { + quote = PM_HEREDOC_QUOTE_DOUBLE; + } + else if (match(parser, '\'')) { + quote = PM_HEREDOC_QUOTE_SINGLE; + } + + const uint8_t *ident_start = parser->current.end; + size_t width = 0; + + if (parser->current.end >= parser->end) { + parser->current.end = end; + } else if (quote == PM_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end)) == 0) { + parser->current.end = end; + } else { + if (quote == PM_HEREDOC_QUOTE_NONE) { + parser->current.end += width; + + while ((parser->current.end < parser->end) && (width = char_is_identifier(parser, parser->current.end))) { + parser->current.end += width; + } + } else { + // If we have quotes, then we're going to go until we find the + // end quote. + while ((parser->current.end < parser->end) && quote != (pm_heredoc_quote_t) (*parser->current.end)) { + parser->current.end++; + } + } + + size_t ident_length = (size_t) (parser->current.end - ident_start); + if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) { + // TODO: handle unterminated heredoc + } + + lex_mode_push(parser, (pm_lex_mode_t) { + .mode = PM_LEX_HEREDOC, + .as.heredoc = { + .ident_start = ident_start, + .ident_length = ident_length, + .next_start = parser->current.end, + .quote = quote, + .indent = indent, + .common_whitespace = (size_t) -1 + } + }); + + if (parser->heredoc_end == NULL) { + const uint8_t *body_start = next_newline(parser->current.end, parser->end - parser->current.end); + + if (body_start == NULL) { + // If there is no newline after the heredoc identifier, then + // this is not a valid heredoc declaration. In this case we + // will add an error, but we will still return a heredoc + // start. + pm_parser_err_current(parser, PM_ERR_EMBDOC_TERM); + body_start = parser->end; + } else { + // Otherwise, we want to indicate that the body of the + // heredoc starts on the character after the next newline. + pm_newline_list_append(&parser->newline_list, body_start); + body_start++; + } + + parser->next_start = body_start; + } else { + parser->next_start = parser->heredoc_end; + } + + LEX(PM_TOKEN_HEREDOC_START); + } + } + + if (match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_LESS_LESS_EQUAL); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true; + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + LEX(PM_TOKEN_LESS_LESS); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true; + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + if (match(parser, '=')) { + if (match(parser, '>')) { + LEX(PM_TOKEN_LESS_EQUAL_GREATER); + } + + LEX(PM_TOKEN_LESS_EQUAL); + } + + LEX(PM_TOKEN_LESS); + + // > >> >>= >= + case '>': + if (match(parser, '>')) { + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + LEX(match(parser, '=') ? PM_TOKEN_GREATER_GREATER_EQUAL : PM_TOKEN_GREATER_GREATER); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + LEX(match(parser, '=') ? PM_TOKEN_GREATER_EQUAL : PM_TOKEN_GREATER); + + // double-quoted string literal + case '"': { + bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser); + lex_mode_push_string(parser, true, label_allowed, '\0', '"'); + LEX(PM_TOKEN_STRING_BEGIN); + } + + // xstring literal + case '`': { + if (lex_state_p(parser, PM_LEX_STATE_FNAME)) { + lex_state_set(parser, PM_LEX_STATE_ENDFN); + LEX(PM_TOKEN_BACKTICK); + } + + if (lex_state_p(parser, PM_LEX_STATE_DOT)) { + if (previous_command_start) { + lex_state_set(parser, PM_LEX_STATE_CMDARG); + } else { + lex_state_set(parser, PM_LEX_STATE_ARG); + } + + LEX(PM_TOKEN_BACKTICK); + } + + lex_mode_push_string(parser, true, false, '\0', '`'); + LEX(PM_TOKEN_BACKTICK); + } + + // single-quoted string literal + case '\'': { + bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser); + lex_mode_push_string(parser, false, label_allowed, '\0', '\''); + LEX(PM_TOKEN_STRING_BEGIN); + } + + // ? character literal + case '?': + LEX(lex_question_mark(parser)); + + // & && &&= &= + case '&': { + if (match(parser, '&')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + + if (match(parser, '=')) { + LEX(PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + } + + LEX(PM_TOKEN_AMPERSAND_AMPERSAND); + } + + if (match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_AMPERSAND_EQUAL); + } + + if (match(parser, '.')) { + lex_state_set(parser, PM_LEX_STATE_DOT); + LEX(PM_TOKEN_AMPERSAND_DOT); + } + + pm_token_type_t type = PM_TOKEN_AMPERSAND; + if (lex_state_spcarg_p(parser, space_seen) || lex_state_beg_p(parser)) { + type = PM_TOKEN_UAMPERSAND; + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + LEX(type); + } + + // | || ||= |= + case '|': + if (match(parser, '|')) { + if (match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_PIPE_PIPE_EQUAL); + } + + if (lex_state_p(parser, PM_LEX_STATE_BEG)) { + parser->current.end--; + LEX(PM_TOKEN_PIPE); + } + + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_PIPE_PIPE); + } + + if (match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_PIPE_EQUAL); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + } + + LEX(PM_TOKEN_PIPE); + + // + += +@ + case '+': { + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + + if (match(parser, '@')) { + LEX(PM_TOKEN_UPLUS); + } + + LEX(PM_TOKEN_PLUS); + } + + if (match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_PLUS_EQUAL); + } + + bool spcarg = lex_state_spcarg_p(parser, space_seen); + if (spcarg) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS); + } + + if (lex_state_beg_p(parser) || spcarg) { + lex_state_set(parser, PM_LEX_STATE_BEG); + + if (pm_char_is_decimal_digit(peek(parser))) { + parser->current.end++; + pm_token_type_t type = lex_numeric(parser); + lex_state_set(parser, PM_LEX_STATE_END); + LEX(type); + } + + LEX(PM_TOKEN_UPLUS); + } + + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_PLUS); + } + + // - -= -@ + case '-': { + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + + if (match(parser, '@')) { + LEX(PM_TOKEN_UMINUS); + } + + LEX(PM_TOKEN_MINUS); + } + + if (match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_MINUS_EQUAL); + } + + if (match(parser, '>')) { + lex_state_set(parser, PM_LEX_STATE_ENDFN); + LEX(PM_TOKEN_MINUS_GREATER); + } + + bool spcarg = lex_state_spcarg_p(parser, space_seen); + if (spcarg) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS); + } + + if (lex_state_beg_p(parser) || spcarg) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS); + } + + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_MINUS); + } + + // . .. ... + case '.': { + bool beg_p = lex_state_beg_p(parser); + + if (match(parser, '.')) { + if (match(parser, '.')) { + // If we're _not_ inside a range within default parameters + if ( + !context_p(parser, PM_CONTEXT_DEFAULT_PARAMS) && + context_p(parser, PM_CONTEXT_DEF_PARAMS) + ) { + if (lex_state_p(parser, PM_LEX_STATE_END)) { + lex_state_set(parser, PM_LEX_STATE_BEG); + } else { + lex_state_set(parser, PM_LEX_STATE_ENDARG); + } + LEX(PM_TOKEN_UDOT_DOT_DOT); + } + + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(beg_p ? PM_TOKEN_UDOT_DOT_DOT : PM_TOKEN_DOT_DOT_DOT); + } + + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(beg_p ? PM_TOKEN_UDOT_DOT : PM_TOKEN_DOT_DOT); + } + + lex_state_set(parser, PM_LEX_STATE_DOT); + LEX(PM_TOKEN_DOT); + } + + // integer + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + pm_token_type_t type = lex_numeric(parser); + lex_state_set(parser, PM_LEX_STATE_END); + LEX(type); + } + + // :: symbol + case ':': + if (match(parser, ':')) { + if (lex_state_beg_p(parser) || lex_state_p(parser, PM_LEX_STATE_CLASS) || (lex_state_p(parser, PM_LEX_STATE_ARG_ANY) && space_seen)) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_UCOLON_COLON); + } + + lex_state_set(parser, PM_LEX_STATE_DOT); + LEX(PM_TOKEN_COLON_COLON); + } + + if (lex_state_end_p(parser) || pm_char_is_whitespace(peek(parser)) || peek(parser) == '#') { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_COLON); + } + + if (peek(parser) == '"' || peek(parser) == '\'') { + lex_mode_push_string(parser, peek(parser) == '"', false, '\0', *parser->current.end); + parser->current.end++; + } + + lex_state_set(parser, PM_LEX_STATE_FNAME); + LEX(PM_TOKEN_SYMBOL_BEGIN); + + // / /= + case '/': + if (lex_state_beg_p(parser)) { + lex_mode_push_regexp(parser, '\0', '/'); + LEX(PM_TOKEN_REGEXP_BEGIN); + } + + if (match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_SLASH_EQUAL); + } + + if (lex_state_spcarg_p(parser, space_seen)) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_SLASH); + lex_mode_push_regexp(parser, '\0', '/'); + LEX(PM_TOKEN_REGEXP_BEGIN); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + LEX(PM_TOKEN_SLASH); + + // ^ ^= + case '^': + if (lex_state_operator_p(parser)) { + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + LEX(match(parser, '=') ? PM_TOKEN_CARET_EQUAL : PM_TOKEN_CARET); + + // ~ ~@ + case '~': + if (lex_state_operator_p(parser)) { + (void) match(parser, '@'); + lex_state_set(parser, PM_LEX_STATE_ARG); + } else { + lex_state_set(parser, PM_LEX_STATE_BEG); + } + + LEX(PM_TOKEN_TILDE); + + // % %= %i %I %q %Q %w %W + case '%': { + // If there is no subsequent character then we have an + // invalid token. We're going to say it's the percent + // operator because we don't want to move into the string + // lex mode unnecessarily. + if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) { + pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT); + LEX(PM_TOKEN_PERCENT); + } + + if (!lex_state_beg_p(parser) && match(parser, '=')) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(PM_TOKEN_PERCENT_EQUAL); + } else if ( + lex_state_beg_p(parser) || + (lex_state_p(parser, PM_LEX_STATE_FITEM) && (peek(parser) == 's')) || + lex_state_spcarg_p(parser, space_seen) + ) { + if (!parser->encoding.alnum_char(parser->current.end, parser->end - parser->current.end)) { + if (*parser->current.end >= 0x80) { + pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT); + } + + lex_mode_push_string(parser, true, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + + size_t eol_length = match_eol(parser); + if (eol_length) { + parser->current.end += eol_length; + pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + } else { + parser->current.end++; + } + + if (parser->current.end < parser->end) { + LEX(PM_TOKEN_STRING_BEGIN); + } + } + + // Delimiters for %-literals cannot be alphanumeric. We + // validate that here. + uint8_t delimiter = peek_offset(parser, 1); + if (delimiter >= 0x80 || parser->encoding.alnum_char(&delimiter, 1)) { + pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT); + goto lex_next_token; + } + + switch (peek(parser)) { + case 'i': { + parser->current.end++; + + if (parser->current.end < parser->end) { + lex_mode_push_list(parser, false, *parser->current.end++); + } else { + lex_mode_push_list_eof(parser); + } + + LEX(PM_TOKEN_PERCENT_LOWER_I); + } + case 'I': { + parser->current.end++; + + if (parser->current.end < parser->end) { + lex_mode_push_list(parser, true, *parser->current.end++); + } else { + lex_mode_push_list_eof(parser); + } + + LEX(PM_TOKEN_PERCENT_UPPER_I); + } + case 'r': { + parser->current.end++; + + if (parser->current.end < parser->end) { + lex_mode_push_regexp(parser, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + pm_newline_list_check_append(&parser->newline_list, parser->current.end); + parser->current.end++; + } else { + lex_mode_push_regexp(parser, '\0', '\0'); + } + + LEX(PM_TOKEN_REGEXP_BEGIN); + } + case 'q': { + parser->current.end++; + + if (parser->current.end < parser->end) { + lex_mode_push_string(parser, false, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + pm_newline_list_check_append(&parser->newline_list, parser->current.end); + parser->current.end++; + } else { + lex_mode_push_string_eof(parser); + } + + LEX(PM_TOKEN_STRING_BEGIN); + } + case 'Q': { + parser->current.end++; + + if (parser->current.end < parser->end) { + lex_mode_push_string(parser, true, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + pm_newline_list_check_append(&parser->newline_list, parser->current.end); + parser->current.end++; + } else { + lex_mode_push_string_eof(parser); + } + + LEX(PM_TOKEN_STRING_BEGIN); + } + case 's': { + parser->current.end++; + + if (parser->current.end < parser->end) { + lex_mode_push_string(parser, false, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM); + parser->current.end++; + } else { + lex_mode_push_string_eof(parser); + } + + LEX(PM_TOKEN_SYMBOL_BEGIN); + } + case 'w': { + parser->current.end++; + + if (parser->current.end < parser->end) { + lex_mode_push_list(parser, false, *parser->current.end++); + } else { + lex_mode_push_list_eof(parser); + } + + LEX(PM_TOKEN_PERCENT_LOWER_W); + } + case 'W': { + parser->current.end++; + + if (parser->current.end < parser->end) { + lex_mode_push_list(parser, true, *parser->current.end++); + } else { + lex_mode_push_list_eof(parser); + } + + LEX(PM_TOKEN_PERCENT_UPPER_W); + } + case 'x': { + parser->current.end++; + + if (parser->current.end < parser->end) { + lex_mode_push_string(parser, true, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + parser->current.end++; + } else { + lex_mode_push_string_eof(parser); + } + + LEX(PM_TOKEN_PERCENT_LOWER_X); + } + default: + // If we get to this point, then we have a % that is completely + // unparseable. In this case we'll just drop it from the parser + // and skip past it and hope that the next token is something + // that we can parse. + pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT); + goto lex_next_token; + } + } + + lex_state_set(parser, lex_state_operator_p(parser) ? PM_LEX_STATE_ARG : PM_LEX_STATE_BEG); + LEX(PM_TOKEN_PERCENT); + } + + // global variable + case '$': { + pm_token_type_t type = lex_global_variable(parser); + + // If we're lexing an embedded variable, then we need to pop back into + // the parent lex context. + if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) { + lex_mode_pop(parser); + } + + lex_state_set(parser, PM_LEX_STATE_END); + LEX(type); + } + + // instance variable, class variable + case '@': + lex_state_set(parser, parser->lex_state & PM_LEX_STATE_FNAME ? PM_LEX_STATE_ENDFN : PM_LEX_STATE_END); + LEX(lex_at_variable(parser)); + + default: { + if (*parser->current.start != '_') { + size_t width = char_is_identifier_start(parser, parser->current.start); + + // If this isn't the beginning of an identifier, then it's an invalid + // token as we've exhausted all of the other options. We'll skip past + // it and return the next token. + if (!width) { + pm_parser_err_current(parser, PM_ERR_INVALID_TOKEN); + goto lex_next_token; + } + + parser->current.end = parser->current.start + width; + } + + pm_token_type_t type = lex_identifier(parser, previous_command_start); + + // If we've hit a __END__ and it was at the start of the line or the + // start of the file and it is followed by either a \n or a \r\n, then + // this is the last token of the file. + if ( + ((parser->current.end - parser->current.start) == 7) && + current_token_starts_line(parser) && + (memcmp(parser->current.start, "__END__", 7) == 0) && + (parser->current.end == parser->end || match_eol(parser)) + ) + { + // Since we know we're about to add an __END__ comment, we know we + // need at add all of the newlines to get the correct column + // information for it. + const uint8_t *cursor = parser->current.end; + while ((cursor = next_newline(cursor, parser->end - cursor)) != NULL) { + pm_newline_list_append(&parser->newline_list, cursor++); + } + + parser->current.end = parser->end; + parser->current.type = PM_TOKEN___END__; + parser_lex_callback(parser); + + parser->data_loc.start = parser->current.start; + parser->data_loc.end = parser->current.end; + + LEX(PM_TOKEN_EOF); + } + + pm_lex_state_t last_state = parser->lex_state; + + if (type == PM_TOKEN_IDENTIFIER || type == PM_TOKEN_CONSTANT || type == PM_TOKEN_METHOD_NAME) { + if (lex_state_p(parser, PM_LEX_STATE_BEG_ANY | PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_DOT)) { + if (previous_command_start) { + lex_state_set(parser, PM_LEX_STATE_CMDARG); + } else { + lex_state_set(parser, PM_LEX_STATE_ARG); + } + } else if (parser->lex_state == PM_LEX_STATE_FNAME) { + lex_state_set(parser, PM_LEX_STATE_ENDFN); + } else { + lex_state_set(parser, PM_LEX_STATE_END); + } + } + + if ( + !(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) && + (type == PM_TOKEN_IDENTIFIER) && + ((pm_parser_local_depth(parser, &parser->current) != -1) || + pm_token_is_numbered_parameter(parser->current.start, parser->current.end)) + ) { + lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL); + } + + LEX(type); + } + } + } + case PM_LEX_LIST: { + if (parser->next_start != NULL) { + parser->current.end = parser->next_start; + parser->next_start = NULL; + } + + // First we'll set the beginning of the token. + parser->current.start = parser->current.end; + + // If there's any whitespace at the start of the list, then we're + // going to trim it off the beginning and create a new token. + size_t whitespace; + + if (parser->heredoc_end) { + whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end); + if (peek_offset(parser, (ptrdiff_t)whitespace) == '\n') { + whitespace += 1; + } + } else { + whitespace = pm_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->newline_list); + } + + if (whitespace > 0) { + parser->current.end += whitespace; + if (peek_offset(parser, -1) == '\n') { + // mutates next_start + parser_flush_heredoc_end(parser); + } + LEX(PM_TOKEN_WORDS_SEP); + } + + // We'll check if we're at the end of the file. If we are, then we + // need to return the EOF token. + if (parser->current.end >= parser->end) { + LEX(PM_TOKEN_EOF); + } + + // Here we'll get a list of the places where strpbrk should break, + // and then find the first one. + pm_lex_mode_t *lex_mode = parser->lex_modes.current; + const uint8_t *breakpoints = lex_mode->as.list.breakpoints; + const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + + // If we haven't found an escape yet, then this buffer will be + // unallocated since we can refer directly to the source string. + pm_token_buffer_t token_buffer = { { 0 }, 0 }; + + while (breakpoint != NULL) { + // If we hit a null byte, skip directly past it. + if (*breakpoint == '\0') { + breakpoint = pm_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + continue; + } + + // If we hit whitespace, then we must have received content by + // now, so we can return an element of the list. + if (pm_char_is_whitespace(*breakpoint)) { + parser->current.end = breakpoint; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + // If we hit the terminator, we need to check which token to + // return. + if (*breakpoint == lex_mode->as.list.terminator) { + // If this terminator doesn't actually close the list, then + // we need to continue on past it. + if (lex_mode->as.list.nesting > 0) { + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + lex_mode->as.list.nesting--; + continue; + } + + // If we've hit the terminator and we've already skipped + // past content, then we can return a list node. + if (breakpoint > parser->current.start) { + parser->current.end = breakpoint; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + // Otherwise, switch back to the default state and return + // the end of the list. + parser->current.end = breakpoint + 1; + lex_mode_pop(parser); + lex_state_set(parser, PM_LEX_STATE_END); + LEX(PM_TOKEN_STRING_END); + } + + // If we hit escapes, then we need to treat the next token + // literally. In this case we'll skip past the next character + // and find the next breakpoint. + if (*breakpoint == '\\') { + parser->current.end = breakpoint + 1; + + // If we've hit the end of the file, then break out of the + // loop by setting the breakpoint to NULL. + if (parser->current.end == parser->end) { + breakpoint = NULL; + continue; + } + + pm_token_buffer_escape(parser, &token_buffer); + uint8_t peeked = peek(parser); + + switch (peeked) { + case ' ': + case '\f': + case '\t': + case '\v': + case '\\': + pm_token_buffer_push(&token_buffer, peeked); + parser->current.end++; + break; + case '\r': + parser->current.end++; + if (peek(parser) != '\n') { + pm_token_buffer_push(&token_buffer, '\r'); + break; + } + /* fallthrough */ + case '\n': + pm_token_buffer_push(&token_buffer, '\n'); + + if (parser->heredoc_end) { + // ... if we are on the same line as a heredoc, + // flush the heredoc and continue parsing after + // heredoc_end. + parser_flush_heredoc_end(parser); + pm_token_buffer_copy(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } else { + // ... else track the newline. + pm_newline_list_append(&parser->newline_list, parser->current.end); + } + + parser->current.end++; + break; + default: + if (peeked == lex_mode->as.list.incrementor || peeked == lex_mode->as.list.terminator) { + pm_token_buffer_push(&token_buffer, peeked); + parser->current.end++; + } else if (lex_mode->as.list.interpolation) { + escape_read(parser, &token_buffer.buffer, PM_ESCAPE_FLAG_NONE); + } else { + pm_token_buffer_push(&token_buffer, '\\'); + pm_token_buffer_push(&token_buffer, peeked); + parser->current.end++; + } + + break; + } + + token_buffer.cursor = parser->current.end; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + continue; + } + + // If we hit a #, then we will attempt to lex interpolation. + if (*breakpoint == '#') { + pm_token_type_t type = lex_interpolation(parser, breakpoint); + + if (type == PM_TOKEN_NOT_PROVIDED) { + // If we haven't returned at this point then we had something + // that looked like an interpolated class or instance variable + // like "#@" but wasn't actually. In this case we'll just skip + // to the next breakpoint. + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + continue; + } + + if (type == PM_TOKEN_STRING_CONTENT) { + pm_token_buffer_flush(parser, &token_buffer); + } + + LEX(type); + } + + // If we've hit the incrementor, then we need to skip past it + // and find the next breakpoint. + assert(*breakpoint == lex_mode->as.list.incrementor); + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + lex_mode->as.list.nesting++; + continue; + } + + if (parser->current.end > parser->current.start) { + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + // If we were unable to find a breakpoint, then this token hits the + // end of the file. + LEX(PM_TOKEN_EOF); + } + case PM_LEX_REGEXP: { + // First, we'll set to start of this token to be the current end. + if (parser->next_start == NULL) { + parser->current.start = parser->current.end; + } else { + parser->current.start = parser->next_start; + parser->current.end = parser->next_start; + parser->next_start = NULL; + } + + // We'll check if we're at the end of the file. If we are, then we need to + // return the EOF token. + if (parser->current.end >= parser->end) { + LEX(PM_TOKEN_EOF); + } + + // Get a reference to the current mode. + pm_lex_mode_t *lex_mode = parser->lex_modes.current; + + // These are the places where we need to split up the content of the + // regular expression. We'll use strpbrk to find the first of these + // characters. + const uint8_t *breakpoints = lex_mode->as.regexp.breakpoints; + const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + pm_token_buffer_t token_buffer = { { 0 }, 0 }; + + while (breakpoint != NULL) { + // If we hit a null byte, skip directly past it. + if (*breakpoint == '\0') { + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + continue; + } + + // If we've hit a newline, then we need to track that in the + // list of newlines. + if (*breakpoint == '\n') { + // For the special case of a newline-terminated regular expression, we will pass + // through this branch twice -- once with PM_TOKEN_REGEXP_BEGIN and then again + // with PM_TOKEN_STRING_CONTENT. Let's avoid tracking the newline twice, by + // tracking it only in the REGEXP_BEGIN case. + if ( + !(lex_mode->as.regexp.terminator == '\n' && parser->current.type != PM_TOKEN_REGEXP_BEGIN) + && parser->heredoc_end == NULL + ) { + pm_newline_list_append(&parser->newline_list, breakpoint); + } + + if (lex_mode->as.regexp.terminator != '\n') { + // If the terminator is not a newline, then we can set + // the next breakpoint and continue. + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + continue; + } + } + + // If we hit the terminator, we need to determine what kind of + // token to return. + if (*breakpoint == lex_mode->as.regexp.terminator) { + if (lex_mode->as.regexp.nesting > 0) { + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + lex_mode->as.regexp.nesting--; + continue; + } + + // Here we've hit the terminator. If we have already consumed + // content then we need to return that content as string content + // first. + if (breakpoint > parser->current.start) { + parser->current.end = breakpoint; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + // Since we've hit the terminator of the regular expression, + // we now need to parse the options. + parser->current.end = breakpoint + 1; + parser->current.end += pm_strspn_regexp_option(parser->current.end, parser->end - parser->current.end); + + lex_mode_pop(parser); + lex_state_set(parser, PM_LEX_STATE_END); + LEX(PM_TOKEN_REGEXP_END); + } + + // If we hit escapes, then we need to treat the next token + // literally. In this case we'll skip past the next character + // and find the next breakpoint. + if (*breakpoint == '\\') { + parser->current.end = breakpoint + 1; + + // If we've hit the end of the file, then break out of the + // loop by setting the breakpoint to NULL. + if (parser->current.end == parser->end) { + breakpoint = NULL; + continue; + } + + pm_token_buffer_escape(parser, &token_buffer); + uint8_t peeked = peek(parser); + + switch (peeked) { + case '\r': + parser->current.end++; + if (peek(parser) != '\n') { + pm_token_buffer_push(&token_buffer, '\\'); + pm_token_buffer_push(&token_buffer, '\r'); + break; + } + /* fallthrough */ + case '\n': + if (parser->heredoc_end) { + // ... if we are on the same line as a heredoc, + // flush the heredoc and continue parsing after + // heredoc_end. + parser_flush_heredoc_end(parser); + pm_token_buffer_copy(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } else { + // ... else track the newline. + pm_newline_list_append(&parser->newline_list, parser->current.end); + } + + parser->current.end++; + break; + case 'c': + case 'C': + case 'M': + case 'u': + case 'x': + escape_read(parser, &token_buffer.buffer, PM_ESCAPE_FLAG_REGEXP); + break; + default: + if (lex_mode->as.regexp.terminator == '/' && peeked == '/') { + pm_token_buffer_push(&token_buffer, peeked); + parser->current.end++; + break; + } + + if (peeked < 0x80) pm_token_buffer_push(&token_buffer, '\\'); + pm_token_buffer_push(&token_buffer, peeked); + parser->current.end++; + break; + } + + token_buffer.cursor = parser->current.end; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + continue; + } + + // If we hit a #, then we will attempt to lex interpolation. + if (*breakpoint == '#') { + pm_token_type_t type = lex_interpolation(parser, breakpoint); + + if (type == PM_TOKEN_NOT_PROVIDED) { + // If we haven't returned at this point then we had + // something that looked like an interpolated class or + // instance variable like "#@" but wasn't actually. In + // this case we'll just skip to the next breakpoint. + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + continue; + } + + if (type == PM_TOKEN_STRING_CONTENT) { + pm_token_buffer_flush(parser, &token_buffer); + } + + LEX(type); + } + + // If we've hit the incrementor, then we need to skip past it + // and find the next breakpoint. + assert(*breakpoint == lex_mode->as.regexp.incrementor); + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + lex_mode->as.regexp.nesting++; + continue; + } + + if (parser->current.end > parser->current.start) { + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + // If we were unable to find a breakpoint, then this token hits the + // end of the file. + LEX(PM_TOKEN_EOF); + } + case PM_LEX_STRING: { + // First, we'll set to start of this token to be the current end. + if (parser->next_start == NULL) { + parser->current.start = parser->current.end; + } else { + parser->current.start = parser->next_start; + parser->current.end = parser->next_start; + parser->next_start = NULL; + } + + // We'll check if we're at the end of the file. If we are, then we need to + // return the EOF token. + if (parser->current.end >= parser->end) { + LEX(PM_TOKEN_EOF); + } + + // These are the places where we need to split up the content of the + // string. We'll use strpbrk to find the first of these characters. + pm_lex_mode_t *lex_mode = parser->lex_modes.current; + const uint8_t *breakpoints = lex_mode->as.string.breakpoints; + const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + + // If we haven't found an escape yet, then this buffer will be + // unallocated since we can refer directly to the source string. + pm_token_buffer_t token_buffer = { { 0 }, 0 }; + + while (breakpoint != NULL) { + // If we hit the incrementor, then we'll increment then nesting and + // continue lexing. + if (lex_mode->as.string.incrementor != '\0' && *breakpoint == lex_mode->as.string.incrementor) { + lex_mode->as.string.nesting++; + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + continue; + } + + // Note that we have to check the terminator here first because we could + // potentially be parsing a % string that has a # character as the + // terminator. + if (*breakpoint == lex_mode->as.string.terminator) { + // If this terminator doesn't actually close the string, then we need + // to continue on past it. + if (lex_mode->as.string.nesting > 0) { + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + lex_mode->as.string.nesting--; + continue; + } + + // Here we've hit the terminator. If we have already consumed content + // then we need to return that content as string content first. + if (breakpoint > parser->current.start) { + parser->current.end = breakpoint; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + // Otherwise we need to switch back to the parent lex mode and + // return the end of the string. + size_t eol_length = match_eol_at(parser, breakpoint); + if (eol_length) { + parser->current.end = breakpoint + eol_length; + pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + } else { + parser->current.end = breakpoint + 1; + } + + if (lex_mode->as.string.label_allowed && (peek(parser) == ':') && (peek_offset(parser, 1) != ':')) { + parser->current.end++; + lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED); + lex_mode_pop(parser); + LEX(PM_TOKEN_LABEL_END); + } + + lex_state_set(parser, PM_LEX_STATE_END); + lex_mode_pop(parser); + LEX(PM_TOKEN_STRING_END); + } + + // When we hit a newline, we need to flush any potential heredocs. Note + // that this has to happen after we check for the terminator in case the + // terminator is a newline character. + if (*breakpoint == '\n') { + if (parser->heredoc_end == NULL) { + pm_newline_list_append(&parser->newline_list, breakpoint); + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + continue; + } else { + parser->current.end = breakpoint + 1; + parser_flush_heredoc_end(parser); + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + } + + switch (*breakpoint) { + case '\0': + // Skip directly past the null character. + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + case '\\': { + // Here we hit escapes. + parser->current.end = breakpoint + 1; + + // If we've hit the end of the file, then break out of + // the loop by setting the breakpoint to NULL. + if (parser->current.end == parser->end) { + breakpoint = NULL; + continue; + } + + pm_token_buffer_escape(parser, &token_buffer); + uint8_t peeked = peek(parser); + + switch (peeked) { + case '\\': + pm_token_buffer_push(&token_buffer, '\\'); + parser->current.end++; + break; + case '\r': + parser->current.end++; + if (peek(parser) != '\n') { + if (!lex_mode->as.string.interpolation) { + pm_token_buffer_push(&token_buffer, '\\'); + } + pm_token_buffer_push(&token_buffer, '\r'); + break; + } + /* fallthrough */ + case '\n': + if (!lex_mode->as.string.interpolation) { + pm_token_buffer_push(&token_buffer, '\\'); + pm_token_buffer_push(&token_buffer, '\n'); + } + + if (parser->heredoc_end) { + // ... if we are on the same line as a heredoc, + // flush the heredoc and continue parsing after + // heredoc_end. + parser_flush_heredoc_end(parser); + pm_token_buffer_copy(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } else { + // ... else track the newline. + pm_newline_list_append(&parser->newline_list, parser->current.end); + } + + parser->current.end++; + break; + default: + if (lex_mode->as.string.incrementor != '\0' && peeked == lex_mode->as.string.incrementor) { + pm_token_buffer_push(&token_buffer, peeked); + parser->current.end++; + } else if (lex_mode->as.string.terminator != '\0' && peeked == lex_mode->as.string.terminator) { + pm_token_buffer_push(&token_buffer, peeked); + parser->current.end++; + } else if (lex_mode->as.string.interpolation) { + escape_read(parser, &token_buffer.buffer, PM_ESCAPE_FLAG_NONE); + } else { + pm_token_buffer_push(&token_buffer, '\\'); + pm_token_buffer_push(&token_buffer, peeked); + parser->current.end++; + } + + break; + } + + token_buffer.cursor = parser->current.end; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + } + case '#': { + pm_token_type_t type = lex_interpolation(parser, breakpoint); + + if (type == PM_TOKEN_NOT_PROVIDED) { + // If we haven't returned at this point then we had something that + // looked like an interpolated class or instance variable like "#@" + // but wasn't actually. In this case we'll just skip to the next + // breakpoint. + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + } + + if (type == PM_TOKEN_STRING_CONTENT) { + pm_token_buffer_flush(parser, &token_buffer); + } + + LEX(type); + } + default: + assert(false && "unreachable"); + } + } + + if (parser->current.end > parser->current.start) { + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + // If we've hit the end of the string, then this is an unterminated + // string. In that case we'll return the EOF token. + LEX(PM_TOKEN_EOF); + } + case PM_LEX_HEREDOC: { + // First, we'll set to start of this token. + if (parser->next_start == NULL) { + parser->current.start = parser->current.end; + } else { + parser->current.start = parser->next_start; + parser->current.end = parser->next_start; + parser->heredoc_end = NULL; + parser->next_start = NULL; + } + + // We'll check if we're at the end of the file. If we are, then we need to + // return the EOF token. + if (parser->current.end >= parser->end) { + LEX(PM_TOKEN_EOF); + } + + // Now let's grab the information about the identifier off of the current + // lex mode. + pm_lex_mode_t *lex_mode = parser->lex_modes.current; + const uint8_t *ident_start = lex_mode->as.heredoc.ident_start; + size_t ident_length = lex_mode->as.heredoc.ident_length; + + // If we are immediately following a newline and we have hit the + // terminator, then we need to return the ending of the heredoc. + if (current_token_starts_line(parser)) { + const uint8_t *start = parser->current.start; + size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.indent); + + if ((start + ident_length <= parser->end) && (memcmp(start, ident_start, ident_length) == 0)) { + bool matched = true; + bool at_end = false; + + size_t eol_length = match_eol_at(parser, start + ident_length); + if (eol_length) { + parser->current.end = start + ident_length + eol_length; + pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + } else if (parser->end == (start + ident_length)) { + parser->current.end = start + ident_length; + at_end = true; + } else { + matched = false; + } + + if (matched) { + if (*lex_mode->as.heredoc.next_start == '\\') { + parser->next_start = NULL; + } else { + parser->next_start = lex_mode->as.heredoc.next_start; + parser->heredoc_end = parser->current.end; + } + + parser->current_string_common_whitespace = parser->lex_modes.current->as.heredoc.common_whitespace; + lex_mode_pop(parser); + if (!at_end) { + lex_state_set(parser, PM_LEX_STATE_END); + } + LEX(PM_TOKEN_HEREDOC_END); + } + } + + if ( + lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE && + (lex_mode->as.heredoc.common_whitespace > whitespace) && + peek_at(parser, start) != '\n' + ) { + lex_mode->as.heredoc.common_whitespace = whitespace; + } + } + + // Otherwise we'll be parsing string content. These are the places + // where we need to split up the content of the heredoc. We'll use + // strpbrk to find the first of these characters. + uint8_t breakpoints[] = "\n\\#"; + + pm_heredoc_quote_t quote = lex_mode->as.heredoc.quote; + if (quote == PM_HEREDOC_QUOTE_SINGLE) { + breakpoints[2] = '\0'; + } + + const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + pm_token_buffer_t token_buffer = { { 0 }, 0 }; + bool was_escaped_newline = false; + + while (breakpoint != NULL) { + switch (*breakpoint) { + case '\0': + // Skip directly past the null character. + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + case '\n': { + if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) { + parser_flush_heredoc_end(parser); + parser->current.end = breakpoint + 1; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + pm_newline_list_append(&parser->newline_list, breakpoint); + + // If we have a - or ~ heredoc, then we can match after + // some leading whitespace. + const uint8_t *start = breakpoint + 1; + size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.indent); + + // If we have hit a newline that is followed by a valid + // terminator, then we need to return the content of the + // heredoc here as string content. Then, the next time a + // token is lexed, it will match again and return the + // end of the heredoc. + if ( + !was_escaped_newline && + (start + ident_length <= parser->end) && + (memcmp(start, ident_start, ident_length) == 0) + ) { + // Heredoc terminators must be followed by a + // newline, CRLF, or EOF to be valid. + if ( + start + ident_length == parser->end || + match_eol_at(parser, start + ident_length) + ) { + parser->current.end = breakpoint + 1; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + } + + if (lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) { + if ((lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') { + lex_mode->as.heredoc.common_whitespace = whitespace; + } + + parser->current.end = breakpoint + 1; + + if (!was_escaped_newline) { + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + } + + // Otherwise we hit a newline and it wasn't followed by + // a terminator, so we can continue parsing. + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + } + case '\\': { + // If we hit an escape, then we need to skip past + // however many characters the escape takes up. However + // it's important that if \n or \r\n are escaped that we + // stop looping before the newline and not after the + // newline so that we can still potentially find the + // terminator of the heredoc. + parser->current.end = breakpoint + 1; + + // If we've hit the end of the file, then break out of + // the loop by setting the breakpoint to NULL. + if (parser->current.end == parser->end) { + breakpoint = NULL; + continue; + } + + pm_token_buffer_escape(parser, &token_buffer); + uint8_t peeked = peek(parser); + + if (quote == PM_HEREDOC_QUOTE_SINGLE) { + switch (peeked) { + case '\r': + parser->current.end++; + if (peek(parser) != '\n') { + pm_token_buffer_push(&token_buffer, '\\'); + pm_token_buffer_push(&token_buffer, '\r'); + break; + } + /* fallthrough */ + case '\n': + pm_token_buffer_push(&token_buffer, '\\'); + pm_token_buffer_push(&token_buffer, '\n'); + token_buffer.cursor = parser->current.end + 1; + breakpoint = parser->current.end; + continue; + default: + parser->current.end++; + pm_token_buffer_push(&token_buffer, '\\'); + pm_token_buffer_push(&token_buffer, peeked); + break; + } + } else { + switch (peeked) { + case '\r': + parser->current.end++; + if (peek(parser) != '\n') { + pm_token_buffer_push(&token_buffer, '\r'); + break; + } + /* fallthrough */ + case '\n': + was_escaped_newline = true; + token_buffer.cursor = parser->current.end + 1; + breakpoint = parser->current.end; + continue; + default: + escape_read(parser, &token_buffer.buffer, PM_ESCAPE_FLAG_NONE); + break; + } + } + + token_buffer.cursor = parser->current.end; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + } + case '#': { + pm_token_type_t type = lex_interpolation(parser, breakpoint); + + if (type == PM_TOKEN_NOT_PROVIDED) { + // If we haven't returned at this point then we had + // something that looked like an interpolated class + // or instance variable like "#@" but wasn't + // actually. In this case we'll just skip to the + // next breakpoint. + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + } + + if (type == PM_TOKEN_STRING_CONTENT) { + pm_token_buffer_flush(parser, &token_buffer); + } + + LEX(type); + } + default: + assert(false && "unreachable"); + } + + was_escaped_newline = false; + } + + if (parser->current.end > parser->current.start) { + parser->current.end = parser->end; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + // If we've hit the end of the string, then this is an unterminated + // heredoc. In that case we'll return the EOF token. + LEX(PM_TOKEN_EOF); + } + } + + assert(false && "unreachable"); +} + +#undef LEX + +/******************************************************************************/ +/* Parse functions */ +/******************************************************************************/ + +/** + * These are the various precedence rules. Because we are using a Pratt parser, + * they are named binding power to represent the manner in which nodes are bound + * together in the stack. + * + * We increment by 2 because we want to leave room for the infix operators to + * specify their associativity by adding or subtracting one. + */ +typedef enum { + PM_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator + PM_BINDING_POWER_STATEMENT = 2, + PM_BINDING_POWER_MODIFIER = 4, // if unless until while + PM_BINDING_POWER_MODIFIER_RESCUE = 6, // rescue + PM_BINDING_POWER_COMPOSITION = 8, // and or + PM_BINDING_POWER_NOT = 10, // not + PM_BINDING_POWER_MATCH = 12, // => in + PM_BINDING_POWER_DEFINED = 14, // defined? + PM_BINDING_POWER_ASSIGNMENT = 16, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **= + PM_BINDING_POWER_TERNARY = 18, // ?: + PM_BINDING_POWER_RANGE = 20, // .. ... + PM_BINDING_POWER_LOGICAL_OR = 22, // || + PM_BINDING_POWER_LOGICAL_AND = 24, // && + PM_BINDING_POWER_EQUALITY = 26, // <=> == === != =~ !~ + PM_BINDING_POWER_COMPARISON = 28, // > >= < <= + PM_BINDING_POWER_BITWISE_OR = 30, // | ^ + PM_BINDING_POWER_BITWISE_AND = 32, // & + PM_BINDING_POWER_SHIFT = 34, // << >> + PM_BINDING_POWER_TERM = 36, // + - + PM_BINDING_POWER_FACTOR = 38, // * / % + PM_BINDING_POWER_UMINUS = 40, // -@ + PM_BINDING_POWER_EXPONENT = 42, // ** + PM_BINDING_POWER_UNARY = 44, // ! ~ +@ + PM_BINDING_POWER_INDEX = 46, // [] []= + PM_BINDING_POWER_CALL = 48, // :: . + PM_BINDING_POWER_MAX = 50 +} pm_binding_power_t; + +/** + * This struct represents a set of binding powers used for a given token. They + * are combined in this way to make it easier to represent associativity. + */ +typedef struct { + /** The left binding power. */ + pm_binding_power_t left; + + /** The right binding power. */ + pm_binding_power_t right; + + /** Whether or not this token can be used as a binary operator. */ + bool binary; + + /** + * Whether or not this token can be used as non-associative binary operator. + * Non-associative operators (e.g. in and =>) need special treatment in parse_expression. + */ + bool nonassoc; +} pm_binding_powers_t; + +#define BINDING_POWER_ASSIGNMENT { PM_BINDING_POWER_UNARY, PM_BINDING_POWER_ASSIGNMENT, true, false } +#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, false } +#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true, false } +#define NON_ASSOCIATIVE(precedence) { precedence + 1, precedence + 1, true, true } +#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false } + +pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = { + // if unless until while + [PM_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER), + [PM_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER), + [PM_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER), + [PM_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER), + + // rescue + [PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER_RESCUE), + + // and or + [PM_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION), + [PM_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION), + + // => in + [PM_TOKEN_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH), + [PM_TOKEN_KEYWORD_IN] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH), + + // &&= &= ^= = >>= <<= -= %= |= += /= *= **= + [PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT, + [PM_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT, + + // ?: + [PM_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_TERNARY), + + // .. ... + [PM_TOKEN_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE), + [PM_TOKEN_DOT_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE), + [PM_TOKEN_UDOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR), + [PM_TOKEN_UDOT_DOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR), + + // || + [PM_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_OR), + + // && + [PM_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_AND), + + // != !~ == === =~ <=> + [PM_TOKEN_BANG_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_BANG_TILDE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_EQUAL_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_EQUAL_EQUAL_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_EQUAL_TILDE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_LESS_EQUAL_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + + // > >= < <= + [PM_TOKEN_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON), + [PM_TOKEN_GREATER_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON), + [PM_TOKEN_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON), + [PM_TOKEN_LESS_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON), + + // ^ | + [PM_TOKEN_CARET] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR), + [PM_TOKEN_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR), + + // & + [PM_TOKEN_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_AND), + + // >> << + [PM_TOKEN_GREATER_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT), + [PM_TOKEN_LESS_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT), + + // - + + [PM_TOKEN_MINUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM), + [PM_TOKEN_PLUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM), + + // % / * + [PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR), + [PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR), + [PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR), + [PM_TOKEN_USTAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR), + + // -@ + [PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS), + [PM_TOKEN_UMINUS_NUM] = { PM_BINDING_POWER_UMINUS, PM_BINDING_POWER_MAX, false, false }, + + // ** + [PM_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_EXPONENT), + [PM_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY), + + // ! ~ +@ + [PM_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY), + [PM_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY), + [PM_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY), + + // [ + [PM_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_INDEX), + + // :: . &. + [PM_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL), + [PM_TOKEN_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL), + [PM_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL) +}; + +#undef BINDING_POWER_ASSIGNMENT +#undef LEFT_ASSOCIATIVE +#undef RIGHT_ASSOCIATIVE +#undef RIGHT_ASSOCIATIVE_UNARY + +/** + * Returns true if the current token is of the given type. + */ +static inline bool +match1(const pm_parser_t *parser, pm_token_type_t type) { + return parser->current.type == type; +} + +/** + * Returns true if the current token is of either of the given types. + */ +static inline bool +match2(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) { + return match1(parser, type1) || match1(parser, type2); +} + +/** + * Returns true if the current token is any of the three given types. + */ +static inline bool +match3(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) { + return match1(parser, type1) || match1(parser, type2) || match1(parser, type3); +} + +/** + * Returns true if the current token is any of the four given types. + */ +static inline bool +match4(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4) { + return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4); +} + +/** + * Returns true if the current token is any of the five given types. + */ +static inline bool +match5(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5) { + return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5); +} + +/** + * Returns true if the current token is any of the six given types. + */ +static inline bool +match6(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6) { + return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6); +} + +/** + * Returns true if the current token is any of the seven given types. + */ +static inline bool +match7(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6, pm_token_type_t type7) { + return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6) || match1(parser, type7); +} + +/** + * Returns true if the current token is any of the eight given types. + */ +static inline bool +match8(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6, pm_token_type_t type7, pm_token_type_t type8) { + return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6) || match1(parser, type7) || match1(parser, type8); +} + +/** + * If the current token is of the specified type, lex forward by one token and + * return true. Otherwise, return false. For example: + * + * if (accept1(parser, PM_TOKEN_COLON)) { ... } + */ +static bool +accept1(pm_parser_t *parser, pm_token_type_t type) { + if (match1(parser, type)) { + parser_lex(parser); + return true; + } + return false; +} + +/** + * If the current token is either of the two given types, lex forward by one + * token and return true. Otherwise return false. + */ +static inline bool +accept2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) { + if (match2(parser, type1, type2)) { + parser_lex(parser); + return true; + } + return false; +} + +/** + * If the current token is any of the three given types, lex forward by one + * token and return true. Otherwise return false. + */ +static inline bool +accept3(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) { + if (match3(parser, type1, type2, type3)) { + parser_lex(parser); + return true; + } + return false; +} + +/** + * This function indicates that the parser expects a token in a specific + * position. For example, if you're parsing a BEGIN block, you know that a { is + * expected immediately after the keyword. In that case you would call this + * function to indicate that that token should be found. + * + * If we didn't find the token that we were expecting, then we're going to add + * an error to the parser's list of errors (to indicate that the tree is not + * valid) and create an artificial token instead. This allows us to recover from + * the fact that the token isn't present and continue parsing. + */ +static void +expect1(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id) { + if (accept1(parser, type)) return; + + const uint8_t *location = parser->previous.end; + pm_parser_err(parser, location, location, diag_id); + + parser->previous.start = location; + parser->previous.type = PM_TOKEN_MISSING; +} + +/** + * This function is the same as expect1, but it expects either of two token + * types. + */ +static void +expect2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_diagnostic_id_t diag_id) { + if (accept2(parser, type1, type2)) return; + + const uint8_t *location = parser->previous.end; + pm_parser_err(parser, location, location, diag_id); + + parser->previous.start = location; + parser->previous.type = PM_TOKEN_MISSING; +} + +/** + * This function is the same as expect2, but it expects one of three token types. + */ +static void +expect3(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_diagnostic_id_t diag_id) { + if (accept3(parser, type1, type2, type3)) return; + + const uint8_t *location = parser->previous.end; + pm_parser_err(parser, location, location, diag_id); + + parser->previous.start = location; + parser->previous.type = PM_TOKEN_MISSING; +} + +static pm_node_t * +parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, pm_diagnostic_id_t diag_id); + +/** + * This is a wrapper of parse_expression, which also checks whether the resulting node is value expression. + */ +static pm_node_t * +parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, pm_diagnostic_id_t diag_id) { + pm_node_t *node = parse_expression(parser, binding_power, diag_id); + pm_assert_value_expression(parser, node); + return node; +} + +/** + * This function controls whether or not we will attempt to parse an expression + * beginning at the subsequent token. It is used when we are in a context where + * an expression is optional. + * + * For example, looking at a range object when we've already lexed the operator, + * we need to know if we should attempt to parse an expression on the right. + * + * For another example, if we've parsed an identifier or a method call and we do + * not have parentheses, then the next token may be the start of an argument or + * it may not. + * + * CRuby parsers that are generated would resolve this by using a lookahead and + * potentially backtracking. We attempt to do this by just looking at the next + * token and making a decision based on that. I am not sure if this is going to + * + * work in all cases, it may need to be refactored later. But it appears to work + * for now. + */ +static inline bool +token_begins_expression_p(pm_token_type_t type) { + switch (type) { + case PM_TOKEN_EQUAL_GREATER: + case PM_TOKEN_KEYWORD_IN: + // We need to special case this because it is a binary operator that + // should not be marked as beginning an expression. + return false; + case PM_TOKEN_BRACE_RIGHT: + case PM_TOKEN_BRACKET_RIGHT: + case PM_TOKEN_COLON: + case PM_TOKEN_COMMA: + case PM_TOKEN_EMBEXPR_END: + case PM_TOKEN_EOF: + case PM_TOKEN_LAMBDA_BEGIN: + case PM_TOKEN_KEYWORD_DO: + case PM_TOKEN_KEYWORD_DO_LOOP: + case PM_TOKEN_KEYWORD_END: + case PM_TOKEN_KEYWORD_ELSE: + case PM_TOKEN_KEYWORD_ELSIF: + case PM_TOKEN_KEYWORD_ENSURE: + case PM_TOKEN_KEYWORD_THEN: + case PM_TOKEN_KEYWORD_RESCUE: + case PM_TOKEN_KEYWORD_WHEN: + case PM_TOKEN_NEWLINE: + case PM_TOKEN_PARENTHESIS_RIGHT: + case PM_TOKEN_SEMICOLON: + // The reason we need this short-circuit is because we're using the + // binding powers table to tell us if the subsequent token could + // potentially be the start of an expression . If there _is_ a binding + // power for one of these tokens, then we should remove it from this list + // and let it be handled by the default case below. + assert(pm_binding_powers[type].left == PM_BINDING_POWER_UNSET); + return false; + case PM_TOKEN_UAMPERSAND: + // This is a special case because this unary operator cannot appear + // as a general operator, it only appears in certain circumstances. + return false; + case PM_TOKEN_UCOLON_COLON: + case PM_TOKEN_UMINUS: + case PM_TOKEN_UMINUS_NUM: + case PM_TOKEN_UPLUS: + case PM_TOKEN_BANG: + case PM_TOKEN_TILDE: + case PM_TOKEN_UDOT_DOT: + case PM_TOKEN_UDOT_DOT_DOT: + // These unary tokens actually do have binding power associated with them + // so that we can correctly place them into the precedence order. But we + // want them to be marked as beginning an expression, so we need to + // special case them here. + return true; + default: + return pm_binding_powers[type].left == PM_BINDING_POWER_UNSET; + } +} + +/** + * Parse an expression with the given binding power that may be optionally + * prefixed by the * operator. + */ +static pm_node_t * +parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, pm_diagnostic_id_t diag_id) { + if (accept1(parser, PM_TOKEN_USTAR)) { + pm_token_t operator = parser->previous; + pm_node_t *expression = parse_value_expression(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR); + return (pm_node_t *) pm_splat_node_create(parser, &operator, expression); + } + + return parse_value_expression(parser, binding_power, diag_id); +} + +/** + * Convert the name of a method into the corresponding write method name. For + * example, foo would be turned into foo=. + */ +static void +parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) { + // The method name needs to change. If we previously had + // foo, we now need foo=. In this case we'll allocate a new + // owned string, copy the previous method name in, and + // append an =. + pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *name_field); + size_t length = constant->length; + uint8_t *name = calloc(length + 1, sizeof(uint8_t)); + if (name == NULL) return; + + memcpy(name, constant->start, length); + name[length] = '='; + + // Now switch the name to the new string. + // This silences clang analyzer warning about leak of memory pointed by `name`. + // NOLINTNEXTLINE(clang-analyzer-*) + *name_field = pm_constant_pool_insert_owned(&parser->constant_pool, name, length + 1); +} + +/** + * Convert the given node into a valid target node. + */ +static pm_node_t * +parse_target(pm_parser_t *parser, pm_node_t *target) { + switch (PM_NODE_TYPE(target)) { + case PM_MISSING_NODE: + return target; + case PM_CLASS_VARIABLE_READ_NODE: + assert(sizeof(pm_class_variable_target_node_t) == sizeof(pm_class_variable_read_node_t)); + target->type = PM_CLASS_VARIABLE_TARGET_NODE; + return target; + case PM_CONSTANT_PATH_NODE: + assert(sizeof(pm_constant_path_target_node_t) == sizeof(pm_constant_path_node_t)); + target->type = PM_CONSTANT_PATH_TARGET_NODE; + return target; + case PM_CONSTANT_READ_NODE: + assert(sizeof(pm_constant_target_node_t) == sizeof(pm_constant_read_node_t)); + target->type = PM_CONSTANT_TARGET_NODE; + return target; + case PM_BACK_REFERENCE_READ_NODE: + case PM_NUMBERED_REFERENCE_READ_NODE: + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_READONLY); + return target; + case PM_GLOBAL_VARIABLE_READ_NODE: + assert(sizeof(pm_global_variable_target_node_t) == sizeof(pm_global_variable_read_node_t)); + target->type = PM_GLOBAL_VARIABLE_TARGET_NODE; + return target; + case PM_LOCAL_VARIABLE_READ_NODE: + if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) { + PM_PARSER_ERR_NODE_FORMAT(parser, target, PM_ERR_PARAMETER_NUMBERED_RESERVED, target->location.start); + } else { + assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t)); + target->type = PM_LOCAL_VARIABLE_TARGET_NODE; + } + + return target; + case PM_INSTANCE_VARIABLE_READ_NODE: + assert(sizeof(pm_instance_variable_target_node_t) == sizeof(pm_instance_variable_read_node_t)); + target->type = PM_INSTANCE_VARIABLE_TARGET_NODE; + return target; + case PM_MULTI_TARGET_NODE: + return target; + case PM_SPLAT_NODE: { + pm_splat_node_t *splat = (pm_splat_node_t *) target; + + if (splat->expression != NULL) { + splat->expression = parse_target(parser, splat->expression); + } + + return (pm_node_t *) splat; + } + case PM_CALL_NODE: { + pm_call_node_t *call = (pm_call_node_t *) target; + + // If we have no arguments to the call node and we need this to be a + // target then this is either a method call or a local variable write. + if ( + (call->message_loc.start != NULL) && + (call->message_loc.end[-1] != '!') && + (call->message_loc.end[-1] != '?') && + (call->opening_loc.start == NULL) && + (call->arguments == NULL) && + (call->block == NULL) + ) { + if (call->receiver == NULL) { + // When we get here, we have a local variable write, because it + // was previously marked as a method call but now we have an =. + // This looks like: + // + // foo = 1 + // + // When it was parsed in the prefix position, foo was seen as a + // method call with no receiver and no arguments. Now we have an + // =, so we know it's a local variable write. + const pm_location_t message = call->message_loc; + + pm_parser_local_add_location(parser, message.start, message.end); + pm_node_destroy(parser, target); + + uint32_t depth = 0; + for (pm_scope_t *scope = parser->current_scope; scope && scope->transparent; depth++, scope = scope->previous); + const pm_token_t name = { .type = PM_TOKEN_IDENTIFIER, .start = message.start, .end = message.end }; + target = (pm_node_t *) pm_local_variable_read_node_create(parser, &name, depth); + + assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t)); + target->type = PM_LOCAL_VARIABLE_TARGET_NODE; + + pm_refute_numbered_parameter(parser, message.start, message.end); + return target; + } + + if (*call->message_loc.start == '_' || parser->encoding.alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) { + parse_write_name(parser, &call->name); + return (pm_node_t *) call; + } + } + + // If there is no call operator and the message is "[]" then this is + // an aref expression, and we can transform it into an aset + // expression. + if ( + (call->call_operator_loc.start == NULL) && + (call->message_loc.start != NULL) && + (call->message_loc.start[0] == '[') && + (call->message_loc.end[-1] == ']') && + (call->block == NULL) + ) { + // Replace the name with "[]=". + call->name = pm_parser_constant_id_constant(parser, "[]=", 3); + return target; + } + } + /* fallthrough */ + default: + // In this case we have a node that we don't know how to convert + // into a target. We need to treat it as an error. For now, we'll + // mark it as an error and just skip right past it. + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED); + return target; + } +} + +/** + * Parse a write target and validate that it is in a valid position for + * assignment. + */ +static pm_node_t * +parse_target_validate(pm_parser_t *parser, pm_node_t *target) { + pm_node_t *result = parse_target(parser, target); + + // Ensure that we have one of an =, an 'in' in for indexes, and a ')' in parens after the targets. + if ( + !match1(parser, PM_TOKEN_EQUAL) && + !(context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) && + !(context_p(parser, PM_CONTEXT_PARENS) && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) + ) { + pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + + return result; +} + +/** + * Convert the given node into a valid write node. + */ +static pm_node_t * +parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_node_t *value) { + switch (PM_NODE_TYPE(target)) { + case PM_MISSING_NODE: + return target; + case PM_CLASS_VARIABLE_READ_NODE: { + pm_class_variable_write_node_t *node = pm_class_variable_write_node_create(parser, (pm_class_variable_read_node_t *) target, operator, value); + pm_node_destroy(parser, target); + return (pm_node_t *) node; + } + case PM_CONSTANT_PATH_NODE: + return (pm_node_t *) pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value); + case PM_CONSTANT_READ_NODE: { + pm_constant_write_node_t *node = pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value); + pm_node_destroy(parser, target); + return (pm_node_t *) node; + } + case PM_BACK_REFERENCE_READ_NODE: + case PM_NUMBERED_REFERENCE_READ_NODE: + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_READONLY); + /* fallthrough */ + case PM_GLOBAL_VARIABLE_READ_NODE: { + pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value); + pm_node_destroy(parser, target); + return (pm_node_t *) node; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_refute_numbered_parameter(parser, target->location.start, target->location.end); + pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target; + + pm_constant_id_t constant_id = local_read->name; + uint32_t depth = local_read->depth; + + pm_location_t name_loc = target->location; + pm_node_destroy(parser, target); + + return (pm_node_t *) pm_local_variable_write_node_create(parser, constant_id, depth, value, &name_loc, operator); + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_node_t *write_node = (pm_node_t *) pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value); + pm_node_destroy(parser, target); + return write_node; + } + case PM_MULTI_TARGET_NODE: + return (pm_node_t *) pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value); + case PM_SPLAT_NODE: { + pm_splat_node_t *splat = (pm_splat_node_t *) target; + + if (splat->expression != NULL) { + splat->expression = parse_write(parser, splat->expression, operator, value); + } + + pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser); + pm_multi_target_node_targets_append(parser, multi_target, (pm_node_t *) splat); + + return (pm_node_t *) pm_multi_write_node_create(parser, multi_target, operator, value); + } + case PM_CALL_NODE: { + pm_call_node_t *call = (pm_call_node_t *) target; + + // If we have no arguments to the call node and we need this to be a + // target then this is either a method call or a local variable + // write. + if ( + (call->message_loc.start != NULL) && + (call->message_loc.end[-1] != '!') && + (call->message_loc.end[-1] != '?') && + (call->opening_loc.start == NULL) && + (call->arguments == NULL) && + (call->block == NULL) + ) { + if (call->receiver == NULL) { + // When we get here, we have a local variable write, because it + // was previously marked as a method call but now we have an =. + // This looks like: + // + // foo = 1 + // + // When it was parsed in the prefix position, foo was seen as a + // method call with no receiver and no arguments. Now we have an + // =, so we know it's a local variable write. + const pm_location_t message = call->message_loc; + + pm_parser_local_add_location(parser, message.start, message.end); + pm_node_destroy(parser, target); + + pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, message.start, message.end); + target = (pm_node_t *) pm_local_variable_write_node_create(parser, constant_id, 0, value, &message, operator); + + pm_refute_numbered_parameter(parser, message.start, message.end); + return target; + } + + if (*call->message_loc.start == '_' || parser->encoding.alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) { + // When we get here, we have a method call, because it was + // previously marked as a method call but now we have an =. This + // looks like: + // + // foo.bar = 1 + // + // When it was parsed in the prefix position, foo.bar was seen as a + // method call with no arguments. Now we have an =, so we know it's + // a method call with an argument. In this case we will create the + // arguments node, parse the argument, and add it to the list. + pm_arguments_node_t *arguments = pm_arguments_node_create(parser); + call->arguments = arguments; + + pm_arguments_node_arguments_append(arguments, value); + call->base.location.end = arguments->base.location.end; + + parse_write_name(parser, &call->name); + return (pm_node_t *) call; + } + } + + // If there is no call operator and the message is "[]" then this is + // an aref expression, and we can transform it into an aset + // expression. + if (pm_call_node_index_p(call)) { + if (call->arguments == NULL) { + call->arguments = pm_arguments_node_create(parser); + } + + pm_arguments_node_arguments_append(call->arguments, value); + target->location.end = value->location.end; + + // Replace the name with "[]=". + call->name = pm_parser_constant_id_constant(parser, "[]=", 3); + return target; + } + + // If there are arguments on the call node, then it can't be a method + // call ending with = or a local variable write, so it must be a + // syntax error. In this case we'll fall through to our default + // handling. We need to free the value that we parsed because there + // is no way for us to attach it to the tree at this point. + pm_node_destroy(parser, value); + } + /* fallthrough */ + default: + // In this case we have a node that we don't know how to convert into a + // target. We need to treat it as an error. For now, we'll mark it as an + // error and just skip right past it. + pm_parser_err_token(parser, operator, PM_ERR_WRITE_TARGET_UNEXPECTED); + return target; + } +} + +/** + * Parse a list of targets for assignment. This is used in the case of a for + * loop or a multi-assignment. For example, in the following code: + * + * for foo, bar in baz + * ^^^^^^^^ + * + * The targets are `foo` and `bar`. This function will either return a single + * target node or a multi-target node. + */ +static pm_node_t * +parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power) { + bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE); + + pm_multi_target_node_t *result = pm_multi_target_node_create(parser); + pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target)); + + while (accept1(parser, PM_TOKEN_COMMA)) { + if (accept1(parser, PM_TOKEN_USTAR)) { + // Here we have a splat operator. It can have a name or be + // anonymous. It can be the final target or be in the middle if + // there haven't been any others yet. + if (has_rest) { + pm_parser_err_previous(parser, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS); + } + + pm_token_t star_operator = parser->previous; + pm_node_t *name = NULL; + + if (token_begins_expression_p(parser->current.type)) { + name = parse_expression(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR); + name = parse_target(parser, name); + } + + pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name); + pm_multi_target_node_targets_append(parser, result, splat); + has_rest = true; + } else if (token_begins_expression_p(parser->current.type)) { + pm_node_t *target = parse_expression(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA); + target = parse_target(parser, target); + + pm_multi_target_node_targets_append(parser, result, target); + } else if (!match1(parser, PM_TOKEN_EOF)) { + // If we get here, then we have a trailing , in a multi target node. + // We'll set the implicit rest flag to indicate this. + pm_node_t *rest = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); + pm_multi_target_node_targets_append(parser, result, rest); + has_rest = true; + break; + } + } + + return (pm_node_t *) result; +} + +/** + * Parse a list of targets and validate that it is in a valid position for + * assignment. + */ +static pm_node_t * +parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power) { + pm_node_t *result = parse_targets(parser, first_target, binding_power); + + // Ensure that we have either an = or a ) after the targets. + if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) { + pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + + return result; +} + +/** + * Parse a list of statements separated by newlines or semicolons. + */ +static pm_statements_node_t * +parse_statements(pm_parser_t *parser, pm_context_t context) { + // First, skip past any optional terminators that might be at the beginning of + // the statements. + while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)); + + // If we have a terminator, then we can just return NULL. + if (context_terminator(context, &parser->current)) return NULL; + + pm_statements_node_t *statements = pm_statements_node_create(parser); + + // At this point we know we have at least one statement, and that it + // immediately follows the current token. + context_push(parser, context); + + while (true) { + pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_ERR_CANNOT_PARSE_EXPRESSION); + pm_statements_node_body_append(statements, node); + + // If we're recovering from a syntax error, then we need to stop parsing the + // statements now. + if (parser->recovering) { + // If this is the level of context where the recovery has happened, then + // we can mark the parser as done recovering. + if (context_terminator(context, &parser->current)) parser->recovering = false; + break; + } + + // If we have a terminator, then we will parse all consequtive terminators + // and then continue parsing the statements list. + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + // If we have a terminator, then we will continue parsing the statements + // list. + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + if (context_terminator(context, &parser->current)) break; + + // Now we can continue parsing the list of statements. + continue; + } + + // At this point we have a list of statements that are not terminated by a + // newline or semicolon. At this point we need to check if we're at the end + // of the statements list. If we are, then we should break out of the loop. + if (context_terminator(context, &parser->current)) break; + + // At this point, we have a syntax error, because the statement was not + // terminated by a newline or semicolon, and we're not at the end of the + // statements list. Ideally we should scan forward to determine if we should + // insert a missing terminator or break out of parsing the statements list + // at this point. + // + // We don't have that yet, so instead we'll do a more naive approach. If we + // were unable to parse an expression, then we will skip past this token and + // continue parsing the statements list. Otherwise we'll add an error and + // continue parsing the statements list. + if (PM_NODE_TYPE_P(node, PM_MISSING_NODE)) { + parser_lex(parser); + + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + if (context_terminator(context, &parser->current)) break; + } else { + expect1(parser, PM_TOKEN_NEWLINE, PM_ERR_EXPECT_EOL_AFTER_STATEMENT); + } + } + + context_pop(parser); + return statements; +} + +/** + * Parse all of the elements of a hash. returns true if a double splat was found. + */ +static bool +parse_assocs(pm_parser_t *parser, pm_node_t *node) { + assert(PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)); + bool contains_keyword_splat = false; + + while (true) { + pm_node_t *element; + + switch (parser->current.type) { + case PM_TOKEN_USTAR_STAR: { + parser_lex(parser); + pm_token_t operator = parser->previous; + pm_node_t *value = NULL; + + if (token_begins_expression_p(parser->current.type)) { + value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH); + } else if (pm_parser_local_depth(parser, &operator) == -1) { + pm_parser_err_token(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH); + } + + element = (pm_node_t *) pm_assoc_splat_node_create(parser, value, &operator); + contains_keyword_splat = true; + break; + } + case PM_TOKEN_LABEL: { + pm_token_t label = parser->current; + parser_lex(parser); + + pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &label); + pm_token_t operator = not_provided(parser); + pm_node_t *value = NULL; + + if (token_begins_expression_p(parser->current.type)) { + value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_HASH_EXPRESSION_AFTER_LABEL); + } else { + if (parser->encoding.isupper_char(label.start, (label.end - 1) - label.start)) { + pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 }; + value = (pm_node_t *) pm_constant_read_node_create(parser, &constant); + } else { + int depth = pm_parser_local_depth(parser, &((pm_token_t) { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 })); + pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 }; + + if (depth == -1) { + value = (pm_node_t *) pm_call_node_variable_call_create(parser, &identifier); + } else { + value = (pm_node_t *) pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth); + } + } + + value->location.end++; + value = (pm_node_t *) pm_implicit_node_create(parser, value); + } + + element = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value); + break; + } + default: { + pm_node_t *key = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_HASH_KEY); + pm_token_t operator; + + if (pm_symbol_node_label_p(key)) { + operator = not_provided(parser); + } else { + expect1(parser, PM_TOKEN_EQUAL_GREATER, PM_ERR_HASH_ROCKET); + operator = parser->previous; + } + + pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_HASH_VALUE); + element = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value); + break; + } + } + + if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) { + pm_hash_node_elements_append((pm_hash_node_t *) node, element); + } else { + pm_keyword_hash_node_elements_append((pm_keyword_hash_node_t *) node, element); + } + + // If there's no comma after the element, then we're done. + if (!accept1(parser, PM_TOKEN_COMMA)) break; + + // If the next element starts with a label or a **, then we know we have + // another element in the hash, so we'll continue parsing. + if (match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)) continue; + + // Otherwise we need to check if the subsequent token begins an expression. + // If it does, then we'll continue parsing. + if (token_begins_expression_p(parser->current.type)) continue; + + // Otherwise by default we will exit out of this loop. + break; + } + return contains_keyword_splat; +} + +/** + * Append an argument to a list of arguments. + */ +static inline void +parse_arguments_append(pm_parser_t *parser, pm_arguments_t *arguments, pm_node_t *argument) { + if (arguments->arguments == NULL) { + arguments->arguments = pm_arguments_node_create(parser); + } + + pm_arguments_node_arguments_append(arguments->arguments, argument); +} + +/** + * Parse a list of arguments. + */ +static void +parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator) { + pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left; + + // First we need to check if the next token is one that could be the start of + // an argument. If it's not, then we can just return. + if ( + match2(parser, terminator, PM_TOKEN_EOF) || + (binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) || + context_terminator(parser->current_context->context, &parser->current) + ) { + return; + } + + bool parsed_first_argument = false; + bool parsed_bare_hash = false; + bool parsed_block_argument = false; + bool parsed_forwarding_arguments = false; + + while (!match1(parser, PM_TOKEN_EOF)) { + if (parsed_block_argument) { + pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK); + } + if (parsed_forwarding_arguments) { + pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES); + } + + pm_node_t *argument = NULL; + + switch (parser->current.type) { + case PM_TOKEN_USTAR_STAR: + case PM_TOKEN_LABEL: { + if (parsed_bare_hash) { + pm_parser_err_current(parser, PM_ERR_ARGUMENT_BARE_HASH); + } + + pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser); + argument = (pm_node_t *) hash; + + bool contains_keyword_splat = false; + if (!match7(parser, terminator, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) { + contains_keyword_splat = parse_assocs(parser, (pm_node_t *) hash); + } + + parsed_bare_hash = true; + parse_arguments_append(parser, arguments, argument); + if (contains_keyword_splat) { + arguments->arguments->base.flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + } + break; + } + case PM_TOKEN_UAMPERSAND: { + parser_lex(parser); + pm_token_t operator = parser->previous; + pm_node_t *expression = NULL; + + if (token_begins_expression_p(parser->current.type)) { + expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_EXPECT_ARGUMENT); + } else { + if (pm_parser_local_depth(parser, &operator) == -1) { + // A block forwarding in a method having `...` parameter (e.g. `def foo(...); bar(&); end`) is available. + pm_constant_id_t ellipsis_id = pm_parser_constant_id_constant(parser, "...", 3); + if (pm_parser_local_depth_constant_id(parser, ellipsis_id) == -1) { + pm_parser_err_token(parser, &operator, PM_ERR_ARGUMENT_NO_FORWARDING_AMP); + } + } + } + + argument = (pm_node_t *) pm_block_argument_node_create(parser, &operator, expression); + if (parsed_block_argument) { + parse_arguments_append(parser, arguments, argument); + } else { + arguments->block = argument; + } + + parsed_block_argument = true; + break; + } + case PM_TOKEN_USTAR: { + parser_lex(parser); + pm_token_t operator = parser->previous; + + if (match4(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_SEMICOLON, PM_TOKEN_BRACKET_RIGHT)) { + if (pm_parser_local_depth(parser, &parser->previous) == -1) { + pm_parser_err_token(parser, &operator, PM_ERR_ARGUMENT_NO_FORWARDING_STAR); + } + + argument = (pm_node_t *) pm_splat_node_create(parser, &operator, NULL); + } else { + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT); + + if (parsed_bare_hash) { + pm_parser_err(parser, operator.start, expression->location.end, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT); + } + + argument = (pm_node_t *) pm_splat_node_create(parser, &operator, expression); + } + + parse_arguments_append(parser, arguments, argument); + break; + } + case PM_TOKEN_UDOT_DOT_DOT: { + if (accepts_forwarding) { + parser_lex(parser); + + if (token_begins_expression_p(parser->current.type)) { + // If the token begins an expression then this ... was not actually + // argument forwarding but was instead a range. + pm_token_t operator = parser->previous; + pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_RANGE, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + argument = (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + } else { + if (pm_parser_local_depth(parser, &parser->previous) == -1) { + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); + } + if (parsed_first_argument && terminator == PM_TOKEN_EOF) { + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND); + } + + argument = (pm_node_t *) pm_forwarding_arguments_node_create(parser, &parser->previous); + parse_arguments_append(parser, arguments, argument); + parsed_forwarding_arguments = true; + break; + } + } + } + /* fallthrough */ + default: { + if (argument == NULL) { + argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_EXPECT_ARGUMENT); + } + + bool contains_keyword_splat = false; + if (pm_symbol_node_label_p(argument) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) { + if (parsed_bare_hash) { + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH); + } + + pm_token_t operator; + if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) { + operator = parser->previous; + } else { + operator = not_provided(parser); + } + + pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser); + + // Finish parsing the one we are part way through + pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_HASH_VALUE); + + argument = (pm_node_t *) pm_assoc_node_create(parser, argument, &operator, value); + pm_keyword_hash_node_elements_append(bare_hash, argument); + argument = (pm_node_t *) bare_hash; + + // Then parse more if we have a comma + if (accept1(parser, PM_TOKEN_COMMA) && ( + token_begins_expression_p(parser->current.type) || + match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL) + )) { + contains_keyword_splat = parse_assocs(parser, (pm_node_t *) bare_hash); + } + + parsed_bare_hash = true; + } + + parse_arguments_append(parser, arguments, argument); + if (contains_keyword_splat) { + arguments->arguments->base.flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + } + break; + } + } + + parsed_first_argument = true; + + // If parsing the argument failed, we need to stop parsing arguments. + if (PM_NODE_TYPE_P(argument, PM_MISSING_NODE) || parser->recovering) break; + + // If the terminator of these arguments is not EOF, then we have a specific + // token we're looking for. In that case we can accept a newline here + // because it is not functioning as a statement terminator. + if (terminator != PM_TOKEN_EOF) accept1(parser, PM_TOKEN_NEWLINE); + + if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) { + // If we previously were on a comma and we just parsed a bare hash, then + // we want to continue parsing arguments. This is because the comma was + // grabbed up by the hash parser. + } else { + // If there is no comma at the end of the argument list then we're done + // parsing arguments and can break out of this loop. + if (!accept1(parser, PM_TOKEN_COMMA)) break; + } + + // If we hit the terminator, then that means we have a trailing comma so we + // can accept that output as well. + if (match1(parser, terminator)) break; + } +} + +/** + * Required parameters on method, block, and lambda declarations can be + * destructured using parentheses. This looks like: + * + * def foo((bar, baz)) + * end + * + * + * It can recurse infinitely down, and splats are allowed to group arguments. + */ +static pm_multi_target_node_t * +parse_required_destructured_parameter(pm_parser_t *parser) { + expect1(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER); + + pm_multi_target_node_t *node = pm_multi_target_node_create(parser); + pm_multi_target_node_opening_set(node, &parser->previous); + + do { + pm_node_t *param; + + // If we get here then we have a trailing comma, which isn't allowed in + // the grammar. In other places, multi targets _do_ allow trailing + // commas, so here we'll assume this is a mistake of the user not + // knowing it's not allowed here. + if (node->lefts.size > 0 && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + param = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); + pm_multi_target_node_targets_append(parser, node, param); + pm_parser_err_current(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); + break; + } + + if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + param = (pm_node_t *) parse_required_destructured_parameter(parser); + } else if (accept1(parser, PM_TOKEN_USTAR)) { + pm_token_t star = parser->previous; + pm_node_t *value = NULL; + + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + pm_token_t name = parser->previous; + value = (pm_node_t *) pm_required_parameter_node_create(parser, &name); + pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name); + } + + param = (pm_node_t *) pm_splat_node_create(parser, &star, value); + } else { + expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER); + pm_token_t name = parser->previous; + + param = (pm_node_t *) pm_required_parameter_node_create(parser, &name); + pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name); + } + + pm_multi_target_node_targets_append(parser, node, param); + } while (accept1(parser, PM_TOKEN_COMMA)); + + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN_REQ_PARAMETER); + pm_multi_target_node_closing_set(node, &parser->previous); + + return node; +} + +/** + * This represents the different order states we can be in when parsing + * method parameters. + */ +typedef enum { + PM_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state + PM_PARAMETERS_ORDER_NOTHING_AFTER = 1, + PM_PARAMETERS_ORDER_KEYWORDS_REST, + PM_PARAMETERS_ORDER_KEYWORDS, + PM_PARAMETERS_ORDER_REST, + PM_PARAMETERS_ORDER_AFTER_OPTIONAL, + PM_PARAMETERS_ORDER_OPTIONAL, + PM_PARAMETERS_ORDER_NAMED, + PM_PARAMETERS_ORDER_NONE, + +} pm_parameters_order_t; + +/** + * This matches parameters tokens with parameters state. + */ +static pm_parameters_order_t parameters_ordering[PM_TOKEN_MAXIMUM] = { + [0] = PM_PARAMETERS_NO_CHANGE, + [PM_TOKEN_UAMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER, + [PM_TOKEN_AMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER, + [PM_TOKEN_UDOT_DOT_DOT] = PM_PARAMETERS_ORDER_NOTHING_AFTER, + [PM_TOKEN_IDENTIFIER] = PM_PARAMETERS_ORDER_NAMED, + [PM_TOKEN_PARENTHESIS_LEFT] = PM_PARAMETERS_ORDER_NAMED, + [PM_TOKEN_EQUAL] = PM_PARAMETERS_ORDER_OPTIONAL, + [PM_TOKEN_LABEL] = PM_PARAMETERS_ORDER_KEYWORDS, + [PM_TOKEN_USTAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL, + [PM_TOKEN_STAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL, + [PM_TOKEN_USTAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST, + [PM_TOKEN_STAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST +}; + +/** + * Check if current parameter follows valid parameters ordering. If not it adds + * an error to the list without stopping the parsing, otherwise sets the + * parameters state to the one corresponding to the current parameter. + */ +static void +update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_order_t *current) { + pm_parameters_order_t state = parameters_ordering[token->type]; + if (state == PM_PARAMETERS_NO_CHANGE) return; + + // If we see another ordered argument after a optional argument + // we only continue parsing ordered arguments until we stop seeing ordered arguments + if (*current == PM_PARAMETERS_ORDER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) { + *current = PM_PARAMETERS_ORDER_AFTER_OPTIONAL; + return; + } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) { + return; + } + + if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { + pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR); + } + + if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) { + // We know what transition we failed on, so we can provide a better error here. + pm_parser_err_token(parser, token, PM_ERR_PARAMETER_ORDER); + } else if (state < *current) { + *current = state; + } +} + +/** + * Parse a list of parameters on a method definition. + */ +static pm_parameters_node_t * +parse_parameters( + pm_parser_t *parser, + pm_binding_power_t binding_power, + bool uses_parentheses, + bool allows_trailing_comma, + bool allows_forwarding_parameters +) { + pm_parameters_node_t *params = pm_parameters_node_create(parser); + bool looping = true; + + pm_do_loop_stack_push(parser, false); + pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE; + + do { + switch (parser->current.type) { + case PM_TOKEN_PARENTHESIS_LEFT: { + update_parameter_state(parser, &parser->current, &order); + pm_node_t *param = (pm_node_t *) parse_required_destructured_parameter(parser); + + if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { + pm_parameters_node_requireds_append(params, param); + } else { + pm_parameters_node_posts_append(params, param); + } + break; + } + case PM_TOKEN_UAMPERSAND: + case PM_TOKEN_AMPERSAND: { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_token_t name; + + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + name = parser->previous; + pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name); + } else { + name = not_provided(parser); + + if (allows_forwarding_parameters) { + pm_parser_local_add_token(parser, &operator); + } + } + + pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator); + if (params->block == NULL) { + pm_parameters_node_block_set(params, param); + } else { + pm_parser_err_node(parser, (pm_node_t *) param, PM_ERR_PARAMETER_BLOCK_MULTI); + pm_parameters_node_posts_append(params, (pm_node_t *) param); + } + + break; + } + case PM_TOKEN_UDOT_DOT_DOT: { + if (!allows_forwarding_parameters) { + pm_parser_err_current(parser, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); + } + + if (order > PM_PARAMETERS_ORDER_NOTHING_AFTER) { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + if (allows_forwarding_parameters) { + pm_parser_local_add_token(parser, &parser->previous); + } + + pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous); + if (params->keyword_rest != NULL) { + // If we already have a keyword rest parameter, then we replace it with the + // forwarding parameter and move the keyword rest parameter to the posts list. + pm_node_t *keyword_rest = params->keyword_rest; + pm_parameters_node_posts_append(params, keyword_rest); + pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD); + params->keyword_rest = NULL; + } + pm_parameters_node_keyword_rest_set(params, (pm_node_t *)param); + } else { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + } + + break; + } + case PM_TOKEN_CLASS_VARIABLE: + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_CONSTANT: + case PM_TOKEN_INSTANCE_VARIABLE: + case PM_TOKEN_GLOBAL_VARIABLE: + case PM_TOKEN_METHOD_NAME: { + parser_lex(parser); + switch (parser->previous.type) { + case PM_TOKEN_CONSTANT: + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT); + break; + case PM_TOKEN_INSTANCE_VARIABLE: + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_IVAR); + break; + case PM_TOKEN_GLOBAL_VARIABLE: + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL); + break; + case PM_TOKEN_CLASS_VARIABLE: + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CLASS); + break; + case PM_TOKEN_METHOD_NAME: + pm_parser_err_previous(parser, PM_ERR_PARAMETER_METHOD_NAME); + break; + default: break; + } + + if (parser->current.type == PM_TOKEN_EQUAL) { + update_parameter_state(parser, &parser->current, &order); + } else { + update_parameter_state(parser, &parser->previous, &order); + } + + pm_token_t name = parser->previous; + pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name); + + if (accept1(parser, PM_TOKEN_EQUAL)) { + pm_token_t operator = parser->previous; + context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); + pm_constant_id_t old_param_name = parser->current_param_name; + parser->current_param_name = pm_parser_constant_id_token(parser, &name); + pm_node_t *value = parse_value_expression(parser, binding_power, PM_ERR_PARAMETER_NO_DEFAULT); + + pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value); + pm_parameters_node_optionals_append(params, param); + + parser->current_param_name = old_param_name; + context_pop(parser); + + // If parsing the value of the parameter resulted in error recovery, + // then we can put a missing node in its place and stop parsing the + // parameters entirely now. + if (parser->recovering) { + looping = false; + break; + } + } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { + pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name); + pm_parameters_node_requireds_append(params, (pm_node_t *) param); + } else { + pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name); + pm_parameters_node_posts_append(params, (pm_node_t *) param); + } + + break; + } + case PM_TOKEN_LABEL: { + if (!uses_parentheses) parser->in_keyword_arg = true; + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + pm_token_t name = parser->previous; + pm_token_t local = name; + local.end -= 1; + + pm_parser_parameter_name_check(parser, &local); + pm_parser_local_add_token(parser, &local); + + switch (parser->current.type) { + case PM_TOKEN_COMMA: + case PM_TOKEN_PARENTHESIS_RIGHT: + case PM_TOKEN_PIPE: { + pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + pm_parameters_node_keywords_append(params, param); + break; + } + case PM_TOKEN_SEMICOLON: + case PM_TOKEN_NEWLINE: { + if (uses_parentheses) { + looping = false; + break; + } + + pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + pm_parameters_node_keywords_append(params, param); + break; + } + default: { + pm_node_t *param; + + if (token_begins_expression_p(parser->current.type)) { + context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); + pm_constant_id_t old_param_name = parser->current_param_name; + parser->current_param_name = pm_parser_constant_id_token(parser, &local); + pm_node_t *value = parse_value_expression(parser, binding_power, PM_ERR_PARAMETER_NO_DEFAULT_KW); + parser->current_param_name = old_param_name; + context_pop(parser); + param = (pm_node_t *) pm_optional_keyword_parameter_node_create(parser, &name, value); + } + else { + param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + } + + pm_parameters_node_keywords_append(params, param); + + // If parsing the value of the parameter resulted in error recovery, + // then we can put a missing node in its place and stop parsing the + // parameters entirely now. + if (parser->recovering) { + looping = false; + break; + } + } + } + + parser->in_keyword_arg = false; + break; + } + case PM_TOKEN_USTAR: + case PM_TOKEN_STAR: { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_token_t name; + + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + name = parser->previous; + pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name); + } else { + name = not_provided(parser); + + if (allows_forwarding_parameters) { + pm_parser_local_add_token(parser, &operator); + } + } + + pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name); + if (params->rest == NULL) { + pm_parameters_node_rest_set(params, param); + } else { + pm_parser_err_node(parser, param, PM_ERR_PARAMETER_SPLAT_MULTI); + pm_parameters_node_posts_append(params, param); + } + + break; + } + case PM_TOKEN_STAR_STAR: + case PM_TOKEN_USTAR_STAR: { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *param; + + if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) { + param = (pm_node_t *) pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous); + } else { + pm_token_t name; + + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + name = parser->previous; + pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name); + } else { + name = not_provided(parser); + + if (allows_forwarding_parameters) { + pm_parser_local_add_token(parser, &operator); + } + } + + param = (pm_node_t *) pm_keyword_rest_parameter_node_create(parser, &operator, &name); + } + + if (params->keyword_rest == NULL) { + pm_parameters_node_keyword_rest_set(params, param); + } else { + pm_parser_err_node(parser, param, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI); + pm_parameters_node_posts_append(params, param); + } + + break; + } + default: + if (parser->previous.type == PM_TOKEN_COMMA) { + if (allows_trailing_comma) { + // If we get here, then we have a trailing comma in a + // block parameter list. + pm_node_t *param = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); + + if (params->rest == NULL) { + pm_parameters_node_rest_set(params, param); + } else { + pm_parser_err_node(parser, (pm_node_t *) param, PM_ERR_PARAMETER_SPLAT_MULTI); + pm_parameters_node_posts_append(params, (pm_node_t *) param); + } + } else { + pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); + } + } + + looping = false; + break; + } + + if (looping && uses_parentheses) { + accept1(parser, PM_TOKEN_NEWLINE); + } + } while (looping && accept1(parser, PM_TOKEN_COMMA)); + + pm_do_loop_stack_pop(parser); + + // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`. + if (params->base.location.start == params->base.location.end) { + pm_node_destroy(parser, (pm_node_t *) params); + return NULL; + } + + return params; +} + +/** + * Parse any number of rescue clauses. This will form a linked list of if + * nodes pointing to each other from the top. + */ +static inline void +parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) { + pm_rescue_node_t *current = NULL; + + while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE)) { + pm_rescue_node_t *rescue = pm_rescue_node_create(parser, &parser->previous); + + switch (parser->current.type) { + case PM_TOKEN_EQUAL_GREATER: { + // Here we have an immediate => after the rescue keyword, in which case + // we're going to have an empty list of exceptions to rescue (which + // implies StandardError). + parser_lex(parser); + pm_rescue_node_operator_set(rescue, &parser->previous); + + pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_ERR_RESCUE_VARIABLE); + reference = parse_target(parser, reference); + + pm_rescue_node_reference_set(rescue, reference); + break; + } + case PM_TOKEN_NEWLINE: + case PM_TOKEN_SEMICOLON: + case PM_TOKEN_KEYWORD_THEN: + // Here we have a terminator for the rescue keyword, in which case we're + // going to just continue on. + break; + default: { + if (token_begins_expression_p(parser->current.type) || match1(parser, PM_TOKEN_USTAR)) { + // Here we have something that could be an exception expression, so + // we'll attempt to parse it here and any others delimited by commas. + + do { + pm_node_t *expression = parse_starred_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_RESCUE_EXPRESSION); + pm_rescue_node_exceptions_append(rescue, expression); + + // If we hit a newline, then this is the end of the rescue expression. We + // can continue on to parse the statements. + if (match3(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_THEN)) break; + + // If we hit a `=>` then we're going to parse the exception variable. Once + // we've done that, we'll break out of the loop and parse the statements. + if (accept1(parser, PM_TOKEN_EQUAL_GREATER)) { + pm_rescue_node_operator_set(rescue, &parser->previous); + + pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_ERR_RESCUE_VARIABLE); + reference = parse_target(parser, reference); + + pm_rescue_node_reference_set(rescue, reference); + break; + } + } while (accept1(parser, PM_TOKEN_COMMA)); + } + } + } + + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + accept1(parser, PM_TOKEN_KEYWORD_THEN); + } else { + expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM); + } + + if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + pm_statements_node_t *statements = parse_statements(parser, def_p ? PM_CONTEXT_RESCUE_DEF : PM_CONTEXT_RESCUE); + if (statements) { + pm_rescue_node_statements_set(rescue, statements); + } + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + if (current == NULL) { + pm_begin_node_rescue_clause_set(parent_node, rescue); + } else { + pm_rescue_node_consequent_set(current, rescue); + } + + current = rescue; + } + + // The end node locations on rescue nodes will not be set correctly + // since we won't know the end until we've found all consequent + // clauses. This sets the end location on all rescues once we know it + if (current) { + const uint8_t *end_to_set = current->base.location.end; + current = parent_node->rescue_clause; + while (current) { + current->base.location.end = end_to_set; + current = current->consequent; + } + } + + if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) { + pm_token_t else_keyword = parser->previous; + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + + pm_statements_node_t *else_statements = NULL; + if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) { + pm_accepts_block_stack_push(parser, true); + else_statements = parse_statements(parser, def_p ? PM_CONTEXT_RESCUE_ELSE_DEF : PM_CONTEXT_RESCUE_ELSE); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + pm_else_node_t *else_clause = pm_else_node_create(parser, &else_keyword, else_statements, &parser->current); + pm_begin_node_else_clause_set(parent_node, else_clause); + } + + if (accept1(parser, PM_TOKEN_KEYWORD_ENSURE)) { + pm_token_t ensure_keyword = parser->previous; + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + + pm_statements_node_t *ensure_statements = NULL; + if (!match1(parser, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + ensure_statements = parse_statements(parser, def_p ? PM_CONTEXT_ENSURE_DEF : PM_CONTEXT_ENSURE); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + pm_ensure_node_t *ensure_clause = pm_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current); + pm_begin_node_ensure_clause_set(parent_node, ensure_clause); + } + + if (parser->current.type == PM_TOKEN_KEYWORD_END) { + pm_begin_node_end_keyword_set(parent_node, &parser->current); + } else { + pm_token_t end_keyword = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + pm_begin_node_end_keyword_set(parent_node, &end_keyword); + } +} + +static inline pm_begin_node_t * +parse_rescues_as_begin(pm_parser_t *parser, pm_statements_node_t *statements, bool def_p) { + pm_token_t no_begin_token = not_provided(parser); + pm_begin_node_t *begin_node = pm_begin_node_create(parser, &no_begin_token, statements); + parse_rescues(parser, begin_node, def_p); + + // All nodes within a begin node are optional, so we look + // for the earliest possible node that we can use to set + // the BeginNode's start location + const uint8_t *start = begin_node->base.location.start; + if (begin_node->statements) { + start = begin_node->statements->base.location.start; + } else if (begin_node->rescue_clause) { + start = begin_node->rescue_clause->base.location.start; + } else if (begin_node->else_clause) { + start = begin_node->else_clause->base.location.start; + } else if (begin_node->ensure_clause) { + start = begin_node->ensure_clause->base.location.start; + } + + begin_node->base.location.start = start; + return begin_node; +} + +/** + * Parse a list of parameters and local on a block definition. + */ +static pm_block_parameters_node_t * +parse_block_parameters( + pm_parser_t *parser, + bool allows_trailing_comma, + const pm_token_t *opening, + bool is_lambda_literal +) { + pm_parameters_node_t *parameters = NULL; + if (!match1(parser, PM_TOKEN_SEMICOLON)) { + parameters = parse_parameters( + parser, + is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX, + false, + allows_trailing_comma, + false + ); + } + + pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening); + if ((opening->type != PM_TOKEN_NOT_PROVIDED) && accept1(parser, PM_TOKEN_SEMICOLON)) { + do { + expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE); + pm_parser_parameter_name_check(parser, &parser->previous); + pm_parser_local_add_token(parser, &parser->previous); + + pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous); + pm_block_parameters_node_append_local(block_parameters, local); + } while (accept1(parser, PM_TOKEN_COMMA)); + } + + return block_parameters; +} + +/** + * Parse a block. + */ +static pm_block_node_t * +parse_block(pm_parser_t *parser) { + pm_token_t opening = parser->previous; + accept1(parser, PM_TOKEN_NEWLINE); + + pm_accepts_block_stack_push(parser, true); + pm_parser_scope_push(parser, false); + pm_block_parameters_node_t *parameters = NULL; + + if (accept1(parser, PM_TOKEN_PIPE)) { + parser->current_scope->explicit_params = true; + pm_token_t block_parameters_opening = parser->previous; + + if (match1(parser, PM_TOKEN_PIPE)) { + parameters = pm_block_parameters_node_create(parser, NULL, &block_parameters_opening); + parser->command_start = true; + parser_lex(parser); + } else { + parameters = parse_block_parameters(parser, true, &block_parameters_opening, false); + accept1(parser, PM_TOKEN_NEWLINE); + parser->command_start = true; + expect1(parser, PM_TOKEN_PIPE, PM_ERR_BLOCK_PARAM_PIPE_TERM); + } + + pm_block_parameters_node_closing_set(parameters, &parser->previous); + } + + accept1(parser, PM_TOKEN_NEWLINE); + pm_node_t *statements = NULL; + + if (opening.type == PM_TOKEN_BRACE_LEFT) { + if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) { + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_BLOCK_BRACES); + } + + expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE); + } else { + if (!match1(parser, PM_TOKEN_KEYWORD_END)) { + if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) { + pm_accepts_block_stack_push(parser, true); + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS); + pm_accepts_block_stack_pop(parser); + } + + if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); + statements = (pm_node_t *) parse_rescues_as_begin(parser, (pm_statements_node_t *) statements, false); + } + } + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END); + } + + pm_constant_id_list_t locals = parser->current_scope->locals; + uint32_t numbered_parameters = parser->current_scope->numbered_parameters; + pm_parser_scope_pop(parser); + pm_accepts_block_stack_pop(parser); + return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous, numbered_parameters); +} + +/** + * Parse a list of arguments and their surrounding parentheses if they are + * present. It returns true if it found any pieces of arguments (parentheses, + * arguments, or blocks). + */ +static bool +parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_block) { + bool found = false; + + if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + found |= true; + arguments->opening_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + + if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + } else { + pm_accepts_block_stack_push(parser, true); + parse_arguments(parser, arguments, true, PM_TOKEN_PARENTHESIS_RIGHT); + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_ARGUMENT_TERM_PAREN); + pm_accepts_block_stack_pop(parser); + + arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + } + } else if ((token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)) && !match1(parser, PM_TOKEN_BRACE_LEFT)) { + found |= true; + pm_accepts_block_stack_push(parser, false); + + // If we get here, then the subsequent token cannot be used as an infix + // operator. In this case we assume the subsequent token is part of an + // argument to this method call. + parse_arguments(parser, arguments, true, PM_TOKEN_EOF); + + // If we have done with the arguments and still not consumed the comma, + // then we have a trailing comma where we need to check whether it is + // allowed or not. + if (parser->previous.type == PM_TOKEN_COMMA && !match1(parser, PM_TOKEN_SEMICOLON)) { + pm_parser_err_previous(parser, PM_ERR_EXPECT_ARGUMENT); + } + + pm_accepts_block_stack_pop(parser); + } + + // If we're at the end of the arguments, we can now check if there is a block + // node that starts with a {. If there is, then we can parse it and add it to + // the arguments. + if (accepts_block) { + pm_block_node_t *block = NULL; + + if (accept1(parser, PM_TOKEN_BRACE_LEFT)) { + found |= true; + block = parse_block(parser); + pm_arguments_validate_block(parser, arguments, block); + } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) { + found |= true; + block = parse_block(parser); + } + + if (block != NULL) { + if (arguments->block == NULL) { + arguments->block = (pm_node_t *) block; + } else { + pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI); + if (arguments->arguments == NULL) { + arguments->arguments = pm_arguments_node_create(parser); + } + pm_arguments_node_arguments_append(arguments->arguments, arguments->block); + arguments->block = (pm_node_t *) block; + } + } + } + + return found; +} + +static inline pm_node_t * +parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword) { + context_push(parser, PM_CONTEXT_PREDICATE); + pm_diagnostic_id_t error_id = context == PM_CONTEXT_IF ? PM_ERR_CONDITIONAL_IF_PREDICATE : PM_ERR_CONDITIONAL_UNLESS_PREDICATE; + pm_node_t *predicate = parse_value_expression(parser, binding_power, error_id); + + // Predicates are closed by a term, a "then", or a term and then a "then". + bool predicate_closed = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + + if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) { + predicate_closed = true; + *then_keyword = parser->previous; + } + + if (!predicate_closed) { + pm_parser_err_current(parser, PM_ERR_CONDITIONAL_PREDICATE_TERM); + } + + context_pop(parser); + return predicate; +} + +static inline pm_node_t * +parse_conditional(pm_parser_t *parser, pm_context_t context) { + pm_token_t keyword = parser->previous; + pm_token_t then_keyword = not_provided(parser); + + pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER, context, &then_keyword); + pm_statements_node_t *statements = NULL; + + if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, context); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + pm_token_t end_keyword = not_provided(parser); + pm_node_t *parent = NULL; + + switch (context) { + case PM_CONTEXT_IF: + parent = (pm_node_t *) pm_if_node_create(parser, &keyword, predicate, &then_keyword, statements, NULL, &end_keyword); + break; + case PM_CONTEXT_UNLESS: + parent = (pm_node_t *) pm_unless_node_create(parser, &keyword, predicate, &then_keyword, statements); + break; + default: + assert(false && "unreachable"); + break; + } + + pm_node_t *current = parent; + + // Parse any number of elsif clauses. This will form a linked list of if + // nodes pointing to each other from the top. + if (context == PM_CONTEXT_IF) { + while (accept1(parser, PM_TOKEN_KEYWORD_ELSIF)) { + pm_token_t elsif_keyword = parser->previous; + pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER, PM_CONTEXT_ELSIF, &then_keyword); + pm_accepts_block_stack_push(parser, true); + pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF); + pm_accepts_block_stack_pop(parser); + + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + + pm_node_t *elsif = (pm_node_t *) pm_if_node_create(parser, &elsif_keyword, predicate, &then_keyword, statements, NULL, &end_keyword); + ((pm_if_node_t *) current)->consequent = elsif; + current = elsif; + } + } + + if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) { + parser_lex(parser); + pm_token_t else_keyword = parser->previous; + + pm_accepts_block_stack_push(parser, true); + pm_statements_node_t *else_statements = parse_statements(parser, PM_CONTEXT_ELSE); + pm_accepts_block_stack_pop(parser); + + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE); + + pm_else_node_t *else_node = pm_else_node_create(parser, &else_keyword, else_statements, &parser->previous); + + switch (context) { + case PM_CONTEXT_IF: + ((pm_if_node_t *) current)->consequent = (pm_node_t *) else_node; + break; + case PM_CONTEXT_UNLESS: + ((pm_unless_node_t *) parent)->consequent = else_node; + break; + default: + assert(false && "unreachable"); + break; + } + } else { + // We should specialize this error message to refer to 'if' or 'unless' explicitly. + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM); + } + + // Set the appropriate end location for all of the nodes in the subtree. + switch (context) { + case PM_CONTEXT_IF: { + pm_node_t *current = parent; + bool recursing = true; + + while (recursing) { + switch (PM_NODE_TYPE(current)) { + case PM_IF_NODE: + pm_if_node_end_keyword_loc_set((pm_if_node_t *) current, &parser->previous); + current = ((pm_if_node_t *) current)->consequent; + recursing = current != NULL; + break; + case PM_ELSE_NODE: + pm_else_node_end_keyword_loc_set((pm_else_node_t *) current, &parser->previous); + recursing = false; + break; + default: { + recursing = false; + break; + } + } + } + break; + } + case PM_CONTEXT_UNLESS: + pm_unless_node_end_keyword_loc_set((pm_unless_node_t *) parent, &parser->previous); + break; + default: + assert(false && "unreachable"); + break; + } + + return parent; +} + +/** + * This macro allows you to define a case statement for all of the keywords. + * It's meant to be used in a switch statement. + */ +#define PM_CASE_KEYWORD PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \ + case PM_TOKEN_KEYWORD_ALIAS: case PM_TOKEN_KEYWORD_AND: case PM_TOKEN_KEYWORD_BEGIN: case PM_TOKEN_KEYWORD_BEGIN_UPCASE: \ + case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_CASE: case PM_TOKEN_KEYWORD_CLASS: case PM_TOKEN_KEYWORD_DEF: \ + case PM_TOKEN_KEYWORD_DEFINED: case PM_TOKEN_KEYWORD_DO: case PM_TOKEN_KEYWORD_DO_LOOP: case PM_TOKEN_KEYWORD_ELSE: \ + case PM_TOKEN_KEYWORD_ELSIF: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_END_UPCASE: case PM_TOKEN_KEYWORD_ENSURE: \ + case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD_FOR: case PM_TOKEN_KEYWORD_IF: case PM_TOKEN_KEYWORD_IN: \ + case PM_TOKEN_KEYWORD_MODULE: case PM_TOKEN_KEYWORD_NEXT: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_NOT: \ + case PM_TOKEN_KEYWORD_OR: case PM_TOKEN_KEYWORD_REDO: case PM_TOKEN_KEYWORD_RESCUE: case PM_TOKEN_KEYWORD_RETRY: \ + case PM_TOKEN_KEYWORD_RETURN: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_SUPER: case PM_TOKEN_KEYWORD_THEN: \ + case PM_TOKEN_KEYWORD_TRUE: case PM_TOKEN_KEYWORD_UNDEF: case PM_TOKEN_KEYWORD_UNLESS: case PM_TOKEN_KEYWORD_UNTIL: \ + case PM_TOKEN_KEYWORD_WHEN: case PM_TOKEN_KEYWORD_WHILE: case PM_TOKEN_KEYWORD_YIELD + +/** + * This macro allows you to define a case statement for all of the operators. + * It's meant to be used in a switch statement. + */ +#define PM_CASE_OPERATOR PM_TOKEN_AMPERSAND: case PM_TOKEN_BACKTICK: case PM_TOKEN_BANG_EQUAL: \ + case PM_TOKEN_BANG_TILDE: case PM_TOKEN_BANG: case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \ + case PM_TOKEN_BRACKET_LEFT_RIGHT: case PM_TOKEN_CARET: case PM_TOKEN_EQUAL_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL: \ + case PM_TOKEN_EQUAL_TILDE: case PM_TOKEN_GREATER_EQUAL: case PM_TOKEN_GREATER_GREATER: case PM_TOKEN_GREATER: \ + case PM_TOKEN_LESS_EQUAL_GREATER: case PM_TOKEN_LESS_EQUAL: case PM_TOKEN_LESS_LESS: case PM_TOKEN_LESS: \ + case PM_TOKEN_MINUS: case PM_TOKEN_PERCENT: case PM_TOKEN_PIPE: case PM_TOKEN_PLUS: case PM_TOKEN_SLASH: \ + case PM_TOKEN_STAR_STAR: case PM_TOKEN_STAR: case PM_TOKEN_TILDE: case PM_TOKEN_UAMPERSAND: case PM_TOKEN_UMINUS: \ + case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_UPLUS: case PM_TOKEN_USTAR: case PM_TOKEN_USTAR_STAR + +/** + * This macro allows you to define a case statement for all of the token types + * that represent the beginning of nodes that are "primitives" in a pattern + * matching expression. + */ +#define PM_CASE_PRIMITIVE PM_TOKEN_INTEGER: case PM_TOKEN_INTEGER_IMAGINARY: case PM_TOKEN_INTEGER_RATIONAL: \ + case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: case PM_TOKEN_FLOAT: case PM_TOKEN_FLOAT_IMAGINARY: \ + case PM_TOKEN_FLOAT_RATIONAL: case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: case PM_TOKEN_SYMBOL_BEGIN: \ + case PM_TOKEN_REGEXP_BEGIN: case PM_TOKEN_BACKTICK: case PM_TOKEN_PERCENT_LOWER_X: case PM_TOKEN_PERCENT_LOWER_I: \ + case PM_TOKEN_PERCENT_LOWER_W: case PM_TOKEN_PERCENT_UPPER_I: case PM_TOKEN_PERCENT_UPPER_W: \ + case PM_TOKEN_STRING_BEGIN: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_TRUE: \ + case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \ + case PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_MINUS_GREATER: case PM_TOKEN_HEREDOC_START: \ + case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_CHARACTER_LITERAL + +/** + * This macro allows you to define a case statement for all of the token types + * that could begin a parameter. + */ +#define PM_CASE_PARAMETER PM_TOKEN_UAMPERSAND: case PM_TOKEN_AMPERSAND: case PM_TOKEN_UDOT_DOT_DOT: \ + case PM_TOKEN_IDENTIFIER: case PM_TOKEN_LABEL: case PM_TOKEN_USTAR: case PM_TOKEN_STAR: case PM_TOKEN_STAR_STAR: \ + case PM_TOKEN_USTAR_STAR: case PM_TOKEN_CONSTANT: case PM_TOKEN_INSTANCE_VARIABLE: case PM_TOKEN_GLOBAL_VARIABLE: \ + case PM_TOKEN_CLASS_VARIABLE + +/** + * This macro allows you to define a case statement for all of the nodes that + * can be transformed into write targets. + */ +#define PM_CASE_WRITABLE PM_CLASS_VARIABLE_READ_NODE: case PM_CONSTANT_PATH_NODE: \ + case PM_CONSTANT_READ_NODE: case PM_GLOBAL_VARIABLE_READ_NODE: case PM_LOCAL_VARIABLE_READ_NODE: \ + case PM_INSTANCE_VARIABLE_READ_NODE: case PM_MULTI_TARGET_NODE: case PM_BACK_REFERENCE_READ_NODE: \ + case PM_NUMBERED_REFERENCE_READ_NODE + +/** + * Parse a node that is part of a string. If the subsequent tokens cannot be + * parsed as a string part, then NULL is returned. + */ +static pm_node_t * +parse_string_part(pm_parser_t *parser) { + switch (parser->current.type) { + // Here the lexer has returned to us plain string content. In this case + // we'll create a string node that has no opening or closing and return that + // as the part. These kinds of parts look like: + // + // "aaa #{bbb} #@ccc ddd" + // ^^^^ ^ ^^^^ + case PM_TOKEN_STRING_CONTENT: { + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); + + parser_lex(parser); + return node; + } + // Here the lexer has returned the beginning of an embedded expression. In + // that case we'll parse the inner statements and return that as the part. + // These kinds of parts look like: + // + // "aaa #{bbb} #@ccc ddd" + // ^^^^^^ + case PM_TOKEN_EMBEXPR_BEGIN: { + pm_lex_state_t state = parser->lex_state; + int brace_nesting = parser->brace_nesting; + + parser->brace_nesting = 0; + lex_state_set(parser, PM_LEX_STATE_BEG); + parser_lex(parser); + + pm_token_t opening = parser->previous; + pm_statements_node_t *statements = NULL; + + if (!match1(parser, PM_TOKEN_EMBEXPR_END)) { + pm_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, PM_CONTEXT_EMBEXPR); + pm_accepts_block_stack_pop(parser); + } + + parser->brace_nesting = brace_nesting; + lex_state_set(parser, state); + + expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END); + pm_token_t closing = parser->previous; + + return (pm_node_t *) pm_embedded_statements_node_create(parser, &opening, statements, &closing); + } + + // Here the lexer has returned the beginning of an embedded variable. + // In that case we'll parse the variable and create an appropriate node + // for it and then return that node. These kinds of parts look like: + // + // "aaa #{bbb} #@ccc ddd" + // ^^^^^ + case PM_TOKEN_EMBVAR: { + lex_state_set(parser, PM_LEX_STATE_BEG); + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *variable; + + switch (parser->current.type) { + // In this case a back reference is being interpolated. We'll + // create a global variable read node. + case PM_TOKEN_BACK_REFERENCE: + parser_lex(parser); + variable = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + break; + // In this case an nth reference is being interpolated. We'll + // create a global variable read node. + case PM_TOKEN_NUMBERED_REFERENCE: + parser_lex(parser); + variable = (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous); + break; + // In this case a global variable is being interpolated. We'll + // create a global variable read node. + case PM_TOKEN_GLOBAL_VARIABLE: + parser_lex(parser); + variable = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous); + break; + // In this case an instance variable is being interpolated. + // We'll create an instance variable read node. + case PM_TOKEN_INSTANCE_VARIABLE: + parser_lex(parser); + variable = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous); + break; + // In this case a class variable is being interpolated. We'll + // create a class variable read node. + case PM_TOKEN_CLASS_VARIABLE: + parser_lex(parser); + variable = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous); + break; + // We can hit here if we got an invalid token. In that case + // we'll not attempt to lex this token and instead just return a + // missing node. + default: + expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EMBVAR_INVALID); + variable = (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + break; + } + + return (pm_node_t *) pm_embedded_variable_node_create(parser, &operator, variable); + } + default: + parser_lex(parser); + pm_parser_err_previous(parser, PM_ERR_CANNOT_PARSE_STRING_PART); + return NULL; + } +} + +static pm_node_t * +parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_state) { + pm_token_t opening = parser->previous; + + if (lex_mode->mode != PM_LEX_STRING) { + if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state); + + switch (parser->current.type) { + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_CONSTANT: + case PM_TOKEN_INSTANCE_VARIABLE: + case PM_TOKEN_METHOD_NAME: + case PM_TOKEN_CLASS_VARIABLE: + case PM_TOKEN_GLOBAL_VARIABLE: + case PM_TOKEN_NUMBERED_REFERENCE: + case PM_TOKEN_BACK_REFERENCE: + case PM_CASE_KEYWORD: + parser_lex(parser); + break; + case PM_CASE_OPERATOR: + lex_state_set(parser, next_state == PM_LEX_STATE_NONE ? PM_LEX_STATE_ENDFN : next_state); + parser_lex(parser); + break; + default: + expect2(parser, PM_TOKEN_IDENTIFIER, PM_TOKEN_METHOD_NAME, PM_ERR_SYMBOL_INVALID); + break; + } + + pm_token_t closing = not_provided(parser); + pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing); + + pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end); + return (pm_node_t *) symbol; + } + + if (lex_mode->as.string.interpolation) { + // If we have the end of the symbol, then we can return an empty symbol. + if (match1(parser, PM_TOKEN_STRING_END)) { + if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state); + parser_lex(parser); + + pm_token_t content = not_provided(parser); + pm_token_t closing = parser->previous; + return (pm_node_t *) pm_symbol_node_create(parser, &opening, &content, &closing); + } + + // Now we can parse the first part of the symbol. + pm_node_t *part = parse_string_part(parser); + + // If we got a string part, then it's possible that we could transform + // what looks like an interpolated symbol into a regular symbol. + if (part && PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state); + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED); + + return (pm_node_t *) pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous); + } + + // Create a node_list first. We'll use this to check if it should be an + // InterpolatedSymbolNode or a SymbolNode. + pm_node_list_t node_list = { 0 }; + if (part) pm_node_list_append(&node_list, part); + + while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + if ((part = parse_string_part(parser)) != NULL) { + pm_node_list_append(&node_list, part); + } + } + + if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state); + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED); + + return (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &node_list, &parser->previous); + } + + pm_token_t content; + pm_string_t unescaped; + + if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + content = parser->current; + unescaped = parser->current_string; + parser_lex(parser); + } else { + content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end }; + pm_string_shared_init(&unescaped, content.start, content.end); + } + + if (next_state != PM_LEX_STATE_NONE) { + lex_state_set(parser, next_state); + } + + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC); + return (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); +} + +/** + * Parse an argument to undef which can either be a bare word, a symbol, a + * constant, or an interpolated symbol. + */ +static inline pm_node_t * +parse_undef_argument(pm_parser_t *parser) { + switch (parser->current.type) { + case PM_CASE_KEYWORD: + case PM_CASE_OPERATOR: + case PM_TOKEN_CONSTANT: + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_METHOD_NAME: { + parser_lex(parser); + + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing); + + pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end); + return (pm_node_t *) symbol; + } + case PM_TOKEN_SYMBOL_BEGIN: { + pm_lex_mode_t lex_mode = *parser->lex_modes.current; + parser_lex(parser); + + return parse_symbol(parser, &lex_mode, PM_LEX_STATE_NONE); + } + default: + pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT); + return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + } +} + +/** + * Parse an argument to alias which can either be a bare word, a symbol, an + * interpolated symbol or a global variable. If this is the first argument, then + * we need to set the lex state to PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM + * between the first and second arguments. + */ +static inline pm_node_t * +parse_alias_argument(pm_parser_t *parser, bool first) { + switch (parser->current.type) { + case PM_CASE_OPERATOR: + case PM_CASE_KEYWORD: + case PM_TOKEN_CONSTANT: + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_METHOD_NAME: { + if (first) { + lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM); + } + + parser_lex(parser); + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing); + + pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end); + return (pm_node_t *) symbol; + } + case PM_TOKEN_SYMBOL_BEGIN: { + pm_lex_mode_t lex_mode = *parser->lex_modes.current; + parser_lex(parser); + + return parse_symbol(parser, &lex_mode, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE); + } + case PM_TOKEN_BACK_REFERENCE: + parser_lex(parser); + return (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + case PM_TOKEN_NUMBERED_REFERENCE: + parser_lex(parser); + return (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous); + case PM_TOKEN_GLOBAL_VARIABLE: + parser_lex(parser); + return (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous); + default: + pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT); + return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + } +} + +/** + * Return true if any of the visible scopes to the current context are using + * numbered parameters. + */ +static bool +outer_scope_using_numbered_parameters_p(pm_parser_t *parser) { + for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) { + if (scope->numbered_parameters) return true; + } + + return false; +} + +/** + * Parse an identifier into either a local variable read or a call. + */ +static pm_node_t * +parse_variable_call(pm_parser_t *parser) { + pm_node_flags_t flags = 0; + + if (!match1(parser, PM_TOKEN_PARENTHESIS_LEFT) && (parser->previous.end[-1] != '!') && (parser->previous.end[-1] != '?')) { + int depth; + if ((depth = pm_parser_local_depth(parser, &parser->previous)) != -1) { + return (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth); + } + + if (!parser->current_scope->closed && pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end)) { + // Now that we know we have a numbered parameter, we need to check + // if it's allowed in this context. If it is, then we will create a + // local variable read. If it's not, then we'll create a normal call + // node but add an error. + if (parser->current_scope->explicit_params) { + pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_NOT_ALLOWED); + } else if (outer_scope_using_numbered_parameters_p(parser)) { + pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE); + } else { + // Indicate that this scope is using numbered params so that child + // scopes cannot. + uint8_t number = parser->previous.start[1]; + + // We subtract the value for the character '0' to get the actual + // integer value of the number (only _1 through _9 are valid) + uint32_t number_as_int = (uint32_t) (number - '0'); + if (number_as_int > parser->current_scope->numbered_parameters) { + parser->current_scope->numbered_parameters = number_as_int; + pm_parser_numbered_parameters_set(parser, number_as_int); + } + + // When you use a numbered parameter, it implies the existence + // of all of the locals that exist before it. For example, + // referencing _2 means that _1 must exist. Therefore here we + // loop through all of the possibilities and add them into the + // constant pool. + uint8_t current = '1'; + uint8_t *value; + + while (current < number) { + value = malloc(2); + value[0] = '_'; + value[1] = current++; + pm_parser_local_add_owned(parser, value, 2); + } + + // Now we can add the actual token that is being used. For + // this one we can add a shared version since it is directly + // referenced in the source. + pm_parser_local_add_token(parser, &parser->previous); + return (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, 0); + } + } + + flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL; + } + + pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous); + node->base.flags |= flags; + + return (pm_node_t *) node; +} + +/** + * Parse the method definition name based on the current token available on the + * parser. If it does not match a valid method definition name, then a missing + * token is returned. + */ +static inline pm_token_t +parse_method_definition_name(pm_parser_t *parser) { + switch (parser->current.type) { + case PM_CASE_KEYWORD: + case PM_TOKEN_CONSTANT: + case PM_TOKEN_METHOD_NAME: + parser_lex(parser); + return parser->previous; + case PM_TOKEN_IDENTIFIER: + pm_refute_numbered_parameter(parser, parser->current.start, parser->current.end); + parser_lex(parser); + return parser->previous; + case PM_CASE_OPERATOR: + lex_state_set(parser, PM_LEX_STATE_ENDFN); + parser_lex(parser); + return parser->previous; + default: + return (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->current.start, .end = parser->current.end }; + } +} + +static void +parse_heredoc_dedent_string(pm_string_t *string, size_t common_whitespace) { + // Get a reference to the string struct that is being held by the string + // node. This is the value we're going to actually manipulate. + pm_string_ensure_owned(string); + + // Now get the bounds of the existing string. We'll use this as a + // destination to move bytes into. We'll also use it for bounds checking + // since we don't require that these strings be null terminated. + size_t dest_length = pm_string_length(string); + const uint8_t *source_cursor = (uint8_t *) string->source; + const uint8_t *source_end = source_cursor + dest_length; + + // We're going to move bytes backward in the string when we get leading + // whitespace, so we'll maintain a pointer to the current position in the + // string that we're writing to. + size_t trimmed_whitespace = 0; + + // While we haven't reached the amount of common whitespace that we need to + // trim and we haven't reached the end of the string, we'll keep trimming + // whitespace. Trimming in this context means skipping over these bytes such + // that they aren't copied into the new string. + while ((source_cursor < source_end) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) { + if (*source_cursor == '\t') { + trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE; + if (trimmed_whitespace > common_whitespace) break; + } else { + trimmed_whitespace++; + } + + source_cursor++; + dest_length--; + } + + memmove((uint8_t *) string->source, source_cursor, (size_t) (source_end - source_cursor)); + string->length = dest_length; +} + +/** + * Take a heredoc node that is indented by a ~ and trim the leading whitespace. + */ +static void +parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_whitespace) { + // The next node should be dedented if it's the first node in the list or if + // if follows a string node. + bool dedent_next = true; + + // Iterate over all nodes, and trim whitespace accordingly. We're going to + // keep around two indices: a read and a write. If we end up trimming all of + // the whitespace from a node, then we'll drop it from the list entirely. + size_t write_index = 0; + + for (size_t read_index = 0; read_index < nodes->size; read_index++) { + pm_node_t *node = nodes->nodes[read_index]; + + // We're not manipulating child nodes that aren't strings. In this case + // we'll skip past it and indicate that the subsequent node should not + // be dedented. + if (!PM_NODE_TYPE_P(node, PM_STRING_NODE)) { + nodes->nodes[write_index++] = node; + dedent_next = false; + continue; + } + + pm_string_node_t *string_node = ((pm_string_node_t *) node); + if (dedent_next) { + parse_heredoc_dedent_string(&string_node->unescaped, common_whitespace); + } + + if (string_node->unescaped.length == 0) { + pm_node_destroy(parser, node); + } else { + nodes->nodes[write_index++] = node; + } + + // We always dedent the next node if it follows a string node. + dedent_next = true; + } + + nodes->size = write_index; +} + +static pm_node_t * +parse_pattern(pm_parser_t *parser, bool top_pattern, pm_diagnostic_id_t diag_id); + +/** + * Accept any number of constants joined by :: delimiters. + */ +static pm_node_t * +parse_pattern_constant_path(pm_parser_t *parser, pm_node_t *node) { + // Now, if there are any :: operators that follow, parse them as constant + // path nodes. + while (accept1(parser, PM_TOKEN_COLON_COLON)) { + pm_token_t delimiter = parser->previous; + expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); + + pm_node_t *child = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); + node = (pm_node_t *)pm_constant_path_node_create(parser, node, &delimiter, child); + } + + // If there is a [ or ( that follows, then this is part of a larger pattern + // expression. We'll parse the inner pattern here, then modify the returned + // inner pattern with our constant path attached. + if (!match2(parser, PM_TOKEN_BRACKET_LEFT, PM_TOKEN_PARENTHESIS_LEFT)) { + return node; + } + + pm_token_t opening; + pm_token_t closing; + pm_node_t *inner = NULL; + + if (accept1(parser, PM_TOKEN_BRACKET_LEFT)) { + opening = parser->previous; + accept1(parser, PM_TOKEN_NEWLINE); + + if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) { + inner = parse_pattern(parser, true, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET); + accept1(parser, PM_TOKEN_NEWLINE); + expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET); + } + + closing = parser->previous; + } else { + parser_lex(parser); + opening = parser->previous; + + if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + inner = parse_pattern(parser, true, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN); + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN); + } + + closing = parser->previous; + } + + if (!inner) { + // If there was no inner pattern, then we have something like Foo() or + // Foo[]. In that case we'll create an array pattern with no requireds. + return (pm_node_t *) pm_array_pattern_node_constant_create(parser, node, &opening, &closing); + } + + // Now that we have the inner pattern, check to see if it's an array, find, + // or hash pattern. If it is, then we'll attach our constant path to it if + // it doesn't already have a constant. If it's not one of those node types + // or it does have a constant, then we'll create an array pattern. + switch (PM_NODE_TYPE(inner)) { + case PM_ARRAY_PATTERN_NODE: { + pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner; + + if (pattern_node->constant == NULL) { + pattern_node->base.location.start = node->location.start; + pattern_node->base.location.end = closing.end; + + pattern_node->constant = node; + pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); + pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); + + return (pm_node_t *) pattern_node; + } + + break; + } + case PM_FIND_PATTERN_NODE: { + pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner; + + if (pattern_node->constant == NULL) { + pattern_node->base.location.start = node->location.start; + pattern_node->base.location.end = closing.end; + + pattern_node->constant = node; + pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); + pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); + + return (pm_node_t *) pattern_node; + } + + break; + } + case PM_HASH_PATTERN_NODE: { + pm_hash_pattern_node_t *pattern_node = (pm_hash_pattern_node_t *) inner; + + if (pattern_node->constant == NULL) { + pattern_node->base.location.start = node->location.start; + pattern_node->base.location.end = closing.end; + + pattern_node->constant = node; + pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); + pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); + + return (pm_node_t *) pattern_node; + } + + break; + } + default: + break; + } + + // If we got here, then we didn't return one of the inner patterns by + // attaching its constant. In this case we'll create an array pattern and + // attach our constant to it. + pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing); + pm_array_pattern_node_requireds_append(pattern_node, inner); + return (pm_node_t *) pattern_node; +} + +/** + * Parse a rest pattern. + */ +static pm_splat_node_t * +parse_pattern_rest(pm_parser_t *parser) { + assert(parser->previous.type == PM_TOKEN_USTAR); + pm_token_t operator = parser->previous; + pm_node_t *name = NULL; + + // Rest patterns don't necessarily have a name associated with them. So we + // will check for that here. If they do, then we'll add it to the local table + // since this pattern will cause it to become a local variable. + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + pm_token_t identifier = parser->previous; + pm_parser_local_add_token(parser, &identifier); + name = (pm_node_t *) pm_local_variable_target_node_create(parser, &identifier); + } + + // Finally we can return the created node. + return pm_splat_node_create(parser, &operator, name); +} + +/** + * Parse a keyword rest node. + */ +static pm_node_t * +parse_pattern_keyword_rest(pm_parser_t *parser) { + assert(parser->current.type == PM_TOKEN_USTAR_STAR); + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *value = NULL; + + if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) { + return (pm_node_t *) pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous); + } + + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + pm_parser_local_add_token(parser, &parser->previous); + value = (pm_node_t *) pm_local_variable_target_node_create(parser, &parser->previous); + } + + return (pm_node_t *) pm_assoc_splat_node_create(parser, value, &operator); +} + +/** + * Parse a hash pattern. + */ +static pm_hash_pattern_node_t * +parse_pattern_hash(pm_parser_t *parser, pm_node_t *first_assoc) { + pm_node_list_t assocs = { 0 }; + pm_node_t *rest = NULL; + + switch (PM_NODE_TYPE(first_assoc)) { + case PM_ASSOC_NODE: { + if (!match7(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + // Here we have a value for the first assoc in the list, so we will + // parse it now and update the first assoc. + pm_node_t *value = parse_pattern(parser, false, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY); + + pm_assoc_node_t *assoc = (pm_assoc_node_t *) first_assoc; + assoc->base.location.end = value->location.end; + assoc->value = value; + } else { + pm_node_t *key = ((pm_assoc_node_t *) first_assoc)->key; + + if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) { + const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc; + pm_parser_local_add_location(parser, value_loc->start, value_loc->end); + } + } + + pm_node_list_append(&assocs, first_assoc); + break; + } + case PM_ASSOC_SPLAT_NODE: + case PM_NO_KEYWORDS_PARAMETER_NODE: + rest = first_assoc; + break; + default: + assert(false); + break; + } + + // If there are any other assocs, then we'll parse them now. + while (accept1(parser, PM_TOKEN_COMMA)) { + // Here we need to break to support trailing commas. + if (match6(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + break; + } + + pm_node_t *assoc; + + if (match1(parser, PM_TOKEN_USTAR_STAR)) { + assoc = parse_pattern_keyword_rest(parser); + } else { + expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA); + pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); + pm_node_t *value = NULL; + + if (!match7(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + value = parse_pattern(parser, false, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY); + } else { + const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc; + pm_parser_local_add_location(parser, value_loc->start, value_loc->end); + } + + pm_token_t operator = not_provided(parser); + assoc = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value); + } + + pm_node_list_append(&assocs, assoc); + } + + pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest); + free(assocs.nodes); + + return node; +} + +/** + * Parse a pattern expression primitive. + */ +static pm_node_t * +parse_pattern_primitive(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { + switch (parser->current.type) { + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_METHOD_NAME: { + parser_lex(parser); + pm_token_t name = parser->previous; + int depth = pm_parser_local_depth(parser, &name); + if (depth < 0) { + depth = 0; + pm_parser_local_add_token(parser, &name); + } + return (pm_node_t *) pm_local_variable_target_node_create_depth(parser, &name, (uint32_t) depth); + } + case PM_TOKEN_BRACKET_LEFT_ARRAY: { + pm_token_t opening = parser->current; + parser_lex(parser); + + if (accept1(parser, PM_TOKEN_BRACKET_RIGHT)) { + // If we have an empty array pattern, then we'll just return a new + // array pattern node. + return (pm_node_t *)pm_array_pattern_node_empty_create(parser, &opening, &parser->previous); + } + + // Otherwise, we'll parse the inner pattern, then deal with it depending + // on the type it returns. + pm_node_t *inner = parse_pattern(parser, true, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET); + + accept1(parser, PM_TOKEN_NEWLINE); + + expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET); + pm_token_t closing = parser->previous; + + switch (PM_NODE_TYPE(inner)) { + case PM_ARRAY_PATTERN_NODE: { + pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner; + if (pattern_node->opening_loc.start == NULL) { + pattern_node->base.location.start = opening.start; + pattern_node->base.location.end = closing.end; + + pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); + pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); + + return (pm_node_t *) pattern_node; + } + + break; + } + case PM_FIND_PATTERN_NODE: { + pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner; + if (pattern_node->opening_loc.start == NULL) { + pattern_node->base.location.start = opening.start; + pattern_node->base.location.end = closing.end; + + pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); + pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); + + return (pm_node_t *) pattern_node; + } + + break; + } + default: + break; + } + + pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing); + pm_array_pattern_node_requireds_append(node, inner); + return (pm_node_t *) node; + } + case PM_TOKEN_BRACE_LEFT: { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = false; + + pm_hash_pattern_node_t *node; + pm_token_t opening = parser->current; + parser_lex(parser); + + if (accept1(parser, PM_TOKEN_BRACE_RIGHT)) { + // If we have an empty hash pattern, then we'll just return a new hash + // pattern node. + node = pm_hash_pattern_node_empty_create(parser, &opening, &parser->previous); + } else { + pm_node_t *first_assoc; + + switch (parser->current.type) { + case PM_TOKEN_LABEL: { + parser_lex(parser); + + pm_symbol_node_t *key = pm_symbol_node_label_create(parser, &parser->previous); + pm_token_t operator = not_provided(parser); + + first_assoc = (pm_node_t *) pm_assoc_node_create(parser, (pm_node_t *) key, &operator, NULL); + break; + } + case PM_TOKEN_USTAR_STAR: + first_assoc = parse_pattern_keyword_rest(parser); + break; + case PM_TOKEN_STRING_BEGIN: { + pm_node_t *key = parse_expression(parser, PM_BINDING_POWER_MAX, PM_ERR_PATTERN_HASH_KEY); + pm_token_t operator = not_provided(parser); + + if (!pm_symbol_node_label_p(key)) { + pm_parser_err_node(parser, key, PM_ERR_PATTERN_HASH_KEY_LABEL); + } + + first_assoc = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, NULL); + break; + } + default: { + parser_lex(parser); + pm_parser_err_previous(parser, PM_ERR_PATTERN_HASH_KEY); + + pm_missing_node_t *key = pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + pm_token_t operator = not_provided(parser); + + first_assoc = (pm_node_t *) pm_assoc_node_create(parser, (pm_node_t *) key, &operator, NULL); + break; + } + } + + node = parse_pattern_hash(parser, first_assoc); + + accept1(parser, PM_TOKEN_NEWLINE); + expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE); + pm_token_t closing = parser->previous; + + node->base.location.start = opening.start; + node->base.location.end = closing.end; + + node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); + node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); + } + + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + return (pm_node_t *) node; + } + case PM_TOKEN_UDOT_DOT: + case PM_TOKEN_UDOT_DOT_DOT: { + pm_token_t operator = parser->current; + parser_lex(parser); + + // Since we have a unary range operator, we need to parse the subsequent + // expression as the right side of the range. + switch (parser->current.type) { + case PM_CASE_PRIMITIVE: { + pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE); + return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + } + default: { + pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE); + pm_node_t *right = (pm_node_t *) pm_missing_node_create(parser, operator.start, operator.end); + return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + } + } + } + case PM_CASE_PRIMITIVE: { + pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_MAX, diag_id); + + // Now that we have a primitive, we need to check if it's part of a range. + if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) { + pm_token_t operator = parser->previous; + + // Now that we have the operator, we need to check if this is followed + // by another expression. If it is, then we will create a full range + // node. Otherwise, we'll create an endless range. + switch (parser->current.type) { + case PM_CASE_PRIMITIVE: { + pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE); + return (pm_node_t *) pm_range_node_create(parser, node, &operator, right); + } + default: + return (pm_node_t *) pm_range_node_create(parser, node, &operator, NULL); + } + } + + return node; + } + case PM_TOKEN_CARET: { + parser_lex(parser); + pm_token_t operator = parser->previous; + + // At this point we have a pin operator. We need to check the subsequent + // expression to determine if it's a variable or an expression. + switch (parser->current.type) { + case PM_TOKEN_IDENTIFIER: { + parser_lex(parser); + pm_node_t *variable = (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, 0); + + return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + } + case PM_TOKEN_INSTANCE_VARIABLE: { + parser_lex(parser); + pm_node_t *variable = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous); + + return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + } + case PM_TOKEN_CLASS_VARIABLE: { + parser_lex(parser); + pm_node_t *variable = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous); + + return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + } + case PM_TOKEN_GLOBAL_VARIABLE: { + parser_lex(parser); + pm_node_t *variable = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous); + + return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + } + case PM_TOKEN_NUMBERED_REFERENCE: { + parser_lex(parser); + pm_node_t *variable = (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous); + + return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + } + case PM_TOKEN_BACK_REFERENCE: { + parser_lex(parser); + pm_node_t *variable = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + + return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + } + case PM_TOKEN_PARENTHESIS_LEFT: { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = false; + + pm_token_t lparen = parser->current; + parser_lex(parser); + + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_STATEMENT, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN); + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + + accept1(parser, PM_TOKEN_NEWLINE); + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN); + return (pm_node_t *) pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous); + } + default: { + // If we get here, then we have a pin operator followed by something + // not understood. We'll create a missing node and return that. + pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN); + pm_node_t *variable = (pm_node_t *) pm_missing_node_create(parser, operator.start, operator.end); + return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + } + } + } + case PM_TOKEN_UCOLON_COLON: { + pm_token_t delimiter = parser->current; + parser_lex(parser); + + expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); + pm_node_t *child = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); + pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, child); + + return parse_pattern_constant_path(parser, (pm_node_t *)node); + } + case PM_TOKEN_CONSTANT: { + pm_token_t constant = parser->current; + parser_lex(parser); + + pm_node_t *node = (pm_node_t *) pm_constant_read_node_create(parser, &constant); + return parse_pattern_constant_path(parser, node); + } + default: + pm_parser_err_current(parser, diag_id); + return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + } +} + +/** + * Parse any number of primitives joined by alternation and ended optionally by + * assignment. + */ +static pm_node_t * +parse_pattern_primitives(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { + pm_node_t *node = NULL; + + do { + pm_token_t operator = parser->previous; + + switch (parser->current.type) { + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_BRACKET_LEFT_ARRAY: + case PM_TOKEN_BRACE_LEFT: + case PM_TOKEN_CARET: + case PM_TOKEN_CONSTANT: + case PM_TOKEN_UCOLON_COLON: + case PM_TOKEN_UDOT_DOT: + case PM_TOKEN_UDOT_DOT_DOT: + case PM_CASE_PRIMITIVE: { + if (node == NULL) { + node = parse_pattern_primitive(parser, diag_id); + } else { + pm_node_t *right = parse_pattern_primitive(parser, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE); + node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); + } + + break; + } + case PM_TOKEN_PARENTHESIS_LEFT: { + parser_lex(parser); + if (node != NULL) { + pm_node_destroy(parser, node); + } + node = parse_pattern(parser, false, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN); + + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN); + break; + } + default: { + pm_parser_err_current(parser, diag_id); + pm_node_t *right = (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + + if (node == NULL) { + node = right; + } else { + node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); + } + + break; + } + } + } while (accept1(parser, PM_TOKEN_PIPE)); + + // If we have an =>, then we are assigning this pattern to a variable. + // In this case we should create an assignment node. + while (accept1(parser, PM_TOKEN_EQUAL_GREATER)) { + pm_token_t operator = parser->previous; + + expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET); + pm_token_t identifier = parser->previous; + int depth = pm_parser_local_depth(parser, &identifier); + if (depth < 0) { + depth = 0; + pm_parser_local_add_token(parser, &identifier); + } + + pm_node_t *target = (pm_node_t *) pm_local_variable_target_node_create_depth(parser, &identifier, (uint32_t) depth); + node = (pm_node_t *) pm_capture_pattern_node_create(parser, node, target, &operator); + } + + return node; +} + +/** + * Parse a pattern matching expression. + */ +static pm_node_t * +parse_pattern(pm_parser_t *parser, bool top_pattern, pm_diagnostic_id_t diag_id) { + pm_node_t *node = NULL; + + bool leading_rest = false; + bool trailing_rest = false; + + switch (parser->current.type) { + case PM_TOKEN_LABEL: { + parser_lex(parser); + pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); + pm_token_t operator = not_provided(parser); + + return (pm_node_t *) parse_pattern_hash(parser, (pm_node_t *) pm_assoc_node_create(parser, key, &operator, NULL)); + } + case PM_TOKEN_USTAR_STAR: { + node = parse_pattern_keyword_rest(parser); + return (pm_node_t *) parse_pattern_hash(parser, node); + } + case PM_TOKEN_USTAR: { + if (top_pattern) { + parser_lex(parser); + node = (pm_node_t *) parse_pattern_rest(parser); + leading_rest = true; + break; + } + } + /* fallthrough */ + default: + node = parse_pattern_primitives(parser, diag_id); + break; + } + + // If we got a dynamic label symbol, then we need to treat it like the + // beginning of a hash pattern. + if (pm_symbol_node_label_p(node)) { + pm_token_t operator = not_provided(parser); + return (pm_node_t *) parse_pattern_hash(parser, (pm_node_t *) pm_assoc_node_create(parser, node, &operator, NULL)); + } + + if (top_pattern && match1(parser, PM_TOKEN_COMMA)) { + // If we have a comma, then we are now parsing either an array pattern or a + // find pattern. We need to parse all of the patterns, put them into a big + // list, and then determine which type of node we have. + pm_node_list_t nodes = { 0 }; + pm_node_list_append(&nodes, node); + + // Gather up all of the patterns into the list. + while (accept1(parser, PM_TOKEN_COMMA)) { + // Break early here in case we have a trailing comma. + if (match5(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + node = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); + pm_node_list_append(&nodes, node); + break; + } + + if (accept1(parser, PM_TOKEN_USTAR)) { + node = (pm_node_t *) parse_pattern_rest(parser); + + // If we have already parsed a splat pattern, then this is an error. We + // will continue to parse the rest of the patterns, but we will indicate + // it as an error. + if (trailing_rest) { + pm_parser_err_previous(parser, PM_ERR_PATTERN_REST); + } + + trailing_rest = true; + } else { + node = parse_pattern_primitives(parser, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA); + } + + pm_node_list_append(&nodes, node); + } + + // If the first pattern and the last pattern are rest patterns, then we will + // call this a find pattern, regardless of how many rest patterns are in + // between because we know we already added the appropriate errors. + // Otherwise we will create an array pattern. + if (PM_NODE_TYPE_P(nodes.nodes[0], PM_SPLAT_NODE) && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) { + node = (pm_node_t *) pm_find_pattern_node_create(parser, &nodes); + } else { + node = (pm_node_t *) pm_array_pattern_node_node_list_create(parser, &nodes); + } + + free(nodes.nodes); + } else if (leading_rest) { + // Otherwise, if we parsed a single splat pattern, then we know we have an + // array pattern, so we can go ahead and create that node. + node = (pm_node_t *) pm_array_pattern_node_rest_create(parser, node); + } + + return node; +} + +/** + * Incorporate a negative sign into a numeric node by subtracting 1 character + * from its start bounds. If it's a compound node, then we will recursively + * apply this function to its value. + */ +static inline void +parse_negative_numeric(pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_INTEGER_NODE: + case PM_FLOAT_NODE: + node->location.start--; + break; + case PM_RATIONAL_NODE: + node->location.start--; + parse_negative_numeric(((pm_rational_node_t *) node)->numeric); + break; + case PM_IMAGINARY_NODE: + node->location.start--; + parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric); + break; + default: + assert(false && "unreachable"); + break; + } +} + +/** + * Returns a string content token at a particular location that is empty. + */ +static pm_token_t +parse_strings_empty_content(const uint8_t *location) { + return (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = location, .end = location }; +} + +/** + * Parse a set of strings that could be concatenated together. + */ +static inline pm_node_t * +parse_strings(pm_parser_t *parser, pm_node_t *current) { + assert(parser->current.type == PM_TOKEN_STRING_BEGIN); + + bool concating = false; + bool state_is_arg_labeled = lex_state_p(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED); + + while (match1(parser, PM_TOKEN_STRING_BEGIN)) { + pm_node_t *node = NULL; + + // Here we have found a string literal. We'll parse it and add it to + // the list of strings. + assert(parser->lex_modes.current->mode == PM_LEX_STRING); + bool lex_interpolation = parser->lex_modes.current->as.string.interpolation; + + pm_token_t opening = parser->current; + parser_lex(parser); + + if (accept1(parser, PM_TOKEN_STRING_END)) { + // If we get here, then we have an end immediately after a + // start. In that case we'll create an empty content token and + // return an uninterpolated string. + pm_token_t content = parse_strings_empty_content(parser->previous.start); + pm_string_node_t *string = pm_string_node_create(parser, &opening, &content, &parser->previous); + + pm_string_shared_init(&string->unescaped, content.start, content.end); + node = (pm_node_t *) string; + } else if (accept1(parser, PM_TOKEN_LABEL_END)) { + // If we get here, then we have an end of a label immediately + // after a start. In that case we'll create an empty symbol + // node. + pm_token_t opening = not_provided(parser); + pm_token_t content = parse_strings_empty_content(parser->previous.start); + pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &content, &parser->previous); + + pm_string_shared_init(&symbol->unescaped, content.start, content.end); + node = (pm_node_t *) symbol; + } else if (!lex_interpolation) { + // If we don't accept interpolation then we expect the string to + // start with a single string content node. + pm_string_t unescaped; + if (match1(parser, PM_TOKEN_EOF)) { + unescaped = PM_STRING_EMPTY; + } else { + unescaped = parser->current_string; + } + + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_EXPECT_STRING_CONTENT); + pm_token_t content = parser->previous; + + // It is unfortunately possible to have multiple string content + // nodes in a row in the case that there's heredoc content in + // the middle of the string, like this cursed example: + // + // <<-END+'b + // a + // END + // c'+'d' + // + // In that case we need to switch to an interpolated string to + // be able to contain all of the parts. + if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + pm_node_list_t parts = { 0 }; + + pm_token_t delimiters = not_provided(parser); + pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &delimiters, &content, &delimiters, &unescaped); + pm_node_list_append(&parts, part); + + do { + part = (pm_node_t *) pm_string_node_create_current_string(parser, &delimiters, &parser->current, &delimiters); + pm_node_list_append(&parts, part); + parser_lex(parser); + } while (match1(parser, PM_TOKEN_STRING_CONTENT)); + + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_TERM); + node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + } else if (accept1(parser, PM_TOKEN_LABEL_END) && !state_is_arg_labeled) { + node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_TERM); + node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + } + } else if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + // In this case we've hit string content so we know the string + // at least has something in it. We'll need to check if the + // following token is the end (in which case we can return a + // plain string) or if it's not then it has interpolation. + pm_token_t content = parser->current; + pm_string_t unescaped = parser->current_string; + parser_lex(parser); + + if (match1(parser, PM_TOKEN_STRING_END)) { + node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); + parser_lex(parser); + } else if (accept1(parser, PM_TOKEN_LABEL_END)) { + node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + } else { + // If we get here, then we have interpolation so we'll need + // to create a string or symbol node with interpolation. + pm_node_list_t parts = { 0 }; + pm_token_t string_opening = not_provided(parser); + pm_token_t string_closing = not_provided(parser); + + pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &string_opening, &parser->previous, &string_closing, &unescaped); + pm_node_list_append(&parts, part); + + while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) { + if ((part = parse_string_part(parser)) != NULL) { + pm_node_list_append(&parts, part); + } + } + + if (accept1(parser, PM_TOKEN_LABEL_END) && !state_is_arg_labeled) { + node = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM); + node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + } + } + } else { + // If we get here, then the first part of the string is not plain + // string content, in which case we need to parse the string as an + // interpolated string. + pm_node_list_t parts = { 0 }; + pm_node_t *part; + + while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) { + if ((part = parse_string_part(parser)) != NULL) { + pm_node_list_append(&parts, part); + } + } + + if (accept1(parser, PM_TOKEN_LABEL_END)) { + node = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM); + node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + } + } + + if (current == NULL) { + // If the node we just parsed is a symbol node, then we can't + // concatenate it with anything else, so we can now return that + // node. + if (PM_NODE_TYPE_P(node, PM_SYMBOL_NODE) || PM_NODE_TYPE_P(node, PM_INTERPOLATED_SYMBOL_NODE)) { + return node; + } + + // If we don't already have a node, then it's fine and we can just + // set the result to be the node we just parsed. + current = node; + } else { + // Otherwise we need to check the type of the node we just parsed. + // If it cannot be concatenated with the previous node, then we'll + // need to add a syntax error. + if (!PM_NODE_TYPE_P(node, PM_STRING_NODE) && !PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) { + pm_parser_err_node(parser, node, PM_ERR_STRING_CONCATENATION); + } + + // If we haven't already created our container for concatenation, + // we'll do that now. + if (!concating) { + concating = true; + pm_token_t bounds = not_provided(parser); + + pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, &bounds, NULL, &bounds); + pm_interpolated_string_node_append(container, current); + current = (pm_node_t *) container; + } + + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, node); + } + } + + return current; +} + +/** + * Parse an expression that begins with the previous node that we just lexed. + */ +static inline pm_node_t * +parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) { + switch (parser->current.type) { + case PM_TOKEN_BRACKET_LEFT_ARRAY: { + parser_lex(parser); + + pm_array_node_t *array = pm_array_node_create(parser, &parser->previous); + pm_accepts_block_stack_push(parser, true); + bool parsed_bare_hash = false; + + while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) { + // Handle the case where we don't have a comma and we have a + // newline followed by a right bracket. + if (accept1(parser, PM_TOKEN_NEWLINE) && match1(parser, PM_TOKEN_BRACKET_RIGHT)) { + break; + } + + if (pm_array_node_size(array) != 0) { + expect1(parser, PM_TOKEN_COMMA, PM_ERR_ARRAY_SEPARATOR); + } + + // If we have a right bracket immediately following a comma, + // this is allowed since it's a trailing comma. In this case we + // can break out of the loop. + if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break; + + pm_node_t *element; + + if (accept1(parser, PM_TOKEN_USTAR)) { + pm_token_t operator = parser->previous; + pm_node_t *expression = NULL; + + if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) { + if (pm_parser_local_depth(parser, &parser->previous) == -1) { + pm_parser_err_token(parser, &operator, PM_ERR_ARGUMENT_NO_FORWARDING_STAR); + } + } else { + expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR); + } + + element = (pm_node_t *) pm_splat_node_create(parser, &operator, expression); + } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) { + if (parsed_bare_hash) { + pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH); + } + + pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser); + element = (pm_node_t *)hash; + + if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) { + parse_assocs(parser, (pm_node_t *) hash); + } + + parsed_bare_hash = true; + } else { + element = parse_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_ARRAY_EXPRESSION); + + if (pm_symbol_node_label_p(element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) { + if (parsed_bare_hash) { + pm_parser_err_previous(parser, PM_ERR_EXPRESSION_BARE_HASH); + } + + pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser); + + pm_token_t operator; + if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) { + operator = parser->previous; + } else { + operator = not_provided(parser); + } + + pm_node_t *value = parse_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_HASH_VALUE); + pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, element, &operator, value); + pm_keyword_hash_node_elements_append(hash, assoc); + + element = (pm_node_t *)hash; + if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) { + parse_assocs(parser, (pm_node_t *) hash); + } + + parsed_bare_hash = true; + } + } + + pm_array_node_elements_append(array, element); + if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break; + } + + accept1(parser, PM_TOKEN_NEWLINE); + expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_ARRAY_TERM); + pm_array_node_close_set(array, &parser->previous); + pm_accepts_block_stack_pop(parser); + + return (pm_node_t *) array; + } + case PM_TOKEN_PARENTHESIS_LEFT: + case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { + pm_token_t opening = parser->current; + parser_lex(parser); + while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)); + + // If this is the end of the file or we match a right parenthesis, then + // we have an empty parentheses node, and we can immediately return. + if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) { + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + return (pm_node_t *) pm_parentheses_node_create(parser, &opening, NULL, &parser->previous); + } + + // Otherwise, we're going to parse the first statement in the list + // of statements within the parentheses. + pm_accepts_block_stack_push(parser, true); + context_push(parser, PM_CONTEXT_PARENS); + pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_ERR_CANNOT_PARSE_EXPRESSION); + context_pop(parser); + + // Determine if this statement is followed by a terminator. In the + // case of a single statement, this is fine. But in the case of + // multiple statements it's required. + bool terminator_found = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + if (terminator_found) { + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + } + + // If we hit a right parenthesis, then we're done parsing the + // parentheses node, and we can check which kind of node we should + // return. + if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) { + lex_state_set(parser, PM_LEX_STATE_ENDARG); + } + parser_lex(parser); + pm_accepts_block_stack_pop(parser); + + if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) { + // If we have a single statement and are ending on a right + // parenthesis, then we need to check if this is possibly a + // multiple target node. + pm_multi_target_node_t *multi_target; + + if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) && ((pm_multi_target_node_t *) statement)->lparen_loc.start == NULL) { + multi_target = (pm_multi_target_node_t *) statement; + } else { + multi_target = pm_multi_target_node_create(parser); + pm_multi_target_node_targets_append(parser, multi_target, statement); + } + + pm_location_t lparen_loc = PM_LOCATION_TOKEN_VALUE(&opening); + pm_location_t rparen_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + + multi_target->lparen_loc = lparen_loc; + multi_target->rparen_loc = rparen_loc; + multi_target->base.location.start = lparen_loc.start; + multi_target->base.location.end = rparen_loc.end; + + if (match1(parser, PM_TOKEN_COMMA)) { + if (binding_power == PM_BINDING_POWER_STATEMENT) { + return parse_targets_validate(parser, (pm_node_t *) multi_target, PM_BINDING_POWER_INDEX); + } + return (pm_node_t *) multi_target; + } + + return parse_target_validate(parser, (pm_node_t *) multi_target); + } + + // If we have a single statement and are ending on a right parenthesis + // and we didn't return a multiple assignment node, then we can return a + // regular parentheses node now. + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(statements, statement); + + return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous); + } + + // If we have more than one statement in the set of parentheses, + // then we are going to parse all of them as a list of statements. + // We'll do that here. + context_push(parser, PM_CONTEXT_PARENS); + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(statements, statement); + + // If we didn't find a terminator and we didn't find a right + // parenthesis, then this is a syntax error. + if (!terminator_found) { + pm_parser_err(parser, parser->current.start, parser->current.start, PM_ERR_EXPECT_EOL_AFTER_STATEMENT); + } + + // Parse each statement within the parentheses. + while (true) { + pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_ERR_CANNOT_PARSE_EXPRESSION); + pm_statements_node_body_append(statements, node); + + // If we're recovering from a syntax error, then we need to stop + // parsing the statements now. + if (parser->recovering) { + // If this is the level of context where the recovery has + // happened, then we can mark the parser as done recovering. + if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false; + break; + } + + // If we couldn't parse an expression at all, then we need to + // bail out of the loop. + if (PM_NODE_TYPE_P(node, PM_MISSING_NODE)) break; + + // If we successfully parsed a statement, then we are going to + // need terminator to delimit them. + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break; + } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + break; + } else { + pm_parser_err(parser, parser->current.start, parser->current.start, PM_ERR_EXPECT_EOL_AFTER_STATEMENT); + } + } + + context_pop(parser); + pm_accepts_block_stack_pop(parser); + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + + return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous); + } + case PM_TOKEN_BRACE_LEFT: { + pm_accepts_block_stack_push(parser, true); + parser_lex(parser); + pm_hash_node_t *node = pm_hash_node_create(parser, &parser->previous); + + if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) { + parse_assocs(parser, (pm_node_t *) node); + accept1(parser, PM_TOKEN_NEWLINE); + } + + pm_accepts_block_stack_pop(parser); + expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM); + pm_hash_node_closing_loc_set(node, &parser->previous); + + return (pm_node_t *) node; + } + case PM_TOKEN_CHARACTER_LITERAL: { + parser_lex(parser); + + pm_token_t opening = parser->previous; + opening.type = PM_TOKEN_STRING_BEGIN; + opening.end = opening.start + 1; + + pm_token_t content = parser->previous; + content.type = PM_TOKEN_STRING_CONTENT; + content.start = content.start + 1; + + pm_token_t closing = not_provided(parser); + pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &content, &closing); + + // Characters can be followed by strings in which case they are + // automatically concatenated. + if (match1(parser, PM_TOKEN_STRING_BEGIN)) { + return parse_strings(parser, node); + } + + return node; + } + case PM_TOKEN_CLASS_VARIABLE: { + parser_lex(parser); + pm_node_t *node = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous); + + if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX); + } + + return node; + } + case PM_TOKEN_CONSTANT: { + parser_lex(parser); + pm_token_t constant = parser->previous; + + // If a constant is immediately followed by parentheses, then this is in + // fact a method call, not a constant read. + if ( + match1(parser, PM_TOKEN_PARENTHESIS_LEFT) || + (binding_power <= PM_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) || + (pm_accepts_block_stack_p(parser) && match2(parser, PM_TOKEN_KEYWORD_DO, PM_TOKEN_BRACE_LEFT)) + ) { + pm_arguments_t arguments = { 0 }; + parse_arguments_list(parser, &arguments, true); + return (pm_node_t *) pm_call_node_fcall_create(parser, &constant, &arguments); + } + + pm_node_t *node = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); + + if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) { + // If we get here, then we have a comma immediately following a + // constant, so we're going to parse this as a multiple assignment. + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX); + } + + return node; + } + case PM_TOKEN_UCOLON_COLON: { + parser_lex(parser); + + pm_token_t delimiter = parser->previous; + expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); + + pm_node_t *constant = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); + pm_node_t *node = (pm_node_t *)pm_constant_path_node_create(parser, NULL, &delimiter, constant); + + if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX); + } + + return node; + } + case PM_TOKEN_UDOT_DOT: + case PM_TOKEN_UDOT_DOT_DOT: { + pm_token_t operator = parser->current; + parser_lex(parser); + + pm_node_t *right = parse_expression(parser, pm_binding_powers[operator.type].left, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + } + case PM_TOKEN_FLOAT: + parser_lex(parser); + return (pm_node_t *) pm_float_node_create(parser, &parser->previous); + case PM_TOKEN_FLOAT_IMAGINARY: + parser_lex(parser); + return (pm_node_t *) pm_float_node_imaginary_create(parser, &parser->previous); + case PM_TOKEN_FLOAT_RATIONAL: + parser_lex(parser); + return (pm_node_t *) pm_float_node_rational_create(parser, &parser->previous); + case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: + parser_lex(parser); + return (pm_node_t *) pm_float_node_rational_imaginary_create(parser, &parser->previous); + case PM_TOKEN_NUMBERED_REFERENCE: { + parser_lex(parser); + pm_node_t *node = (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous); + + if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX); + } + + return node; + } + case PM_TOKEN_GLOBAL_VARIABLE: { + parser_lex(parser); + pm_node_t *node = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous); + + if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX); + } + + return node; + } + case PM_TOKEN_BACK_REFERENCE: { + parser_lex(parser); + pm_node_t *node = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + + if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX); + } + + return node; + } + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_METHOD_NAME: { + parser_lex(parser); + pm_token_t identifier = parser->previous; + pm_node_t *node = parse_variable_call(parser); + + if (PM_NODE_TYPE_P(node, PM_CALL_NODE)) { + // If parse_variable_call returned with a call node, then we + // know the identifier is not in the local table. In that case + // we need to check if there are arguments following the + // identifier. + pm_call_node_t *call = (pm_call_node_t *) node; + pm_arguments_t arguments = { 0 }; + + if (parse_arguments_list(parser, &arguments, true)) { + // Since we found arguments, we need to turn off the + // variable call bit in the flags. + call->base.flags &= (pm_node_flags_t) ~PM_CALL_NODE_FLAGS_VARIABLE_CALL; + + call->opening_loc = arguments.opening_loc; + call->arguments = arguments.arguments; + call->closing_loc = arguments.closing_loc; + call->block = arguments.block; + + if (arguments.block != NULL) { + call->base.location.end = arguments.block->location.end; + } else if (arguments.closing_loc.start == NULL) { + if (arguments.arguments != NULL) { + call->base.location.end = arguments.arguments->base.location.end; + } else { + call->base.location.end = call->message_loc.end; + } + } else { + call->base.location.end = arguments.closing_loc.end; + } + } + } else { + // Otherwise, we know the identifier is in the local table. This + // can still be a method call if it is followed by arguments or + // a block, so we need to check for that here. + if ( + (binding_power <= PM_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) || + (pm_accepts_block_stack_p(parser) && match2(parser, PM_TOKEN_KEYWORD_DO, PM_TOKEN_BRACE_LEFT)) + ) { + pm_arguments_t arguments = { 0 }; + parse_arguments_list(parser, &arguments, true); + + pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments); + pm_node_destroy(parser, node); + return (pm_node_t *) fcall; + } + } + + if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX); + } + + return node; + } + case PM_TOKEN_HEREDOC_START: { + // Here we have found a heredoc. We'll parse it and add it to the + // list of strings. + pm_lex_mode_t *lex_mode = parser->lex_modes.current; + assert(lex_mode->mode == PM_LEX_HEREDOC); + pm_heredoc_quote_t quote = lex_mode->as.heredoc.quote; + pm_heredoc_indent_t indent = lex_mode->as.heredoc.indent; + + parser_lex(parser); + pm_token_t opening = parser->previous; + + pm_node_t *node; + pm_node_t *part; + + if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) { + // If we get here, then we have an empty heredoc. We'll create + // an empty content token and return an empty string node. + lex_state_set(parser, PM_LEX_STATE_END); + expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM); + pm_token_t content = parse_strings_empty_content(parser->previous.start); + + if (quote == PM_HEREDOC_QUOTE_BACKTICK) { + node = (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY); + } else { + node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY); + } + + node->location.end = opening.end; + } else if ((part = parse_string_part(parser)) == NULL) { + // If we get here, then we tried to find something in the + // heredoc but couldn't actually parse anything, so we'll just + // return a missing node. + node = (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) { + // If we get here, then the part that we parsed was plain string + // content and we're at the end of the heredoc, so we can return + // just a string node with the heredoc opening and closing as + // its opening and closing. + pm_string_node_t *cast = (pm_string_node_t *) part; + + cast->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); + cast->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->current); + cast->base.location = cast->opening_loc; + + if (quote == PM_HEREDOC_QUOTE_BACKTICK) { + assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t)); + cast->base.type = PM_X_STRING_NODE; + } + + size_t common_whitespace = parser->current_string_common_whitespace; + if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) { + parse_heredoc_dedent_string(&cast->unescaped, common_whitespace); + } + + node = (pm_node_t *) cast; + lex_state_set(parser, PM_LEX_STATE_END); + expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM); + } else { + // If we get here, then we have multiple parts in the heredoc, + // so we'll need to create an interpolated string node to hold + // them all. + pm_node_list_t parts = { 0 }; + pm_node_list_append(&parts, part); + + while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) { + if ((part = parse_string_part(parser)) != NULL) { + pm_node_list_append(&parts, part); + } + } + + // Now that we have all of the parts, create the correct type of + // interpolated node. + if (quote == PM_HEREDOC_QUOTE_BACKTICK) { + pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening); + cast->parts = parts; + + lex_state_set(parser, PM_LEX_STATE_END); + expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM); + + pm_interpolated_xstring_node_closing_set(cast, &parser->previous); + cast->base.location = cast->opening_loc; + node = (pm_node_t *) cast; + } else { + pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening); + + lex_state_set(parser, PM_LEX_STATE_END); + expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM); + + pm_interpolated_string_node_closing_set(cast, &parser->previous); + cast->base.location = cast->opening_loc; + node = (pm_node_t *) cast; + } + + // If this is a heredoc that is indented with a ~, then we need + // to dedent each line by the common leading whitespace. + size_t common_whitespace = parser->current_string_common_whitespace; + if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) { + pm_node_list_t *nodes; + if (quote == PM_HEREDOC_QUOTE_BACKTICK) { + nodes = &((pm_interpolated_x_string_node_t *) node)->parts; + } else { + nodes = &((pm_interpolated_string_node_t *) node)->parts; + } + + parse_heredoc_dedent(parser, nodes, common_whitespace); + } + } + + if (match1(parser, PM_TOKEN_STRING_BEGIN)) { + return parse_strings(parser, node); + } + + return node; + } + case PM_TOKEN_INSTANCE_VARIABLE: { + parser_lex(parser); + pm_node_t *node = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous); + + if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX); + } + + return node; + } + case PM_TOKEN_INTEGER: { + pm_node_flags_t base = parser->integer_base; + parser_lex(parser); + return (pm_node_t *) pm_integer_node_create(parser, base, &parser->previous); + } + case PM_TOKEN_INTEGER_IMAGINARY: { + pm_node_flags_t base = parser->integer_base; + parser_lex(parser); + return (pm_node_t *) pm_integer_node_imaginary_create(parser, base, &parser->previous); + } + case PM_TOKEN_INTEGER_RATIONAL: { + pm_node_flags_t base = parser->integer_base; + parser_lex(parser); + return (pm_node_t *) pm_integer_node_rational_create(parser, base, &parser->previous); + } + case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: { + pm_node_flags_t base = parser->integer_base; + parser_lex(parser); + return (pm_node_t *) pm_integer_node_rational_imaginary_create(parser, base, &parser->previous); + } + case PM_TOKEN_KEYWORD___ENCODING__: + parser_lex(parser); + return (pm_node_t *) pm_source_encoding_node_create(parser, &parser->previous); + case PM_TOKEN_KEYWORD___FILE__: + parser_lex(parser); + return (pm_node_t *) pm_source_file_node_create(parser, &parser->previous); + case PM_TOKEN_KEYWORD___LINE__: + parser_lex(parser); + return (pm_node_t *) pm_source_line_node_create(parser, &parser->previous); + case PM_TOKEN_KEYWORD_ALIAS: { + if (binding_power != PM_BINDING_POWER_STATEMENT) { + pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS); + } + + parser_lex(parser); + pm_token_t keyword = parser->previous; + + pm_node_t *new_name = parse_alias_argument(parser, true); + pm_node_t *old_name = parse_alias_argument(parser, false); + + switch (PM_NODE_TYPE(new_name)) { + case PM_BACK_REFERENCE_READ_NODE: + case PM_NUMBERED_REFERENCE_READ_NODE: + case PM_GLOBAL_VARIABLE_READ_NODE: { + if (PM_NODE_TYPE_P(old_name, PM_BACK_REFERENCE_READ_NODE) || PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE) || PM_NODE_TYPE_P(old_name, PM_GLOBAL_VARIABLE_READ_NODE)) { + if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) { + pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT); + } + } else { + pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT); + } + + return (pm_node_t *) pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name); + } + case PM_SYMBOL_NODE: + case PM_INTERPOLATED_SYMBOL_NODE: { + if (!PM_NODE_TYPE_P(old_name, PM_SYMBOL_NODE) && !PM_NODE_TYPE_P(old_name, PM_INTERPOLATED_SYMBOL_NODE)) { + pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT); + } + } + /* fallthrough */ + default: + return (pm_node_t *) pm_alias_method_node_create(parser, &keyword, new_name, old_name); + } + } + case PM_TOKEN_KEYWORD_CASE: { + parser_lex(parser); + pm_token_t case_keyword = parser->previous; + pm_node_t *predicate = NULL; + + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + predicate = NULL; + } else if (match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_END)) { + predicate = NULL; + } else if (!token_begins_expression_p(parser->current.type)) { + predicate = NULL; + } else { + predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_ERR_CASE_EXPRESSION_AFTER_CASE); + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + } + + if (accept1(parser, PM_TOKEN_KEYWORD_END)) { + pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS); + return (pm_node_t *) pm_case_node_create(parser, &case_keyword, predicate, &parser->previous); + } + + // At this point we can create a case node, though we don't yet know if it + // is a case-in or case-when node. + pm_token_t end_keyword = not_provided(parser); + pm_node_t *node; + + if (match1(parser, PM_TOKEN_KEYWORD_WHEN)) { + pm_case_node_t *case_node = pm_case_node_create(parser, &case_keyword, predicate, &end_keyword); + + // At this point we've seen a when keyword, so we know this is a + // case-when node. We will continue to parse the when nodes until we hit + // the end of the list. + while (accept1(parser, PM_TOKEN_KEYWORD_WHEN)) { + pm_token_t when_keyword = parser->previous; + pm_when_node_t *when_node = pm_when_node_create(parser, &when_keyword); + + do { + if (accept1(parser, PM_TOKEN_USTAR)) { + pm_token_t operator = parser->previous; + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR); + + pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression); + pm_when_node_conditions_append(when_node, (pm_node_t *) splat_node); + + if (PM_NODE_TYPE_P(expression, PM_MISSING_NODE)) break; + } else { + pm_node_t *condition = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_CASE_EXPRESSION_AFTER_WHEN); + pm_when_node_conditions_append(when_node, condition); + + if (PM_NODE_TYPE_P(condition, PM_MISSING_NODE)) break; + } + } while (accept1(parser, PM_TOKEN_COMMA)); + + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + accept1(parser, PM_TOKEN_KEYWORD_THEN); + } else { + expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER); + } + + if (!match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_CASE_WHEN); + if (statements != NULL) { + pm_when_node_statements_set(when_node, statements); + } + } + + pm_case_node_condition_append(case_node, (pm_node_t *) when_node); + } + + // If we didn't parse any conditions (in or when) then we need + // to indicate that we have an error. + if (case_node->conditions.size == 0) { + pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS); + } + + node = (pm_node_t *) case_node; + } else { + pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate, &end_keyword); + + // If this is a case-match node (i.e., it is a pattern matching + // case statement) then we must have a predicate. + if (predicate == NULL) { + pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MATCH_MISSING_PREDICATE); + } + + // At this point we expect that we're parsing a case-in node. We will + // continue to parse the in nodes until we hit the end of the list. + while (match1(parser, PM_TOKEN_KEYWORD_IN)) { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = true; + + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + parser->command_start = false; + parser_lex(parser); + + pm_token_t in_keyword = parser->previous; + pm_node_t *pattern = parse_pattern(parser, true, PM_ERR_PATTERN_EXPRESSION_AFTER_IN); + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + + // Since we're in the top-level of the case-in node we need to check + // for guard clauses in the form of `if` or `unless` statements. + if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) { + pm_token_t keyword = parser->previous; + pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_CONDITIONAL_IF_PREDICATE); + pattern = (pm_node_t *) pm_if_node_modifier_create(parser, pattern, &keyword, predicate); + } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) { + pm_token_t keyword = parser->previous; + pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_CONDITIONAL_UNLESS_PREDICATE); + pattern = (pm_node_t *) pm_unless_node_modifier_create(parser, pattern, &keyword, predicate); + } + + // Now we need to check for the terminator of the in node's pattern. + // It can be a newline or semicolon optionally followed by a `then` + // keyword. + pm_token_t then_keyword; + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) { + then_keyword = parser->previous; + } else { + then_keyword = not_provided(parser); + } + } else { + expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER); + then_keyword = parser->previous; + } + + // Now we can actually parse the statements associated with the in + // node. + pm_statements_node_t *statements; + if (match3(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + statements = NULL; + } else { + statements = parse_statements(parser, PM_CONTEXT_CASE_IN); + } + + // Now that we have the full pattern and statements, we can create the + // node and attach it to the case node. + pm_node_t *condition = (pm_node_t *) pm_in_node_create(parser, pattern, statements, &in_keyword, &then_keyword); + pm_case_match_node_condition_append(case_node, condition); + } + + // If we didn't parse any conditions (in or when) then we need + // to indicate that we have an error. + if (case_node->conditions.size == 0) { + pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS); + } + + node = (pm_node_t *) case_node; + } + + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) { + pm_token_t else_keyword = parser->previous; + pm_else_node_t *else_node; + + if (!match1(parser, PM_TOKEN_KEYWORD_END)) { + else_node = pm_else_node_create(parser, &else_keyword, parse_statements(parser, PM_CONTEXT_ELSE), &parser->current); + } else { + else_node = pm_else_node_create(parser, &else_keyword, NULL, &parser->current); + } + + if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) { + pm_case_node_consequent_set((pm_case_node_t *) node, else_node); + } else { + pm_case_match_node_consequent_set((pm_case_match_node_t *) node, else_node); + } + } + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM); + if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) { + pm_case_node_end_keyword_loc_set((pm_case_node_t *) node, &parser->previous); + } else { + pm_case_match_node_end_keyword_loc_set((pm_case_match_node_t *) node, &parser->previous); + } + + return node; + } + case PM_TOKEN_KEYWORD_BEGIN: { + parser_lex(parser); + + pm_token_t begin_keyword = parser->previous; + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + pm_statements_node_t *begin_statements = NULL; + + if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + pm_begin_node_t *begin_node = pm_begin_node_create(parser, &begin_keyword, begin_statements); + parse_rescues(parser, begin_node, false); + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM); + begin_node->base.location.end = parser->previous.end; + pm_begin_node_end_keyword_set(begin_node, &parser->previous); + + if ((begin_node->else_clause != NULL) && (begin_node->rescue_clause == NULL)) { + pm_parser_err_node(parser, (pm_node_t *) begin_node->else_clause, PM_ERR_BEGIN_LONELY_ELSE); + } + + return (pm_node_t *) begin_node; + } + case PM_TOKEN_KEYWORD_BEGIN_UPCASE: { + if (binding_power != PM_BINDING_POWER_STATEMENT) { + pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN); + } + + parser_lex(parser); + pm_token_t keyword = parser->previous; + + expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_BEGIN_UPCASE_BRACE); + pm_token_t opening = parser->previous; + pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE); + + expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM); + pm_context_t context = parser->current_context->context; + if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) { + pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL); + } + return (pm_node_t *) pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous); + } + case PM_TOKEN_KEYWORD_BREAK: + case PM_TOKEN_KEYWORD_NEXT: + case PM_TOKEN_KEYWORD_RETURN: { + parser_lex(parser); + + pm_token_t keyword = parser->previous; + pm_arguments_t arguments = { 0 }; + + if ( + token_begins_expression_p(parser->current.type) || + match2(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR) + ) { + pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left; + + if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) { + parse_arguments(parser, &arguments, false, PM_TOKEN_EOF); + } + } + + switch (keyword.type) { + case PM_TOKEN_KEYWORD_BREAK: + return (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments); + case PM_TOKEN_KEYWORD_NEXT: + return (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments); + case PM_TOKEN_KEYWORD_RETURN: { + if ( + (parser->current_context->context == PM_CONTEXT_CLASS) || + (parser->current_context->context == PM_CONTEXT_MODULE) + ) { + pm_parser_err_current(parser, PM_ERR_RETURN_INVALID); + } + return (pm_node_t *) pm_return_node_create(parser, &keyword, arguments.arguments); + } + default: + assert(false && "unreachable"); + return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + } + } + case PM_TOKEN_KEYWORD_SUPER: { + parser_lex(parser); + + pm_token_t keyword = parser->previous; + pm_arguments_t arguments = { 0 }; + parse_arguments_list(parser, &arguments, true); + + if ( + arguments.opening_loc.start == NULL && + arguments.arguments == NULL && + ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE)) + ) { + return (pm_node_t *) pm_forwarding_super_node_create(parser, &keyword, &arguments); + } + + return (pm_node_t *) pm_super_node_create(parser, &keyword, &arguments); + } + case PM_TOKEN_KEYWORD_YIELD: { + parser_lex(parser); + + pm_token_t keyword = parser->previous; + pm_arguments_t arguments = { 0 }; + parse_arguments_list(parser, &arguments, false); + + return (pm_node_t *) pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc); + } + case PM_TOKEN_KEYWORD_CLASS: { + parser_lex(parser); + pm_token_t class_keyword = parser->previous; + pm_do_loop_stack_push(parser, false); + + if (accept1(parser, PM_TOKEN_LESS_LESS)) { + pm_token_t operator = parser->previous; + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_NOT, PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS); + + pm_constant_id_t old_param_name = parser->current_param_name; + parser->current_param_name = 0; + pm_parser_scope_push(parser, true); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + + pm_node_t *statements = NULL; + if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_SCLASS); + pm_accepts_block_stack_pop(parser); + } + + if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); + statements = (pm_node_t *) parse_rescues_as_begin(parser, (pm_statements_node_t *) statements, false); + } + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM); + + pm_constant_id_list_t locals = parser->current_scope->locals; + pm_parser_scope_pop(parser); + parser->current_param_name = old_param_name; + pm_do_loop_stack_pop(parser); + return (pm_node_t *) pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous); + } + + pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_ERR_CLASS_NAME); + pm_token_t name = parser->previous; + if (name.type != PM_TOKEN_CONSTANT) { + pm_parser_err_token(parser, &name, PM_ERR_CLASS_NAME); + } + + pm_token_t inheritance_operator; + pm_node_t *superclass; + + if (match1(parser, PM_TOKEN_LESS)) { + inheritance_operator = parser->current; + lex_state_set(parser, PM_LEX_STATE_BEG); + + parser->command_start = true; + parser_lex(parser); + + superclass = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_ERR_CLASS_SUPERCLASS); + } else { + inheritance_operator = not_provided(parser); + superclass = NULL; + } + + pm_constant_id_t old_param_name = parser->current_param_name; + parser->current_param_name = 0; + pm_parser_scope_push(parser, true); + if (inheritance_operator.type != PM_TOKEN_NOT_PROVIDED) { + expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CLASS_UNEXPECTED_END); + } else { + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + pm_node_t *statements = NULL; + + if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_CLASS); + pm_accepts_block_stack_pop(parser); + } + + if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); + statements = (pm_node_t *) parse_rescues_as_begin(parser, (pm_statements_node_t *) statements, false); + } + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM); + + if (context_def_p(parser)) { + pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD); + } + + pm_constant_id_list_t locals = parser->current_scope->locals; + pm_parser_scope_pop(parser); + parser->current_param_name = old_param_name; + pm_do_loop_stack_pop(parser); + + if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) { + pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME); + } + + return (pm_node_t *) pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous); + } + case PM_TOKEN_KEYWORD_DEF: { + pm_token_t def_keyword = parser->current; + + pm_node_t *receiver = NULL; + pm_token_t operator = not_provided(parser); + pm_token_t name = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = def_keyword.end, .end = def_keyword.end }; + + // This context is necessary for lexing `...` in a bare params correctly. + // It must be pushed before lexing the first param, so it is here. + context_push(parser, PM_CONTEXT_DEF_PARAMS); + parser_lex(parser); + pm_constant_id_t old_param_name = parser->current_param_name; + + switch (parser->current.type) { + case PM_CASE_OPERATOR: + pm_parser_scope_push(parser, true); + parser->current_param_name = 0; + lex_state_set(parser, PM_LEX_STATE_ENDFN); + parser_lex(parser); + name = parser->previous; + break; + case PM_TOKEN_IDENTIFIER: { + parser_lex(parser); + + if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) { + receiver = parse_variable_call(parser); + + pm_parser_scope_push(parser, true); + parser->current_param_name = 0; + lex_state_set(parser, PM_LEX_STATE_FNAME); + parser_lex(parser); + + operator = parser->previous; + name = parse_method_definition_name(parser); + } else { + pm_refute_numbered_parameter(parser, parser->previous.start, parser->previous.end); + pm_parser_scope_push(parser, true); + parser->current_param_name = 0; + name = parser->previous; + } + + break; + } + case PM_TOKEN_CONSTANT: + case PM_TOKEN_INSTANCE_VARIABLE: + case PM_TOKEN_CLASS_VARIABLE: + case PM_TOKEN_GLOBAL_VARIABLE: + case PM_TOKEN_KEYWORD_NIL: + case PM_TOKEN_KEYWORD_SELF: + case PM_TOKEN_KEYWORD_TRUE: + case PM_TOKEN_KEYWORD_FALSE: + case PM_TOKEN_KEYWORD___FILE__: + case PM_TOKEN_KEYWORD___LINE__: + case PM_TOKEN_KEYWORD___ENCODING__: { + pm_parser_scope_push(parser, true); + parser->current_param_name = 0; + parser_lex(parser); + pm_token_t identifier = parser->previous; + + if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) { + lex_state_set(parser, PM_LEX_STATE_FNAME); + parser_lex(parser); + operator = parser->previous; + + switch (identifier.type) { + case PM_TOKEN_CONSTANT: + receiver = (pm_node_t *) pm_constant_read_node_create(parser, &identifier); + break; + case PM_TOKEN_INSTANCE_VARIABLE: + receiver = (pm_node_t *) pm_instance_variable_read_node_create(parser, &identifier); + break; + case PM_TOKEN_CLASS_VARIABLE: + receiver = (pm_node_t *) pm_class_variable_read_node_create(parser, &identifier); + break; + case PM_TOKEN_GLOBAL_VARIABLE: + receiver = (pm_node_t *) pm_global_variable_read_node_create(parser, &identifier); + break; + case PM_TOKEN_KEYWORD_NIL: + receiver = (pm_node_t *) pm_nil_node_create(parser, &identifier); + break; + case PM_TOKEN_KEYWORD_SELF: + receiver = (pm_node_t *) pm_self_node_create(parser, &identifier); + break; + case PM_TOKEN_KEYWORD_TRUE: + receiver = (pm_node_t *) pm_true_node_create(parser, &identifier); + break; + case PM_TOKEN_KEYWORD_FALSE: + receiver = (pm_node_t *)pm_false_node_create(parser, &identifier); + break; + case PM_TOKEN_KEYWORD___FILE__: + receiver = (pm_node_t *) pm_source_file_node_create(parser, &identifier); + break; + case PM_TOKEN_KEYWORD___LINE__: + receiver = (pm_node_t *) pm_source_line_node_create(parser, &identifier); + break; + case PM_TOKEN_KEYWORD___ENCODING__: + receiver = (pm_node_t *) pm_source_encoding_node_create(parser, &identifier); + break; + default: + break; + } + + name = parse_method_definition_name(parser); + } else { + name = identifier; + } + break; + } + case PM_TOKEN_PARENTHESIS_LEFT: { + // The current context is `PM_CONTEXT_DEF_PARAMS`, however the inner expression + // of this parenthesis should not be processed under this context. + // Thus, the context is popped here. + context_pop(parser); + parser_lex(parser); + + pm_token_t lparen = parser->previous; + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_STATEMENT, PM_ERR_DEF_RECEIVER); + + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + pm_token_t rparen = parser->previous; + + lex_state_set(parser, PM_LEX_STATE_FNAME); + expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM); + + operator = parser->previous; + receiver = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, expression, &rparen); + + pm_parser_scope_push(parser, true); + parser->current_param_name = 0; + + // To push `PM_CONTEXT_DEF_PARAMS` again is for the same reason as described the above. + context_push(parser, PM_CONTEXT_DEF_PARAMS); + name = parse_method_definition_name(parser); + break; + } + default: + pm_parser_scope_push(parser, true); + parser->current_param_name = 0; + name = parse_method_definition_name(parser); + break; + } + + // If, after all that, we were unable to find a method name, add an + // error to the error list. + if (name.type == PM_TOKEN_MISSING) { + pm_parser_err_previous(parser, PM_ERR_DEF_NAME); + } + + pm_token_t lparen; + pm_token_t rparen; + pm_parameters_node_t *params; + + switch (parser->current.type) { + case PM_TOKEN_PARENTHESIS_LEFT: { + parser_lex(parser); + lparen = parser->previous; + + if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + params = NULL; + } else { + params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, true, false, true); + } + + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->command_start = true; + + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_DEF_PARAMS_TERM_PAREN); + rparen = parser->previous; + break; + } + case PM_CASE_PARAMETER: { + // If we're about to lex a label, we need to add the label + // state to make sure the next newline is ignored. + if (parser->current.type == PM_TOKEN_LABEL) { + lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL); + } + + lparen = not_provided(parser); + rparen = not_provided(parser); + params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, false, false, true); + break; + } + default: { + lparen = not_provided(parser); + rparen = not_provided(parser); + params = NULL; + break; + } + } + + context_pop(parser); + pm_node_t *statements = NULL; + pm_token_t equal; + pm_token_t end_keyword; + + if (accept1(parser, PM_TOKEN_EQUAL)) { + if (token_is_setter_name(&name)) { + pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER); + } + equal = parser->previous; + + context_push(parser, PM_CONTEXT_DEF); + pm_do_loop_stack_push(parser, false); + statements = (pm_node_t *) pm_statements_node_create(parser); + + pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, PM_ERR_DEF_ENDLESS); + + if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + pm_token_t rescue_keyword = parser->previous; + pm_node_t *value = parse_expression(parser, binding_power, PM_ERR_RESCUE_MODIFIER_VALUE); + pm_rescue_modifier_node_t *rescue_node = pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value); + statement = (pm_node_t *)rescue_node; + } + + pm_statements_node_body_append((pm_statements_node_t *) statements, statement); + pm_do_loop_stack_pop(parser); + context_pop(parser); + end_keyword = not_provided(parser); + } else { + equal = not_provided(parser); + + if (lparen.type == PM_TOKEN_NOT_PROVIDED) { + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->command_start = true; + expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_DEF_PARAMS_TERM); + } else { + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + pm_accepts_block_stack_push(parser, true); + pm_do_loop_stack_push(parser, false); + + if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_DEF); + pm_accepts_block_stack_pop(parser); + } + + if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); + statements = (pm_node_t *) parse_rescues_as_begin(parser, (pm_statements_node_t *) statements, true); + } + + pm_accepts_block_stack_pop(parser); + pm_do_loop_stack_pop(parser); + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM); + end_keyword = parser->previous; + } + + pm_constant_id_list_t locals = parser->current_scope->locals; + parser->current_param_name = old_param_name; + pm_parser_scope_pop(parser); + + return (pm_node_t *) pm_def_node_create( + parser, + &name, + receiver, + params, + statements, + &locals, + &def_keyword, + &operator, + &lparen, + &rparen, + &equal, + &end_keyword + ); + } + case PM_TOKEN_KEYWORD_DEFINED: { + parser_lex(parser); + pm_token_t keyword = parser->previous; + + pm_token_t lparen; + pm_token_t rparen; + pm_node_t *expression; + + if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + lparen = parser->previous; + expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_ERR_DEFINED_EXPRESSION); + + if (parser->recovering) { + rparen = not_provided(parser); + } else { + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + rparen = parser->previous; + } + } else { + lparen = not_provided(parser); + rparen = not_provided(parser); + expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_DEFINED_EXPRESSION); + } + + return (pm_node_t *) pm_defined_node_create( + parser, + &lparen, + expression, + &rparen, + &PM_LOCATION_TOKEN_VALUE(&keyword) + ); + } + case PM_TOKEN_KEYWORD_END_UPCASE: { + if (binding_power != PM_BINDING_POWER_STATEMENT) { + pm_parser_err_current(parser, PM_ERR_STATEMENT_POSTEXE_END); + } + + parser_lex(parser); + pm_token_t keyword = parser->previous; + + if (context_def_p(parser)) { + pm_parser_warn_token(parser, &keyword, PM_WARN_END_IN_METHOD); + } + + expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_END_UPCASE_BRACE); + pm_token_t opening = parser->previous; + pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE); + + expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM); + return (pm_node_t *) pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous); + } + case PM_TOKEN_KEYWORD_FALSE: + parser_lex(parser); + return (pm_node_t *)pm_false_node_create(parser, &parser->previous); + case PM_TOKEN_KEYWORD_FOR: { + parser_lex(parser); + pm_token_t for_keyword = parser->previous; + pm_node_t *index; + + pm_parser_scope_push_transparent(parser); + context_push(parser, PM_CONTEXT_FOR_INDEX); + + // First, parse out the first index expression. + if (accept1(parser, PM_TOKEN_USTAR)) { + pm_token_t star_operator = parser->previous; + pm_node_t *name = NULL; + + if (token_begins_expression_p(parser->current.type)) { + name = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR); + } + + index = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name); + } else if (token_begins_expression_p(parser->current.type)) { + index = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA); + } else { + pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX); + index = (pm_node_t *) pm_missing_node_create(parser, for_keyword.start, for_keyword.end); + } + + // Now, if there are multiple index expressions, parse them out. + if (match1(parser, PM_TOKEN_COMMA)) { + index = parse_targets(parser, index, PM_BINDING_POWER_INDEX); + } else { + index = parse_target(parser, index); + } + + context_pop(parser); + pm_parser_scope_pop(parser); + pm_do_loop_stack_push(parser, true); + + expect1(parser, PM_TOKEN_KEYWORD_IN, PM_ERR_FOR_IN); + pm_token_t in_keyword = parser->previous; + + pm_node_t *collection = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_ERR_FOR_COLLECTION); + pm_do_loop_stack_pop(parser); + + pm_token_t do_keyword; + if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) { + do_keyword = parser->previous; + } else { + do_keyword = not_provided(parser); + } + + accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE); + pm_statements_node_t *statements = NULL; + + if (!accept1(parser, PM_TOKEN_KEYWORD_END)) { + pm_parser_scope_push_transparent(parser); + statements = parse_statements(parser, PM_CONTEXT_FOR); + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM); + pm_parser_scope_pop(parser); + } + + return (pm_node_t *) pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, &do_keyword, &parser->previous); + } + case PM_TOKEN_KEYWORD_IF: + parser_lex(parser); + return parse_conditional(parser, PM_CONTEXT_IF); + case PM_TOKEN_KEYWORD_UNDEF: { + if (binding_power != PM_BINDING_POWER_STATEMENT) { + pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF); + } + + parser_lex(parser); + pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous); + pm_node_t *name = parse_undef_argument(parser); + + if (PM_NODE_TYPE_P(name, PM_MISSING_NODE)) { + pm_node_destroy(parser, name); + } else { + pm_undef_node_append(undef, name); + + while (match1(parser, PM_TOKEN_COMMA)) { + lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM); + parser_lex(parser); + name = parse_undef_argument(parser); + + if (PM_NODE_TYPE_P(name, PM_MISSING_NODE)) { + pm_node_destroy(parser, name); + break; + } + + pm_undef_node_append(undef, name); + } + } + + return (pm_node_t *) undef; + } + case PM_TOKEN_KEYWORD_NOT: { + parser_lex(parser); + + pm_token_t message = parser->previous; + pm_arguments_t arguments = { 0 }; + pm_node_t *receiver = NULL; + + accept1(parser, PM_TOKEN_NEWLINE); + + if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + arguments.opening_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + + if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + arguments.closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + } else { + receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_ERR_NOT_EXPRESSION); + pm_conditional_predicate(receiver); + + if (!parser->recovering) { + accept1(parser, PM_TOKEN_NEWLINE); + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + arguments.closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + } + } + } else { + receiver = parse_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_NOT_EXPRESSION); + pm_conditional_predicate(receiver); + } + + return (pm_node_t *) pm_call_node_not_create(parser, receiver, &message, &arguments); + } + case PM_TOKEN_KEYWORD_UNLESS: + parser_lex(parser); + return parse_conditional(parser, PM_CONTEXT_UNLESS); + case PM_TOKEN_KEYWORD_MODULE: { + parser_lex(parser); + + pm_token_t module_keyword = parser->previous; + pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_ERR_MODULE_NAME); + pm_token_t name; + + // If we can recover from a syntax error that occurred while parsing + // the name of the module, then we'll handle that here. + if (PM_NODE_TYPE_P(constant_path, PM_MISSING_NODE)) { + pm_token_t missing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + return (pm_node_t *) pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing); + } + + while (accept1(parser, PM_TOKEN_COLON_COLON)) { + pm_token_t double_colon = parser->previous; + + expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); + pm_node_t *constant = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); + + constant_path = (pm_node_t *) pm_constant_path_node_create(parser, constant_path, &double_colon, constant); + } + + // Here we retrieve the name of the module. If it wasn't a constant, + // then it's possible that `module foo` was passed, which is a + // syntax error. We handle that here as well. + name = parser->previous; + if (name.type != PM_TOKEN_CONSTANT) { + pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME); + } + + pm_constant_id_t old_param_name = parser->current_param_name; + parser->current_param_name = 0; + pm_parser_scope_push(parser, true); + accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE); + pm_node_t *statements = NULL; + + if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_MODULE); + pm_accepts_block_stack_pop(parser); + } + + if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); + statements = (pm_node_t *) parse_rescues_as_begin(parser, (pm_statements_node_t *) statements, false); + } + + pm_constant_id_list_t locals = parser->current_scope->locals; + pm_parser_scope_pop(parser); + parser->current_param_name = old_param_name; + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM); + + if (context_def_p(parser)) { + pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD); + } + + return (pm_node_t *) pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous); + } + case PM_TOKEN_KEYWORD_NIL: + parser_lex(parser); + return (pm_node_t *) pm_nil_node_create(parser, &parser->previous); + case PM_TOKEN_KEYWORD_REDO: + parser_lex(parser); + return (pm_node_t *) pm_redo_node_create(parser, &parser->previous); + case PM_TOKEN_KEYWORD_RETRY: + parser_lex(parser); + return (pm_node_t *) pm_retry_node_create(parser, &parser->previous); + case PM_TOKEN_KEYWORD_SELF: + parser_lex(parser); + return (pm_node_t *) pm_self_node_create(parser, &parser->previous); + case PM_TOKEN_KEYWORD_TRUE: + parser_lex(parser); + return (pm_node_t *) pm_true_node_create(parser, &parser->previous); + case PM_TOKEN_KEYWORD_UNTIL: { + pm_do_loop_stack_push(parser, true); + parser_lex(parser); + pm_token_t keyword = parser->previous; + + pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_ERR_CONDITIONAL_UNTIL_PREDICATE); + pm_do_loop_stack_pop(parser); + + expect3(parser, PM_TOKEN_KEYWORD_DO_LOOP, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE); + pm_statements_node_t *statements = NULL; + + if (!accept1(parser, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, PM_CONTEXT_UNTIL); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM); + } + + return (pm_node_t *) pm_until_node_create(parser, &keyword, &parser->previous, predicate, statements, 0); + } + case PM_TOKEN_KEYWORD_WHILE: { + pm_do_loop_stack_push(parser, true); + parser_lex(parser); + pm_token_t keyword = parser->previous; + + pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_ERR_CONDITIONAL_WHILE_PREDICATE); + pm_do_loop_stack_pop(parser); + + expect3(parser, PM_TOKEN_KEYWORD_DO_LOOP, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE); + pm_statements_node_t *statements = NULL; + + if (!accept1(parser, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, PM_CONTEXT_WHILE); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM); + } + + return (pm_node_t *) pm_while_node_create(parser, &keyword, &parser->previous, predicate, statements, 0); + } + case PM_TOKEN_PERCENT_LOWER_I: { + parser_lex(parser); + pm_array_node_t *array = pm_array_node_create(parser, &parser->previous); + + while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + accept1(parser, PM_TOKEN_WORDS_SEP); + if (match1(parser, PM_TOKEN_STRING_END)) break; + + if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_array_node_elements_append(array, (pm_node_t *) pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing)); + } + + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT); + } + + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM); + pm_array_node_close_set(array, &parser->previous); + + return (pm_node_t *) array; + } + case PM_TOKEN_PERCENT_UPPER_I: { + parser_lex(parser); + pm_array_node_t *array = pm_array_node_create(parser, &parser->previous); + + // This is the current node that we are parsing that will be added to the + // list of elements. + pm_node_t *current = NULL; + + while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + switch (parser->current.type) { + case PM_TOKEN_WORDS_SEP: { + if (current == NULL) { + // If we hit a separator before we have any content, then we don't + // need to do anything. + } else { + // If we hit a separator after we've hit content, then we need to + // append that content to the list and reset the current node. + pm_array_node_elements_append(array, current); + current = NULL; + } + + parser_lex(parser); + break; + } + case PM_TOKEN_STRING_CONTENT: { + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + + if (current == NULL) { + // If we hit content and the current node is NULL, then this is + // the first string content we've seen. In that case we're going + // to create a new string node and set that to the current. + current = (pm_node_t *) pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing); + parser_lex(parser); + } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) { + // If we hit string content and the current node is an + // interpolated string, then we need to append the string content + // to the list of child nodes. + pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); + parser_lex(parser); + + pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, string); + } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) { + // If we hit string content and the current node is a string node, + // then we need to convert the current node into an interpolated + // string and add the string content to the list of child nodes. + pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->previous, &closing); + parser_lex(parser); + + pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + pm_interpolated_symbol_node_append(interpolated, current); + pm_interpolated_symbol_node_append(interpolated, string); + current = (pm_node_t *) interpolated; + } else { + assert(false && "unreachable"); + } + + break; + } + case PM_TOKEN_EMBVAR: { + bool start_location_set = false; + if (current == NULL) { + // If we hit an embedded variable and the current node is NULL, + // then this is the start of a new string. We'll set the current + // node to a new interpolated string. + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + current = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) { + // If we hit an embedded variable and the current node is a string + // node, then we'll convert the current into an interpolated + // string and add the string node to the list of parts. + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + + current = (pm_node_t *) pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current); + pm_interpolated_symbol_node_append(interpolated, current); + interpolated->base.location.start = current->location.start; + start_location_set = true; + current = (pm_node_t *) interpolated; + } else { + // If we hit an embedded variable and the current node is an + // interpolated string, then we'll just add the embedded variable. + } + + pm_node_t *part = parse_string_part(parser); + pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part); + if (!start_location_set) { + current->location.start = part->location.start; + } + break; + } + case PM_TOKEN_EMBEXPR_BEGIN: { + bool start_location_set = false; + if (current == NULL) { + // If we hit an embedded expression and the current node is NULL, + // then this is the start of a new string. We'll set the current + // node to a new interpolated string. + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + current = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) { + // If we hit an embedded expression and the current node is a + // string node, then we'll convert the current into an + // interpolated string and add the string node to the list of + // parts. + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + + current = (pm_node_t *) pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current); + pm_interpolated_symbol_node_append(interpolated, current); + interpolated->base.location.start = current->location.start; + start_location_set = true; + current = (pm_node_t *) interpolated; + } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) { + // If we hit an embedded expression and the current node is an + // interpolated string, then we'll just continue on. + } else { + assert(false && "unreachable"); + } + + pm_node_t *part = parse_string_part(parser); + pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part); + if (!start_location_set) { + current->location.start = part->location.start; + } + break; + } + default: + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_UPPER_ELEMENT); + parser_lex(parser); + break; + } + } + + // If we have a current node, then we need to append it to the list. + if (current) { + pm_array_node_elements_append(array, current); + } + + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM); + pm_array_node_close_set(array, &parser->previous); + + return (pm_node_t *) array; + } + case PM_TOKEN_PERCENT_LOWER_W: { + parser_lex(parser); + pm_array_node_t *array = pm_array_node_create(parser, &parser->previous); + + // skip all leading whitespaces + accept1(parser, PM_TOKEN_WORDS_SEP); + + while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + accept1(parser, PM_TOKEN_WORDS_SEP); + if (match1(parser, PM_TOKEN_STRING_END)) break; + + if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + + pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); + pm_array_node_elements_append(array, string); + } + + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT); + } + + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM); + pm_array_node_close_set(array, &parser->previous); + + return (pm_node_t *) array; + } + case PM_TOKEN_PERCENT_UPPER_W: { + parser_lex(parser); + pm_array_node_t *array = pm_array_node_create(parser, &parser->previous); + + // This is the current node that we are parsing that will be added to the + // list of elements. + pm_node_t *current = NULL; + + while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + switch (parser->current.type) { + case PM_TOKEN_WORDS_SEP: { + if (current == NULL) { + // If we hit a separator before we have any content, then we don't + // need to do anything. + } else { + // If we hit a separator after we've hit content, then we need to + // append that content to the list and reset the current node. + pm_array_node_elements_append(array, current); + current = NULL; + } + + parser_lex(parser); + break; + } + case PM_TOKEN_STRING_CONTENT: { + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + + pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); + parser_lex(parser); + + if (current == NULL) { + // If we hit content and the current node is NULL, then this is + // the first string content we've seen. In that case we're going + // to create a new string node and set that to the current. + current = string; + } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { + // If we hit string content and the current node is an + // interpolated string, then we need to append the string content + // to the list of child nodes. + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, string); + } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { + // If we hit string content and the current node is a string node, + // then we need to convert the current node into an interpolated + // string and add the string content to the list of child nodes. + pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); + pm_interpolated_string_node_append(interpolated, current); + pm_interpolated_string_node_append(interpolated, string); + current = (pm_node_t *) interpolated; + } else { + assert(false && "unreachable"); + } + + break; + } + case PM_TOKEN_EMBVAR: { + if (current == NULL) { + // If we hit an embedded variable and the current node is NULL, + // then this is the start of a new string. We'll set the current + // node to a new interpolated string. + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + current = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, NULL, &closing); + } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { + // If we hit an embedded variable and the current node is a string + // node, then we'll convert the current into an interpolated + // string and add the string node to the list of parts. + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); + pm_interpolated_string_node_append(interpolated, current); + current = (pm_node_t *) interpolated; + } else { + // If we hit an embedded variable and the current node is an + // interpolated string, then we'll just add the embedded variable. + } + + pm_node_t *part = parse_string_part(parser); + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part); + break; + } + case PM_TOKEN_EMBEXPR_BEGIN: { + if (current == NULL) { + // If we hit an embedded expression and the current node is NULL, + // then this is the start of a new string. We'll set the current + // node to a new interpolated string. + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + current = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, NULL, &closing); + } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { + // If we hit an embedded expression and the current node is a + // string node, then we'll convert the current into an + // interpolated string and add the string node to the list of + // parts. + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); + pm_interpolated_string_node_append(interpolated, current); + current = (pm_node_t *) interpolated; + } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { + // If we hit an embedded expression and the current node is an + // interpolated string, then we'll just continue on. + } else { + assert(false && "unreachable"); + } + + pm_node_t *part = parse_string_part(parser); + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part); + break; + } + default: + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_UPPER_ELEMENT); + parser_lex(parser); + break; + } + } + + // If we have a current node, then we need to append it to the list. + if (current) { + pm_array_node_elements_append(array, current); + } + + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM); + pm_array_node_close_set(array, &parser->previous); + + return (pm_node_t *) array; + } + case PM_TOKEN_REGEXP_BEGIN: { + pm_token_t opening = parser->current; + parser_lex(parser); + + if (match1(parser, PM_TOKEN_REGEXP_END)) { + // If we get here, then we have an end immediately after a start. In + // that case we'll create an empty content token and return an + // uninterpolated regular expression. + pm_token_t content = (pm_token_t) { + .type = PM_TOKEN_STRING_CONTENT, + .start = parser->previous.end, + .end = parser->previous.end + }; + + parser_lex(parser); + return (pm_node_t *) pm_regular_expression_node_create(parser, &opening, &content, &parser->previous); + } + + pm_interpolated_regular_expression_node_t *node; + + if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + // In this case we've hit string content so we know the regular + // expression at least has something in it. We'll need to check if the + // following token is the end (in which case we can return a plain + // regular expression) or if it's not then it has interpolation. + pm_string_t unescaped = parser->current_string; + pm_token_t content = parser->current; + parser_lex(parser); + + // If we hit an end, then we can create a regular expression node + // without interpolation, which can be represented more succinctly and + // more easily compiled. + if (accept1(parser, PM_TOKEN_REGEXP_END)) { + return (pm_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + } + + // If we get here, then we have interpolation so we'll need to create + // a regular expression node with interpolation. + node = pm_interpolated_regular_expression_node_create(parser, &opening); + + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped); + pm_interpolated_regular_expression_node_append(node, part); + } else { + // If the first part of the body of the regular expression is not a + // string content, then we have interpolation and we need to create an + // interpolated regular expression node. + node = pm_interpolated_regular_expression_node_create(parser, &opening); + } + + // Now that we're here and we have interpolation, we'll parse all of the + // parts into the list. + pm_node_t *part; + while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) { + if ((part = parse_string_part(parser)) != NULL) { + pm_interpolated_regular_expression_node_append(node, part); + } + } + + expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM); + pm_interpolated_regular_expression_node_closing_set(node, &parser->previous); + + return (pm_node_t *) node; + } + case PM_TOKEN_BACKTICK: + case PM_TOKEN_PERCENT_LOWER_X: { + parser_lex(parser); + pm_token_t opening = parser->previous; + + // When we get here, we don't know if this string is going to have + // interpolation or not, even though it is allowed. Still, we want to be + // able to return a string node without interpolation if we can since + // it'll be faster. + if (match1(parser, PM_TOKEN_STRING_END)) { + // If we get here, then we have an end immediately after a start. In + // that case we'll create an empty content token and return an + // uninterpolated string. + pm_token_t content = (pm_token_t) { + .type = PM_TOKEN_STRING_CONTENT, + .start = parser->previous.end, + .end = parser->previous.end + }; + + parser_lex(parser); + return (pm_node_t *) pm_xstring_node_create(parser, &opening, &content, &parser->previous); + } + + pm_interpolated_x_string_node_t *node; + + if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + // In this case we've hit string content so we know the string + // at least has something in it. We'll need to check if the + // following token is the end (in which case we can return a + // plain string) or if it's not then it has interpolation. + pm_string_t unescaped = parser->current_string; + pm_token_t content = parser->current; + parser_lex(parser); + + if (accept1(parser, PM_TOKEN_STRING_END)) { + return (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + } + + // If we get here, then we have interpolation so we'll need to + // create a string node with interpolation. + node = pm_interpolated_xstring_node_create(parser, &opening, &opening); + + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped); + + pm_interpolated_xstring_node_append(node, part); + } else { + // If the first part of the body of the string is not a string + // content, then we have interpolation and we need to create an + // interpolated string node. + node = pm_interpolated_xstring_node_create(parser, &opening, &opening); + } + + pm_node_t *part; + while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + if ((part = parse_string_part(parser)) != NULL) { + pm_interpolated_xstring_node_append(node, part); + } + } + + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM); + pm_interpolated_xstring_node_closing_set(node, &parser->previous); + return (pm_node_t *) node; + } + case PM_TOKEN_USTAR: { + parser_lex(parser); + + // * operators at the beginning of expressions are only valid in the + // context of a multiple assignment. We enforce that here. We'll + // still lex past it though and create a missing node place. + if (binding_power != PM_BINDING_POWER_STATEMENT) { + return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + } + + pm_token_t operator = parser->previous; + pm_node_t *name = NULL; + + if (token_begins_expression_p(parser->current.type)) { + name = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR); + } + + pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &operator, name); + + if (match1(parser, PM_TOKEN_COMMA)) { + return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX); + } else { + return parse_target_validate(parser, splat); + } + } + case PM_TOKEN_BANG: { + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, PM_ERR_UNARY_RECEIVER_BANG); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!"); + + pm_conditional_predicate(receiver); + return (pm_node_t *) node; + } + case PM_TOKEN_TILDE: { + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, PM_ERR_UNARY_RECEIVER_TILDE); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~"); + + return (pm_node_t *) node; + } + case PM_TOKEN_UMINUS: { + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, PM_ERR_UNARY_RECEIVER_MINUS); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "-@"); + + return (pm_node_t *) node; + } + case PM_TOKEN_UMINUS_NUM: { + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *node = parse_expression(parser, pm_binding_powers[parser->previous.type].right, PM_ERR_UNARY_RECEIVER_MINUS); + + if (accept1(parser, PM_TOKEN_STAR_STAR)) { + pm_token_t exponent_operator = parser->previous; + pm_node_t *exponent = parse_expression(parser, pm_binding_powers[exponent_operator.type].right, PM_ERR_EXPECT_ARGUMENT); + node = (pm_node_t *) pm_call_node_binary_create(parser, node, &exponent_operator, exponent); + node = (pm_node_t *) pm_call_node_unary_create(parser, &operator, node, "-@"); + } else { + switch (PM_NODE_TYPE(node)) { + case PM_INTEGER_NODE: + case PM_FLOAT_NODE: + case PM_RATIONAL_NODE: + case PM_IMAGINARY_NODE: + parse_negative_numeric(node); + break; + default: + node = (pm_node_t *) pm_call_node_unary_create(parser, &operator, node, "-@"); + break; + } + } + + return node; + } + case PM_TOKEN_MINUS_GREATER: { + int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting; + parser->lambda_enclosure_nesting = parser->enclosure_nesting; + + pm_accepts_block_stack_push(parser, true); + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_parser_scope_push(parser, false); + pm_block_parameters_node_t *params; + + switch (parser->current.type) { + case PM_TOKEN_PARENTHESIS_LEFT: { + parser->current_scope->explicit_params = true; + pm_token_t opening = parser->current; + parser_lex(parser); + + if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + params = pm_block_parameters_node_create(parser, NULL, &opening); + } else { + params = parse_block_parameters(parser, false, &opening, true); + } + + accept1(parser, PM_TOKEN_NEWLINE); + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + + pm_block_parameters_node_closing_set(params, &parser->previous); + break; + } + case PM_CASE_PARAMETER: { + parser->current_scope->explicit_params = true; + pm_accepts_block_stack_push(parser, false); + pm_token_t opening = not_provided(parser); + params = parse_block_parameters(parser, false, &opening, true); + pm_accepts_block_stack_pop(parser); + break; + } + default: { + params = NULL; + break; + } + } + + pm_token_t opening; + pm_node_t *body = NULL; + parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting; + + if (accept1(parser, PM_TOKEN_LAMBDA_BEGIN)) { + opening = parser->previous; + + if (!accept1(parser, PM_TOKEN_BRACE_RIGHT)) { + body = (pm_node_t *) parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES); + expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE); + } + } else { + expect1(parser, PM_TOKEN_KEYWORD_DO, PM_ERR_LAMBDA_OPEN); + opening = parser->previous; + + if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { + pm_accepts_block_stack_push(parser, true); + body = (pm_node_t *) parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END); + pm_accepts_block_stack_pop(parser); + } + + if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { + assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE)); + body = (pm_node_t *) parse_rescues_as_begin(parser, (pm_statements_node_t *) body, false); + } + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END); + } + + pm_constant_id_list_t locals = parser->current_scope->locals; + uint32_t numbered_parameters = parser->current_scope->numbered_parameters; + pm_parser_scope_pop(parser); + pm_accepts_block_stack_pop(parser); + return (pm_node_t *) pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, params, body, numbered_parameters); + } + case PM_TOKEN_UPLUS: { + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, PM_ERR_UNARY_RECEIVER_PLUS); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "+@"); + + return (pm_node_t *) node; + } + case PM_TOKEN_STRING_BEGIN: + return parse_strings(parser, NULL); + case PM_TOKEN_SYMBOL_BEGIN: { + pm_lex_mode_t lex_mode = *parser->lex_modes.current; + parser_lex(parser); + + return parse_symbol(parser, &lex_mode, PM_LEX_STATE_END); + } + default: + if (context_recoverable(parser, &parser->current)) { + parser->recovering = true; + } + + return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + } +} + +static inline pm_node_t * +parse_assignment_value(pm_parser_t *parser, pm_binding_power_t binding_power, pm_diagnostic_id_t diag_id) { + pm_node_t *value = parse_value_expression(parser, binding_power, diag_id); + + // Contradicting binding powers, the right-hand-side value of rthe assignment allows the `rescue` modifier. + if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + pm_token_t rescue = parser->current; + parser_lex(parser); + pm_node_t *right = parse_expression(parser, binding_power, PM_ERR_RESCUE_MODIFIER_VALUE); + + return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right); + } + + return value; +} + + +static inline pm_node_t * +parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, pm_diagnostic_id_t diag_id) { + pm_node_t *value = parse_starred_expression(parser, binding_power, diag_id); + + bool is_single_value = true; + if (previous_binding_power == PM_BINDING_POWER_STATEMENT && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) { + is_single_value = false; + pm_token_t opening = not_provided(parser); + pm_array_node_t *array = pm_array_node_create(parser, &opening); + + pm_array_node_elements_append(array, value); + value = (pm_node_t *) array; + + while (accept1(parser, PM_TOKEN_COMMA)) { + pm_node_t *element = parse_starred_expression(parser, binding_power, PM_ERR_ARRAY_ELEMENT); + pm_array_node_elements_append(array, element); + if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break; + } + } + + // Contradicting binding powers, the right-hand-side value of the assignment allows the `rescue` modifier. + if (is_single_value && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + pm_token_t rescue = parser->current; + parser_lex(parser); + pm_node_t *right = parse_expression(parser, binding_power, PM_ERR_RESCUE_MODIFIER_VALUE); + + return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right); + } + + return value; +} + +/** + * Ensures a call node that is about to become a call operator node does not + * have arguments or a block attached. If it does, then we'll need to add an + * error message and destroy the arguments/block. Ideally we would keep the node + * around so that consumers would still have access to it, but we don't have a + * great structure for that at the moment. + */ +static void +parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) { + if (call_node->arguments != NULL) { + pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS); + pm_node_destroy(parser, (pm_node_t *) call_node->arguments); + call_node->arguments = NULL; + } + + if (call_node->block != NULL) { + pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK); + pm_node_destroy(parser, (pm_node_t *) call_node->block); + call_node->block = NULL; + } +} + +static bool +name_is_identifier(pm_parser_t *parser, const uint8_t *source, size_t length) { + if (length == 0) { + return false; + } + + size_t width = char_is_identifier_start(parser, source); + if (!width) { + return false; + } + + uint8_t *cursor = ((uint8_t *)source) + width; + while (cursor < source + length && (width = char_is_identifier(parser, cursor))) { + cursor += width; + } + + return cursor == source + length; +} + +/** + * Potentially change a =~ with a regular expression with named captures into a + * match write node. + */ +static pm_node_t * +parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call) { + pm_string_list_t named_captures = { 0 }; + pm_node_t *result; + + if (pm_regexp_named_capture_group_names(pm_string_source(content), pm_string_length(content), &named_captures, parser->encoding_changed, &parser->encoding) && (named_captures.length > 0)) { + // Since we should not create a MatchWriteNode when all capture names + // are invalid, creating a MatchWriteNode is delayed here. + pm_match_write_node_t *match = NULL; + pm_constant_id_list_t names = { 0 }; + + for (size_t index = 0; index < named_captures.length; index++) { + pm_string_t *string = &named_captures.strings[index]; + + const uint8_t *source = pm_string_source(string); + size_t length = pm_string_length(string); + + pm_location_t location; + pm_constant_id_t name; + + // If the name of the capture group isn't a valid identifier, we do + // not add it to the local table. + if (!name_is_identifier(parser, source, length)) continue; + + if (content->type == PM_STRING_SHARED) { + // If the unescaped string is a slice of the source, then we can + // copy the names directly. The pointers will line up. + location = (pm_location_t) { .start = source, .end = source + length }; + name = pm_parser_constant_id_location(parser, location.start, location.end); + pm_refute_numbered_parameter(parser, source, source + length); + } else { + // Otherwise, the name is a slice of the malloc-ed owned string, + // in which case we need to copy it out into a new string. + location = call->receiver->location; + + void *memory = malloc(length); + if (memory == NULL) abort(); + + memcpy(memory, source, length); + name = pm_parser_constant_id_owned(parser, (const uint8_t *) memory, length); + + if (pm_token_is_numbered_parameter(source, source + length)) { + const pm_location_t *location = &call->receiver->location; + PM_PARSER_ERR_LOCATION_FORMAT(parser, location, PM_ERR_PARAMETER_NUMBERED_RESERVED, location->start); + } + } + + if (name != 0) { + // We dont want to create duplicate targets if the capture name + // is duplicated. + if (pm_constant_id_list_includes(&names, name)) continue; + pm_constant_id_list_append(&names, name); + + // Here we lazily create the MatchWriteNode since we know we're + // about to add a target. + if (match == NULL) match = pm_match_write_node_create(parser, call); + + // First, find the depth of the local that is being assigned. + int depth; + if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) { + pm_parser_local_add(parser, name); + } + + // Next, create the local variable target and add it to the + // list of targets for the match. + pm_node_t *target = (pm_node_t *) pm_local_variable_target_node_create_values(parser, &location, name, depth == -1 ? 0 : (uint32_t) depth); + pm_node_list_append(&match->targets, target); + } + } + + if (match != NULL) { + result = (pm_node_t *) match; + } else { + result = (pm_node_t *) call; + } + + pm_constant_id_list_free(&names); + } else { + result = (pm_node_t *) call; + } + + pm_string_list_free(&named_captures); + return result; +} + +static inline pm_node_t * +parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power) { + pm_token_t token = parser->current; + + switch (token.type) { + case PM_TOKEN_EQUAL: { + switch (PM_NODE_TYPE(node)) { + case PM_CALL_NODE: { + // If we have no arguments to the call node and we need this + // to be a target then this is either a method call or a + // local variable write. This _must_ happen before the value + // is parsed because it could be referenced in the value. + pm_call_node_t *call_node = (pm_call_node_t *) node; + if (pm_call_node_variable_call_p(call_node)) { + pm_parser_local_add_location(parser, call_node->message_loc.start, call_node->message_loc.end); + } + } + /* fallthrough */ + case PM_CASE_WRITABLE: { + parser_lex(parser); + pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL); + return parse_write(parser, node, &token, value); + } + case PM_SPLAT_NODE: { + pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser); + pm_multi_target_node_targets_append(parser, multi_target, node); + + parser_lex(parser); + pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL); + return parse_write(parser, (pm_node_t *) multi_target, &token, value); + } + default: + parser_lex(parser); + + // In this case we have an = sign, but we don't know what it's for. We + // need to treat it as an error. For now, we'll mark it as an error + // and just skip right past it. + pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL); + return node; + } + } + case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL: { + switch (PM_NODE_TYPE(node)) { + case PM_BACK_REFERENCE_READ_NODE: + case PM_NUMBERED_REFERENCE_READ_NODE: + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_READONLY); + /* fallthrough */ + case PM_GLOBAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + pm_node_t *result = (pm_node_t *) pm_global_variable_and_write_node_create(parser, node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_CLASS_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + pm_node_t *result = (pm_node_t *) pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_CONSTANT_PATH_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + return (pm_node_t *) pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value); + } + case PM_CONSTANT_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + pm_node_t *result = (pm_node_t *) pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + pm_node_t *result = (pm_node_t *) pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, node, &token, value, cast->name, cast->depth); + + pm_node_destroy(parser, node); + return result; + } + case PM_CALL_NODE: { + parser_lex(parser); + pm_call_node_t *cast = (pm_call_node_t *) node; + + // If we have a vcall (a method with no arguments and no + // receiver that could have been a local variable) then we + // will transform it into a local variable write. + if (pm_call_node_variable_call_p(cast)) { + pm_location_t *message_loc = &cast->message_loc; + pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end); + + pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end); + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); + + pm_node_destroy(parser, (pm_node_t *) cast); + return result; + } + + // If there is no call operator and the message is "[]" then + // this is an aref expression, and we can transform it into + // an aset expression. + if (pm_call_node_index_p(cast)) { + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + return (pm_node_t *) pm_index_and_write_node_create(parser, cast, &token, value); + } + + // If this node cannot be writable, then we have an error. + if (pm_call_node_writable_p(cast)) { + parse_write_name(parser, &cast->name); + } else { + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + + parse_call_operator_write(parser, cast, &token); + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + return (pm_node_t *) pm_call_and_write_node_create(parser, cast, &token, value); + } + case PM_MULTI_WRITE_NODE: { + parser_lex(parser); + pm_parser_err_token(parser, &token, PM_ERR_AMPAMPEQ_MULTI_ASSIGN); + return node; + } + default: + parser_lex(parser); + + // In this case we have an &&= sign, but we don't know what it's for. + // We need to treat it as an error. For now, we'll mark it as an error + // and just skip right past it. + pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); + return node; + } + } + case PM_TOKEN_PIPE_PIPE_EQUAL: { + switch (PM_NODE_TYPE(node)) { + case PM_BACK_REFERENCE_READ_NODE: + case PM_NUMBERED_REFERENCE_READ_NODE: + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_READONLY); + /* fallthrough */ + case PM_GLOBAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + pm_node_t *result = (pm_node_t *) pm_global_variable_or_write_node_create(parser, node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_CLASS_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + pm_node_t *result = (pm_node_t *) pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_CONSTANT_PATH_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + return (pm_node_t *) pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value); + } + case PM_CONSTANT_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + pm_node_t *result = (pm_node_t *) pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + pm_node_t *result = (pm_node_t *) pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, node, &token, value, cast->name, cast->depth); + + pm_node_destroy(parser, node); + return result; + } + case PM_CALL_NODE: { + parser_lex(parser); + pm_call_node_t *cast = (pm_call_node_t *) node; + + // If we have a vcall (a method with no arguments and no + // receiver that could have been a local variable) then we + // will transform it into a local variable write. + if (pm_call_node_variable_call_p(cast)) { + pm_location_t *message_loc = &cast->message_loc; + pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end); + + pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end); + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); + + pm_node_destroy(parser, (pm_node_t *) cast); + return result; + } + + // If there is no call operator and the message is "[]" then + // this is an aref expression, and we can transform it into + // an aset expression. + if (pm_call_node_index_p(cast)) { + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + return (pm_node_t *) pm_index_or_write_node_create(parser, cast, &token, value); + } + + // If this node cannot be writable, then we have an error. + if (pm_call_node_writable_p(cast)) { + parse_write_name(parser, &cast->name); + } else { + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + + parse_call_operator_write(parser, cast, &token); + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + return (pm_node_t *) pm_call_or_write_node_create(parser, cast, &token, value); + } + case PM_MULTI_WRITE_NODE: { + parser_lex(parser); + pm_parser_err_token(parser, &token, PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN); + return node; + } + default: + parser_lex(parser); + + // In this case we have an ||= sign, but we don't know what it's for. + // We need to treat it as an error. For now, we'll mark it as an error + // and just skip right past it. + pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); + return node; + } + } + case PM_TOKEN_AMPERSAND_EQUAL: + case PM_TOKEN_CARET_EQUAL: + case PM_TOKEN_GREATER_GREATER_EQUAL: + case PM_TOKEN_LESS_LESS_EQUAL: + case PM_TOKEN_MINUS_EQUAL: + case PM_TOKEN_PERCENT_EQUAL: + case PM_TOKEN_PIPE_EQUAL: + case PM_TOKEN_PLUS_EQUAL: + case PM_TOKEN_SLASH_EQUAL: + case PM_TOKEN_STAR_EQUAL: + case PM_TOKEN_STAR_STAR_EQUAL: { + switch (PM_NODE_TYPE(node)) { + case PM_BACK_REFERENCE_READ_NODE: + case PM_NUMBERED_REFERENCE_READ_NODE: + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_READONLY); + /* fallthrough */ + case PM_GLOBAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + pm_node_t *result = (pm_node_t *) pm_global_variable_operator_write_node_create(parser, node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_CLASS_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + pm_node_t *result = (pm_node_t *) pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_CONSTANT_PATH_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + return (pm_node_t *) pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value); + } + case PM_CONSTANT_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + pm_node_t *result = (pm_node_t *) pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + pm_node_t *result = (pm_node_t *) pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return result; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, node, &token, value, cast->name, cast->depth); + + pm_node_destroy(parser, node); + return result; + } + case PM_CALL_NODE: { + parser_lex(parser); + pm_call_node_t *cast = (pm_call_node_t *) node; + + // If we have a vcall (a method with no arguments and no + // receiver that could have been a local variable) then we + // will transform it into a local variable write. + if (pm_call_node_variable_call_p(cast)) { + pm_location_t *message_loc = &cast->message_loc; + pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end); + + pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end); + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); + + pm_node_destroy(parser, (pm_node_t *) cast); + return result; + } + + // If there is no call operator and the message is "[]" then + // this is an aref expression, and we can transform it into + // an aset expression. + if (pm_call_node_index_p(cast)) { + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + return (pm_node_t *) pm_index_operator_write_node_create(parser, cast, &token, value); + } + + // If this node cannot be writable, then we have an error. + if (pm_call_node_writable_p(cast)) { + parse_write_name(parser, &cast->name); + } else { + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + + parse_call_operator_write(parser, cast, &token); + pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + return (pm_node_t *) pm_call_operator_write_node_create(parser, cast, &token, value); + } + case PM_MULTI_WRITE_NODE: { + parser_lex(parser); + pm_parser_err_token(parser, &token, PM_ERR_OPERATOR_MULTI_ASSIGN); + return node; + } + default: + parser_lex(parser); + + // In this case we have an operator but we don't know what it's for. + // We need to treat it as an error. For now, we'll mark it as an error + // and just skip right past it. + pm_parser_err_previous(parser, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + return node; + } + } + case PM_TOKEN_AMPERSAND_AMPERSAND: + case PM_TOKEN_KEYWORD_AND: { + parser_lex(parser); + + pm_node_t *right = parse_expression(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + return (pm_node_t *) pm_and_node_create(parser, node, &token, right); + } + case PM_TOKEN_KEYWORD_OR: + case PM_TOKEN_PIPE_PIPE: { + parser_lex(parser); + + pm_node_t *right = parse_expression(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + return (pm_node_t *) pm_or_node_create(parser, node, &token, right); + } + case PM_TOKEN_EQUAL_TILDE: { + // Note that we _must_ parse the value before adding the local + // variables in order to properly mirror the behavior of Ruby. For + // example, + // + // /(?bar)/ =~ foo + // + // In this case, `foo` should be a method call and not a local yet. + parser_lex(parser); + pm_node_t *argument = parse_expression(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + + // By default, we're going to create a call node and then return it. + pm_call_node_t *call = pm_call_node_binary_create(parser, node, &token, argument); + pm_node_t *result = (pm_node_t *) call; + + // If the receiver of this =~ is a regular expression node, then we + // need to introduce local variables for it based on its named + // capture groups. + if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE)) { + // It's possible to have an interpolated regular expression node + // that only contains strings. This is because it can be split + // up by a heredoc. In this case we need to concat the unescaped + // strings together and then parse them as a regular expression. + pm_node_list_t *parts = &((pm_interpolated_regular_expression_node_t *) node)->parts; + + bool interpolated = false; + size_t total_length = 0; + + for (size_t index = 0; index < parts->size; index++) { + pm_node_t *part = parts->nodes[index]; + + if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) { + total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped); + } else { + interpolated = true; + break; + } + } + + if (!interpolated && total_length > 0) { + void *memory = malloc(total_length); + if (!memory) abort(); + + uint8_t *cursor = memory; + for (size_t index = 0; index < parts->size; index++) { + pm_string_t *unescaped = &((pm_string_node_t *) parts->nodes[index])->unescaped; + size_t length = pm_string_length(unescaped); + + memcpy(cursor, pm_string_source(unescaped), length); + cursor += length; + } + + pm_string_t owned; + pm_string_owned_init(&owned, (uint8_t *) memory, total_length); + + result = parse_regular_expression_named_captures(parser, &owned, call); + pm_string_free(&owned); + } + } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) { + // If we have a regular expression node, then we can just parse + // the named captures directly off the unescaped string. + const pm_string_t *content = &((pm_regular_expression_node_t *) node)->unescaped; + result = parse_regular_expression_named_captures(parser, content, call); + } + + return result; + } + case PM_TOKEN_UAMPERSAND: + case PM_TOKEN_USTAR: + case PM_TOKEN_USTAR_STAR: + // The only times this will occur are when we are in an error state, + // but we'll put them in here so that errors can propagate. + case PM_TOKEN_BANG_EQUAL: + case PM_TOKEN_BANG_TILDE: + case PM_TOKEN_EQUAL_EQUAL: + case PM_TOKEN_EQUAL_EQUAL_EQUAL: + case PM_TOKEN_LESS_EQUAL_GREATER: + case PM_TOKEN_GREATER: + case PM_TOKEN_GREATER_EQUAL: + case PM_TOKEN_LESS: + case PM_TOKEN_LESS_EQUAL: + case PM_TOKEN_CARET: + case PM_TOKEN_PIPE: + case PM_TOKEN_AMPERSAND: + case PM_TOKEN_GREATER_GREATER: + case PM_TOKEN_LESS_LESS: + case PM_TOKEN_MINUS: + case PM_TOKEN_PLUS: + case PM_TOKEN_PERCENT: + case PM_TOKEN_SLASH: + case PM_TOKEN_STAR: + case PM_TOKEN_STAR_STAR: { + parser_lex(parser); + + pm_node_t *argument = parse_expression(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument); + } + case PM_TOKEN_AMPERSAND_DOT: + case PM_TOKEN_DOT: { + parser_lex(parser); + pm_token_t operator = parser->previous; + pm_arguments_t arguments = { 0 }; + + // This if statement handles the foo.() syntax. + if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + parse_arguments_list(parser, &arguments, true); + return (pm_node_t *) pm_call_node_shorthand_create(parser, node, &operator, &arguments); + } + + pm_token_t message; + + switch (parser->current.type) { + case PM_CASE_OPERATOR: + case PM_CASE_KEYWORD: + case PM_TOKEN_CONSTANT: + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_METHOD_NAME: { + parser_lex(parser); + message = parser->previous; + break; + } + default: { + pm_parser_err_current(parser, PM_ERR_DEF_NAME); + message = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + } + } + + parse_arguments_list(parser, &arguments, true); + pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments); + + if ( + (previous_binding_power == PM_BINDING_POWER_STATEMENT) && + arguments.arguments == NULL && + arguments.opening_loc.start == NULL && + match1(parser, PM_TOKEN_COMMA) + ) { + return parse_targets_validate(parser, (pm_node_t *) call, PM_BINDING_POWER_INDEX); + } else { + return (pm_node_t *) call; + } + } + case PM_TOKEN_DOT_DOT: + case PM_TOKEN_DOT_DOT_DOT: { + parser_lex(parser); + + pm_node_t *right = NULL; + if (token_begins_expression_p(parser->current.type)) { + right = parse_expression(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + } + + return (pm_node_t *) pm_range_node_create(parser, node, &token, right); + } + case PM_TOKEN_KEYWORD_IF_MODIFIER: { + pm_token_t keyword = parser->current; + parser_lex(parser); + + pm_node_t *predicate = parse_value_expression(parser, binding_power, PM_ERR_CONDITIONAL_IF_PREDICATE); + return (pm_node_t *) pm_if_node_modifier_create(parser, node, &keyword, predicate); + } + case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: { + pm_token_t keyword = parser->current; + parser_lex(parser); + + pm_node_t *predicate = parse_value_expression(parser, binding_power, PM_ERR_CONDITIONAL_UNLESS_PREDICATE); + return (pm_node_t *) pm_unless_node_modifier_create(parser, node, &keyword, predicate); + } + case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: { + parser_lex(parser); + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(statements, node); + + pm_node_t *predicate = parse_value_expression(parser, binding_power, PM_ERR_CONDITIONAL_UNTIL_PREDICATE); + return (pm_node_t *) pm_until_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0); + } + case PM_TOKEN_KEYWORD_WHILE_MODIFIER: { + parser_lex(parser); + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(statements, node); + + pm_node_t *predicate = parse_value_expression(parser, binding_power, PM_ERR_CONDITIONAL_WHILE_PREDICATE); + return (pm_node_t *) pm_while_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0); + } + case PM_TOKEN_QUESTION_MARK: { + pm_token_t qmark = parser->current; + parser_lex(parser); + pm_node_t *true_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_TERNARY_EXPRESSION_TRUE); + + if (parser->recovering) { + // If parsing the true expression of this ternary resulted in a syntax + // error that we can recover from, then we're going to put missing nodes + // and tokens into the remaining places. We want to be sure to do this + // before the `expect` function call to make sure it doesn't + // accidentally move past a ':' token that occurs after the syntax + // error. + pm_token_t colon = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + pm_node_t *false_expression = (pm_node_t *) pm_missing_node_create(parser, colon.start, colon.end); + + return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression); + } + + accept1(parser, PM_TOKEN_NEWLINE); + expect1(parser, PM_TOKEN_COLON, PM_ERR_TERNARY_COLON); + + pm_token_t colon = parser->previous; + pm_node_t *false_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_TERNARY_EXPRESSION_FALSE); + + return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression); + } + case PM_TOKEN_COLON_COLON: { + parser_lex(parser); + pm_token_t delimiter = parser->previous; + + switch (parser->current.type) { + case PM_TOKEN_CONSTANT: { + parser_lex(parser); + pm_node_t *path; + + if ( + (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) || + (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)) + ) { + // If we have a constant immediately following a '::' operator, then + // this can either be a constant path or a method call, depending on + // what follows the constant. + // + // If we have parentheses, then this is a method call. That would + // look like Foo::Bar(). + pm_token_t message = parser->previous; + pm_arguments_t arguments = { 0 }; + + parse_arguments_list(parser, &arguments, true); + path = (pm_node_t *) pm_call_node_call_create(parser, node, &delimiter, &message, &arguments); + } else { + // Otherwise, this is a constant path. That would look like Foo::Bar. + pm_node_t *child = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); + path = (pm_node_t *)pm_constant_path_node_create(parser, node, &delimiter, child); + } + + // If this is followed by a comma then it is a multiple assignment. + if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { + return parse_targets_validate(parser, path, PM_BINDING_POWER_INDEX); + } + + return path; + } + case PM_CASE_OPERATOR: + case PM_CASE_KEYWORD: + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_METHOD_NAME: { + parser_lex(parser); + pm_token_t message = parser->previous; + + // If we have an identifier following a '::' operator, then it is for + // sure a method call. + pm_arguments_t arguments = { 0 }; + parse_arguments_list(parser, &arguments, true); + pm_call_node_t *call = pm_call_node_call_create(parser, node, &delimiter, &message, &arguments); + + // If this is followed by a comma then it is a multiple assignment. + if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { + return parse_targets_validate(parser, (pm_node_t *) call, PM_BINDING_POWER_INDEX); + } + + return (pm_node_t *) call; + } + case PM_TOKEN_PARENTHESIS_LEFT: { + // If we have a parenthesis following a '::' operator, then it is the + // method call shorthand. That would look like Foo::(bar). + pm_arguments_t arguments = { 0 }; + parse_arguments_list(parser, &arguments, true); + + return (pm_node_t *) pm_call_node_shorthand_create(parser, node, &delimiter, &arguments); + } + default: { + pm_parser_err_token(parser, &delimiter, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); + pm_node_t *child = (pm_node_t *) pm_missing_node_create(parser, delimiter.start, delimiter.end); + return (pm_node_t *)pm_constant_path_node_create(parser, node, &delimiter, child); + } + } + } + case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: { + parser_lex(parser); + accept1(parser, PM_TOKEN_NEWLINE); + pm_node_t *value = parse_expression(parser, binding_power, PM_ERR_RESCUE_MODIFIER_VALUE); + + return (pm_node_t *) pm_rescue_modifier_node_create(parser, node, &token, value); + } + case PM_TOKEN_BRACKET_LEFT: { + parser_lex(parser); + + pm_arguments_t arguments = { 0 }; + arguments.opening_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + + if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) { + pm_accepts_block_stack_push(parser, true); + parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT); + pm_accepts_block_stack_pop(parser); + expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET); + } + + arguments.closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + + // If we have a comma after the closing bracket then this is a multiple + // assignment and we should parse the targets. + if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { + pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments); + return parse_targets_validate(parser, (pm_node_t *) aref, PM_BINDING_POWER_INDEX); + } + + // If we're at the end of the arguments, we can now check if there is a + // block node that starts with a {. If there is, then we can parse it and + // add it to the arguments. + pm_block_node_t *block = NULL; + if (accept1(parser, PM_TOKEN_BRACE_LEFT)) { + block = parse_block(parser); + pm_arguments_validate_block(parser, &arguments, block); + } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) { + block = parse_block(parser); + } + + if (block != NULL) { + if (arguments.block != NULL) { + pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_AFTER_BLOCK); + if (arguments.arguments == NULL) { + arguments.arguments = pm_arguments_node_create(parser); + } + pm_arguments_node_arguments_append(arguments.arguments, arguments.block); + } + + arguments.block = (pm_node_t *) block; + } + + return (pm_node_t *) pm_call_node_aref_create(parser, node, &arguments); + } + case PM_TOKEN_KEYWORD_IN: { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = true; + + pm_token_t operator = parser->current; + parser->command_start = false; + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + + parser_lex(parser); + + pm_node_t *pattern = parse_pattern(parser, true, PM_ERR_PATTERN_EXPRESSION_AFTER_IN); + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + + return (pm_node_t *) pm_match_predicate_node_create(parser, node, pattern, &operator); + } + case PM_TOKEN_EQUAL_GREATER: { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = true; + + pm_token_t operator = parser->current; + parser->command_start = false; + lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); + + parser_lex(parser); + + pm_node_t *pattern = parse_pattern(parser, true, PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET); + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + + return (pm_node_t *) pm_match_required_node_create(parser, node, pattern, &operator); + } + default: + assert(false && "unreachable"); + return NULL; + } +} + +/** + * Parse an expression at the given point of the parser using the given binding + * power to parse subsequent chains. If this function finds a syntax error, it + * will append the error message to the parser's error list. + * + * Consumers of this function should always check parser->recovering to + * determine if they need to perform additional cleanup. + */ +static pm_node_t * +parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, pm_diagnostic_id_t diag_id) { + pm_token_t recovery = parser->previous; + pm_node_t *node = parse_expression_prefix(parser, binding_power); + + switch (PM_NODE_TYPE(node)) { + case PM_MISSING_NODE: + // If we found a syntax error, then the type of node returned by + // parse_expression_prefix is going to be a missing node. In that + // case we need to add the error message to the parser's error list. + pm_parser_err(parser, recovery.end, recovery.end, diag_id); + return node; + case PM_PRE_EXECUTION_NODE: + case PM_POST_EXECUTION_NODE: + case PM_ALIAS_GLOBAL_VARIABLE_NODE: + case PM_ALIAS_METHOD_NODE: + case PM_UNDEF_NODE: + // These expressions are statements, and cannot be followed by + // operators (except modifiers). + if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER_RESCUE) { + return node; + } + break; + case PM_RANGE_NODE: + // Range operators are non-associative, so that it does not + // associate with other range operators (i.e. `..1..` should be + // rejected.) For this reason, we check such a case for unary ranges + // here, and if so, it returns the node immediately, + if ((((pm_range_node_t *) node)->left == NULL) && pm_binding_powers[parser->current.type].left >= PM_BINDING_POWER_RANGE) { + return node; + } + break; + default: + break; + } + + // Otherwise we'll look and see if the next token can be parsed as an infix + // operator. If it can, then we'll parse it using parse_expression_infix. + pm_binding_powers_t current_binding_powers; + while ( + current_binding_powers = pm_binding_powers[parser->current.type], + binding_power <= current_binding_powers.left && + current_binding_powers.binary + ) { + node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right); + if ( + current_binding_powers.nonassoc && + current_binding_powers.right <= pm_binding_powers[parser->current.type].left + ) { + break; + } + } + + return node; +} + +static pm_node_t * +parse_program(pm_parser_t *parser) { + pm_parser_scope_push(parser, !parser->current_scope); + parser_lex(parser); + + pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN); + if (!statements) { + statements = pm_statements_node_create(parser); + } + pm_constant_id_list_t locals = parser->current_scope->locals; + pm_parser_scope_pop(parser); + + // If this is an empty file, then we're still going to parse all of the + // statements in order to gather up all of the comments and such. Here we'll + // correct the location information. + if (pm_statements_node_body_length(statements) == 0) { + pm_statements_node_location_set(statements, parser->start, parser->start); + } + + return (pm_node_t *) pm_program_node_create(parser, &locals, statements); +} + +/******************************************************************************/ +/* External functions */ +/******************************************************************************/ + +/** + * Initialize a parser with the given start and end pointers. + */ +PRISM_EXPORTED_FUNCTION void +pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) { + assert(source != NULL); + + *parser = (pm_parser_t) { + .lex_state = PM_LEX_STATE_BEG, + .enclosure_nesting = 0, + .lambda_enclosure_nesting = -1, + .brace_nesting = 0, + .do_loop_stack = 0, + .accepts_block_stack = 0, + .lex_modes = { + .index = 0, + .stack = {{ .mode = PM_LEX_DEFAULT }}, + .current = &parser->lex_modes.stack[0], + }, + .start = source, + .end = source + size, + .previous = { .type = PM_TOKEN_EOF, .start = source, .end = source }, + .current = { .type = PM_TOKEN_EOF, .start = source, .end = source }, + .next_start = NULL, + .heredoc_end = NULL, + .comment_list = { 0 }, + .magic_comment_list = { 0 }, + .warning_list = { 0 }, + .error_list = { 0 }, + .current_scope = NULL, + .current_context = NULL, + .encoding = pm_encoding_utf_8, + .encoding_changed_callback = NULL, + .encoding_decode_callback = NULL, + .encoding_comment_start = source, + .lex_callback = NULL, + .filepath_string = { 0 }, + .constant_pool = { 0 }, + .newline_list = { 0 }, + .integer_base = 0, + .current_string = PM_STRING_EMPTY, + .start_line = 1, + .command_start = true, + .recovering = false, + .encoding_changed = false, + .pattern_matching_newlines = false, + .in_keyword_arg = false, + .current_param_name = 0, + .semantic_token_seen = false, + .frozen_string_literal = false, + .suppress_warnings = false + }; + + // Initialize the constant pool. We're going to completely guess as to the + // number of constants that we'll need based on the size of the input. The + // ratio we chose here is actually less arbitrary than you might think. + // + // We took ~50K Ruby files and measured the size of the file versus the + // number of constants that were found in those files. Then we found the + // average and standard deviation of the ratios of constants/bytesize. Then + // we added 1.34 standard deviations to the average to get a ratio that + // would fit 75% of the files (for a two-tailed distribution). This works + // because there was about a 0.77 correlation and the distribution was + // roughly normal. + // + // This ratio will need to change if we add more constants to the constant + // pool for another node type. + uint32_t constant_size = ((uint32_t) size) / 95; + pm_constant_pool_init(&parser->constant_pool, constant_size < 4 ? 4 : constant_size); + + // Initialize the newline list. Similar to the constant pool, we're going to + // guess at the number of newlines that we'll need based on the size of the + // input. + size_t newline_size = size / 22; + pm_newline_list_init(&parser->newline_list, source, newline_size < 4 ? 4 : newline_size); + + // If options were provided to this parse, establish them here. + if (options != NULL) { + // filepath option + parser->filepath_string = options->filepath; + + // line option + parser->start_line = options->line; + + // encoding option + size_t encoding_length = pm_string_length(&options->encoding); + if (encoding_length > 0) { + const uint8_t *encoding_source = pm_string_source(&options->encoding); + parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length); + } + + // frozen_string_literal option + if (options->frozen_string_literal) { + parser->frozen_string_literal = true; + } + + // suppress_warnings option + if (options->suppress_warnings) { + parser->suppress_warnings = true; + } + + // scopes option + for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) { + const pm_options_scope_t *scope = pm_options_scope_get(options, scope_index); + pm_parser_scope_push(parser, scope_index == 0); + + for (size_t local_index = 0; local_index < scope->locals_count; local_index++) { + const pm_string_t *local = pm_options_scope_local_get(scope, local_index); + + const uint8_t *source = pm_string_source(local); + size_t length = pm_string_length(local); + + uint8_t *allocated = malloc(length); + if (allocated == NULL) continue; + + memcpy((void *) allocated, source, length); + pm_parser_local_add_owned(parser, allocated, length); + } + } + } + + pm_accepts_block_stack_push(parser, true); + + // Skip past the UTF-8 BOM if it exists. + if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) { + parser->current.end += 3; + parser->encoding_comment_start += 3; + } + + // If the first two bytes of the source are a shebang, then we'll indicate + // that the encoding comment is at the end of the shebang. + if (peek(parser) == '#' && peek_offset(parser, 1) == '!') { + const uint8_t *encoding_comment_start = next_newline(source, (ptrdiff_t) size); + if (encoding_comment_start) { + parser->encoding_comment_start = encoding_comment_start + 1; + } + } +} + +/** + * Register a callback that will be called whenever prism changes the encoding + * it is using to parse based on the magic comment. + */ +PRISM_EXPORTED_FUNCTION void +pm_parser_register_encoding_changed_callback(pm_parser_t *parser, pm_encoding_changed_callback_t callback) { + parser->encoding_changed_callback = callback; +} + +/** + * Register a callback that will be called when prism encounters a magic comment + * with an encoding referenced that it doesn't understand. The callback should + * return NULL if it also doesn't understand the encoding or it should return a + * pointer to a pm_encoding_t struct that contains the functions necessary to + * parse identifiers. + */ +PRISM_EXPORTED_FUNCTION void +pm_parser_register_encoding_decode_callback(pm_parser_t *parser, pm_encoding_decode_callback_t callback) { + parser->encoding_decode_callback = callback; +} + +/** + * Free all of the memory associated with the comment list. + */ +static inline void +pm_comment_list_free(pm_list_t *list) { + pm_list_node_t *node, *next; + + for (node = list->head; node != NULL; node = next) { + next = node->next; + + pm_comment_t *comment = (pm_comment_t *) node; + free(comment); + } +} + +/** + * Free all of the memory associated with the magic comment list. + */ +static inline void +pm_magic_comment_list_free(pm_list_t *list) { + pm_list_node_t *node, *next; + + for (node = list->head; node != NULL; node = next) { + next = node->next; + + pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) node; + free(magic_comment); + } +} + +/** + * Free any memory associated with the given parser. + */ +PRISM_EXPORTED_FUNCTION void +pm_parser_free(pm_parser_t *parser) { + pm_string_free(&parser->filepath_string); + pm_diagnostic_list_free(&parser->error_list); + pm_diagnostic_list_free(&parser->warning_list); + pm_comment_list_free(&parser->comment_list); + pm_magic_comment_list_free(&parser->magic_comment_list); + pm_constant_pool_free(&parser->constant_pool); + pm_newline_list_free(&parser->newline_list); + + while (parser->current_scope != NULL) { + // Normally, popping the scope doesn't free the locals since it is + // assumed that ownership has transferred to the AST. However if we have + // scopes while we're freeing the parser, it's likely they came from + // eval scopes and we need to free them explicitly here. + pm_constant_id_list_free(&parser->current_scope->locals); + pm_parser_scope_pop(parser); + } + + while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) { + lex_mode_pop(parser); + } +} + +/** + * Parse the Ruby source associated with the given parser and return the tree. + */ +PRISM_EXPORTED_FUNCTION pm_node_t * +pm_parse(pm_parser_t *parser) { + return parse_program(parser); +} + +static inline void +pm_serialize_header(pm_buffer_t *buffer) { + pm_buffer_append_string(buffer, "PRISM", 5); + pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR); + pm_buffer_append_byte(buffer, PRISM_VERSION_MINOR); + pm_buffer_append_byte(buffer, PRISM_VERSION_PATCH); + pm_buffer_append_byte(buffer, PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS ? 1 : 0); +} + +/** + * Serialize the AST represented by the given node to the given buffer. + */ +PRISM_EXPORTED_FUNCTION void +pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { + pm_serialize_header(buffer); + pm_serialize_content(parser, node, buffer); + pm_buffer_append_byte(buffer, '\0'); +} + +/** + * Parse and serialize the AST represented by the given source to the given + * buffer. + */ +PRISM_EXPORTED_FUNCTION void +pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) { + pm_options_t options = { 0 }; + pm_options_read(&options, data); + + pm_parser_t parser; + pm_parser_init(&parser, source, size, &options); + + pm_node_t *node = pm_parse(&parser); + + pm_serialize_header(buffer); + pm_serialize_content(&parser, node, buffer); + pm_buffer_append_byte(buffer, '\0'); + + pm_node_destroy(&parser, node); + pm_parser_free(&parser); + pm_options_free(&options); +} + +/** + * Parse and serialize the comments in the given source to the given buffer. + */ +PRISM_EXPORTED_FUNCTION void +pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) { + pm_options_t options = { 0 }; + pm_options_read(&options, data); + + pm_parser_t parser; + pm_parser_init(&parser, source, size, &options); + + pm_node_t *node = pm_parse(&parser); + pm_serialize_header(buffer); + pm_serialize_encoding(&parser.encoding, buffer); + pm_buffer_append_varsint(buffer, parser.start_line); + pm_serialize_comment_list(&parser, &parser.comment_list, buffer); + + pm_node_destroy(&parser, node); + pm_parser_free(&parser); + pm_options_free(&options); +} + +#undef PM_CASE_KEYWORD +#undef PM_CASE_OPERATOR +#undef PM_CASE_WRITABLE +#undef PM_STRING_EMPTY +#undef PM_LOCATION_NODE_BASE_VALUE +#undef PM_LOCATION_NODE_VALUE +#undef PM_LOCATION_NULL_VALUE +#undef PM_LOCATION_TOKEN_VALUE diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/regexp.c b/crates/prism-sys/vendor/prism-0.18.0/src/regexp.c new file mode 100644 index 00000000..cd52857f --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/regexp.c @@ -0,0 +1,638 @@ +#include "prism/regexp.h" + +/** + * This is the parser that is going to handle parsing regular expressions. + */ +typedef struct { + /** A pointer to the start of the source that we are parsing. */ + const uint8_t *start; + + /** A pointer to the current position in the source. */ + const uint8_t *cursor; + + /** A pointer to the end of the source that we are parsing. */ + const uint8_t *end; + + /** A list of named captures that we've found. */ + pm_string_list_t *named_captures; + + /** Whether the encoding has changed from the default. */ + bool encoding_changed; + + /** The encoding of the source. */ + pm_encoding_t *encoding; +} pm_regexp_parser_t; + +/** + * This initializes a new parser with the given source. + */ +static void +pm_regexp_parser_init(pm_regexp_parser_t *parser, const uint8_t *start, const uint8_t *end, pm_string_list_t *named_captures, bool encoding_changed, pm_encoding_t *encoding) { + *parser = (pm_regexp_parser_t) { + .start = start, + .cursor = start, + .end = end, + .named_captures = named_captures, + .encoding_changed = encoding_changed, + .encoding = encoding + }; +} + +/** + * This appends a new string to the list of named captures. + */ +static void +pm_regexp_parser_named_capture(pm_regexp_parser_t *parser, const uint8_t *start, const uint8_t *end) { + pm_string_t string; + pm_string_shared_init(&string, start, end); + pm_string_list_append(parser->named_captures, &string); + pm_string_free(&string); +} + +/** + * Returns true if the next character is the end of the source. + */ +static inline bool +pm_regexp_char_is_eof(pm_regexp_parser_t *parser) { + return parser->cursor >= parser->end; +} + +/** + * Optionally accept a char and consume it if it exists. + */ +static inline bool +pm_regexp_char_accept(pm_regexp_parser_t *parser, uint8_t value) { + if (!pm_regexp_char_is_eof(parser) && *parser->cursor == value) { + parser->cursor++; + return true; + } + return false; +} + +/** + * Expect a character to be present and consume it. + */ +static inline bool +pm_regexp_char_expect(pm_regexp_parser_t *parser, uint8_t value) { + if (!pm_regexp_char_is_eof(parser) && *parser->cursor == value) { + parser->cursor++; + return true; + } + return false; +} + +/** + * This advances the current token to the next instance of the given character. + */ +static bool +pm_regexp_char_find(pm_regexp_parser_t *parser, uint8_t value) { + if (pm_regexp_char_is_eof(parser)) { + return false; + } + + const uint8_t *end = (const uint8_t *) pm_memchr(parser->cursor, value, (size_t) (parser->end - parser->cursor), parser->encoding_changed, parser->encoding); + if (end == NULL) { + return false; + } + + parser->cursor = end + 1; + return true; +} + +/** + * Range quantifiers are a special class of quantifiers that look like + * + * * {digit} + * * {digit,} + * * {digit,digit} + * * {,digit} + * + * Unfortunately, if there are any spaces in between, then this just becomes a + * regular character match expression and we have to backtrack. So when this + * function first starts running, we'll create a "save" point and then attempt + * to parse the quantifier. If it fails, we'll restore the save point and + * return. + * + * The properly track everything, we're going to build a little state machine. + * It looks something like the following: + * + * +-------+ +---------+ ------------+ + * ---- lbrace ---> | start | ---- digit ---> | minimum | | + * +-------+ +---------+ <--- digit -+ + * | | | + * +-------+ | | rbrace + * | comma | <----- comma +---- comma -------+ | + * +-------+ V V + * | +---------+ +---------+ + * +-- digit --> | maximum | -- rbrace --> || final || + * +---------+ +---------+ + * | ^ + * +- digit -+ + * + * Note that by the time we've hit this function, the lbrace has already been + * consumed so we're in the start state. + */ +static bool +pm_regexp_parse_range_quantifier(pm_regexp_parser_t *parser) { + const uint8_t *savepoint = parser->cursor; + + enum { + PM_REGEXP_RANGE_QUANTIFIER_STATE_START, + PM_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM, + PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM, + PM_REGEXP_RANGE_QUANTIFIER_STATE_COMMA + } state = PM_REGEXP_RANGE_QUANTIFIER_STATE_START; + + while (1) { + switch (state) { + case PM_REGEXP_RANGE_QUANTIFIER_STATE_START: + switch (*parser->cursor) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + parser->cursor++; + state = PM_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM; + break; + case ',': + parser->cursor++; + state = PM_REGEXP_RANGE_QUANTIFIER_STATE_COMMA; + break; + default: + parser->cursor = savepoint; + return true; + } + break; + case PM_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM: + switch (*parser->cursor) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + parser->cursor++; + break; + case ',': + parser->cursor++; + state = PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM; + break; + case '}': + parser->cursor++; + return true; + default: + parser->cursor = savepoint; + return true; + } + break; + case PM_REGEXP_RANGE_QUANTIFIER_STATE_COMMA: + switch (*parser->cursor) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + parser->cursor++; + state = PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM; + break; + default: + parser->cursor = savepoint; + return true; + } + break; + case PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM: + switch (*parser->cursor) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + parser->cursor++; + break; + case '}': + parser->cursor++; + return true; + default: + parser->cursor = savepoint; + return true; + } + break; + } + } + + return true; +} + +/** + * quantifier : star-quantifier + * | plus-quantifier + * | optional-quantifier + * | range-quantifier + * | + * ; + */ +static bool +pm_regexp_parse_quantifier(pm_regexp_parser_t *parser) { + if (pm_regexp_char_is_eof(parser)) return true; + + switch (*parser->cursor) { + case '*': + case '+': + case '?': + parser->cursor++; + return true; + case '{': + parser->cursor++; + return pm_regexp_parse_range_quantifier(parser); + default: + // In this case there is no quantifier. + return true; + } +} + +/** + * match-posix-class : '[' '[' ':' '^'? CHAR+ ':' ']' ']' + * ; + */ +static bool +pm_regexp_parse_posix_class(pm_regexp_parser_t *parser) { + if (!pm_regexp_char_expect(parser, ':')) { + return false; + } + + pm_regexp_char_accept(parser, '^'); + + return ( + pm_regexp_char_find(parser, ':') && + pm_regexp_char_expect(parser, ']') && + pm_regexp_char_expect(parser, ']') + ); +} + +// Forward declaration because character sets can be nested. +static bool +pm_regexp_parse_lbracket(pm_regexp_parser_t *parser); + +/** + * match-char-set : '[' '^'? (match-range | match-char)* ']' + * ; + */ +static bool +pm_regexp_parse_character_set(pm_regexp_parser_t *parser) { + pm_regexp_char_accept(parser, '^'); + + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ']') { + switch (*parser->cursor++) { + case '[': + pm_regexp_parse_lbracket(parser); + break; + case '\\': + if (!pm_regexp_char_is_eof(parser)) { + parser->cursor++; + } + break; + default: + // do nothing, we've already advanced the cursor + break; + } + } + + return pm_regexp_char_expect(parser, ']'); +} + +/** + * A left bracket can either mean a POSIX class or a character set. + */ +static bool +pm_regexp_parse_lbracket(pm_regexp_parser_t *parser) { + const uint8_t *reset = parser->cursor; + + if ((parser->cursor + 2 < parser->end) && parser->cursor[0] == '[' && parser->cursor[1] == ':') { + parser->cursor++; + if (pm_regexp_parse_posix_class(parser)) return true; + + parser->cursor = reset; + } + + return pm_regexp_parse_character_set(parser); +} + +// Forward declaration here since parsing groups needs to go back up the grammar +// to parse expressions within them. +static bool +pm_regexp_parse_expression(pm_regexp_parser_t *parser); + +/** + * These are the states of the options that are configurable on the regular + * expression (or from within a group). + */ +typedef enum { + PM_REGEXP_OPTION_STATE_INVALID, + PM_REGEXP_OPTION_STATE_TOGGLEABLE, + PM_REGEXP_OPTION_STATE_ADDABLE, + PM_REGEXP_OPTION_STATE_ADDED, + PM_REGEXP_OPTION_STATE_REMOVED +} pm_regexp_option_state_t; + +// These are the options that are configurable on the regular expression (or +// from within a group). + +#define PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM 'a' +#define PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM 'x' +#define PRISM_REGEXP_OPTION_STATE_SLOTS (PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM + 1) + +/** + * This is the set of options that are configurable on the regular expression. + */ +typedef struct { + /** The current state of each option. */ + uint8_t values[PRISM_REGEXP_OPTION_STATE_SLOTS]; +} pm_regexp_options_t; + +/** + * Initialize a new set of options to their default values. + */ +static void +pm_regexp_options_init(pm_regexp_options_t *options) { + memset(options, PM_REGEXP_OPTION_STATE_INVALID, sizeof(uint8_t) * PRISM_REGEXP_OPTION_STATE_SLOTS); + options->values['i' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_TOGGLEABLE; + options->values['m' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_TOGGLEABLE; + options->values['x' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_TOGGLEABLE; + options->values['d' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_ADDABLE; + options->values['a' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_ADDABLE; + options->values['u' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_ADDABLE; +} + +/** + * Attempt to add the given option to the set of options. Returns true if it was + * added, false if it was already present. + */ +static bool +pm_regexp_options_add(pm_regexp_options_t *options, uint8_t key) { + if (key >= PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM) { + key = (uint8_t) (key - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM); + + switch (options->values[key]) { + case PM_REGEXP_OPTION_STATE_INVALID: + case PM_REGEXP_OPTION_STATE_REMOVED: + return false; + case PM_REGEXP_OPTION_STATE_TOGGLEABLE: + case PM_REGEXP_OPTION_STATE_ADDABLE: + options->values[key] = PM_REGEXP_OPTION_STATE_ADDED; + return true; + case PM_REGEXP_OPTION_STATE_ADDED: + return true; + } + } + + return false; +} + +/** + * Attempt to remove the given option from the set of options. Returns true if + * it was removed, false if it was already absent. + */ +static bool +pm_regexp_options_remove(pm_regexp_options_t *options, uint8_t key) { + if (key >= PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM) { + key = (uint8_t) (key - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM); + + switch (options->values[key]) { + case PM_REGEXP_OPTION_STATE_INVALID: + case PM_REGEXP_OPTION_STATE_ADDABLE: + return false; + case PM_REGEXP_OPTION_STATE_TOGGLEABLE: + case PM_REGEXP_OPTION_STATE_ADDED: + case PM_REGEXP_OPTION_STATE_REMOVED: + options->values[key] = PM_REGEXP_OPTION_STATE_REMOVED; + return true; + } + } + + return false; +} + +/** + * Groups can have quite a few different patterns for syntax. They basically + * just wrap a set of expressions, but they can potentially have options after a + * question mark. If there _isn't_ a question mark, then it's just a set of + * expressions. If there _is_, then here are the options: + * + * * (?#...) - inline comments + * * (?:subexp) - non-capturing group + * * (?=subexp) - positive lookahead + * * (?!subexp) - negative lookahead + * * (?>subexp) - atomic group + * * (?~subexp) - absence operator + * * (?<=subexp) - positive lookbehind + * * (?subexp) - named capturing group + * * (?'name'subexp) - named capturing group + * * (?(cond)yes-subexp) - conditional expression + * * (?(cond)yes-subexp|no-subexp) - conditional expression + * * (?imxdau-imx) - turn on and off configuration + * * (?imxdau-imx:subexp) - turn on and off configuration for an expression + */ +static bool +pm_regexp_parse_group(pm_regexp_parser_t *parser) { + // First, parse any options for the group. + if (pm_regexp_char_accept(parser, '?')) { + if (pm_regexp_char_is_eof(parser)) { + return false; + } + pm_regexp_options_t options; + pm_regexp_options_init(&options); + + switch (*parser->cursor) { + case '#': { // inline comments + if (parser->encoding_changed && parser->encoding->multibyte) { + bool escaped = false; + + // Here we're going to take a slow path and iterate through + // each multibyte character to find the close paren. We do + // this because \ can be a trailing byte in some encodings. + while (parser->cursor < parser->end) { + if (!escaped && *parser->cursor == ')') { + parser->cursor++; + return true; + } + + size_t width = parser->encoding->char_width(parser->cursor, (ptrdiff_t) (parser->end - parser->cursor)); + if (width == 0) return false; + + escaped = (width == 1) && (*parser->cursor == '\\'); + parser->cursor += width; + } + + return false; + } else { + // Here we can take the fast path and use memchr to find the + // next ) because we are safe checking backward for \ since + // it cannot be a trailing character. + bool found = pm_regexp_char_find(parser, ')'); + + while (found && (parser->start <= parser->cursor - 2) && (*(parser->cursor - 2) == '\\')) { + found = pm_regexp_char_find(parser, ')'); + } + + return found; + } + } + case ':': // non-capturing group + case '=': // positive lookahead + case '!': // negative lookahead + case '>': // atomic group + case '~': // absence operator + parser->cursor++; + break; + case '<': + parser->cursor++; + if (pm_regexp_char_is_eof(parser)) { + return false; + } + + switch (*parser->cursor) { + case '=': // positive lookbehind + case '!': // negative lookbehind + parser->cursor++; + break; + default: { // named capture group + const uint8_t *start = parser->cursor; + if (!pm_regexp_char_find(parser, '>')) { + return false; + } + pm_regexp_parser_named_capture(parser, start, parser->cursor - 1); + break; + } + } + break; + case '\'': { // named capture group + const uint8_t *start = ++parser->cursor; + if (!pm_regexp_char_find(parser, '\'')) { + return false; + } + + pm_regexp_parser_named_capture(parser, start, parser->cursor - 1); + break; + } + case '(': // conditional expression + if (!pm_regexp_char_find(parser, ')')) { + return false; + } + break; + case 'i': case 'm': case 'x': case 'd': case 'a': case 'u': // options + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != '-' && *parser->cursor != ':' && *parser->cursor != ')') { + if (!pm_regexp_options_add(&options, *parser->cursor)) { + return false; + } + parser->cursor++; + } + + if (pm_regexp_char_is_eof(parser)) { + return false; + } + + // If we hit a -, then we're done parsing options. + if (*parser->cursor != '-') break; + + // Otherwise, fallthrough to the - case. + /* fallthrough */ + case '-': + parser->cursor++; + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ':' && *parser->cursor != ')') { + if (!pm_regexp_options_remove(&options, *parser->cursor)) { + return false; + } + parser->cursor++; + } + + if (pm_regexp_char_is_eof(parser)) { + return false; + } + break; + default: + return false; + } + } + + // Now, parse the expressions within this group. + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')') { + if (!pm_regexp_parse_expression(parser)) { + return false; + } + pm_regexp_char_accept(parser, '|'); + } + + // Finally, make sure we have a closing parenthesis. + return pm_regexp_char_expect(parser, ')'); +} + +/** + * item : anchor + * | match-posix-class + * | match-char-set + * | match-char-class + * | match-char-prop + * | match-char + * | match-any + * | group + * | quantified + * ; + */ +static bool +pm_regexp_parse_item(pm_regexp_parser_t *parser) { + switch (*parser->cursor++) { + case '^': + case '$': + return true; + case '\\': + if (!pm_regexp_char_is_eof(parser)) { + parser->cursor++; + } + return pm_regexp_parse_quantifier(parser); + case '(': + return pm_regexp_parse_group(parser) && pm_regexp_parse_quantifier(parser); + case '[': + return pm_regexp_parse_lbracket(parser) && pm_regexp_parse_quantifier(parser); + default: + return pm_regexp_parse_quantifier(parser); + } +} + +/** + * expression : item+ + * ; + */ +static bool +pm_regexp_parse_expression(pm_regexp_parser_t *parser) { + if (!pm_regexp_parse_item(parser)) { + return false; + } + + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')' && *parser->cursor != '|') { + if (!pm_regexp_parse_item(parser)) { + return false; + } + } + + return true; +} + +/** + * pattern : EOF + * | expression EOF + * | expression '|' pattern + * ; + */ +static bool +pm_regexp_parse_pattern(pm_regexp_parser_t *parser) { + return ( + ( + // Exit early if the pattern is empty. + pm_regexp_char_is_eof(parser) || + // Parse the first expression in the pattern. + pm_regexp_parse_expression(parser) + ) && + ( + // Return now if we've parsed the entire pattern. + pm_regexp_char_is_eof(parser) || + // Otherwise, we should have a pipe character. + (pm_regexp_char_expect(parser, '|') && pm_regexp_parse_pattern(parser)) + ) + ); +} + +/** + * Parse a regular expression and extract the names of all of the named capture + * groups. + */ +PRISM_EXPORTED_FUNCTION bool +pm_regexp_named_capture_group_names(const uint8_t *source, size_t size, pm_string_list_t *named_captures, bool encoding_changed, pm_encoding_t *encoding) { + pm_regexp_parser_t parser; + pm_regexp_parser_init(&parser, source, source + size, named_captures, encoding_changed, encoding); + return pm_regexp_parse_pattern(&parser); +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/serialize.c b/crates/prism-sys/vendor/prism-0.18.0/src/serialize.c new file mode 100644 index 00000000..ade73ba0 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/serialize.c @@ -0,0 +1,2040 @@ +/******************************************************************************/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/serialize.c.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +#include "prism.h" + +#include + +static inline uint32_t +pm_ptrdifft_to_u32(ptrdiff_t value) { + assert(value >= 0 && ((unsigned long) value) < UINT32_MAX); + return (uint32_t) value; +} + +static inline uint32_t +pm_sizet_to_u32(size_t value) { + assert(value < UINT32_MAX); + return (uint32_t) value; +} + +static void +pm_serialize_location(const pm_parser_t *parser, const pm_location_t *location, pm_buffer_t *buffer) { + assert(location->start); + assert(location->end); + assert(location->start <= location->end); + + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(location->start - parser->start)); + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(location->end - location->start)); +} + +static void +pm_serialize_string(pm_parser_t *parser, pm_string_t *string, pm_buffer_t *buffer) { + switch (string->type) { + case PM_STRING_SHARED: { + pm_buffer_append_byte(buffer, 1); + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(pm_string_source(string) - parser->start)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(pm_string_length(string))); + break; + } + case PM_STRING_OWNED: + case PM_STRING_CONSTANT: { + uint32_t length = pm_sizet_to_u32(pm_string_length(string)); + pm_buffer_append_byte(buffer, 2); + pm_buffer_append_varuint(buffer, length); + pm_buffer_append_bytes(buffer, pm_string_source(string), length); + break; + } + case PM_STRING_MAPPED: + assert(false && "Cannot serialize mapped strings."); + break; + } +} + +static void +pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { + pm_buffer_append_byte(buffer, (uint8_t) PM_NODE_TYPE(node)); + + size_t offset = buffer->length; + + pm_serialize_location(parser, &node->location, buffer); + + switch (PM_NODE_TYPE(node)) { + // We do not need to serialize a ScopeNode ever as + // it is not part of the AST + case PM_SCOPE_NODE: + return; + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_alias_global_variable_node_t *)node)->new_name, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_alias_global_variable_node_t *)node)->old_name, buffer); + pm_serialize_location(parser, &((pm_alias_global_variable_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_ALIAS_METHOD_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_alias_method_node_t *)node)->new_name, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_alias_method_node_t *)node)->old_name, buffer); + pm_serialize_location(parser, &((pm_alias_method_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_ALTERNATION_PATTERN_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_alternation_pattern_node_t *)node)->left, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_alternation_pattern_node_t *)node)->right, buffer); + pm_serialize_location(parser, &((pm_alternation_pattern_node_t *)node)->operator_loc, buffer); + break; + } + case PM_AND_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_and_node_t *)node)->left, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_and_node_t *)node)->right, buffer); + pm_serialize_location(parser, &((pm_and_node_t *)node)->operator_loc, buffer); + break; + } + case PM_ARGUMENTS_NODE: { + uint32_t arguments_size = pm_sizet_to_u32(((pm_arguments_node_t *)node)->arguments.size); + pm_buffer_append_varuint(buffer, arguments_size); + for (uint32_t index = 0; index < arguments_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_arguments_node_t *)node)->arguments.nodes[index], buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_ARRAY_NODE: { + uint32_t elements_size = pm_sizet_to_u32(((pm_array_node_t *)node)->elements.size); + pm_buffer_append_varuint(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_array_node_t *)node)->elements.nodes[index], buffer); + } + if (((pm_array_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_array_node_t *)node)->opening_loc, buffer); + } + if (((pm_array_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_array_node_t *)node)->closing_loc, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_ARRAY_PATTERN_NODE: { + if (((pm_array_pattern_node_t *)node)->constant == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_array_pattern_node_t *)node)->constant, buffer); + } + uint32_t requireds_size = pm_sizet_to_u32(((pm_array_pattern_node_t *)node)->requireds.size); + pm_buffer_append_varuint(buffer, requireds_size); + for (uint32_t index = 0; index < requireds_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_array_pattern_node_t *)node)->requireds.nodes[index], buffer); + } + if (((pm_array_pattern_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_array_pattern_node_t *)node)->rest, buffer); + } + uint32_t posts_size = pm_sizet_to_u32(((pm_array_pattern_node_t *)node)->posts.size); + pm_buffer_append_varuint(buffer, posts_size); + for (uint32_t index = 0; index < posts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_array_pattern_node_t *)node)->posts.nodes[index], buffer); + } + if (((pm_array_pattern_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_array_pattern_node_t *)node)->opening_loc, buffer); + } + if (((pm_array_pattern_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_array_pattern_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_ASSOC_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_assoc_node_t *)node)->key, buffer); + if (((pm_assoc_node_t *)node)->value == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_assoc_node_t *)node)->value, buffer); + } + if (((pm_assoc_node_t *)node)->operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_assoc_node_t *)node)->operator_loc, buffer); + } + break; + } + case PM_ASSOC_SPLAT_NODE: { + if (((pm_assoc_splat_node_t *)node)->value == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_assoc_splat_node_t *)node)->value, buffer); + } + pm_serialize_location(parser, &((pm_assoc_splat_node_t *)node)->operator_loc, buffer); + break; + } + case PM_BACK_REFERENCE_READ_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_back_reference_read_node_t *)node)->name)); + break; + } + case PM_BEGIN_NODE: { + if (((pm_begin_node_t *)node)->begin_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_begin_node_t *)node)->begin_keyword_loc, buffer); + } + if (((pm_begin_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_begin_node_t *)node)->statements, buffer); + } + if (((pm_begin_node_t *)node)->rescue_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_begin_node_t *)node)->rescue_clause, buffer); + } + if (((pm_begin_node_t *)node)->else_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_begin_node_t *)node)->else_clause, buffer); + } + if (((pm_begin_node_t *)node)->ensure_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_begin_node_t *)node)->ensure_clause, buffer); + } + if (((pm_begin_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_begin_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case PM_BLOCK_ARGUMENT_NODE: { + if (((pm_block_argument_node_t *)node)->expression == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_block_argument_node_t *)node)->expression, buffer); + } + pm_serialize_location(parser, &((pm_block_argument_node_t *)node)->operator_loc, buffer); + break; + } + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_block_local_variable_node_t *)node)->name)); + break; + } + case PM_BLOCK_NODE: { + uint32_t locals_size = pm_sizet_to_u32(((pm_block_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_block_node_t *)node)->locals.ids[index])); + } + if (((pm_block_node_t *)node)->parameters == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_block_node_t *)node)->parameters, buffer); + } + if (((pm_block_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_block_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_block_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_block_node_t *)node)->closing_loc, buffer); + pm_buffer_append_varuint(buffer, ((pm_block_node_t *)node)->numbered_parameters); + break; + } + case PM_BLOCK_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_block_parameter_node_t *)node)->name)); + if (((pm_block_parameter_node_t *)node)->name_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_block_parameter_node_t *)node)->name_loc, buffer); + } + pm_serialize_location(parser, &((pm_block_parameter_node_t *)node)->operator_loc, buffer); + break; + } + case PM_BLOCK_PARAMETERS_NODE: { + if (((pm_block_parameters_node_t *)node)->parameters == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_block_parameters_node_t *)node)->parameters, buffer); + } + uint32_t locals_size = pm_sizet_to_u32(((pm_block_parameters_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_block_parameters_node_t *)node)->locals.nodes[index], buffer); + } + if (((pm_block_parameters_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_block_parameters_node_t *)node)->opening_loc, buffer); + } + if (((pm_block_parameters_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_block_parameters_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_BREAK_NODE: { + if (((pm_break_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_break_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_break_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_CALL_AND_WRITE_NODE: { + if (((pm_call_and_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_and_write_node_t *)node)->receiver, buffer); + } + if (((pm_call_and_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_and_write_node_t *)node)->call_operator_loc, buffer); + } + if (((pm_call_and_write_node_t *)node)->message_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_and_write_node_t *)node)->message_loc, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_and_write_node_t *)node)->read_name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_and_write_node_t *)node)->write_name)); + pm_serialize_location(parser, &((pm_call_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_call_and_write_node_t *)node)->value, buffer); + break; + } + case PM_CALL_NODE: { + if (((pm_call_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_node_t *)node)->receiver, buffer); + } + if (((pm_call_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_node_t *)node)->call_operator_loc, buffer); + } + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_node_t *)node)->name)); + if (((pm_call_node_t *)node)->message_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_node_t *)node)->message_loc, buffer); + } + if (((pm_call_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_node_t *)node)->opening_loc, buffer); + } + if (((pm_call_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_node_t *)node)->arguments, buffer); + } + if (((pm_call_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_node_t *)node)->closing_loc, buffer); + } + if (((pm_call_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_node_t *)node)->block, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: { + if (((pm_call_operator_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_operator_write_node_t *)node)->receiver, buffer); + } + if (((pm_call_operator_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_operator_write_node_t *)node)->call_operator_loc, buffer); + } + if (((pm_call_operator_write_node_t *)node)->message_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_operator_write_node_t *)node)->message_loc, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_operator_write_node_t *)node)->read_name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_operator_write_node_t *)node)->write_name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_operator_write_node_t *)node)->operator)); + pm_serialize_location(parser, &((pm_call_operator_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_call_operator_write_node_t *)node)->value, buffer); + break; + } + case PM_CALL_OR_WRITE_NODE: { + if (((pm_call_or_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_or_write_node_t *)node)->receiver, buffer); + } + if (((pm_call_or_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_or_write_node_t *)node)->call_operator_loc, buffer); + } + if (((pm_call_or_write_node_t *)node)->message_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_or_write_node_t *)node)->message_loc, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_or_write_node_t *)node)->read_name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_or_write_node_t *)node)->write_name)); + pm_serialize_location(parser, &((pm_call_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_call_or_write_node_t *)node)->value, buffer); + break; + } + case PM_CAPTURE_PATTERN_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_capture_pattern_node_t *)node)->value, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_capture_pattern_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_capture_pattern_node_t *)node)->operator_loc, buffer); + break; + } + case PM_CASE_MATCH_NODE: { + if (((pm_case_match_node_t *)node)->predicate == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_match_node_t *)node)->predicate, buffer); + } + uint32_t conditions_size = pm_sizet_to_u32(((pm_case_match_node_t *)node)->conditions.size); + pm_buffer_append_varuint(buffer, conditions_size); + for (uint32_t index = 0; index < conditions_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_case_match_node_t *)node)->conditions.nodes[index], buffer); + } + if (((pm_case_match_node_t *)node)->consequent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_match_node_t *)node)->consequent, buffer); + } + pm_serialize_location(parser, &((pm_case_match_node_t *)node)->case_keyword_loc, buffer); + pm_serialize_location(parser, &((pm_case_match_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_CASE_NODE: { + if (((pm_case_node_t *)node)->predicate == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_node_t *)node)->predicate, buffer); + } + uint32_t conditions_size = pm_sizet_to_u32(((pm_case_node_t *)node)->conditions.size); + pm_buffer_append_varuint(buffer, conditions_size); + for (uint32_t index = 0; index < conditions_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_case_node_t *)node)->conditions.nodes[index], buffer); + } + if (((pm_case_node_t *)node)->consequent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_node_t *)node)->consequent, buffer); + } + pm_serialize_location(parser, &((pm_case_node_t *)node)->case_keyword_loc, buffer); + pm_serialize_location(parser, &((pm_case_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_CLASS_NODE: { + uint32_t locals_size = pm_sizet_to_u32(((pm_class_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_class_node_t *)node)->class_keyword_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_node_t *)node)->constant_path, buffer); + if (((pm_class_node_t *)node)->inheritance_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_class_node_t *)node)->inheritance_operator_loc, buffer); + } + if (((pm_class_node_t *)node)->superclass == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_class_node_t *)node)->superclass, buffer); + } + if (((pm_class_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_class_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_class_node_t *)node)->end_keyword_loc, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_node_t *)node)->name)); + break; + } + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_and_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_class_variable_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_class_variable_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_variable_and_write_node_t *)node)->value, buffer); + break; + } + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_operator_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_class_variable_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_class_variable_operator_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_variable_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_operator_write_node_t *)node)->operator)); + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_or_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_class_variable_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_class_variable_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_variable_or_write_node_t *)node)->value, buffer); + break; + } + case PM_CLASS_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_read_node_t *)node)->name)); + break; + } + case PM_CLASS_VARIABLE_TARGET_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_target_node_t *)node)->name)); + break; + } + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_class_variable_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_variable_write_node_t *)node)->value, buffer); + if (((pm_class_variable_write_node_t *)node)->operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_class_variable_write_node_t *)node)->operator_loc, buffer); + } + break; + } + case PM_CONSTANT_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_and_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_constant_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_and_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_operator_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_constant_operator_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_operator_write_node_t *)node)->operator)); + break; + } + case PM_CONSTANT_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_or_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_constant_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_or_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_and_write_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_constant_path_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_and_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_PATH_NODE: { + if (((pm_constant_path_node_t *)node)->parent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_node_t *)node)->parent, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_node_t *)node)->child, buffer); + pm_serialize_location(parser, &((pm_constant_path_node_t *)node)->delimiter_loc, buffer); + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_operator_write_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_constant_path_operator_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_path_operator_write_node_t *)node)->operator)); + break; + } + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_or_write_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_constant_path_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_or_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_PATH_TARGET_NODE: { + if (((pm_constant_path_target_node_t *)node)->parent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_target_node_t *)node)->parent, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_target_node_t *)node)->child, buffer); + pm_serialize_location(parser, &((pm_constant_path_target_node_t *)node)->delimiter_loc, buffer); + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_write_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_constant_path_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_READ_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_read_node_t *)node)->name)); + break; + } + case PM_CONSTANT_TARGET_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_target_node_t *)node)->name)); + break; + } + case PM_CONSTANT_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_write_node_t *)node)->value, buffer); + pm_serialize_location(parser, &((pm_constant_write_node_t *)node)->operator_loc, buffer); + break; + } + case PM_DEF_NODE: { + // serialize length + // encoding of location u32s make us need to save this offset. + size_t length_offset = buffer->length; + pm_buffer_append_string(buffer, "\0\0\0\0", 4); /* consume 4 bytes, updated below */ + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_def_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_def_node_t *)node)->name_loc, buffer); + if (((pm_def_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_def_node_t *)node)->receiver, buffer); + } + if (((pm_def_node_t *)node)->parameters == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_def_node_t *)node)->parameters, buffer); + } + if (((pm_def_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_def_node_t *)node)->body, buffer); + } + uint32_t locals_size = pm_sizet_to_u32(((pm_def_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_def_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_def_node_t *)node)->def_keyword_loc, buffer); + if (((pm_def_node_t *)node)->operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->operator_loc, buffer); + } + if (((pm_def_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->lparen_loc, buffer); + } + if (((pm_def_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->rparen_loc, buffer); + } + if (((pm_def_node_t *)node)->equal_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->equal_loc, buffer); + } + if (((pm_def_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->end_keyword_loc, buffer); + } + // serialize length + uint32_t length = pm_sizet_to_u32(buffer->length - offset - sizeof(uint32_t)); + memcpy(buffer->value + length_offset, &length, sizeof(uint32_t)); + break; + } + case PM_DEFINED_NODE: { + if (((pm_defined_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_defined_node_t *)node)->lparen_loc, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_defined_node_t *)node)->value, buffer); + if (((pm_defined_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_defined_node_t *)node)->rparen_loc, buffer); + } + pm_serialize_location(parser, &((pm_defined_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_ELSE_NODE: { + pm_serialize_location(parser, &((pm_else_node_t *)node)->else_keyword_loc, buffer); + if (((pm_else_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_else_node_t *)node)->statements, buffer); + } + if (((pm_else_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_else_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_serialize_location(parser, &((pm_embedded_statements_node_t *)node)->opening_loc, buffer); + if (((pm_embedded_statements_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_embedded_statements_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_embedded_statements_node_t *)node)->closing_loc, buffer); + break; + } + case PM_EMBEDDED_VARIABLE_NODE: { + pm_serialize_location(parser, &((pm_embedded_variable_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_embedded_variable_node_t *)node)->variable, buffer); + break; + } + case PM_ENSURE_NODE: { + pm_serialize_location(parser, &((pm_ensure_node_t *)node)->ensure_keyword_loc, buffer); + if (((pm_ensure_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_ensure_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_ensure_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_FALSE_NODE: { + break; + } + case PM_FIND_PATTERN_NODE: { + if (((pm_find_pattern_node_t *)node)->constant == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_find_pattern_node_t *)node)->constant, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_find_pattern_node_t *)node)->left, buffer); + uint32_t requireds_size = pm_sizet_to_u32(((pm_find_pattern_node_t *)node)->requireds.size); + pm_buffer_append_varuint(buffer, requireds_size); + for (uint32_t index = 0; index < requireds_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_find_pattern_node_t *)node)->requireds.nodes[index], buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_find_pattern_node_t *)node)->right, buffer); + if (((pm_find_pattern_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_find_pattern_node_t *)node)->opening_loc, buffer); + } + if (((pm_find_pattern_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_find_pattern_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_FLIP_FLOP_NODE: { + if (((pm_flip_flop_node_t *)node)->left == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_flip_flop_node_t *)node)->left, buffer); + } + if (((pm_flip_flop_node_t *)node)->right == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_flip_flop_node_t *)node)->right, buffer); + } + pm_serialize_location(parser, &((pm_flip_flop_node_t *)node)->operator_loc, buffer); + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_FLOAT_NODE: { + break; + } + case PM_FOR_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_for_node_t *)node)->index, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_for_node_t *)node)->collection, buffer); + if (((pm_for_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_for_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_for_node_t *)node)->for_keyword_loc, buffer); + pm_serialize_location(parser, &((pm_for_node_t *)node)->in_keyword_loc, buffer); + if (((pm_for_node_t *)node)->do_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_for_node_t *)node)->do_keyword_loc, buffer); + } + pm_serialize_location(parser, &((pm_for_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_FORWARDING_ARGUMENTS_NODE: { + break; + } + case PM_FORWARDING_PARAMETER_NODE: { + break; + } + case PM_FORWARDING_SUPER_NODE: { + if (((pm_forwarding_super_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_forwarding_super_node_t *)node)->block, buffer); + } + break; + } + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_and_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_global_variable_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_global_variable_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_global_variable_and_write_node_t *)node)->value, buffer); + break; + } + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_operator_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_global_variable_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_global_variable_operator_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_global_variable_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_operator_write_node_t *)node)->operator)); + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_or_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_global_variable_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_global_variable_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_global_variable_or_write_node_t *)node)->value, buffer); + break; + } + case PM_GLOBAL_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_read_node_t *)node)->name)); + break; + } + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_target_node_t *)node)->name)); + break; + } + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_global_variable_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_global_variable_write_node_t *)node)->value, buffer); + pm_serialize_location(parser, &((pm_global_variable_write_node_t *)node)->operator_loc, buffer); + break; + } + case PM_HASH_NODE: { + pm_serialize_location(parser, &((pm_hash_node_t *)node)->opening_loc, buffer); + uint32_t elements_size = pm_sizet_to_u32(((pm_hash_node_t *)node)->elements.size); + pm_buffer_append_varuint(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_hash_node_t *)node)->elements.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_hash_node_t *)node)->closing_loc, buffer); + break; + } + case PM_HASH_PATTERN_NODE: { + if (((pm_hash_pattern_node_t *)node)->constant == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_hash_pattern_node_t *)node)->constant, buffer); + } + uint32_t elements_size = pm_sizet_to_u32(((pm_hash_pattern_node_t *)node)->elements.size); + pm_buffer_append_varuint(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_hash_pattern_node_t *)node)->elements.nodes[index], buffer); + } + if (((pm_hash_pattern_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_hash_pattern_node_t *)node)->rest, buffer); + } + if (((pm_hash_pattern_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_hash_pattern_node_t *)node)->opening_loc, buffer); + } + if (((pm_hash_pattern_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_hash_pattern_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_IF_NODE: { + if (((pm_if_node_t *)node)->if_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_if_node_t *)node)->if_keyword_loc, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_if_node_t *)node)->predicate, buffer); + if (((pm_if_node_t *)node)->then_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_if_node_t *)node)->then_keyword_loc, buffer); + } + if (((pm_if_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_if_node_t *)node)->statements, buffer); + } + if (((pm_if_node_t *)node)->consequent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_if_node_t *)node)->consequent, buffer); + } + if (((pm_if_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_if_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case PM_IMAGINARY_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_imaginary_node_t *)node)->numeric, buffer); + break; + } + case PM_IMPLICIT_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_implicit_node_t *)node)->value, buffer); + break; + } + case PM_IMPLICIT_REST_NODE: { + break; + } + case PM_IN_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_in_node_t *)node)->pattern, buffer); + if (((pm_in_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_in_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_in_node_t *)node)->in_loc, buffer); + if (((pm_in_node_t *)node)->then_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_in_node_t *)node)->then_loc, buffer); + } + break; + } + case PM_INDEX_AND_WRITE_NODE: { + if (((pm_index_and_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_and_write_node_t *)node)->receiver, buffer); + } + if (((pm_index_and_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_index_and_write_node_t *)node)->call_operator_loc, buffer); + } + pm_serialize_location(parser, &((pm_index_and_write_node_t *)node)->opening_loc, buffer); + if (((pm_index_and_write_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_and_write_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_index_and_write_node_t *)node)->closing_loc, buffer); + if (((pm_index_and_write_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_and_write_node_t *)node)->block, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + pm_serialize_location(parser, &((pm_index_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_index_and_write_node_t *)node)->value, buffer); + break; + } + case PM_INDEX_OPERATOR_WRITE_NODE: { + if (((pm_index_operator_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_operator_write_node_t *)node)->receiver, buffer); + } + if (((pm_index_operator_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_index_operator_write_node_t *)node)->call_operator_loc, buffer); + } + pm_serialize_location(parser, &((pm_index_operator_write_node_t *)node)->opening_loc, buffer); + if (((pm_index_operator_write_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_operator_write_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_index_operator_write_node_t *)node)->closing_loc, buffer); + if (((pm_index_operator_write_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_operator_write_node_t *)node)->block, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_index_operator_write_node_t *)node)->operator)); + pm_serialize_location(parser, &((pm_index_operator_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_index_operator_write_node_t *)node)->value, buffer); + break; + } + case PM_INDEX_OR_WRITE_NODE: { + if (((pm_index_or_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_or_write_node_t *)node)->receiver, buffer); + } + if (((pm_index_or_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_index_or_write_node_t *)node)->call_operator_loc, buffer); + } + pm_serialize_location(parser, &((pm_index_or_write_node_t *)node)->opening_loc, buffer); + if (((pm_index_or_write_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_or_write_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_index_or_write_node_t *)node)->closing_loc, buffer); + if (((pm_index_or_write_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_or_write_node_t *)node)->block, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + pm_serialize_location(parser, &((pm_index_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_index_or_write_node_t *)node)->value, buffer); + break; + } + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_and_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_instance_variable_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_instance_variable_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_instance_variable_and_write_node_t *)node)->value, buffer); + break; + } + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_operator_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_instance_variable_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_instance_variable_operator_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_instance_variable_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_operator_write_node_t *)node)->operator)); + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_or_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_instance_variable_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_instance_variable_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_instance_variable_or_write_node_t *)node)->value, buffer); + break; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_read_node_t *)node)->name)); + break; + } + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_target_node_t *)node)->name)); + break; + } + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_instance_variable_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_instance_variable_write_node_t *)node)->value, buffer); + pm_serialize_location(parser, &((pm_instance_variable_write_node_t *)node)->operator_loc, buffer); + break; + } + case PM_INTEGER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_serialize_location(parser, &((pm_interpolated_match_last_line_node_t *)node)->opening_loc, buffer); + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_match_last_line_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_match_last_line_node_t *)node)->parts.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_interpolated_match_last_line_node_t *)node)->closing_loc, buffer); + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_serialize_location(parser, &((pm_interpolated_regular_expression_node_t *)node)->opening_loc, buffer); + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_regular_expression_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_regular_expression_node_t *)node)->parts.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_interpolated_regular_expression_node_t *)node)->closing_loc, buffer); + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_INTERPOLATED_STRING_NODE: { + if (((pm_interpolated_string_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_interpolated_string_node_t *)node)->opening_loc, buffer); + } + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_string_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_string_node_t *)node)->parts.nodes[index], buffer); + } + if (((pm_interpolated_string_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_interpolated_string_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_INTERPOLATED_SYMBOL_NODE: { + if (((pm_interpolated_symbol_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_interpolated_symbol_node_t *)node)->opening_loc, buffer); + } + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_symbol_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_symbol_node_t *)node)->parts.nodes[index], buffer); + } + if (((pm_interpolated_symbol_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_interpolated_symbol_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_INTERPOLATED_X_STRING_NODE: { + pm_serialize_location(parser, &((pm_interpolated_x_string_node_t *)node)->opening_loc, buffer); + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_x_string_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_x_string_node_t *)node)->parts.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_interpolated_x_string_node_t *)node)->closing_loc, buffer); + break; + } + case PM_KEYWORD_HASH_NODE: { + uint32_t elements_size = pm_sizet_to_u32(((pm_keyword_hash_node_t *)node)->elements.size); + pm_buffer_append_varuint(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_keyword_hash_node_t *)node)->elements.nodes[index], buffer); + } + break; + } + case PM_KEYWORD_REST_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_keyword_rest_parameter_node_t *)node)->name)); + if (((pm_keyword_rest_parameter_node_t *)node)->name_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_keyword_rest_parameter_node_t *)node)->name_loc, buffer); + } + pm_serialize_location(parser, &((pm_keyword_rest_parameter_node_t *)node)->operator_loc, buffer); + break; + } + case PM_LAMBDA_NODE: { + uint32_t locals_size = pm_sizet_to_u32(((pm_lambda_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_lambda_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_lambda_node_t *)node)->operator_loc, buffer); + pm_serialize_location(parser, &((pm_lambda_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_lambda_node_t *)node)->closing_loc, buffer); + if (((pm_lambda_node_t *)node)->parameters == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_lambda_node_t *)node)->parameters, buffer); + } + if (((pm_lambda_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_lambda_node_t *)node)->body, buffer); + } + pm_buffer_append_varuint(buffer, ((pm_lambda_node_t *)node)->numbered_parameters); + break; + } + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_serialize_location(parser, &((pm_local_variable_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_local_variable_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_local_variable_and_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_and_write_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_and_write_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_serialize_location(parser, &((pm_local_variable_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_local_variable_operator_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_local_variable_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_operator_write_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_operator_write_node_t *)node)->operator)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_operator_write_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_serialize_location(parser, &((pm_local_variable_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_local_variable_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_local_variable_or_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_or_write_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_or_write_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_read_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_read_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_TARGET_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_target_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_target_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_write_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_write_node_t *)node)->depth); + pm_serialize_location(parser, &((pm_local_variable_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_local_variable_write_node_t *)node)->value, buffer); + pm_serialize_location(parser, &((pm_local_variable_write_node_t *)node)->operator_loc, buffer); + break; + } + case PM_MATCH_LAST_LINE_NODE: { + pm_serialize_location(parser, &((pm_match_last_line_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_match_last_line_node_t *)node)->content_loc, buffer); + pm_serialize_location(parser, &((pm_match_last_line_node_t *)node)->closing_loc, buffer); + pm_serialize_string(parser, &((pm_match_last_line_node_t *)node)->unescaped, buffer); + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_MATCH_PREDICATE_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_match_predicate_node_t *)node)->value, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_match_predicate_node_t *)node)->pattern, buffer); + pm_serialize_location(parser, &((pm_match_predicate_node_t *)node)->operator_loc, buffer); + break; + } + case PM_MATCH_REQUIRED_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_match_required_node_t *)node)->value, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_match_required_node_t *)node)->pattern, buffer); + pm_serialize_location(parser, &((pm_match_required_node_t *)node)->operator_loc, buffer); + break; + } + case PM_MATCH_WRITE_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_match_write_node_t *)node)->call, buffer); + uint32_t targets_size = pm_sizet_to_u32(((pm_match_write_node_t *)node)->targets.size); + pm_buffer_append_varuint(buffer, targets_size); + for (uint32_t index = 0; index < targets_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_match_write_node_t *)node)->targets.nodes[index], buffer); + } + break; + } + case PM_MISSING_NODE: { + break; + } + case PM_MODULE_NODE: { + uint32_t locals_size = pm_sizet_to_u32(((pm_module_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_module_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_module_node_t *)node)->module_keyword_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_module_node_t *)node)->constant_path, buffer); + if (((pm_module_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_module_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_module_node_t *)node)->end_keyword_loc, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_module_node_t *)node)->name)); + break; + } + case PM_MULTI_TARGET_NODE: { + uint32_t lefts_size = pm_sizet_to_u32(((pm_multi_target_node_t *)node)->lefts.size); + pm_buffer_append_varuint(buffer, lefts_size); + for (uint32_t index = 0; index < lefts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_multi_target_node_t *)node)->lefts.nodes[index], buffer); + } + if (((pm_multi_target_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_multi_target_node_t *)node)->rest, buffer); + } + uint32_t rights_size = pm_sizet_to_u32(((pm_multi_target_node_t *)node)->rights.size); + pm_buffer_append_varuint(buffer, rights_size); + for (uint32_t index = 0; index < rights_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_multi_target_node_t *)node)->rights.nodes[index], buffer); + } + if (((pm_multi_target_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_multi_target_node_t *)node)->lparen_loc, buffer); + } + if (((pm_multi_target_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_multi_target_node_t *)node)->rparen_loc, buffer); + } + break; + } + case PM_MULTI_WRITE_NODE: { + uint32_t lefts_size = pm_sizet_to_u32(((pm_multi_write_node_t *)node)->lefts.size); + pm_buffer_append_varuint(buffer, lefts_size); + for (uint32_t index = 0; index < lefts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_multi_write_node_t *)node)->lefts.nodes[index], buffer); + } + if (((pm_multi_write_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_multi_write_node_t *)node)->rest, buffer); + } + uint32_t rights_size = pm_sizet_to_u32(((pm_multi_write_node_t *)node)->rights.size); + pm_buffer_append_varuint(buffer, rights_size); + for (uint32_t index = 0; index < rights_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_multi_write_node_t *)node)->rights.nodes[index], buffer); + } + if (((pm_multi_write_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_multi_write_node_t *)node)->lparen_loc, buffer); + } + if (((pm_multi_write_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_multi_write_node_t *)node)->rparen_loc, buffer); + } + pm_serialize_location(parser, &((pm_multi_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_multi_write_node_t *)node)->value, buffer); + break; + } + case PM_NEXT_NODE: { + if (((pm_next_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_next_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_next_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_NIL_NODE: { + break; + } + case PM_NO_KEYWORDS_PARAMETER_NODE: { + pm_serialize_location(parser, &((pm_no_keywords_parameter_node_t *)node)->operator_loc, buffer); + pm_serialize_location(parser, &((pm_no_keywords_parameter_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_NUMBERED_REFERENCE_READ_NODE: { + pm_buffer_append_varuint(buffer, ((pm_numbered_reference_read_node_t *)node)->number); + break; + } + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_optional_keyword_parameter_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_optional_keyword_parameter_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_optional_keyword_parameter_node_t *)node)->value, buffer); + break; + } + case PM_OPTIONAL_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_optional_parameter_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_optional_parameter_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_optional_parameter_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_optional_parameter_node_t *)node)->value, buffer); + break; + } + case PM_OR_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_or_node_t *)node)->left, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_or_node_t *)node)->right, buffer); + pm_serialize_location(parser, &((pm_or_node_t *)node)->operator_loc, buffer); + break; + } + case PM_PARAMETERS_NODE: { + uint32_t requireds_size = pm_sizet_to_u32(((pm_parameters_node_t *)node)->requireds.size); + pm_buffer_append_varuint(buffer, requireds_size); + for (uint32_t index = 0; index < requireds_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_parameters_node_t *)node)->requireds.nodes[index], buffer); + } + uint32_t optionals_size = pm_sizet_to_u32(((pm_parameters_node_t *)node)->optionals.size); + pm_buffer_append_varuint(buffer, optionals_size); + for (uint32_t index = 0; index < optionals_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_parameters_node_t *)node)->optionals.nodes[index], buffer); + } + if (((pm_parameters_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_parameters_node_t *)node)->rest, buffer); + } + uint32_t posts_size = pm_sizet_to_u32(((pm_parameters_node_t *)node)->posts.size); + pm_buffer_append_varuint(buffer, posts_size); + for (uint32_t index = 0; index < posts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_parameters_node_t *)node)->posts.nodes[index], buffer); + } + uint32_t keywords_size = pm_sizet_to_u32(((pm_parameters_node_t *)node)->keywords.size); + pm_buffer_append_varuint(buffer, keywords_size); + for (uint32_t index = 0; index < keywords_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_parameters_node_t *)node)->keywords.nodes[index], buffer); + } + if (((pm_parameters_node_t *)node)->keyword_rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_parameters_node_t *)node)->keyword_rest, buffer); + } + if (((pm_parameters_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_parameters_node_t *)node)->block, buffer); + } + break; + } + case PM_PARENTHESES_NODE: { + if (((pm_parentheses_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_parentheses_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_parentheses_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_parentheses_node_t *)node)->closing_loc, buffer); + break; + } + case PM_PINNED_EXPRESSION_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_pinned_expression_node_t *)node)->expression, buffer); + pm_serialize_location(parser, &((pm_pinned_expression_node_t *)node)->operator_loc, buffer); + pm_serialize_location(parser, &((pm_pinned_expression_node_t *)node)->lparen_loc, buffer); + pm_serialize_location(parser, &((pm_pinned_expression_node_t *)node)->rparen_loc, buffer); + break; + } + case PM_PINNED_VARIABLE_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_pinned_variable_node_t *)node)->variable, buffer); + pm_serialize_location(parser, &((pm_pinned_variable_node_t *)node)->operator_loc, buffer); + break; + } + case PM_POST_EXECUTION_NODE: { + if (((pm_post_execution_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_post_execution_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_post_execution_node_t *)node)->keyword_loc, buffer); + pm_serialize_location(parser, &((pm_post_execution_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_post_execution_node_t *)node)->closing_loc, buffer); + break; + } + case PM_PRE_EXECUTION_NODE: { + if (((pm_pre_execution_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_pre_execution_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_pre_execution_node_t *)node)->keyword_loc, buffer); + pm_serialize_location(parser, &((pm_pre_execution_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_pre_execution_node_t *)node)->closing_loc, buffer); + break; + } + case PM_PROGRAM_NODE: { + uint32_t locals_size = pm_sizet_to_u32(((pm_program_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_program_node_t *)node)->locals.ids[index])); + } + pm_serialize_node(parser, (pm_node_t *)((pm_program_node_t *)node)->statements, buffer); + break; + } + case PM_RANGE_NODE: { + if (((pm_range_node_t *)node)->left == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_range_node_t *)node)->left, buffer); + } + if (((pm_range_node_t *)node)->right == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_range_node_t *)node)->right, buffer); + } + pm_serialize_location(parser, &((pm_range_node_t *)node)->operator_loc, buffer); + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_RATIONAL_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_rational_node_t *)node)->numeric, buffer); + break; + } + case PM_REDO_NODE: { + break; + } + case PM_REGULAR_EXPRESSION_NODE: { + pm_serialize_location(parser, &((pm_regular_expression_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_regular_expression_node_t *)node)->content_loc, buffer); + pm_serialize_location(parser, &((pm_regular_expression_node_t *)node)->closing_loc, buffer); + pm_serialize_string(parser, &((pm_regular_expression_node_t *)node)->unescaped, buffer); + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_required_keyword_parameter_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_required_keyword_parameter_node_t *)node)->name_loc, buffer); + break; + } + case PM_REQUIRED_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_required_parameter_node_t *)node)->name)); + break; + } + case PM_RESCUE_MODIFIER_NODE: { + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_modifier_node_t *)node)->expression, buffer); + pm_serialize_location(parser, &((pm_rescue_modifier_node_t *)node)->keyword_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_modifier_node_t *)node)->rescue_expression, buffer); + break; + } + case PM_RESCUE_NODE: { + pm_serialize_location(parser, &((pm_rescue_node_t *)node)->keyword_loc, buffer); + uint32_t exceptions_size = pm_sizet_to_u32(((pm_rescue_node_t *)node)->exceptions.size); + pm_buffer_append_varuint(buffer, exceptions_size); + for (uint32_t index = 0; index < exceptions_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_rescue_node_t *)node)->exceptions.nodes[index], buffer); + } + if (((pm_rescue_node_t *)node)->operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_rescue_node_t *)node)->operator_loc, buffer); + } + if (((pm_rescue_node_t *)node)->reference == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_node_t *)node)->reference, buffer); + } + if (((pm_rescue_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_node_t *)node)->statements, buffer); + } + if (((pm_rescue_node_t *)node)->consequent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_node_t *)node)->consequent, buffer); + } + break; + } + case PM_REST_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_rest_parameter_node_t *)node)->name)); + if (((pm_rest_parameter_node_t *)node)->name_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_rest_parameter_node_t *)node)->name_loc, buffer); + } + pm_serialize_location(parser, &((pm_rest_parameter_node_t *)node)->operator_loc, buffer); + break; + } + case PM_RETRY_NODE: { + break; + } + case PM_RETURN_NODE: { + pm_serialize_location(parser, &((pm_return_node_t *)node)->keyword_loc, buffer); + if (((pm_return_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_return_node_t *)node)->arguments, buffer); + } + break; + } + case PM_SELF_NODE: { + break; + } + case PM_SINGLETON_CLASS_NODE: { + uint32_t locals_size = pm_sizet_to_u32(((pm_singleton_class_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_singleton_class_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_singleton_class_node_t *)node)->class_keyword_loc, buffer); + pm_serialize_location(parser, &((pm_singleton_class_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_singleton_class_node_t *)node)->expression, buffer); + if (((pm_singleton_class_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_singleton_class_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_singleton_class_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_SOURCE_ENCODING_NODE: { + break; + } + case PM_SOURCE_FILE_NODE: { + pm_serialize_string(parser, &((pm_source_file_node_t *)node)->filepath, buffer); + break; + } + case PM_SOURCE_LINE_NODE: { + break; + } + case PM_SPLAT_NODE: { + pm_serialize_location(parser, &((pm_splat_node_t *)node)->operator_loc, buffer); + if (((pm_splat_node_t *)node)->expression == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_splat_node_t *)node)->expression, buffer); + } + break; + } + case PM_STATEMENTS_NODE: { + uint32_t body_size = pm_sizet_to_u32(((pm_statements_node_t *)node)->body.size); + pm_buffer_append_varuint(buffer, body_size); + for (uint32_t index = 0; index < body_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_statements_node_t *)node)->body.nodes[index], buffer); + } + break; + } + case PM_STRING_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + if (((pm_string_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_string_node_t *)node)->opening_loc, buffer); + } + pm_serialize_location(parser, &((pm_string_node_t *)node)->content_loc, buffer); + if (((pm_string_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_string_node_t *)node)->closing_loc, buffer); + } + pm_serialize_string(parser, &((pm_string_node_t *)node)->unescaped, buffer); + break; + } + case PM_SUPER_NODE: { + pm_serialize_location(parser, &((pm_super_node_t *)node)->keyword_loc, buffer); + if (((pm_super_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_super_node_t *)node)->lparen_loc, buffer); + } + if (((pm_super_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_super_node_t *)node)->arguments, buffer); + } + if (((pm_super_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_super_node_t *)node)->rparen_loc, buffer); + } + if (((pm_super_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_super_node_t *)node)->block, buffer); + } + break; + } + case PM_SYMBOL_NODE: { + if (((pm_symbol_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_symbol_node_t *)node)->opening_loc, buffer); + } + if (((pm_symbol_node_t *)node)->value_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_symbol_node_t *)node)->value_loc, buffer); + } + if (((pm_symbol_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_symbol_node_t *)node)->closing_loc, buffer); + } + pm_serialize_string(parser, &((pm_symbol_node_t *)node)->unescaped, buffer); + break; + } + case PM_TRUE_NODE: { + break; + } + case PM_UNDEF_NODE: { + uint32_t names_size = pm_sizet_to_u32(((pm_undef_node_t *)node)->names.size); + pm_buffer_append_varuint(buffer, names_size); + for (uint32_t index = 0; index < names_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_undef_node_t *)node)->names.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_undef_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_UNLESS_NODE: { + pm_serialize_location(parser, &((pm_unless_node_t *)node)->keyword_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_unless_node_t *)node)->predicate, buffer); + if (((pm_unless_node_t *)node)->then_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_unless_node_t *)node)->then_keyword_loc, buffer); + } + if (((pm_unless_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_unless_node_t *)node)->statements, buffer); + } + if (((pm_unless_node_t *)node)->consequent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_unless_node_t *)node)->consequent, buffer); + } + if (((pm_unless_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_unless_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case PM_UNTIL_NODE: { + pm_serialize_location(parser, &((pm_until_node_t *)node)->keyword_loc, buffer); + if (((pm_until_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_until_node_t *)node)->closing_loc, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_until_node_t *)node)->predicate, buffer); + if (((pm_until_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_until_node_t *)node)->statements, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_WHEN_NODE: { + pm_serialize_location(parser, &((pm_when_node_t *)node)->keyword_loc, buffer); + uint32_t conditions_size = pm_sizet_to_u32(((pm_when_node_t *)node)->conditions.size); + pm_buffer_append_varuint(buffer, conditions_size); + for (uint32_t index = 0; index < conditions_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_when_node_t *)node)->conditions.nodes[index], buffer); + } + if (((pm_when_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_when_node_t *)node)->statements, buffer); + } + break; + } + case PM_WHILE_NODE: { + pm_serialize_location(parser, &((pm_while_node_t *)node)->keyword_loc, buffer); + if (((pm_while_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_while_node_t *)node)->closing_loc, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_while_node_t *)node)->predicate, buffer); + if (((pm_while_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_while_node_t *)node)->statements, buffer); + } + pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); + break; + } + case PM_X_STRING_NODE: { + pm_serialize_location(parser, &((pm_x_string_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_x_string_node_t *)node)->content_loc, buffer); + pm_serialize_location(parser, &((pm_x_string_node_t *)node)->closing_loc, buffer); + pm_serialize_string(parser, &((pm_x_string_node_t *)node)->unescaped, buffer); + break; + } + case PM_YIELD_NODE: { + pm_serialize_location(parser, &((pm_yield_node_t *)node)->keyword_loc, buffer); + if (((pm_yield_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_yield_node_t *)node)->lparen_loc, buffer); + } + if (((pm_yield_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_yield_node_t *)node)->arguments, buffer); + } + if (((pm_yield_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_yield_node_t *)node)->rparen_loc, buffer); + } + break; + } + } +} + +static void +pm_serialize_comment(pm_parser_t *parser, pm_comment_t *comment, pm_buffer_t *buffer) { + // serialize type + pm_buffer_append_byte(buffer, (uint8_t) comment->type); + + // serialize location + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(comment->start - parser->start)); + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(comment->end - comment->start)); +} + +/** + * Serialize the given list of comments to the given buffer. + */ +void +pm_serialize_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(pm_list_size(list))); + + pm_comment_t *comment; + for (comment = (pm_comment_t *) list->head; comment != NULL; comment = (pm_comment_t *) comment->node.next) { + pm_serialize_comment(parser, comment, buffer); + } +} + +static void +pm_serialize_magic_comment(pm_parser_t *parser, pm_magic_comment_t *magic_comment, pm_buffer_t *buffer) { + // serialize key location + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(magic_comment->key_start - parser->start)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(magic_comment->key_length)); + + // serialize value location + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(magic_comment->value_start - parser->start)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(magic_comment->value_length)); +} + +static void +pm_serialize_magic_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(pm_list_size(list))); + + pm_magic_comment_t *magic_comment; + for (magic_comment = (pm_magic_comment_t *) list->head; magic_comment != NULL; magic_comment = (pm_magic_comment_t *) magic_comment->node.next) { + pm_serialize_magic_comment(parser, magic_comment, buffer); + } +} + +static void +pm_serialize_data_loc(const pm_parser_t *parser, pm_buffer_t *buffer) { + if (parser->data_loc.end == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &parser->data_loc, buffer); + } +} + +static void +pm_serialize_diagnostic(pm_parser_t *parser, pm_diagnostic_t *diagnostic, pm_buffer_t *buffer) { + // serialize message + size_t message_length = strlen(diagnostic->message); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(message_length)); + pm_buffer_append_string(buffer, diagnostic->message, message_length); + + // serialize location + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(diagnostic->start - parser->start)); + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(diagnostic->end - diagnostic->start)); +} + +static void +pm_serialize_diagnostic_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(pm_list_size(list))); + + pm_diagnostic_t *diagnostic; + for (diagnostic = (pm_diagnostic_t *) list->head; diagnostic != NULL; diagnostic = (pm_diagnostic_t *) diagnostic->node.next) { + pm_serialize_diagnostic(parser, diagnostic, buffer); + } +} + +/** + * Serialize the name of the encoding to the buffer. + */ +void +pm_serialize_encoding(pm_encoding_t *encoding, pm_buffer_t *buffer) { + size_t encoding_length = strlen(encoding->name); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(encoding_length)); + pm_buffer_append_string(buffer, encoding->name, encoding_length); +} + +#line 216 "serialize.c.erb" +/** + * Serialize the encoding, metadata, nodes, and constant pool. + */ +void +pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { + pm_serialize_encoding(&parser->encoding, buffer); + pm_buffer_append_varsint(buffer, parser->start_line); + pm_serialize_comment_list(parser, &parser->comment_list, buffer); + pm_serialize_magic_comment_list(parser, &parser->magic_comment_list, buffer); + pm_serialize_data_loc(parser, buffer); + pm_serialize_diagnostic_list(parser, &parser->error_list, buffer); + pm_serialize_diagnostic_list(parser, &parser->warning_list, buffer); + + // Here we're going to leave space for the offset of the constant pool in + // the buffer. + size_t offset = buffer->length; + pm_buffer_append_zeroes(buffer, 4); + + // Next, encode the length of the constant pool. + pm_buffer_append_varuint(buffer, parser->constant_pool.size); + + // Now we're going to serialize the content of the node. + pm_serialize_node(parser, node, buffer); + + // Now we're going to serialize the offset of the constant pool back where + // we left space for it. + uint32_t length = pm_sizet_to_u32(buffer->length); + memcpy(buffer->value + offset, &length, sizeof(uint32_t)); + + // Now we're going to serialize the constant pool. + offset = buffer->length; + pm_buffer_append_zeroes(buffer, parser->constant_pool.size * 8); + + for (uint32_t index = 0; index < parser->constant_pool.capacity; index++) { + pm_constant_pool_bucket_t *bucket = &parser->constant_pool.buckets[index]; + + // If we find a constant at this index, serialize it at the correct + // index in the buffer. + if (bucket->id != 0) { + pm_constant_t *constant = &parser->constant_pool.constants[bucket->id - 1]; + size_t buffer_offset = offset + ((((size_t)bucket->id) - 1) * 8); + + if (bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED || bucket->type == PM_CONSTANT_POOL_BUCKET_CONSTANT) { + // Since this is an owned or constant constant, we are going to + // write its contents into the buffer after the constant pool. + // So effectively in place of the source offset, we have a + // buffer offset. We will add a leading 1 to indicate that this + // is a buffer offset. + uint32_t content_offset = pm_sizet_to_u32(buffer->length); + uint32_t owned_mask = (uint32_t) (1 << 31); + + assert(content_offset < owned_mask); + content_offset |= owned_mask; + + memcpy(buffer->value + buffer_offset, &content_offset, 4); + pm_buffer_append_bytes(buffer, constant->start, constant->length); + } else { + // Since this is a shared constant, we are going to write its + // source offset directly into the buffer. + uint32_t source_offset = pm_ptrdifft_to_u32(constant->start - parser->start); + memcpy(buffer->value + buffer_offset, &source_offset, 4); + } + + // Now we can write the length of the constant into the buffer. + uint32_t constant_length = pm_sizet_to_u32(constant->length); + memcpy(buffer->value + buffer_offset + 4, &constant_length, 4); + } + } +} + +static void +serialize_token(void *data, pm_parser_t *parser, pm_token_t *token) { + pm_buffer_t *buffer = (pm_buffer_t *) data; + + pm_buffer_append_varuint(buffer, token->type); + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(token->start - parser->start)); + pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(token->end - token->start)); + pm_buffer_append_varuint(buffer, parser->lex_state); +} + +/** + * Lex the given source and serialize to the given buffer. + */ +PRISM_EXPORTED_FUNCTION void +pm_serialize_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) { + pm_options_t options = { 0 }; + pm_options_read(&options, data); + + pm_parser_t parser; + pm_parser_init(&parser, source, size, &options); + + pm_lex_callback_t lex_callback = (pm_lex_callback_t) { + .data = (void *) buffer, + .callback = serialize_token, + }; + + parser.lex_callback = &lex_callback; + pm_node_t *node = pm_parse(&parser); + + // Append 0 to mark end of tokens. + pm_buffer_append_byte(buffer, 0); + + pm_serialize_encoding(&parser.encoding, buffer); + pm_buffer_append_varsint(buffer, parser.start_line); + pm_serialize_comment_list(&parser, &parser.comment_list, buffer); + pm_serialize_magic_comment_list(&parser, &parser.magic_comment_list, buffer); + pm_serialize_data_loc(&parser, buffer); + pm_serialize_diagnostic_list(&parser, &parser.error_list, buffer); + pm_serialize_diagnostic_list(&parser, &parser.warning_list, buffer); + + pm_node_destroy(&parser, node); + pm_parser_free(&parser); + pm_options_free(&options); +} + +/** + * Parse and serialize both the AST and the tokens represented by the given + * source to the given buffer. + */ +PRISM_EXPORTED_FUNCTION void +pm_serialize_parse_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) { + pm_options_t options = { 0 }; + pm_options_read(&options, data); + + pm_parser_t parser; + pm_parser_init(&parser, source, size, &options); + + pm_lex_callback_t lex_callback = (pm_lex_callback_t) { + .data = (void *) buffer, + .callback = serialize_token, + }; + + parser.lex_callback = &lex_callback; + pm_node_t *node = pm_parse(&parser); + + pm_buffer_append_byte(buffer, 0); + pm_serialize(&parser, node, buffer); + + pm_node_destroy(&parser, node); + pm_parser_free(&parser); + pm_options_free(&options); +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/token_type.c b/crates/prism-sys/vendor/prism-0.18.0/src/token_type.c new file mode 100644 index 00000000..1cb395bf --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/token_type.c @@ -0,0 +1,351 @@ +/******************************************************************************/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/token_type.c.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +#include + +#include "prism/ast.h" + +/** + * Returns a string representation of the given token type. + */ +PRISM_EXPORTED_FUNCTION const char * +pm_token_type_to_str(pm_token_type_t token_type) +{ + switch (token_type) { + case PM_TOKEN_EOF: + return "EOF"; + case PM_TOKEN_MISSING: + return "MISSING"; + case PM_TOKEN_NOT_PROVIDED: + return "NOT_PROVIDED"; + case PM_TOKEN_AMPERSAND: + return "AMPERSAND"; + case PM_TOKEN_AMPERSAND_AMPERSAND: + return "AMPERSAND_AMPERSAND"; + case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL: + return "AMPERSAND_AMPERSAND_EQUAL"; + case PM_TOKEN_AMPERSAND_DOT: + return "AMPERSAND_DOT"; + case PM_TOKEN_AMPERSAND_EQUAL: + return "AMPERSAND_EQUAL"; + case PM_TOKEN_BACKTICK: + return "BACKTICK"; + case PM_TOKEN_BACK_REFERENCE: + return "BACK_REFERENCE"; + case PM_TOKEN_BANG: + return "BANG"; + case PM_TOKEN_BANG_EQUAL: + return "BANG_EQUAL"; + case PM_TOKEN_BANG_TILDE: + return "BANG_TILDE"; + case PM_TOKEN_BRACE_LEFT: + return "BRACE_LEFT"; + case PM_TOKEN_BRACE_RIGHT: + return "BRACE_RIGHT"; + case PM_TOKEN_BRACKET_LEFT: + return "BRACKET_LEFT"; + case PM_TOKEN_BRACKET_LEFT_ARRAY: + return "BRACKET_LEFT_ARRAY"; + case PM_TOKEN_BRACKET_LEFT_RIGHT: + return "BRACKET_LEFT_RIGHT"; + case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: + return "BRACKET_LEFT_RIGHT_EQUAL"; + case PM_TOKEN_BRACKET_RIGHT: + return "BRACKET_RIGHT"; + case PM_TOKEN_CARET: + return "CARET"; + case PM_TOKEN_CARET_EQUAL: + return "CARET_EQUAL"; + case PM_TOKEN_CHARACTER_LITERAL: + return "CHARACTER_LITERAL"; + case PM_TOKEN_CLASS_VARIABLE: + return "CLASS_VARIABLE"; + case PM_TOKEN_COLON: + return "COLON"; + case PM_TOKEN_COLON_COLON: + return "COLON_COLON"; + case PM_TOKEN_COMMA: + return "COMMA"; + case PM_TOKEN_COMMENT: + return "COMMENT"; + case PM_TOKEN_CONSTANT: + return "CONSTANT"; + case PM_TOKEN_DOT: + return "DOT"; + case PM_TOKEN_DOT_DOT: + return "DOT_DOT"; + case PM_TOKEN_DOT_DOT_DOT: + return "DOT_DOT_DOT"; + case PM_TOKEN_EMBDOC_BEGIN: + return "EMBDOC_BEGIN"; + case PM_TOKEN_EMBDOC_END: + return "EMBDOC_END"; + case PM_TOKEN_EMBDOC_LINE: + return "EMBDOC_LINE"; + case PM_TOKEN_EMBEXPR_BEGIN: + return "EMBEXPR_BEGIN"; + case PM_TOKEN_EMBEXPR_END: + return "EMBEXPR_END"; + case PM_TOKEN_EMBVAR: + return "EMBVAR"; + case PM_TOKEN_EQUAL: + return "EQUAL"; + case PM_TOKEN_EQUAL_EQUAL: + return "EQUAL_EQUAL"; + case PM_TOKEN_EQUAL_EQUAL_EQUAL: + return "EQUAL_EQUAL_EQUAL"; + case PM_TOKEN_EQUAL_GREATER: + return "EQUAL_GREATER"; + case PM_TOKEN_EQUAL_TILDE: + return "EQUAL_TILDE"; + case PM_TOKEN_FLOAT: + return "FLOAT"; + case PM_TOKEN_FLOAT_IMAGINARY: + return "FLOAT_IMAGINARY"; + case PM_TOKEN_FLOAT_RATIONAL: + return "FLOAT_RATIONAL"; + case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: + return "FLOAT_RATIONAL_IMAGINARY"; + case PM_TOKEN_GLOBAL_VARIABLE: + return "GLOBAL_VARIABLE"; + case PM_TOKEN_GREATER: + return "GREATER"; + case PM_TOKEN_GREATER_EQUAL: + return "GREATER_EQUAL"; + case PM_TOKEN_GREATER_GREATER: + return "GREATER_GREATER"; + case PM_TOKEN_GREATER_GREATER_EQUAL: + return "GREATER_GREATER_EQUAL"; + case PM_TOKEN_HEREDOC_END: + return "HEREDOC_END"; + case PM_TOKEN_HEREDOC_START: + return "HEREDOC_START"; + case PM_TOKEN_IDENTIFIER: + return "IDENTIFIER"; + case PM_TOKEN_IGNORED_NEWLINE: + return "IGNORED_NEWLINE"; + case PM_TOKEN_INSTANCE_VARIABLE: + return "INSTANCE_VARIABLE"; + case PM_TOKEN_INTEGER: + return "INTEGER"; + case PM_TOKEN_INTEGER_IMAGINARY: + return "INTEGER_IMAGINARY"; + case PM_TOKEN_INTEGER_RATIONAL: + return "INTEGER_RATIONAL"; + case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: + return "INTEGER_RATIONAL_IMAGINARY"; + case PM_TOKEN_KEYWORD_ALIAS: + return "KEYWORD_ALIAS"; + case PM_TOKEN_KEYWORD_AND: + return "KEYWORD_AND"; + case PM_TOKEN_KEYWORD_BEGIN: + return "KEYWORD_BEGIN"; + case PM_TOKEN_KEYWORD_BEGIN_UPCASE: + return "KEYWORD_BEGIN_UPCASE"; + case PM_TOKEN_KEYWORD_BREAK: + return "KEYWORD_BREAK"; + case PM_TOKEN_KEYWORD_CASE: + return "KEYWORD_CASE"; + case PM_TOKEN_KEYWORD_CLASS: + return "KEYWORD_CLASS"; + case PM_TOKEN_KEYWORD_DEF: + return "KEYWORD_DEF"; + case PM_TOKEN_KEYWORD_DEFINED: + return "KEYWORD_DEFINED"; + case PM_TOKEN_KEYWORD_DO: + return "KEYWORD_DO"; + case PM_TOKEN_KEYWORD_DO_LOOP: + return "KEYWORD_DO_LOOP"; + case PM_TOKEN_KEYWORD_ELSE: + return "KEYWORD_ELSE"; + case PM_TOKEN_KEYWORD_ELSIF: + return "KEYWORD_ELSIF"; + case PM_TOKEN_KEYWORD_END: + return "KEYWORD_END"; + case PM_TOKEN_KEYWORD_END_UPCASE: + return "KEYWORD_END_UPCASE"; + case PM_TOKEN_KEYWORD_ENSURE: + return "KEYWORD_ENSURE"; + case PM_TOKEN_KEYWORD_FALSE: + return "KEYWORD_FALSE"; + case PM_TOKEN_KEYWORD_FOR: + return "KEYWORD_FOR"; + case PM_TOKEN_KEYWORD_IF: + return "KEYWORD_IF"; + case PM_TOKEN_KEYWORD_IF_MODIFIER: + return "KEYWORD_IF_MODIFIER"; + case PM_TOKEN_KEYWORD_IN: + return "KEYWORD_IN"; + case PM_TOKEN_KEYWORD_MODULE: + return "KEYWORD_MODULE"; + case PM_TOKEN_KEYWORD_NEXT: + return "KEYWORD_NEXT"; + case PM_TOKEN_KEYWORD_NIL: + return "KEYWORD_NIL"; + case PM_TOKEN_KEYWORD_NOT: + return "KEYWORD_NOT"; + case PM_TOKEN_KEYWORD_OR: + return "KEYWORD_OR"; + case PM_TOKEN_KEYWORD_REDO: + return "KEYWORD_REDO"; + case PM_TOKEN_KEYWORD_RESCUE: + return "KEYWORD_RESCUE"; + case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: + return "KEYWORD_RESCUE_MODIFIER"; + case PM_TOKEN_KEYWORD_RETRY: + return "KEYWORD_RETRY"; + case PM_TOKEN_KEYWORD_RETURN: + return "KEYWORD_RETURN"; + case PM_TOKEN_KEYWORD_SELF: + return "KEYWORD_SELF"; + case PM_TOKEN_KEYWORD_SUPER: + return "KEYWORD_SUPER"; + case PM_TOKEN_KEYWORD_THEN: + return "KEYWORD_THEN"; + case PM_TOKEN_KEYWORD_TRUE: + return "KEYWORD_TRUE"; + case PM_TOKEN_KEYWORD_UNDEF: + return "KEYWORD_UNDEF"; + case PM_TOKEN_KEYWORD_UNLESS: + return "KEYWORD_UNLESS"; + case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: + return "KEYWORD_UNLESS_MODIFIER"; + case PM_TOKEN_KEYWORD_UNTIL: + return "KEYWORD_UNTIL"; + case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: + return "KEYWORD_UNTIL_MODIFIER"; + case PM_TOKEN_KEYWORD_WHEN: + return "KEYWORD_WHEN"; + case PM_TOKEN_KEYWORD_WHILE: + return "KEYWORD_WHILE"; + case PM_TOKEN_KEYWORD_WHILE_MODIFIER: + return "KEYWORD_WHILE_MODIFIER"; + case PM_TOKEN_KEYWORD_YIELD: + return "KEYWORD_YIELD"; + case PM_TOKEN_KEYWORD___ENCODING__: + return "KEYWORD___ENCODING__"; + case PM_TOKEN_KEYWORD___FILE__: + return "KEYWORD___FILE__"; + case PM_TOKEN_KEYWORD___LINE__: + return "KEYWORD___LINE__"; + case PM_TOKEN_LABEL: + return "LABEL"; + case PM_TOKEN_LABEL_END: + return "LABEL_END"; + case PM_TOKEN_LAMBDA_BEGIN: + return "LAMBDA_BEGIN"; + case PM_TOKEN_LESS: + return "LESS"; + case PM_TOKEN_LESS_EQUAL: + return "LESS_EQUAL"; + case PM_TOKEN_LESS_EQUAL_GREATER: + return "LESS_EQUAL_GREATER"; + case PM_TOKEN_LESS_LESS: + return "LESS_LESS"; + case PM_TOKEN_LESS_LESS_EQUAL: + return "LESS_LESS_EQUAL"; + case PM_TOKEN_METHOD_NAME: + return "METHOD_NAME"; + case PM_TOKEN_MINUS: + return "MINUS"; + case PM_TOKEN_MINUS_EQUAL: + return "MINUS_EQUAL"; + case PM_TOKEN_MINUS_GREATER: + return "MINUS_GREATER"; + case PM_TOKEN_NEWLINE: + return "NEWLINE"; + case PM_TOKEN_NUMBERED_REFERENCE: + return "NUMBERED_REFERENCE"; + case PM_TOKEN_PARENTHESIS_LEFT: + return "PARENTHESIS_LEFT"; + case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: + return "PARENTHESIS_LEFT_PARENTHESES"; + case PM_TOKEN_PARENTHESIS_RIGHT: + return "PARENTHESIS_RIGHT"; + case PM_TOKEN_PERCENT: + return "PERCENT"; + case PM_TOKEN_PERCENT_EQUAL: + return "PERCENT_EQUAL"; + case PM_TOKEN_PERCENT_LOWER_I: + return "PERCENT_LOWER_I"; + case PM_TOKEN_PERCENT_LOWER_W: + return "PERCENT_LOWER_W"; + case PM_TOKEN_PERCENT_LOWER_X: + return "PERCENT_LOWER_X"; + case PM_TOKEN_PERCENT_UPPER_I: + return "PERCENT_UPPER_I"; + case PM_TOKEN_PERCENT_UPPER_W: + return "PERCENT_UPPER_W"; + case PM_TOKEN_PIPE: + return "PIPE"; + case PM_TOKEN_PIPE_EQUAL: + return "PIPE_EQUAL"; + case PM_TOKEN_PIPE_PIPE: + return "PIPE_PIPE"; + case PM_TOKEN_PIPE_PIPE_EQUAL: + return "PIPE_PIPE_EQUAL"; + case PM_TOKEN_PLUS: + return "PLUS"; + case PM_TOKEN_PLUS_EQUAL: + return "PLUS_EQUAL"; + case PM_TOKEN_QUESTION_MARK: + return "QUESTION_MARK"; + case PM_TOKEN_REGEXP_BEGIN: + return "REGEXP_BEGIN"; + case PM_TOKEN_REGEXP_END: + return "REGEXP_END"; + case PM_TOKEN_SEMICOLON: + return "SEMICOLON"; + case PM_TOKEN_SLASH: + return "SLASH"; + case PM_TOKEN_SLASH_EQUAL: + return "SLASH_EQUAL"; + case PM_TOKEN_STAR: + return "STAR"; + case PM_TOKEN_STAR_EQUAL: + return "STAR_EQUAL"; + case PM_TOKEN_STAR_STAR: + return "STAR_STAR"; + case PM_TOKEN_STAR_STAR_EQUAL: + return "STAR_STAR_EQUAL"; + case PM_TOKEN_STRING_BEGIN: + return "STRING_BEGIN"; + case PM_TOKEN_STRING_CONTENT: + return "STRING_CONTENT"; + case PM_TOKEN_STRING_END: + return "STRING_END"; + case PM_TOKEN_SYMBOL_BEGIN: + return "SYMBOL_BEGIN"; + case PM_TOKEN_TILDE: + return "TILDE"; + case PM_TOKEN_UAMPERSAND: + return "UAMPERSAND"; + case PM_TOKEN_UCOLON_COLON: + return "UCOLON_COLON"; + case PM_TOKEN_UDOT_DOT: + return "UDOT_DOT"; + case PM_TOKEN_UDOT_DOT_DOT: + return "UDOT_DOT_DOT"; + case PM_TOKEN_UMINUS: + return "UMINUS"; + case PM_TOKEN_UMINUS_NUM: + return "UMINUS_NUM"; + case PM_TOKEN_UPLUS: + return "UPLUS"; + case PM_TOKEN_USTAR: + return "USTAR"; + case PM_TOKEN_USTAR_STAR: + return "USTAR_STAR"; + case PM_TOKEN_WORDS_SEP: + return "WORDS_SEP"; + case PM_TOKEN___END__: + return "__END__"; + case PM_TOKEN_MAXIMUM: + return "MAXIMUM"; + } + return "\0"; +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_buffer.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_buffer.c new file mode 100644 index 00000000..307b55d0 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_buffer.c @@ -0,0 +1,179 @@ +#include "prism/util/pm_buffer.h" + +/** + * Return the size of the pm_buffer_t struct. + */ +size_t +pm_buffer_sizeof(void) { + return sizeof(pm_buffer_t); +} + +/** + * Initialize a pm_buffer_t with the given capacity. + */ +bool +pm_buffer_init_capacity(pm_buffer_t *buffer, size_t capacity) { + buffer->length = 0; + buffer->capacity = capacity; + + buffer->value = (char *) malloc(capacity); + return buffer->value != NULL; +} + +/** + * Initialize a pm_buffer_t with its default values. + */ +bool +pm_buffer_init(pm_buffer_t *buffer) { + return pm_buffer_init_capacity(buffer, 1024); +} + +/** + * Return the value of the buffer. + */ +char * +pm_buffer_value(pm_buffer_t *buffer) { + return buffer->value; +} + +/** + * Return the length of the buffer. + */ +size_t +pm_buffer_length(pm_buffer_t *buffer) { + return buffer->length; +} + +/** + * Append the given amount of space to the buffer. + */ +static inline void +pm_buffer_append_length(pm_buffer_t *buffer, size_t length) { + size_t next_length = buffer->length + length; + + if (next_length > buffer->capacity) { + if (buffer->capacity == 0) { + buffer->capacity = 1; + } + + while (next_length > buffer->capacity) { + buffer->capacity *= 2; + } + + buffer->value = realloc(buffer->value, buffer->capacity); + } + + buffer->length = next_length; +} + +/** + * Append a generic pointer to memory to the buffer. + */ +static inline void +pm_buffer_append(pm_buffer_t *buffer, const void *source, size_t length) { + size_t cursor = buffer->length; + pm_buffer_append_length(buffer, length); + memcpy(buffer->value + cursor, source, length); +} + +/** + * Append the given amount of space as zeroes to the buffer. + */ +void +pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length) { + size_t cursor = buffer->length; + pm_buffer_append_length(buffer, length); + memset(buffer->value + cursor, 0, length); +} + +/** + * Append a formatted string to the buffer. + */ +void +pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) { + va_list arguments; + va_start(arguments, format); + int result = vsnprintf(NULL, 0, format, arguments); + va_end(arguments); + + if (result < 0) return; + size_t length = (size_t) (result + 1); + + size_t cursor = buffer->length; + pm_buffer_append_length(buffer, length); + + va_start(arguments, format); + vsnprintf(buffer->value + cursor, length, format, arguments); + va_end(arguments); + + buffer->length--; +} + +/** + * Append a string to the buffer. + */ +void +pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length) { + pm_buffer_append(buffer, value, length); +} + +/** + * Append a list of bytes to the buffer. + */ +void +pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length) { + pm_buffer_append(buffer, (const char *) value, length); +} + +/** + * Append a single byte to the buffer. + */ +void +pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value) { + const void *source = &value; + pm_buffer_append(buffer, source, sizeof(uint8_t)); +} + +/** + * Append a 32-bit unsigned integer to the buffer as a variable-length integer. + */ +void +pm_buffer_append_varuint(pm_buffer_t *buffer, uint32_t value) { + if (value < 128) { + pm_buffer_append_byte(buffer, (uint8_t) value); + } else { + uint32_t n = value; + while (n >= 128) { + pm_buffer_append_byte(buffer, (uint8_t) (n | 128)); + n >>= 7; + } + pm_buffer_append_byte(buffer, (uint8_t) n); + } +} + +/** + * Append a 32-bit signed integer to the buffer as a variable-length integer. + */ +void +pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value) { + uint32_t unsigned_int = ((uint32_t)(value) << 1) ^ ((uint32_t)(value >> 31)); + pm_buffer_append_varuint(buffer, unsigned_int); +} + +/** + * Concatenate one buffer onto another. + */ +void +pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source) { + if (source->length > 0) { + pm_buffer_append(destination, source->value, source->length); + } +} + +/** + * Free the memory associated with the buffer. + */ +void +pm_buffer_free(pm_buffer_t *buffer) { + free(buffer->value); +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_char.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_char.c new file mode 100644 index 00000000..13eddbba --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_char.c @@ -0,0 +1,318 @@ +#include "prism/util/pm_char.h" + +#define PRISM_CHAR_BIT_WHITESPACE (1 << 0) +#define PRISM_CHAR_BIT_INLINE_WHITESPACE (1 << 1) +#define PRISM_CHAR_BIT_REGEXP_OPTION (1 << 2) + +#define PRISM_NUMBER_BIT_BINARY_DIGIT (1 << 0) +#define PRISM_NUMBER_BIT_BINARY_NUMBER (1 << 1) +#define PRISM_NUMBER_BIT_OCTAL_DIGIT (1 << 2) +#define PRISM_NUMBER_BIT_OCTAL_NUMBER (1 << 3) +#define PRISM_NUMBER_BIT_DECIMAL_DIGIT (1 << 4) +#define PRISM_NUMBER_BIT_DECIMAL_NUMBER (1 << 5) +#define PRISM_NUMBER_BIT_HEXADECIMAL_DIGIT (1 << 6) +#define PRISM_NUMBER_BIT_HEXADECIMAL_NUMBER (1 << 7) + +static const uint8_t pm_byte_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 3, 3, 3, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5x + 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 4, 4, // 6x + 0, 0, 0, 4, 0, 4, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +static const uint8_t pm_number_table[256] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2x + 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3x + 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, // 5x + 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ax + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Bx + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Cx + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dx + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ex + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Fx +}; + +/** + * Returns the number of characters at the start of the string that match the + * given kind. Disallows searching past the given maximum number of characters. + */ +static inline size_t +pm_strspn_char_kind(const uint8_t *string, ptrdiff_t length, uint8_t kind) { + if (length <= 0) return 0; + + size_t size = 0; + size_t maximum = (size_t) length; + + while (size < maximum && (pm_byte_table[string[size]] & kind)) size++; + return size; +} + +/** + * Returns the number of characters at the start of the string that are + * whitespace. Disallows searching past the given maximum number of characters. + */ +size_t +pm_strspn_whitespace(const uint8_t *string, ptrdiff_t length) { + return pm_strspn_char_kind(string, length, PRISM_CHAR_BIT_WHITESPACE); +} + +/** + * Returns the number of characters at the start of the string that are + * whitespace while also tracking the location of each newline. Disallows + * searching past the given maximum number of characters. + */ +size_t +pm_strspn_whitespace_newlines(const uint8_t *string, ptrdiff_t length, pm_newline_list_t *newline_list) { + if (length <= 0) return 0; + + size_t size = 0; + size_t maximum = (size_t) length; + + while (size < maximum && (pm_byte_table[string[size]] & PRISM_CHAR_BIT_WHITESPACE)) { + if (string[size] == '\n') { + pm_newline_list_append(newline_list, string + size); + } + + size++; + } + + return size; +} + +/** + * Returns the number of characters at the start of the string that are inline + * whitespace. Disallows searching past the given maximum number of characters. + */ +size_t +pm_strspn_inline_whitespace(const uint8_t *string, ptrdiff_t length) { + return pm_strspn_char_kind(string, length, PRISM_CHAR_BIT_INLINE_WHITESPACE); +} + +/** + * Returns the number of characters at the start of the string that are regexp + * options. Disallows searching past the given maximum number of characters. + */ +size_t +pm_strspn_regexp_option(const uint8_t *string, ptrdiff_t length) { + return pm_strspn_char_kind(string, length, PRISM_CHAR_BIT_REGEXP_OPTION); +} + +/** + * Returns true if the given character matches the given kind. + */ +static inline bool +pm_char_is_char_kind(const uint8_t b, uint8_t kind) { + return (pm_byte_table[b] & kind) != 0; +} + +/** + * Returns true if the given character is a whitespace character. + */ +bool +pm_char_is_whitespace(const uint8_t b) { + return pm_char_is_char_kind(b, PRISM_CHAR_BIT_WHITESPACE); +} + +/** + * Returns true if the given character is an inline whitespace character. + */ +bool +pm_char_is_inline_whitespace(const uint8_t b) { + return pm_char_is_char_kind(b, PRISM_CHAR_BIT_INLINE_WHITESPACE); +} + +/** + * Scan through the string and return the number of characters at the start of + * the string that match the given kind. Disallows searching past the given + * maximum number of characters. + */ +static inline size_t +pm_strspn_number_kind(const uint8_t *string, ptrdiff_t length, uint8_t kind) { + if (length <= 0) return 0; + + size_t size = 0; + size_t maximum = (size_t) length; + + while (size < maximum && (pm_number_table[string[size]] & kind)) size++; + return size; +} + +/** + * Scan through the string and return the number of characters at the start of + * the string that match the given kind. Disallows searching past the given + * maximum number of characters. + * + * Additionally, report the location of the last invalid underscore character + * found in the string through the out invalid parameter. + */ +static inline size_t +pm_strspn_number_kind_underscores(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid, uint8_t kind) { + if (length <= 0) return 0; + + size_t size = 0; + size_t maximum = (size_t) length; + + bool underscore = false; + while (size < maximum && (pm_number_table[string[size]] & kind)) { + if (string[size] == '_') { + if (underscore) *invalid = string + size; + underscore = true; + } else { + underscore = false; + } + + size++; + } + + if (string[size - 1] == '_') *invalid = string + size - 1; + return size; +} + +/** + * Returns the number of characters at the start of the string that are binary + * digits or underscores. Disallows searching past the given maximum number of + * characters. + * + * If multiple underscores are found in a row or if an underscore is + * found at the end of the number, then the invalid pointer is set to the index + * of the first invalid underscore. + */ +size_t +pm_strspn_binary_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid) { + return pm_strspn_number_kind_underscores(string, length, invalid, PRISM_NUMBER_BIT_BINARY_NUMBER); +} + +/** + * Returns the number of characters at the start of the string that are octal + * digits or underscores. Disallows searching past the given maximum number of + * characters. + * + * If multiple underscores are found in a row or if an underscore is + * found at the end of the number, then the invalid pointer is set to the index + * of the first invalid underscore. + */ +size_t +pm_strspn_octal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid) { + return pm_strspn_number_kind_underscores(string, length, invalid, PRISM_NUMBER_BIT_OCTAL_NUMBER); +} + +/** + * Returns the number of characters at the start of the string that are decimal + * digits. Disallows searching past the given maximum number of characters. + */ +size_t +pm_strspn_decimal_digit(const uint8_t *string, ptrdiff_t length) { + return pm_strspn_number_kind(string, length, PRISM_NUMBER_BIT_DECIMAL_DIGIT); +} + +/** + * Returns the number of characters at the start of the string that are decimal + * digits or underscores. Disallows searching past the given maximum number of + * characters. + * + * If multiple underscores are found in a row or if an underscore is + * found at the end of the number, then the invalid pointer is set to the index + * of the first invalid underscore + */ +size_t +pm_strspn_decimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid) { + return pm_strspn_number_kind_underscores(string, length, invalid, PRISM_NUMBER_BIT_DECIMAL_NUMBER); +} + +/** + * Returns the number of characters at the start of the string that are + * hexadecimal digits. Disallows searching past the given maximum number of + * characters. + */ +size_t +pm_strspn_hexadecimal_digit(const uint8_t *string, ptrdiff_t length) { + return pm_strspn_number_kind(string, length, PRISM_NUMBER_BIT_HEXADECIMAL_DIGIT); +} + +/** + * Returns the number of characters at the start of the string that are + * hexadecimal digits or underscores. Disallows searching past the given maximum + * number of characters. + * + * If multiple underscores are found in a row or if an underscore is + * found at the end of the number, then the invalid pointer is set to the index + * of the first invalid underscore. + */ +size_t +pm_strspn_hexadecimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid) { + return pm_strspn_number_kind_underscores(string, length, invalid, PRISM_NUMBER_BIT_HEXADECIMAL_NUMBER); +} + +/** + * Returns true if the given character matches the given kind. + */ +static inline bool +pm_char_is_number_kind(const uint8_t b, uint8_t kind) { + return (pm_number_table[b] & kind) != 0; +} + +/** + * Returns true if the given character is a binary digit. + */ +bool +pm_char_is_binary_digit(const uint8_t b) { + return pm_char_is_number_kind(b, PRISM_NUMBER_BIT_BINARY_DIGIT); +} + +/** + * Returns true if the given character is an octal digit. + */ +bool +pm_char_is_octal_digit(const uint8_t b) { + return pm_char_is_number_kind(b, PRISM_NUMBER_BIT_OCTAL_DIGIT); +} + +/** + * Returns true if the given character is a decimal digit. + */ +bool +pm_char_is_decimal_digit(const uint8_t b) { + return pm_char_is_number_kind(b, PRISM_NUMBER_BIT_DECIMAL_DIGIT); +} + +/** + * Returns true if the given character is a hexadecimal digit. + */ +bool +pm_char_is_hexadecimal_digit(const uint8_t b) { + return pm_char_is_number_kind(b, PRISM_NUMBER_BIT_HEXADECIMAL_DIGIT); +} + +#undef PRISM_CHAR_BIT_WHITESPACE +#undef PRISM_CHAR_BIT_INLINE_WHITESPACE +#undef PRISM_CHAR_BIT_REGEXP_OPTION + +#undef PRISM_NUMBER_BIT_BINARY_DIGIT +#undef PRISM_NUMBER_BIT_BINARY_NUMBER +#undef PRISM_NUMBER_BIT_OCTAL_DIGIT +#undef PRISM_NUMBER_BIT_OCTAL_NUMBER +#undef PRISM_NUMBER_BIT_DECIMAL_DIGIT +#undef PRISM_NUMBER_BIT_DECIMAL_NUMBER +#undef PRISM_NUMBER_BIT_HEXADECIMAL_NUMBER +#undef PRISM_NUMBER_BIT_HEXADECIMAL_DIGIT diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_constant_pool.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_constant_pool.c new file mode 100644 index 00000000..e06682eb --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_constant_pool.c @@ -0,0 +1,296 @@ +#include "prism/util/pm_constant_pool.h" + +/** + * Initialize a list of constant ids. + */ +void +pm_constant_id_list_init(pm_constant_id_list_t *list) { + list->ids = NULL; + list->size = 0; + list->capacity = 0; +} + +/** + * Append a constant id to a list of constant ids. Returns false if any + * potential reallocations fail. + */ +bool +pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id) { + if (list->size >= list->capacity) { + list->capacity = list->capacity == 0 ? 8 : list->capacity * 2; + list->ids = (pm_constant_id_t *) realloc(list->ids, sizeof(pm_constant_id_t) * list->capacity); + if (list->ids == NULL) return false; + } + + list->ids[list->size++] = id; + return true; +} + +/** + * Checks if the current constant id list includes the given constant id. + */ +bool +pm_constant_id_list_includes(pm_constant_id_list_t *list, pm_constant_id_t id) { + for (size_t index = 0; index < list->size; index++) { + if (list->ids[index] == id) return true; + } + return false; +} + +/** + * Get the memory size of a list of constant ids. + */ +size_t +pm_constant_id_list_memsize(pm_constant_id_list_t *list) { + return sizeof(pm_constant_id_list_t) + (list->capacity * sizeof(pm_constant_id_t)); +} + +/** + * Free the memory associated with a list of constant ids. + */ +void +pm_constant_id_list_free(pm_constant_id_list_t *list) { + if (list->ids != NULL) { + free(list->ids); + } +} + +/** + * A relatively simple hash function (djb2) that is used to hash strings. We are + * optimizing here for simplicity and speed. + */ +static inline uint32_t +pm_constant_pool_hash(const uint8_t *start, size_t length) { + // This is a prime number used as the initial value for the hash function. + uint32_t value = 5381; + + for (size_t index = 0; index < length; index++) { + value = ((value << 5) + value) + start[index]; + } + + return value; +} + +/** + * https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + */ +static uint32_t +next_power_of_two(uint32_t v) { + // Avoid underflow in subtraction on next line. + if (v == 0) { + // 1 is the nearest power of 2 to 0 (2^0) + return 1; + } + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +#ifndef NDEBUG +static bool +is_power_of_two(uint32_t size) { + return (size & (size - 1)) == 0; +} +#endif + +/** + * Resize a constant pool to a given capacity. + */ +static inline bool +pm_constant_pool_resize(pm_constant_pool_t *pool) { + assert(is_power_of_two(pool->capacity)); + + uint32_t next_capacity = pool->capacity * 2; + if (next_capacity < pool->capacity) return false; + + const uint32_t mask = next_capacity - 1; + const size_t element_size = sizeof(pm_constant_pool_bucket_t) + sizeof(pm_constant_t); + + void *next = calloc(next_capacity, element_size); + if (next == NULL) return false; + + pm_constant_pool_bucket_t *next_buckets = next; + pm_constant_t *next_constants = (void *)(((char *) next) + next_capacity * sizeof(pm_constant_pool_bucket_t)); + + // For each bucket in the current constant pool, find the index in the + // next constant pool, and insert it. + for (uint32_t index = 0; index < pool->capacity; index++) { + pm_constant_pool_bucket_t *bucket = &pool->buckets[index]; + + // If an id is set on this constant, then we know we have content here. + // In this case we need to insert it into the next constant pool. + if (bucket->id != 0) { + uint32_t next_index = bucket->hash & mask; + + // This implements linear scanning to find the next available slot + // in case this index is already taken. We don't need to bother + // comparing the values since we know that the hash is unique. + while (next_buckets[next_index].id != 0) { + next_index = (next_index + 1) & mask; + } + + // Here we copy over the entire bucket, which includes the id so + // that they are consistent between resizes. + next_buckets[next_index] = *bucket; + } + } + + // The constants are stable with respect to hash table resizes. + memcpy(next_constants, pool->constants, pool->size * sizeof(pm_constant_t)); + + // pool->constants and pool->buckets are allocated out of the same chunk + // of memory, with the buckets coming first. + free(pool->buckets); + pool->constants = next_constants; + pool->buckets = next_buckets; + pool->capacity = next_capacity; + return true; +} + +/** + * Initialize a new constant pool with a given capacity. + */ +bool +pm_constant_pool_init(pm_constant_pool_t *pool, uint32_t capacity) { + const uint32_t maximum = (~((uint32_t) 0)); + if (capacity >= ((maximum / 2) + 1)) return false; + + capacity = next_power_of_two(capacity); + const size_t element_size = sizeof(pm_constant_pool_bucket_t) + sizeof(pm_constant_t); + void *memory = calloc(capacity, element_size); + if (memory == NULL) return false; + + pool->buckets = memory; + pool->constants = (void *)(((char *)memory) + capacity * sizeof(pm_constant_pool_bucket_t)); + pool->size = 0; + pool->capacity = capacity; + return true; +} + +/** + * Return a pointer to the constant indicated by the given constant id. + */ +pm_constant_t * +pm_constant_pool_id_to_constant(const pm_constant_pool_t *pool, pm_constant_id_t constant_id) { + assert(constant_id > 0 && constant_id <= pool->size); + return &pool->constants[constant_id - 1]; +} + +/** + * Insert a constant into a constant pool and return its index in the pool. + */ +static inline pm_constant_id_t +pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t length, pm_constant_pool_bucket_type_t type) { + if (pool->size >= (pool->capacity / 4 * 3)) { + if (!pm_constant_pool_resize(pool)) return 0; + } + + assert(is_power_of_two(pool->capacity)); + const uint32_t mask = pool->capacity - 1; + + uint32_t hash = pm_constant_pool_hash(start, length); + uint32_t index = hash & mask; + pm_constant_pool_bucket_t *bucket; + + while (bucket = &pool->buckets[index], bucket->id != 0) { + // If there is a collision, then we need to check if the content is the + // same as the content we are trying to insert. If it is, then we can + // return the id of the existing constant. + pm_constant_t *constant = &pool->constants[bucket->id - 1]; + + if ((constant->length == length) && memcmp(constant->start, start, length) == 0) { + // Since we have found a match, we need to check if this is + // attempting to insert a shared or an owned constant. We want to + // prefer shared constants since they don't require allocations. + if (type == PM_CONSTANT_POOL_BUCKET_OWNED) { + // If we're attempting to insert an owned constant and we have + // an existing constant, then either way we don't want the given + // memory. Either it's duplicated with the existing constant or + // it's not necessary because we have a shared version. + free((void *) start); + } else if (bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) { + // If we're attempting to insert a shared constant and the + // existing constant is owned, then we can free the owned + // constant and replace it with the shared constant. + free((void *) constant->start); + constant->start = start; + bucket->type = (unsigned int) (PM_CONSTANT_POOL_BUCKET_DEFAULT & 0x3); + } + + return bucket->id; + } + + index = (index + 1) & mask; + } + + // IDs are allocated starting at 1, since the value 0 denotes a non-existant + // constant. + uint32_t id = ++pool->size; + assert(pool->size < ((uint32_t) (1 << 30))); + + *bucket = (pm_constant_pool_bucket_t) { + .id = (unsigned int) (id & 0x3fffffff), + .type = (unsigned int) (type & 0x3), + .hash = hash + }; + + pool->constants[id - 1] = (pm_constant_t) { + .start = start, + .length = length, + }; + + return id; +} + +/** + * Insert a constant into a constant pool. Returns the id of the constant, or 0 + * if any potential calls to resize fail. + */ +pm_constant_id_t +pm_constant_pool_insert_shared(pm_constant_pool_t *pool, const uint8_t *start, size_t length) { + return pm_constant_pool_insert(pool, start, length, PM_CONSTANT_POOL_BUCKET_DEFAULT); +} + +/** + * Insert a constant into a constant pool from memory that is now owned by the + * constant pool. Returns the id of the constant, or 0 if any potential calls to + * resize fail. + */ +pm_constant_id_t +pm_constant_pool_insert_owned(pm_constant_pool_t *pool, const uint8_t *start, size_t length) { + return pm_constant_pool_insert(pool, start, length, PM_CONSTANT_POOL_BUCKET_OWNED); +} + +/** + * Insert a constant into a constant pool from memory that is constant. Returns + * the id of the constant, or 0 if any potential calls to resize fail. + */ +pm_constant_id_t +pm_constant_pool_insert_constant(pm_constant_pool_t *pool, const uint8_t *start, size_t length) { + return pm_constant_pool_insert(pool, start, length, PM_CONSTANT_POOL_BUCKET_CONSTANT); +} + +/** + * Free the memory associated with a constant pool. + */ +void +pm_constant_pool_free(pm_constant_pool_t *pool) { + // For each constant in the current constant pool, free the contents if the + // contents are owned. + for (uint32_t index = 0; index < pool->capacity; index++) { + pm_constant_pool_bucket_t *bucket = &pool->buckets[index]; + + // If an id is set on this constant, then we know we have content here. + if (bucket->id != 0 && bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) { + pm_constant_t *constant = &pool->constants[bucket->id - 1]; + free((void *) constant->start); + } + } + + free(pool->buckets); +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_list.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_list.c new file mode 100644 index 00000000..62cfe47c --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_list.c @@ -0,0 +1,49 @@ +#include "prism/util/pm_list.h" + +/** + * Returns true if the given list is empty. + */ +PRISM_EXPORTED_FUNCTION bool +pm_list_empty_p(pm_list_t *list) { + return list->head == NULL; +} + +/** + * Returns the size of the list. + */ +PRISM_EXPORTED_FUNCTION size_t +pm_list_size(pm_list_t *list) { + return list->size; +} + +/** + * Append a node to the given list. + */ +void +pm_list_append(pm_list_t *list, pm_list_node_t *node) { + if (list->head == NULL) { + list->head = node; + } else { + list->tail->next = node; + } + + list->tail = node; + list->size++; +} + +/** + * Deallocate the internal state of the given list. + */ +PRISM_EXPORTED_FUNCTION void +pm_list_free(pm_list_t *list) { + pm_list_node_t *node = list->head; + pm_list_node_t *next; + + while (node != NULL) { + next = node->next; + free(node); + node = next; + } + + list->size = 0; +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_memchr.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_memchr.c new file mode 100644 index 00000000..b2049250 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_memchr.c @@ -0,0 +1,35 @@ +#include "prism/util/pm_memchr.h" + +#define PRISM_MEMCHR_TRAILING_BYTE_MINIMUM 0x40 + +/** + * We need to roll our own memchr to handle cases where the encoding changes and + * we need to search for a character in a buffer that could be the trailing byte + * of a multibyte character. + */ +void * +pm_memchr(const void *memory, int character, size_t number, bool encoding_changed, pm_encoding_t *encoding) { + if (encoding_changed && encoding->multibyte && character >= PRISM_MEMCHR_TRAILING_BYTE_MINIMUM) { + const uint8_t *source = (const uint8_t *) memory; + size_t index = 0; + + while (index < number) { + if (source[index] == character) { + return (void *) (source + index); + } + + size_t width = encoding->char_width(source + index, (ptrdiff_t) (number - index)); + if (width == 0) { + return NULL; + } + + index += width; + } + + return NULL; + } else { + return memchr(memory, character, number); + } +} + +#undef PRISM_MEMCHR_TRAILING_BYTE_MINIMUM diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_newline_list.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_newline_list.c new file mode 100644 index 00000000..f27bb75b --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_newline_list.c @@ -0,0 +1,96 @@ +#include "prism/util/pm_newline_list.h" + +/** + * Initialize a new newline list with the given capacity. Returns true if the + * allocation of the offsets succeeds, otherwise returns false. + */ +bool +pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capacity) { + list->offsets = (size_t *) calloc(capacity, sizeof(size_t)); + if (list->offsets == NULL) return false; + + list->start = start; + + // This is 1 instead of 0 because we want to include the first line of the + // file as having offset 0, which is set because of calloc. + list->size = 1; + list->capacity = capacity; + + return true; +} + +/** + * Append a new offset to the newline list. Returns true if the reallocation of + * the offsets succeeds (if one was necessary), otherwise returns false. + */ +bool +pm_newline_list_append(pm_newline_list_t *list, const uint8_t *cursor) { + if (list->size == list->capacity) { + size_t *original_offsets = list->offsets; + + list->capacity = (list->capacity * 3) / 2; + list->offsets = (size_t *) calloc(list->capacity, sizeof(size_t)); + memcpy(list->offsets, original_offsets, list->size * sizeof(size_t)); + free(original_offsets); + if (list->offsets == NULL) return false; + } + + assert(*cursor == '\n'); + assert(cursor >= list->start); + size_t newline_offset = (size_t) (cursor - list->start + 1); + + assert(list->size == 0 || newline_offset > list->offsets[list->size - 1]); + list->offsets[list->size++] = newline_offset; + + return true; +} + +/** + * Conditionally append a new offset to the newline list, if the value passed in + * is a newline. + */ +bool +pm_newline_list_check_append(pm_newline_list_t *list, const uint8_t *cursor) { + if (*cursor != '\n') { + return true; + } + return pm_newline_list_append(list, cursor); +} + +/** + * Returns the line and column of the given offset. If the offset is not in the + * list, the line and column of the closest offset less than the given offset + * are returned. + */ +pm_line_column_t +pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor) { + assert(cursor >= list->start); + size_t offset = (size_t) (cursor - list->start); + + size_t left = 0; + size_t right = list->size - 1; + + while (left <= right) { + size_t mid = left + (right - left) / 2; + + if (list->offsets[mid] == offset) { + return ((pm_line_column_t) { mid, 0 }); + } + + if (list->offsets[mid] < offset) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return ((pm_line_column_t) { left - 1, offset - list->offsets[left - 1] }); +} + +/** + * Free the internal memory allocated for the newline list. + */ +void +pm_newline_list_free(pm_newline_list_t *list) { + free(list->offsets); +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_state_stack.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_state_stack.c new file mode 100644 index 00000000..2a424b4c --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_state_stack.c @@ -0,0 +1,25 @@ +#include "prism/util/pm_state_stack.h" + +/** + * Pushes a value onto the stack. + */ +void +pm_state_stack_push(pm_state_stack_t *stack, bool value) { + *stack = (*stack << 1) | (value & 1); +} + +/** + * Pops a value off the stack. + */ +void +pm_state_stack_pop(pm_state_stack_t *stack) { + *stack >>= 1; +} + +/** + * Returns the value at the top of the stack. + */ +bool +pm_state_stack_p(pm_state_stack_t *stack) { + return *stack & 1; +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_string.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_string.c new file mode 100644 index 00000000..f4d3033a --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_string.c @@ -0,0 +1,210 @@ +#include "prism/util/pm_string.h" + +/** + * Returns the size of the pm_string_t struct. This is necessary to allocate the + * correct amount of memory in the FFI backend. + */ +PRISM_EXPORTED_FUNCTION size_t +pm_string_sizeof(void) { + return sizeof(pm_string_t); +} + +/** + * Initialize a shared string that is based on initial input. + */ +void +pm_string_shared_init(pm_string_t *string, const uint8_t *start, const uint8_t *end) { + assert(start <= end); + + *string = (pm_string_t) { + .type = PM_STRING_SHARED, + .source = start, + .length = (size_t) (end - start) + }; +} + +/** + * Initialize an owned string that is responsible for freeing allocated memory. + */ +void +pm_string_owned_init(pm_string_t *string, uint8_t *source, size_t length) { + *string = (pm_string_t) { + .type = PM_STRING_OWNED, + .source = source, + .length = length + }; +} + +/** + * Initialize a constant string that doesn't own its memory source. + */ +void +pm_string_constant_init(pm_string_t *string, const char *source, size_t length) { + *string = (pm_string_t) { + .type = PM_STRING_CONSTANT, + .source = (const uint8_t *) source, + .length = length + }; +} + +/** + * Read the file indicated by the filepath parameter into source and load its + * contents and size into the given `pm_string_t`. The given `pm_string_t` + * should be freed using `pm_string_free` when it is no longer used. + * + * We want to use demand paging as much as possible in order to avoid having to + * read the entire file into memory (which could be detrimental to performance + * for large files). This means that if we're on windows we'll use + * `MapViewOfFile`, on POSIX systems that have access to `mmap` we'll use + * `mmap`, and on other POSIX systems we'll use `read`. + */ +bool +pm_string_mapped_init(pm_string_t *string, const char *filepath) { +#ifdef _WIN32 + // Open the file for reading. + HANDLE file = CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file == INVALID_HANDLE_VALUE) { + perror("CreateFile failed"); + return false; + } + + // Get the file size. + DWORD file_size = GetFileSize(file, NULL); + if (file_size == INVALID_FILE_SIZE) { + CloseHandle(file); + perror("GetFileSize failed"); + return false; + } + + // If the file is empty, then we don't need to do anything else, we'll set + // the source to a constant empty string and return. + if (file_size == 0) { + CloseHandle(file); + const uint8_t source[] = ""; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + return true; + } + + // Create a mapping of the file. + HANDLE mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL); + if (mapping == NULL) { + CloseHandle(file); + perror("CreateFileMapping failed"); + return false; + } + + // Map the file into memory. + uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + CloseHandle(mapping); + CloseHandle(file); + + if (source == NULL) { + perror("MapViewOfFile failed"); + return false; + } + + *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = (size_t) file_size }; + return true; +#else + // Open the file for reading + int fd = open(filepath, O_RDONLY); + if (fd == -1) { + perror("open"); + return false; + } + + // Stat the file to get the file size + struct stat sb; + if (fstat(fd, &sb) == -1) { + close(fd); + perror("fstat"); + return false; + } + + // mmap the file descriptor to virtually get the contents + size_t size = (size_t) sb.st_size; + uint8_t *source = NULL; + + if (size == 0) { + close(fd); + const uint8_t source[] = ""; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + return true; + } + + source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (source == MAP_FAILED) { + perror("Map failed"); + return false; + } + + close(fd); + *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = size }; + return true; +#endif +} + +/** + * Returns the memory size associated with the string. + */ +size_t +pm_string_memsize(const pm_string_t *string) { + size_t size = sizeof(pm_string_t); + if (string->type == PM_STRING_OWNED) { + size += string->length; + } + return size; +} + +/** + * Ensure the string is owned. If it is not, then reinitialize it as owned and + * copy over the previous source. + */ +void +pm_string_ensure_owned(pm_string_t *string) { + if (string->type == PM_STRING_OWNED) return; + + size_t length = pm_string_length(string); + const uint8_t *source = pm_string_source(string); + + uint8_t *memory = malloc(length); + if (!memory) return; + + pm_string_owned_init(string, memory, length); + memcpy((void *) string->source, source, length); +} + +/** + * Returns the length associated with the string. + */ +PRISM_EXPORTED_FUNCTION size_t +pm_string_length(const pm_string_t *string) { + return string->length; +} + +/** + * Returns the start pointer associated with the string. + */ +PRISM_EXPORTED_FUNCTION const uint8_t * +pm_string_source(const pm_string_t *string) { + return string->source; +} + +/** + * Free the associated memory of the given string. + */ +PRISM_EXPORTED_FUNCTION void +pm_string_free(pm_string_t *string) { + void *memory = (void *) string->source; + + if (string->type == PM_STRING_OWNED) { + free(memory); + } else if (string->type == PM_STRING_MAPPED && string->length) { +#if defined(_WIN32) + UnmapViewOfFile(memory); +#else + munmap(memory, string->length); +#endif + } +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_string_list.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_string_list.c new file mode 100644 index 00000000..d49e4ed7 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_string_list.c @@ -0,0 +1,28 @@ +#include "prism/util/pm_string_list.h" + +/** + * Append a pm_string_t to the given string list. + */ +void +pm_string_list_append(pm_string_list_t *string_list, pm_string_t *string) { + if (string_list->length + 1 > string_list->capacity) { + if (string_list->capacity == 0) { + string_list->capacity = 1; + } else { + string_list->capacity *= 2; + } + + string_list->strings = realloc(string_list->strings, string_list->capacity * sizeof(pm_string_t)); + if (string_list->strings == NULL) abort(); + } + + string_list->strings[string_list->length++] = *string; +} + +/** + * Free the memory associated with the string list + */ +void +pm_string_list_free(pm_string_list_t *string_list) { + free(string_list->strings); +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_strncasecmp.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_strncasecmp.c new file mode 100644 index 00000000..2240bf81 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_strncasecmp.c @@ -0,0 +1,24 @@ +#include "prism/util/pm_strncasecmp.h" + +/** + * Compare two strings, ignoring case, up to the given length. Returns 0 if the + * strings are equal, a negative number if string1 is less than string2, or a + * positive number if string1 is greater than string2. + * + * Note that this is effectively our own implementation of strncasecmp, but it's + * not available on all of the platforms we want to support so we're rolling it + * here. + */ +int +pm_strncasecmp(const uint8_t *string1, const uint8_t *string2, size_t length) { + size_t offset = 0; + int difference = 0; + + while (offset < length && string1[offset] != '\0') { + if (string2[offset] == '\0') return string1[offset]; + if ((difference = tolower(string1[offset]) - tolower(string2[offset])) != 0) return difference; + offset++; + } + + return difference; +} diff --git a/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_strpbrk.c b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_strpbrk.c new file mode 100644 index 00000000..ce1f3691 --- /dev/null +++ b/crates/prism-sys/vendor/prism-0.18.0/src/util/pm_strpbrk.c @@ -0,0 +1,72 @@ +#include "prism/util/pm_strpbrk.h" + +/** + * This is the slow path that does care about the encoding. + */ +static inline const uint8_t * +pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum) { + size_t index = 0; + + while (index < maximum) { + if (strchr((const char *) charset, source[index]) != NULL) { + return source + index; + } + + size_t width = parser->encoding.char_width(source + index, (ptrdiff_t) (maximum - index)); + if (width == 0) { + return NULL; + } + + index += width; + } + + return NULL; +} + +/** + * This is the fast path that does not care about the encoding. + */ +static inline const uint8_t * +pm_strpbrk_single_byte(const uint8_t *source, const uint8_t *charset, size_t maximum) { + size_t index = 0; + + while (index < maximum) { + if (strchr((const char *) charset, source[index]) != NULL) { + return source + index; + } + + index++; + } + + return NULL; +} + +/** + * Here we have rolled our own version of strpbrk. The standard library strpbrk + * has undefined behavior when the source string is not null-terminated. We want + * to support strings that are not null-terminated because pm_parse does not + * have the contract that the string is null-terminated. (This is desirable + * because it means the extension can call pm_parse with the result of a call to + * mmap). + * + * The standard library strpbrk also does not support passing a maximum length + * to search. We want to support this for the reason mentioned above, but we + * also don't want it to stop on null bytes. Ruby actually allows null bytes + * within strings, comments, regular expressions, etc. So we need to be able to + * skip past them. + * + * Finally, we want to support encodings wherein the charset could contain + * characters that are trailing bytes of multi-byte characters. For example, in + * Shift-JIS, the backslash character can be a trailing byte. In that case we + * need to take a slower path and iterate one multi-byte character at a time. + */ +const uint8_t * +pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length) { + if (length <= 0) { + return NULL; + } else if (parser->encoding_changed && parser->encoding.multibyte) { + return pm_strpbrk_multi_byte(parser, source, charset, (size_t) length); + } else { + return pm_strpbrk_single_byte(source, charset, (size_t) length); + } +}