commit eb5172548ae4862eb750f75334346ad9d0eb5cd6 Author: Intege-rs Date: Sat Aug 15 08:44:02 2026 -0400 fix msvc diff --git a/.cargo-ok b/.cargo-ok new file mode 100644 index 0000000..5f8b795 --- /dev/null +++ b/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..c0fcaa3 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "82ec1c3024f9df3c4d7a98a7814e199b1f41deaf" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a95ea42 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,247 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cc" +version = "1.2.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "ruby-comp" +version = "0.4.0" +dependencies = [ + "bindgen", + "cc", + "glob", + "libc", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..49ac3fd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,62 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +name = "ruby-comp" +version = "0.4.0" +authors = [ + "Uchio Kondo ", + "Hitoshi Hasumi", + "prism developers", +] +build = "build.rs" +include = [ + "src/lib.rs", + "build.rs", + "examples/*.rs", + "Cargo.toml", + "vendor/*", + "lib/.keep", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "mruby-compiler2 bindings for Rust" +readme = "README.md" +license-file = "LICENSE" + +[lib] +name = "mruby_compiler2_sys" +path = "src/lib.rs" + +[[example]] +name = "cfunc" +path = "examples/cfunc.rs" + +[[example]] +name = "smoke" +path = "examples/smoke.rs" + +[dependencies.libc] +version = "0.2.182" +optional = true + +[build-dependencies.bindgen] +version = ">= 0" + +[build-dependencies.cc] +version = ">= 1" + +[build-dependencies.glob] +version = ">= 0" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..aa4aeff --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,32 @@ +[package] +name = "mruby-compiler2-sys" +version = "0.4.0" +edition = "2024" +authors = [ + "Uchio Kondo ", + "Hitoshi Hasumi", + "prism developers", +] +description = "mruby-compiler2 bindings for Rust" +license-file = "LICENSE" +include = [ + "src/lib.rs", + "build.rs", + "examples/*.rs", + "Cargo.toml", + "vendor/*", + "lib/.keep", +] + +[dependencies] +libc = { version = "0.2.182", optional = true } + +[build-dependencies] +bindgen = ">= 0" +cc = ">= 1" +glob = ">= 0" + +[features] +default = ["std"] +std = ["dep:libc"] +no-std = [] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5ecf83a --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +mruby-compiler2-sys + +Copyright (c) Uchio Kondo 2025 - + +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. + +[ MIT license: http://www.opensource.org/licenses/mit-license.php ] diff --git a/README.md b/README.md new file mode 100644 index 0000000..a219bf1 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# mruby-compiler2-sys +Naive mruby-compiler2 binding for Rust + +See Rust doc [mruby-compiler2-sys](https://crates.io/crates/mruby-compiler2-sys) for details. + +## License +See LICENSE file. + +NOTE: This crate includes code from mruby-compiler2 and prism, which are under the MIT License. diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..4348e47 --- /dev/null +++ b/build.rs @@ -0,0 +1,56 @@ +extern crate cc; +use glob::glob; + +fn main() { + let out_dir = std::env::var("OUT_DIR").unwrap(); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rustc-link-search={}", out_dir); + cc::Build::new() + .files( + glob("./vendor/mruby-compiler2/src/**/*.c") + .expect("cannot find c source") + .map(|x| x.unwrap()), + ) + .files( + glob("./vendor/mruby-compiler2/lib/prism/src/**/*.c") + .expect("cannot find c source") + .map(|x| x.unwrap()), + ) + .warnings(false) + .define("MRB_NO_PRESYM", "") + .define("MRB_INT64", "1") + .define("PRISM_XALLOCATOR", "") + .define("PRISM_BUILD_MINIMAL", "") + .define("PICORB_VM_MRUBYC", "") + .define("MRBC_ALLOC_LIBC", "") + .include("./vendor/include") + .include("./vendor/mruby-compiler2/include") + .include("./vendor/mruby-compiler2/lib/prism/include") + // .flag("-fPIC") + .flag("-c") + .compile("mrubycompiler2"); + + println!("cargo:rustc-link-lib=mrubycompiler2"); + let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default(); + let mut builder = bindgen::Builder::default() + .header("./vendor/mruby-compiler2/include/mruby_compiler.h") + .header("./vendor/mruby-compiler2/include/mrc_codedump.h") + .clang_arg("-I./vendor/mruby-compiler2/include") + .clang_arg("-I./vendor/mruby-compiler2/lib/prism/include") + .blocklist_item("FP_NAN") + .blocklist_item("FP_INFINITE") + .blocklist_item("FP_ZERO") + .blocklist_item("FP_SUBNORMAL") + .blocklist_item("FP_NORMAL") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); + if target_arch == "wasm32" { + builder = builder.blocklist_type("max_align_t"); + } + let bindings = builder.generate().expect("Unable to generate bindings"); + + let out = std::path::PathBuf::from(out_dir).join("bindings.rs"); + bindings + .write_to_file(out) + .expect("Couldn't write bindings!"); +} diff --git a/examples/cfunc.rs b/examples/cfunc.rs new file mode 100644 index 0000000..3829ef1 --- /dev/null +++ b/examples/cfunc.rs @@ -0,0 +1,15 @@ +extern crate mruby_compiler2_sys; +use mruby_compiler2_sys::MRubyCompiler2Context; + +fn main() -> Result<(), Box> { + unsafe { + let mut cxt = MRubyCompiler2Context::new(); + cxt.compile_to_c_function( + "puts \"Hello, mruby-compiler2!\"", + "init_test_func", + std::path::Path::new("examples/out.c"), + )?; + } + println!("Created examples/out.c"); + Ok(()) +} \ No newline at end of file diff --git a/examples/smoke.rs b/examples/smoke.rs new file mode 100644 index 0000000..534ae26 --- /dev/null +++ b/examples/smoke.rs @@ -0,0 +1,17 @@ +extern crate mruby_compiler2_sys; +use mruby_compiler2_sys::MRubyCompiler2Context; + +fn main() -> Result<(), Box> { + unsafe { + let mut cxt = MRubyCompiler2Context::new(); + cxt.dump_bytecode("puts \"Hello, mruby-compiler2!\"")?; + + let bin = cxt.compile("puts \"Hello, mruby-compiler2!\"")?; + + let out = std::fs::File::create("examples/out.mrb")?; + std::io::Write::write_all(&mut &out, &bin)?; + + println!("Compiled bytecode file: examples/out.mrb, size: {}", bin.len()); + } + Ok(()) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..950bb22 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,101 @@ +use std::ptr::null_mut; + +mod bindings { + #![allow(nonstandard_style)] + #![allow(unused)] + #![allow(unnecessary_transmutes)] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} +use bindings::{ + MRC_DUMP_OK, mrc_ccontext, mrc_ccontext_free, mrc_ccontext_new, mrc_dump_irep, mrc_irep, + mrc_irep_free, mrc_load_string_cxt, +}; + +#[derive(Debug)] +pub struct MRubyCompiler2Error { + details: String, +} + +impl MRubyCompiler2Error { + fn new(msg: &str) -> MRubyCompiler2Error { + MRubyCompiler2Error { + details: msg.to_string(), + } + } + + #[allow(unused)] + fn from_error(msg: &str, err: E) -> MRubyCompiler2Error { + MRubyCompiler2Error { + details: format!("{}: {}", msg, err.to_string()), + } + } +} + +impl std::fmt::Display for MRubyCompiler2Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.details) + } +} + +impl std::error::Error for MRubyCompiler2Error {} + +pub struct MRubyCompiler2Context { + c: *mut mrc_ccontext, +} + +impl MRubyCompiler2Context { + /// Creates a new MRubyCompiler2Context + pub unsafe fn new() -> Self { + unsafe { + let ccontext = mrc_ccontext_new(null_mut()); + MRubyCompiler2Context { c: ccontext } + } + } + + /// Compiles the given mruby code into mruby bytecode binary + /// Returns the bytecode as a `Vec` + pub unsafe fn compile(&mut self, code: &str) -> Result, MRubyCompiler2Error> { + unsafe { + let c_code = std::ffi::CString::new(code) + .map_err(|_| MRubyCompiler2Error::new("Code includes null bytes"))?; + let mut ptr = c_code.as_ptr() as *const u8; + let irep = + mrc_load_string_cxt(self.c, &mut ptr as *mut *const u8, c_code.as_bytes().len()); + + if irep.is_null() { + return Err(MRubyCompiler2Error::new("Failed to compile code")); + } + + // Set dummy capacity, deduced from code length + // And leak for safety rather than memory efficiency + let bin: &'static mut [u8] = Vec::with_capacity(code.len() * 2).leak(); + let bin_ptr = bin.as_mut_ptr(); + let mut bin_size: usize = 0; + + let result = mrc_dump_irep( + self.c, + irep as *mut mrc_irep, + 0, + &bin_ptr as *const *mut u8 as *mut *mut u8, + &mut bin_size as *mut usize, + ); + mrc_irep_free(self.c, irep as *mut mrc_irep); + if result as u32 != MRC_DUMP_OK { + return Err(MRubyCompiler2Error::new("Failed to dump irep binary")); + } + + let newvec = Vec::from_raw_parts(bin_ptr, bin_size, bin_size); + Ok(newvec) + } + } + + +} + +impl Drop for MRubyCompiler2Context { + fn drop(&mut self) { + unsafe { + mrc_ccontext_free(self.c); + } + } +} diff --git a/target/CACHEDIR.TAG b/target/CACHEDIR.TAG new file mode 100644 index 0000000..20d7c31 --- /dev/null +++ b/target/CACHEDIR.TAG @@ -0,0 +1,3 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by cargo. +# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/target/debug/.cargo-lock b/target/debug/.cargo-lock new file mode 100644 index 0000000..e69de29 diff --git a/vendor/include/mrubyc.h b/vendor/include/mrubyc.h new file mode 100644 index 0000000..8041d95 --- /dev/null +++ b/vendor/include/mrubyc.h @@ -0,0 +1 @@ +// Dummy inclusion header \ No newline at end of file diff --git a/vendor/mruby-compiler2/LICENSE b/vendor/mruby-compiler2/LICENSE new file mode 100644 index 0000000..a3c85e4 --- /dev/null +++ b/vendor/mruby-compiler2/LICENSE @@ -0,0 +1,24 @@ +mruby-compiler2 + +Copyright (c) HASUMI Hitoshi 2024 + +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. + +[ MIT license: http://www.opensource.org/licenses/mit-license.php ] diff --git a/vendor/mruby-compiler2/README.md b/vendor/mruby-compiler2/README.md new file mode 100644 index 0000000..5308b9c --- /dev/null +++ b/vendor/mruby-compiler2/README.md @@ -0,0 +1,20 @@ +# mruby-compiler2 + +New mruby compiler based on Prism. + +## install by mrbgems + +- add conf.gem line to `build_config.rb` + +```ruby +MRuby::Build.new do |conf| + + # ... (snip) ... + + conf.gem :github => 'picoruby/mruby-compiler2' +end +``` + +## License +under the MIT License: +- see LICENSE file diff --git a/vendor/mruby-compiler2/include/mrc_ccontext.h b/vendor/mruby-compiler2/include/mrc_ccontext.h new file mode 100644 index 0000000..11a2982 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_ccontext.h @@ -0,0 +1,87 @@ +#ifndef MRC_CCONTEXT_H +#define MRC_CCONTEXT_H + +#include "mrc_common.h" +#include "mrc_diagnostic.h" +#include "mrc_throw.h" +#include "mrc_pool.h" +#include + +MRC_BEGIN_DECL + +typedef pm_node_t mrc_node; +typedef pm_parser_t mrc_parser_state; +typedef pm_constant_id_list_t mrc_constant_id_list; +typedef struct { + pm_parser_t parser; + pm_options_t options; + pm_string_t input; + bool parsed; +} pm_parse_result_t; + +struct mrc_diagnostic_list; + +typedef struct mrc_filename_table { + const char *filename; + uint32_t start; +} mrc_filename_table; + +typedef struct mrc_ccontext { + mrb_state *mrb; + struct mrc_jmpbuf *jmp; + mrc_parser_state *p; + pm_options_t *options; // instead of mrb_sym *syms + int slen; + char *filename; + uint16_t lineno; + struct RClass *target_class; + mrc_bool capture_errors:1; + mrc_bool dump_result:1; + mrc_bool no_exec:1; + mrc_bool keep_lv:1; + mrc_bool no_optimize:1; + mrc_bool no_ext_ops:1; +#if defined(MRC_TARGET_MRUBY) + const struct RProc *upper; +#endif + + // TODO + //size_t parser_nerr; + struct mrc_diagnostic_list *diagnostic_list; + + // For PICOIRB + uint16_t scope_sp; + +#ifndef MRC_NO_STDIO + mrc_pool *pool; // for codedump + + mrc_filename_table *filename_table; + uint16_t filename_table_length; + uint16_t current_filename_index; +#endif +} mrc_ccontext; /* compiler context */ + +#ifdef MRC_TARGET_MRUBY +static inline int mrc_gc_arena_save(mrc_ccontext *c) +{ + if (!c->mrb) return 0; + return mrb_gc_arena_save(c->mrb); +} +static inline void mrc_gc_arena_restore(mrc_ccontext *c, int ai) +{ + if (!c->mrb) return; + mrb_gc_arena_restore(c->mrb, ai); +} +#else +# define mrc_gc_arena_save(c) 0;(void)ai +# define mrc_gc_arena_restore(c,ai) +#endif + +mrc_ccontext *mrc_ccontext_new(mrb_state *mrb); +void mrc_ccontext_cleanup_local_variables(mrc_ccontext *c); +const char *mrc_ccontext_filename(mrc_ccontext *c, const char *s); +void mrc_ccontext_free(mrc_ccontext *c); + +MRC_END_DECL + +#endif // MRC_CCONTEXT_H diff --git a/vendor/mruby-compiler2/include/mrc_cdump.h b/vendor/mruby-compiler2/include/mrc_cdump.h new file mode 100644 index 0000000..6d7c36d --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_cdump.h @@ -0,0 +1,14 @@ +#ifndef MRC_CDUMP_H +#define MRC_CDUMP_H + +#include "mrc_ccontext.h" +#include "mrc_irep.h" + +MRC_BEGIN_DECL + +MRC_END_DECL + +int mrc_dump_irep_cstruct(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, FILE *fp, const char *initname); + +#endif // MRC_CDUMP_H + diff --git a/vendor/mruby-compiler2/include/mrc_codedump.h b/vendor/mruby-compiler2/include/mrc_codedump.h new file mode 100644 index 0000000..9f7146f --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_codedump.h @@ -0,0 +1,17 @@ +#ifndef MRC_CODEDUMP_H +#define MRC_CODEDUMP_H + +#include "mrc_ccontext.h" + +MRC_BEGIN_DECL + +#ifndef MRC_NO_STDIO +void mrc_codedump_all_file(mrc_ccontext *c, mrc_irep *irep, FILE *out); +#endif + +void mrc_codedump_all(mrc_ccontext *c, mrc_irep *irep); + +MRC_END_DECL + +#endif // MRC_CODEDUMP_H + diff --git a/vendor/mruby-compiler2/include/mrc_codegen.h b/vendor/mruby-compiler2/include/mrc_codegen.h new file mode 100644 index 0000000..cf7de55 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_codegen.h @@ -0,0 +1,15 @@ +#ifndef MRC_CODEGEN_H +#define MRC_CODEGEN_H + +#include "mrc_common.h" +#include "mrc_ccontext.h" +#include "mrc_irep.h" + +MRC_BEGIN_DECL + +mrc_irep *mrc_generate_code(mrc_ccontext *c, mrc_node *node); + +MRC_END_DECL + +#endif // MRC_CODEGEN_H + diff --git a/vendor/mruby-compiler2/include/mrc_common.h b/vendor/mruby-compiler2/include/mrc_common.h new file mode 100644 index 0000000..7c17cd4 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_common.h @@ -0,0 +1,165 @@ +#ifndef MRC_COMMON_H +#define MRC_COMMON_H + +#include + +#define MRC_STRINGIZE0(expr) #expr +#define MRC_STRINGIZE(expr) MRC_STRINGIZE0(expr) + +#if defined(PICORB_VM_MRUBY) + #if !defined(MRC_TARGET_MRUBY) + #define MRC_TARGET_MRUBY + #endif + #include +#endif +#if defined(PICORB_VM_MRUBYC) + #if !defined(MRC_TARGET_MRUBYC) + #define MRC_TARGET_MRUBYC + #endif + #include + #define mrb_state void +#endif + +#if !defined(MRC_TARGET_MRUBY) && !defined(PICORB_VM_MRUBYC) + /* May be building mrbc (picorbc) */ + #define mrb_state void +#endif + +#if !defined(PRISM_XALLOCATOR) + #define PRISM_XALLOCATOR +#endif +#include "prism.h" + +#ifndef PICORUBY_VERSION + #define MRC_VERSION "unknown (standalone)" +#else + #define MRC_VERSION PICORUBY_VERSION +#endif + +#define MRC_RELEASE_YEAR 2026 +#define MRC_RELEASE_MONTH 1 +#define MRC_RELEASE_DAY 21 +#define MRC_RELEASE_DATE MRC_STRINGIZE(MRC_RELEASE_YEAR) "-" \ + MRC_STRINGIZE(MRC_RELEASE_MONTH) "-" \ + MRC_STRINGIZE(MRC_RELEASE_DAY) + +#ifdef MRB_USE_CXX_ABI +#define MRC_USE_CXX_ABI +#endif + +#ifdef __cplusplus + #ifdef MRC_USE_CXX_ABI + #define MRC_BEGIN_DECL + #define MRC_END_DECL + #else + #define MRC_BEGIN_DECL extern "C" { + #define MRC_END_DECL } + #endif +#else + /** Start declarations in C mode */ + # define MRC_BEGIN_DECL + /** End declarations in C mode */ + # define MRC_END_DECL +#endif + +/** Declare a public mruby API function. */ +#ifndef MRC_API +#if defined(MRC_BUILD_AS_DLL) +#if defined(MRC_CORE) || defined(MRC_LIB) +# define MRC_API __declspec(dllexport) +#else +# define MRC_API __declspec(dllimport) +#endif +#else +# define MRC_API extern +#endif +#endif + +#if defined(__cplusplus) || (defined(__bool_true_false_are_defined) && __bool_true_false_are_defined) +typedef bool mrc_bool; + +# ifndef FALSE +# define FALSE false +# endif +# ifndef TRUE +# define TRUE true +# endif +#else +# if __STDC_VERSION__ >= 199901L +typedef _Bool mrc_bool; +# else +typedef uint8_t mrc_bool; +# endif + +# ifndef FALSE +# define FALSE 0 +# endif +# ifndef TRUE +# define TRUE 1 +# endif +#endif + +#if !defined(MRC_INT32) +#define MRC_INT64 1 +#endif +#if !defined(MRC_32BIT) +#define MRC_64BIT 1 +#endif + +#if defined(MRC_INT64) + typedef int64_t mrc_int; + typedef uint64_t mrc_uint; + #define MRC_INT_BIT 64 + #define MRC_INT_MIN INT64_MIN + #define MRC_INT_MAX INT64_MAX + #define MRC_PRIo PRIo64 + #define MRC_PRId PRId64 + #define MRC_PRIx PRIx64 +#else + typedef int32_t mrc_int; + typedef uint32_t mrc_uint; + #define MRC_INT_BIT 32 + #define MRC_INT_MIN INT32_MIN + #define MRC_INT_MAX INT32_MAX + #define MRC_PRIo PRIo32 + #define MRC_PRId PRId32 + #define MRC_PRIx PRIx32 +#endif + +#ifdef MRB_NO_FLOAT +#define MRC_NO_FLOAT +#endif +#ifdef MRB_USE_FLOAT32 +#define MRC_USE_FLOAT32 +#endif + +#ifndef MRC_NO_FLOAT +#ifdef MRC_USE_FLOAT32 + typedef float mrc_float; +#else + typedef double mrc_float; +#endif +#endif + +typedef uint32_t mrc_sym; +typedef uint8_t mrc_code; + +/** + * \class mrb_aspec + * + * Specifies the number of arguments a function takes + * + * Example: `MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1)` for a method that expects 2..3 arguments + */ +typedef uint32_t mrc_aspec; + +#ifdef MRC_DEBUG + #include + #define mrc_assert(p) assert(p) + #define mrc_assert_int_fit(t1,n,t2,max) assert((n)>=0 && ((sizeof(n)<=sizeof(t2))||(n<=(t1)(max)))) +#else + #define mrc_assert(p) ((void)0) + #define mrc_assert_int_fit(t1,n,t2,max) ((void)0) +#endif + +#endif /* MRC_COMMON_H */ diff --git a/vendor/mruby-compiler2/include/mrc_compile.h b/vendor/mruby-compiler2/include/mrc_compile.h new file mode 100644 index 0000000..a896d0d --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_compile.h @@ -0,0 +1,14 @@ +#ifndef MRC_COMPILE_H +#define MRC_COMPILE_H + +#include "mrc_ccontext.h" +#include "mrc_irep.h" + +MRC_BEGIN_DECL + +mrc_irep *mrc_load_file_cxt(mrc_ccontext *c, const char **filenames, uint8_t **source); +mrc_irep *mrc_load_string_cxt(mrc_ccontext *c, const uint8_t **source, size_t length); + +MRC_END_DECL + +#endif /* MRC_COMPILE_H */ diff --git a/vendor/mruby-compiler2/include/mrc_debug.h b/vendor/mruby-compiler2/include/mrc_debug.h new file mode 100644 index 0000000..9b2533b --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_debug.h @@ -0,0 +1,78 @@ +/** +** @file mruby/debug.h - mruby debug info +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRC_DEBUG_H +#define MRC_DEBUG_H + +#include "mrc_ccontext.h" +#include "mrc_irep.h" + +/** + * mruby Debugging. + */ +MRC_BEGIN_DECL + +typedef enum mrc_debug_line_type { + mrc_debug_line_ary = 0, + mrc_debug_line_flat_map, + mrc_debug_line_packed_map +} mrc_debug_line_type; + +typedef struct mrc_irep_debug_info_line { + uint32_t start_pos; + uint16_t line; +} mrc_irep_debug_info_line; + +typedef struct mrc_irep_debug_info_file { + uint32_t start_pos; + mrc_sym filename_sym; + uint32_t line_entry_count; + mrc_debug_line_type line_type; + union { + const char *s; + void *ptr; + const uint16_t *ary; + const mrc_irep_debug_info_line *flat_map; + const uint8_t *packed_map; + } lines; +} mrc_irep_debug_info_file; + +typedef struct mrc_irep_debug_info { + uint32_t pc_count; + uint16_t flen; + mrc_irep_debug_info_file **files; +} mrc_irep_debug_info; + +/* + * get filename from irep's debug info and program counter + * @return returns NULL if not found + */ +const char *mrc_debug_get_filename(mrc_ccontext *c, const mrc_irep *irep, uint32_t pc); + +/* + * get line from irep's debug info and program counter + * @return returns -1 if not found + */ +int32_t mrc_debug_get_line(mrc_ccontext *c, const mrc_irep *irep, uint32_t pc); + +/* + * get line and filename from irep's debug info and program counter + * @return returns FALSE if not found + */ +mrc_bool mrc_debug_get_position(mrc_ccontext *c, const mrc_irep *irep, uint32_t pc, int32_t *lp, const char **fp); + +const char *mrc_debug_get_filename(mrc_ccontext *c, const mrc_irep *irep, uint32_t pc); +mrc_irep_debug_info *mrc_debug_info_alloc(mrc_ccontext *c, mrc_irep *irep); +mrc_irep_debug_info_file *mrc_debug_info_append_file( + mrc_ccontext *c, mrc_irep_debug_info *info, + const char *filename, uint16_t *lines, + uint32_t start_pos, uint32_t end_pos); +void mrc_debug_info_free(mrc_ccontext *c, mrc_irep_debug_info *d); + +MRC_END_DECL + +#endif /* MRC_DEBUG_H */ + diff --git a/vendor/mruby-compiler2/include/mrc_diagnostic.h b/vendor/mruby-compiler2/include/mrc_diagnostic.h new file mode 100644 index 0000000..fc48db6 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_diagnostic.h @@ -0,0 +1,30 @@ +#ifndef MRC_DIAGNOSTIC_H +#define MRC_DIAGNOSTIC_H + +#include "mrc_ccontext.h" + +MRC_BEGIN_DECL + +typedef enum { + MRC_PARSER_WARNING = 0, + MRC_PARSER_ERROR = 1, + MRC_GENERATOR_WARNING = 2, + MRC_GENERATOR_ERROR = 3, +} mrc_diagnostic_code; + +typedef struct mrc_diagnostic_list { + mrc_diagnostic_code code; + char *message; + uint32_t line; + uint32_t column; + struct mrc_diagnostic_list *next; +} mrc_diagnostic_list; + +struct mrc_ccontext; + +void mrc_diagnostic_list_append(struct mrc_ccontext *c, const uint8_t *location_start, const char *message, mrc_diagnostic_code code); +void mrc_diagnostic_list_free(struct mrc_ccontext *c); + +MRC_END_DECL + +#endif // MRC_DIAGNOSTIC_H diff --git a/vendor/mruby-compiler2/include/mrc_dump.h b/vendor/mruby-compiler2/include/mrc_dump.h new file mode 100644 index 0000000..7e64f8a --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_dump.h @@ -0,0 +1,154 @@ +#ifndef MRC_DUMP_H +#define MRC_DUMP_H + +#include "mrc_irep.h" +#include "mrc_ccontext.h" + +MRC_BEGIN_DECL + +#define MRC_DUMP_DEBUG_INFO 1 +#define MRC_DUMP_STATIC 2 + +#ifndef MRC_NO_STDIO +int mrc_dump_irep_cfunc(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, FILE *fp, const char *initname); +int mrc_dump_irep_binary(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, FILE* fp); +int mrc_dump_irep(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size); +#endif + +/* dump/load error code + * + * NOTE: MRC_DUMP_GENERAL_FAILURE is caused by + * unspecified issues like malloc failed. + */ +#define MRC_DUMP_OK 0 +#define MRC_DUMP_GENERAL_FAILURE (-1) +#define MRC_DUMP_WRITE_FAULT (-2) +#define MRC_DUMP_READ_FAULT (-3) +#define MRC_DUMP_INVALID_FILE_HEADER (-4) +#define MRC_DUMP_INVALID_IREP (-5) +#define MRC_DUMP_INVALID_ARGUMENT (-6) + +/* null symbol length */ +#define MRC_DUMP_NULL_SYM_LEN 0xFFFF + +/* Rite Binary File header */ +#define RITE_BINARY_IDENT "RITE" +/* Binary Format Version Major:Minor */ +/* Major: Incompatible to prior versions */ +/* Minor: Upper-compatible to prior versions */ +#define RITE_BINARY_MAJOR_VER "03" +#define RITE_BINARY_MINOR_VER "00" +#define RITE_BINARY_FORMAT_VER RITE_BINARY_MAJOR_VER RITE_BINARY_MINOR_VER +#if defined(RITE_COMPILER_NAME) +#undef RITE_COMPILER_NAME +#endif +#define RITE_COMPILER_NAME "HSMK" +#define RITE_PARSER_NAME "Prism" +#define RITE_COMPILER_VERSION "0000" + +#define RITE_VM_VER "0300" + +#define RITE_BINARY_EOF "END\0" +#define RITE_SECTION_IREP_IDENT "IREP" +#define RITE_SECTION_DEBUG_IDENT "DBG\0" +#define RITE_SECTION_LV_IDENT "LVAR" + +#define MRC_DUMP_DEFAULT_STR_LEN 128 +#define MRC_DUMP_ALIGNMENT sizeof(uint32_t) + +/* binary header */ +struct rite_binary_header { + uint8_t binary_ident[4]; /* Binary Identifier */ + uint8_t major_version[2]; /* Binary Format Major Version */ + uint8_t minor_version[2]; /* Binary Format Minor Version */ + uint8_t binary_size[4]; /* Binary Size */ + uint8_t compiler_name[4]; /* Compiler name */ + uint8_t compiler_version[4]; +}; + +/* section header */ +#define RITE_SECTION_HEADER \ + uint8_t section_ident[4]; \ + uint8_t section_size[4] + +struct rite_section_header { + RITE_SECTION_HEADER; +}; + +struct rite_section_irep_header { + RITE_SECTION_HEADER; + + uint8_t rite_version[4]; /* Rite Instruction Specification Version */ +}; + +struct rite_section_debug_header { + RITE_SECTION_HEADER; +}; + +struct rite_section_lv_header { + RITE_SECTION_HEADER; +}; + +#define RITE_LV_NULL_MARK UINT16_MAX + +struct rite_binary_footer { + RITE_SECTION_HEADER; +}; + +static inline size_t +mrc_uint8_to_bin(uint8_t s, uint8_t *bin) +{ + *bin = s; + return sizeof(uint8_t); +} + +static inline size_t +mrc_uint16_to_bin(uint16_t s, uint8_t *bin) +{ + *bin++ = (s >> 8) & 0xff; + *bin = s & 0xff; + return sizeof(uint16_t); +} + +static inline size_t +mrc_uint32_to_bin(uint32_t l, uint8_t *bin) +{ + *bin++ = (l >> 24) & 0xff; + *bin++ = (l >> 16) & 0xff; + *bin++ = (l >> 8) & 0xff; + *bin = l & 0xff; + return sizeof(uint32_t); +} + +static inline uint32_t +mrc_bin_to_uint32(const uint8_t *bin) +{ + return (uint32_t)bin[0] << 24 | + (uint32_t)bin[1] << 16 | + (uint32_t)bin[2] << 8 | + (uint32_t)bin[3]; +} + +static inline uint16_t +mrc_bin_to_uint16(const uint8_t *bin) +{ + return (uint16_t)bin[0] << 8 | + (uint16_t)bin[1]; +} + +static inline uint8_t +mrc_bin_to_uint8(const uint8_t *bin) +{ + return (uint8_t)bin[0]; +} + +static inline const char* +mrc_description(void) +{ + return MRC_VERSION " (" MRC_RELEASE_DATE ") Parser: " RITE_PARSER_NAME ", RITE: " RITE_BINARY_FORMAT_VER; +} + +MRC_END_DECL + +#endif // MRC_DUMP_H + diff --git a/vendor/mruby-compiler2/include/mrc_endian.h b/vendor/mruby-compiler2/include/mrc_endian.h new file mode 100644 index 0000000..a30e720 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_endian.h @@ -0,0 +1,45 @@ +/** +** @file mruby/endian.h - detect endian-ness +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRC_ENDIAN_H +#define MRC_ENDIAN_H + +#include + +MRC_BEGIN_DECL + +#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER__) +# define BYTE_ORDER __BYTE_ORDER__ +#endif +#if !defined(BIG_ENDIAN) && defined(__ORDER_BIG_ENDIAN__) +# define BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#endif +#if !defined(LITTLE_ENDIAN) && defined(__ORDER_LITTLE_ENDIAN__) +# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#endif + +#ifdef BYTE_ORDER +# if BYTE_ORDER == BIG_ENDIAN +# define littleendian 0 +# elif BYTE_ORDER == LITTLE_ENDIAN +# define littleendian 1 +# endif +#endif +#ifndef littleendian +/* can't distinguish endian in compile time */ +static inline int +check_little_endian(void) +{ + unsigned int n = 1; + return (*(unsigned char*)&n == 1); +} +# define littleendian check_little_endian() +#endif + +MRC_END_DECL + +#endif /* MRC_ENDIAN_H */ + diff --git a/vendor/mruby-compiler2/include/mrc_irep.h b/vendor/mruby-compiler2/include/mrc_irep.h new file mode 100644 index 0000000..3ae3ea8 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_irep.h @@ -0,0 +1,98 @@ +/** +** @file mruby/irep.h - mrc_irep structure +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRC_IREP_H +#define MRC_IREP_H + +#include "mrc_ccontext.h" + +/** + * Compiled mruby scripts. + */ +MRC_BEGIN_DECL + +#define IREP_TT_NFLAG 1 /* number (non string) flag */ +#define IREP_TT_SFLAG 2 /* static string flag */ + +typedef struct mrc_pool_value { + uint32_t tt; /* packed type and length (for string) */ + union { + const char *str; + int32_t i32; + int64_t i64; +#ifndef MRC_NO_FLOAT + mrc_float f; +#endif + } u; +} mrc_pool_value; + +enum mrc_catch_type { + MRC_CATCH_RESCUE = 0, + MRC_CATCH_ENSURE = 1, +}; + +struct mrc_irep_catch_handler { + uint8_t type; /* enum mrc_catch_type */ + uint8_t begin[4]; /* The starting address to match the handler. Includes this. */ + uint8_t end[4]; /* The endpoint address that matches the handler. Not Includes this. */ + uint8_t target[4]; /* The address to jump to if a match is made. */ +}; + +/* Program data array struct */ +typedef struct mrc_irep { + uint16_t nlocals; /* Number of local variables */ + uint16_t nregs; /* Number of register variables */ + uint16_t clen; /* Number of catch handlers */ + uint8_t flags; + + const mrc_code *iseq; + /* + * A catch handler table is placed after the iseq entity. + * The reason it doesn't add fields to the structure is to keep the mrc_irep structure from bloating. + * The catch handler table can be obtained with `mrc_irep_catch_handler_table(irep)`. + */ + const mrc_pool_value *pool; + const mrc_sym *syms; + const struct mrc_irep * const *reps; + + mrc_sym *lv; // Remove const for mrc_resolve_intern + /* debug info */ + struct mrc_irep_debug_info* debug_info; + + uint32_t ilen; + uint16_t plen, slen; + uint16_t rlen; + uint16_t refcnt; +} mrc_irep; + +#define MRC_ISEQ_NO_FREE 1 +#define MRC_IREP_NO_FREE 2 + +struct mrc_insn_data { + uint8_t insn; + uint32_t a; + uint16_t b; + uint16_t cc; + const mrc_code *addr; +}; + +#define mrc_irep_catch_handler_pack(n, v) mrc_uint32_to_bin(n, v) +#define mrc_irep_catch_handler_unpack(v) mrc_bin_to_uint32(v) + +void mrc_irep_remove_lv(mrc_ccontext *c, mrc_irep *irep); +void mrc_irep_free(mrc_ccontext *c, mrc_irep *irep); + +#define MRC_ASPEC_REQ(a) (((a) >> 18) & 0x1f) +#define MRC_ASPEC_OPT(a) (((a) >> 13) & 0x1f) +#define MRC_ASPEC_REST(a) (((a) >> 12) & 0x1) +#define MRC_ASPEC_POST(a) (((a) >> 7) & 0x1f) +#define MRC_ASPEC_KEY(a) (((a) >> 2) & 0x1f) +#define MRC_ASPEC_KDICT(a) (((a) >> 1) & 0x1) +#define MRC_ASPEC_BLOCK(a) ((a) & 1) + +MRC_END_DECL + +#endif /* MRC_IREP_H */ diff --git a/vendor/mruby-compiler2/include/mrc_irep_pool_type.h b/vendor/mruby-compiler2/include/mrc_irep_pool_type.h new file mode 100644 index 0000000..c0d861a --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_irep_pool_type.h @@ -0,0 +1,17 @@ +#ifndef MRB_IREP_POOL_TYPE_H +#define MRB_IREP_POOL_TYPE_H + +MRC_BEGIN_DECL + +enum irep_pool_type { + IREP_TT_STR = 0, /* string (need free) */ + IREP_TT_SSTR = 2, /* string (static) */ + IREP_TT_INT32 = 1, /* 32bit integer */ + IREP_TT_INT64 = 3, /* 64bit integer */ + IREP_TT_BIGINT = 7, /* big integer (not yet supported) */ + IREP_TT_FLOAT = 5, /* float (double/float) */ +}; + +MRC_END_DECL + +#endif diff --git a/vendor/mruby-compiler2/include/mrc_opcode.h b/vendor/mruby-compiler2/include/mrc_opcode.h new file mode 100644 index 0000000..bfcee66 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_opcode.h @@ -0,0 +1,69 @@ +/** +** @file mruby/opcode.h - RiteVM operation codes +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRUBY_OPCODE_H +#define MRUBY_OPCODE_H + +enum mrb_insn { +#define OPCODE(x,_) OP_ ## x, +#include "mrc_ops.h" +#undef OPCODE +}; + +#define OP_L_STRICT 1 +#define OP_L_CAPTURE 2 +#define OP_L_METHOD OP_L_STRICT +#define OP_L_LAMBDA (OP_L_STRICT|OP_L_CAPTURE) +#define OP_L_BLOCK OP_L_CAPTURE + +#define PEEK_B(pc) (*(pc)) +#define PEEK_S(pc) ((pc)[0]<<8|(pc)[1]) +#define PEEK_W(pc) ((pc)[0]<<16|(pc)[1]<<8|(pc)[2]) + +#define READ_B() PEEK_B(pc++) +#define READ_S() (pc+=2, PEEK_S(pc-2)) +#define READ_W() (pc+=3, PEEK_W(pc-3)) + +#define FETCH_Z() /* nothing */ +#define FETCH_B() do {a=READ_B();} while (0) +#define FETCH_BB() do {a=READ_B(); b=READ_B();} while (0) +#define FETCH_BBB() do {a=READ_B(); b=READ_B(); cc=READ_B();} while (0) +#define FETCH_BS() do {a=READ_B(); b=READ_S();} while (0) +#define FETCH_BSS() do {a=READ_B(); b=READ_S(); cc=READ_S();} while (0) +#define FETCH_S() do {a=READ_S();} while (0) +#define FETCH_W() do {a=READ_W();} while (0) + +/* with OP_EXT1 (1st 16bit) */ +#define FETCH_Z_1() FETCH_Z() +#define FETCH_B_1() FETCH_S() +#define FETCH_BB_1() do {a=READ_S(); b=READ_B();} while (0) +#define FETCH_BBB_1() do {a=READ_S(); b=READ_B(); cc=READ_B();} while (0) +#define FETCH_BS_1() do {a=READ_S(); b=READ_S();} while (0) +#define FETCH_BSS_1() do {a=READ_S(); b=READ_S();cc=READ_S();} while (0) +#define FETCH_S_1() FETCH_S() +#define FETCH_W_1() FETCH_W() + +/* with OP_EXT2 (2nd 16bit) */ +#define FETCH_Z_2() FETCH_Z() +#define FETCH_B_2() FETCH_B() +#define FETCH_BB_2() do {a=READ_B(); b=READ_S();} while (0) +#define FETCH_BBB_2() do {a=READ_B(); b=READ_S(); cc=READ_B();} while (0) +#define FETCH_BS_2() FETCH_BS() +#define FETCH_BSS_2() FETCH_BSS() +#define FETCH_S_2() FETCH_S() +#define FETCH_W_2() FETCH_W() + +/* with OP_EXT3 (1st & 2nd 16bit) */ +#define FETCH_Z_3() FETCH_Z() +#define FETCH_B_3() FETCH_B() +#define FETCH_BB_3() do {a=READ_S(); b=READ_S();} while (0) +#define FETCH_BBB_3() do {a=READ_S(); b=READ_S(); cc=READ_B();} while (0) +#define FETCH_BS_3() do {a=READ_S(); b=READ_S();} while (0) +#define FETCH_BSS_3() FETCH_BSS_1() +#define FETCH_S_3() FETCH_S() +#define FETCH_W_3() FETCH_W() + +#endif /* MRUBY_OPCODE_H */ diff --git a/vendor/mruby-compiler2/include/mrc_ops.h b/vendor/mruby-compiler2/include/mrc_ops.h new file mode 100644 index 0000000..cda796f --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_ops.h @@ -0,0 +1,120 @@ +/* operand types: + + Z: no operand + + B: 8bit + + BB: 8+8bit + + BBB: 8+8+8bit + + BS: 8+16bit + + BSS: 8+16+16bit + + S: 16bit + + W: 24bit +*/ + +/*----------------------------------------------------------------------- +operation code operands semantics +------------------------------------------------------------------------*/ +OPCODE(NOP, Z) /* no operation */ +OPCODE(MOVE, BB) /* R[a] = R[b] */ +OPCODE(LOADL, BB) /* R[a] = Pool[b] */ +OPCODE(LOADI, BB) /* R[a] = mrb_int(b) */ +OPCODE(LOADINEG, BB) /* R[a] = mrb_int(-b) */ +OPCODE(LOADI__1, B) /* R[a] = mrb_int(-1) */ +OPCODE(LOADI_0, B) /* R[a] = mrb_int(0) */ +OPCODE(LOADI_1, B) /* R[a] = mrb_int(1) */ +OPCODE(LOADI_2, B) /* R[a] = mrb_int(2) */ +OPCODE(LOADI_3, B) /* R[a] = mrb_int(3) */ +OPCODE(LOADI_4, B) /* R[a] = mrb_int(4) */ +OPCODE(LOADI_5, B) /* R[a] = mrb_int(5) */ +OPCODE(LOADI_6, B) /* R[a] = mrb_int(6) */ +OPCODE(LOADI_7, B) /* R[a] = mrb_int(7) */ +OPCODE(LOADI16, BS) /* R[a] = mrb_int(b) */ +OPCODE(LOADI32, BSS) /* R[a] = mrb_int((b<<16)+c) */ +OPCODE(LOADSYM, BB) /* R[a] = Syms[b] */ +OPCODE(LOADNIL, B) /* R[a] = nil */ +OPCODE(LOADSELF, B) /* R[a] = self */ +OPCODE(LOADT, B) /* R[a] = true */ +OPCODE(LOADF, B) /* R[a] = false */ +OPCODE(GETGV, BB) /* R[a] = getglobal(Syms[b]) */ +OPCODE(SETGV, BB) /* setglobal(Syms[b], R[a]) */ +OPCODE(GETSV, BB) /* R[a] = Special[Syms[b]] */ +OPCODE(SETSV, BB) /* Special[Syms[b]] = R[a] */ +OPCODE(GETIV, BB) /* R[a] = ivget(Syms[b]) */ +OPCODE(SETIV, BB) /* ivset(Syms[b],R[a]) */ +OPCODE(GETCV, BB) /* R[a] = cvget(Syms[b]) */ +OPCODE(SETCV, BB) /* cvset(Syms[b],R[a]) */ +OPCODE(GETCONST, BB) /* R[a] = constget(Syms[b]) */ +OPCODE(SETCONST, BB) /* constset(Syms[b],R[a]) */ +OPCODE(GETMCNST, BB) /* R[a] = R[a]::Syms[b] */ +OPCODE(SETMCNST, BB) /* R[a+1]::Syms[b] = R[a] */ +OPCODE(GETUPVAR, BBB) /* R[a] = uvget(b,c) */ +OPCODE(SETUPVAR, BBB) /* uvset(b,c,R[a]) */ +OPCODE(GETIDX, B) /* R[a] = R[a][R[a+1]] */ +OPCODE(SETIDX, B) /* R[a][R[a+1]] = R[a+2] */ +OPCODE(JMP, S) /* pc+=a */ +OPCODE(JMPIF, BS) /* if R[a] pc+=b */ +OPCODE(JMPNOT, BS) /* if !R[a] pc+=b */ +OPCODE(JMPNIL, BS) /* if R[a]==nil pc+=b */ +OPCODE(JMPUW, S) /* unwind_and_jump_to(a) */ +OPCODE(EXCEPT, B) /* R[a] = exc */ +OPCODE(RESCUE, BB) /* R[b] = R[a].isa?(R[b]) */ +OPCODE(RAISEIF, B) /* raise(R[a]) if R[a] */ +OPCODE(SSEND, BBB) /* R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n|k<<4) */ +OPCODE(SSENDB, BBB) /* R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..,&R[a+n+2k+1]) */ +OPCODE(SEND, BBB) /* R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n|k<<4) */ +OPCODE(SENDB, BBB) /* R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..,&R[a+n+2k+1]) */ +OPCODE(CALL, Z) /* self.call(*, **, &) (But overlay the current call frame; tailcall) */ +OPCODE(SUPER, BB) /* R[a] = super(R[a+1],... ,R[a+b+1]) */ +OPCODE(ARGARY, BS) /* R[a] = argument array (16=m5:r1:m5:d1:lv4) */ +OPCODE(ENTER, W) /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */ +OPCODE(KEY_P, BB) /* R[a] = kdict.key?(Syms[b]) */ +OPCODE(KEYEND, Z) /* raise unless kdict.empty? */ +OPCODE(KARG, BB) /* R[a] = kdict[Syms[b]]; kdict.delete(Syms[b]) */ +OPCODE(RETURN, B) /* return R[a] (normal) */ +OPCODE(RETURN_BLK, B) /* return R[a] (in-block return) */ +OPCODE(BREAK, B) /* break R[a] */ +OPCODE(BLKPUSH, BS) /* R[a] = block (16=m5:r1:m5:d1:lv4) */ +OPCODE(ADD, B) /* R[a] = R[a]+R[a+1] */ +OPCODE(ADDI, BB) /* R[a] = R[a]+mrb_int(b) */ +OPCODE(SUB, B) /* R[a] = R[a]-R[a+1] */ +OPCODE(SUBI, BB) /* R[a] = R[a]-mrb_int(b) */ +OPCODE(MUL, B) /* R[a] = R[a]*R[a+1] */ +OPCODE(DIV, B) /* R[a] = R[a]/R[a+1] */ +OPCODE(EQ, B) /* R[a] = R[a]==R[a+1] */ +OPCODE(LT, B) /* R[a] = R[a]R[a+1] */ +OPCODE(GE, B) /* R[a] = R[a]>=R[a+1] */ +OPCODE(ARRAY, BB) /* R[a] = ary_new(R[a],R[a+1]..R[a+b]) */ +OPCODE(ARRAY2, BBB) /* R[a] = ary_new(R[b],R[b+1]..R[b+c]) */ +OPCODE(ARYCAT, B) /* ary_cat(R[a],R[a+1]) */ +OPCODE(ARYPUSH, BB) /* ary_push(R[a],R[a+1]..R[a+b]) */ +OPCODE(ARYSPLAT, B) /* R[a] = ary_splat(R[a]) */ +OPCODE(AREF, BBB) /* R[a] = R[b][c] */ +OPCODE(ASET, BBB) /* R[b][c] = R[a] */ +OPCODE(APOST, BBB) /* *R[a],R[a+1]..R[a+c] = R[a][b..] */ +OPCODE(INTERN, B) /* R[a] = intern(R[a]) */ +OPCODE(SYMBOL, BB) /* R[a] = intern(Pool[b]) */ +OPCODE(STRING, BB) /* R[a] = str_dup(Pool[b]) */ +OPCODE(STRCAT, B) /* str_cat(R[a],R[a+1]) */ +OPCODE(HASH, BB) /* R[a] = hash_new(R[a],R[a+1]..R[a+b*2-1]) */ +OPCODE(HASHADD, BB) /* hash_push(R[a],R[a+1]..R[a+b*2]) */ +OPCODE(HASHCAT, B) /* R[a] = hash_cat(R[a],R[a+1]) */ +OPCODE(LAMBDA, BB) /* R[a] = lambda(Irep[b],L_LAMBDA) */ +OPCODE(BLOCK, BB) /* R[a] = lambda(Irep[b],L_BLOCK) */ +OPCODE(METHOD, BB) /* R[a] = lambda(Irep[b],L_METHOD) */ +OPCODE(RANGE_INC, B) /* R[a] = range_new(R[a],R[a+1],FALSE) */ +OPCODE(RANGE_EXC, B) /* R[a] = range_new(R[a],R[a+1],TRUE) */ +OPCODE(OCLASS, B) /* R[a] = ::Object */ +OPCODE(CLASS, BB) /* R[a] = newclass(R[a],Syms[b],R[a+1]) */ +OPCODE(MODULE, BB) /* R[a] = newmodule(R[a],Syms[b]) */ +OPCODE(EXEC, BB) /* R[a] = blockexec(R[a],Irep[b]) */ +OPCODE(DEF, BB) /* R[a].newmethod(Syms[b],R[a+1]); R[a] = Syms[b] */ +OPCODE(ALIAS, BB) /* alias_method(target_class,Syms[a],Syms[b]) */ +OPCODE(UNDEF, B) /* undef_method(target_class,Syms[a]) */ +OPCODE(SCLASS, B) /* R[a] = R[a].singleton_class */ +OPCODE(TCLASS, B) /* R[a] = target_class */ +OPCODE(DEBUG, BBB) /* print a,b,c */ +OPCODE(ERR, B) /* raise(LocalJumpError, Pool[a]) */ +OPCODE(EXT1, Z) /* make 1st operand (a) 16bit */ +OPCODE(EXT2, Z) /* make 2nd operand (b) 16bit */ +OPCODE(EXT3, Z) /* make 1st and 2nd operands 16bit */ +OPCODE(STOP, Z) /* stop VM */ diff --git a/vendor/mruby-compiler2/include/mrc_parser_util.h b/vendor/mruby-compiler2/include/mrc_parser_util.h new file mode 100644 index 0000000..b24015d --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_parser_util.h @@ -0,0 +1,12 @@ +#ifndef MRC_PARSER_UTIL_H +#define MRC_PARSER_UTIL_H + +#include "mrc_ccontext.h" + +MRC_BEGIN_DECL + +const char* mrc_sym_name_len(mrc_ccontext *c, mrc_sym sym, mrc_int *lenp); + +MRC_END_DECL + +#endif // MRC_PARSER_UTIL_H diff --git a/vendor/mruby-compiler2/include/mrc_pool.h b/vendor/mruby-compiler2/include/mrc_pool.h new file mode 100644 index 0000000..b093b43 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_pool.h @@ -0,0 +1,23 @@ +#ifndef MRC_POOL_H +#define MRC_POOL_H + +#include +#include "mrc_ccontext.h" + +MRC_BEGIN_DECL + +typedef struct mrc_pool { + struct mrc_ccontext *c; + struct mrc_pool_page *pages; +} mrc_pool; + + +MRC_API mrc_pool *mrc_pool_open(struct mrc_ccontext *c); +MRC_API void mrc_pool_close(mrc_pool *pool); +MRC_API void *mrc_pool_alloc(mrc_pool *pool, size_t len); +MRC_API void *mrc_pool_realloc(mrc_pool *pool, void *p, size_t oldlen, size_t newlen); + +MRC_END_DECL + +#endif // MRC_POOL_H + diff --git a/vendor/mruby-compiler2/include/mrc_presym.h b/vendor/mruby-compiler2/include/mrc_presym.h new file mode 100644 index 0000000..62aed84 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_presym.h @@ -0,0 +1,26 @@ +#ifndef MRC_PRESYM_H +#define MRC_PRESYM_H + +MRC_BEGIN_DECL + +enum mrc_opsym { +#define MRC_OPSYM_2(name, lit, num) MRC_OPSYM_2__##name = num, +#define MRC_SYM_1(name, num) MRC_SYM_1__##name = num, +#define MRC_SYM_2(name, lit, num) MRC_SYM_2__##name = num, +#include "mrc_presym.inc" +#undef MRC_OPSYM_2 +#undef MRC_SYM_1 +#undef MRC_SYM_2 +}; + +#define MRC_OPSYM_2(name) mrc_sym_offset(MRC_OPSYM_2__##name) +#define MRC_SYM_1(name) mrc_sym_offset(MRC_SYM_1__##name) +#define MRC_SYM_2(name) mrc_sym_offset(MRC_SYM_2__##name) + +void mrc_init_presym(pm_constant_pool_t *pool); +mrc_sym mrc_sym_offset(mrc_sym sym); + +MRC_END_DECL + +#endif // MRC_PRESYM_H + diff --git a/vendor/mruby-compiler2/include/mrc_presym.inc b/vendor/mruby-compiler2/include/mrc_presym.inc new file mode 100644 index 0000000..e204890 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_presym.inc @@ -0,0 +1,33 @@ + +MRC_OPSYM_2(aref, [], 1) +MRC_OPSYM_2(lshift, <<, 2) +MRC_OPSYM_2(rshift, >>, 3) +MRC_OPSYM_2(mod, %, 4) +MRC_OPSYM_2(and, &, 5) +MRC_OPSYM_2(or, |, 6) +MRC_OPSYM_2(xor, ^, 7) +MRC_OPSYM_2(neg, ~, 8) +MRC_OPSYM_2(pow, **, 9) +MRC_OPSYM_2(add, +, 10) +MRC_OPSYM_2(sub, -, 11) +MRC_OPSYM_2(mul, *, 12) +MRC_OPSYM_2(div, /, 13) +MRC_OPSYM_2(lt, <, 14) +MRC_OPSYM_2(le, <=, 15) +MRC_OPSYM_2(gt, >, 16) +MRC_OPSYM_2(ge, >=, 17) +MRC_OPSYM_2(eq, ==, 18) +MRC_OPSYM_2(aset, []=, 19) +MRC_OPSYM_2(eqq, ===, 20) +MRC_OPSYM_2(tick, `, 21) +MRC_SYM_1(each, 22) +MRC_SYM_1(__case_eqq, 23) +MRC_SYM_1(StandardError, 24) +MRC_SYM_1(call, 25) +MRC_SYM_1(Kernel, 26) +MRC_SYM_1(Regexp, 27) +MRC_SYM_1(compile, 28) +MRC_SYM_1(__ENCODING__, 29) +MRC_SYM_2(nil_p, nil?,30) +MRC_SYM_2(back_ref, $+, 31) +MRC_SYM_2(defined_p, defined?, 32) diff --git a/vendor/mruby-compiler2/include/mrc_proc.h b/vendor/mruby-compiler2/include/mrc_proc.h new file mode 100644 index 0000000..61aa263 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_proc.h @@ -0,0 +1,30 @@ +#ifndef MRC_PROC_H +#define MRC_PROC_H + +MRC_BEGIN_DECL + +#define MRC_OBJECT_HEADER \ + struct RClass *c; \ + struct RBasic *gcnext; \ + enum mrb_vtype tt:8; \ + unsigned int gc_color:3; \ + unsigned int frozen:1; \ + uint32_t flags:20 + +struct RProc { + MRC_OBJECT_HEADER; + union { + const mrc_irep *irep; + mrb_func_t func; + mrb_sym mid; + } body; + const struct RProc *upper; + union { + struct RClass *target_class; + struct REnv *env; + } e; +}; + +MRC_END_DECL + +#endif // MRC_PROC_H diff --git a/vendor/mruby-compiler2/include/mrc_throw.h b/vendor/mruby-compiler2/include/mrc_throw.h new file mode 100644 index 0000000..73dbd49 --- /dev/null +++ b/vendor/mruby-compiler2/include/mrc_throw.h @@ -0,0 +1,57 @@ +/** +** @file mruby/throw.h - mruby exception throwing handler +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRC_THROW_H +#define MRC_THROW_H + +#if defined(MRC_USE_CXX_ABI) && !defined(__cplusplus) +# error Trying to use C++ exception handling in C code +#endif + +#if defined(MRC_USE_CXX_EXCEPTION) + +# if defined(__cplusplus) + +#define MRC_TRY(buf) try { +#define MRC_CATCH(buf) } catch(mrc_jmpbuf *e) { if (e != (buf)) { throw e; } +#define MRC_END_EXC(buf) } + +#define MRC_THROW(buf) throw(buf) +typedef void *mrc_jmpbuf_impl; + +# else +# error "need to be compiled with C++ compiler" +# endif /* __cplusplus */ + +#else + +#include + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#define MRC_SETJMP _setjmp +#define MRC_LONGJMP _longjmp +#elif defined(__MINGW64__) && defined(__GNUC__) && __GNUC__ >= 4 +#define MRC_SETJMP __builtin_setjmp +#define MRC_LONGJMP __builtin_longjmp +#else +#define MRC_SETJMP setjmp +#define MRC_LONGJMP longjmp +#endif + +#define MRC_TRY(buf) if (MRC_SETJMP((buf)->impl) == 0) { +#define MRC_CATCH(buf) } else { +#define MRC_END_EXC(buf) } + +#define MRC_THROW(buf) MRC_LONGJMP((buf)->impl, 1); +#define mrc_jmpbuf_impl jmp_buf + +#endif + +struct mrc_jmpbuf { + mrc_jmpbuf_impl impl; +}; + +#endif /* MRC_THROW_H */ diff --git a/vendor/mruby-compiler2/include/mruby_compiler.h b/vendor/mruby-compiler2/include/mruby_compiler.h new file mode 100644 index 0000000..d6841ff --- /dev/null +++ b/vendor/mruby-compiler2/include/mruby_compiler.h @@ -0,0 +1,9 @@ +#ifndef MRUBY_COMPILER_H +#define MRUBY_COMPILER_H + +#include "mrc_ccontext.h" +#include "mrc_irep.h" +#include "mrc_compile.h" +#include "mrc_dump.h" + +#endif // MRUBY_COMPILER_H diff --git a/vendor/mruby-compiler2/include/prism_xallocator.h b/vendor/mruby-compiler2/include/prism_xallocator.h new file mode 100644 index 0000000..a387ec0 --- /dev/null +++ b/vendor/mruby-compiler2/include/prism_xallocator.h @@ -0,0 +1,73 @@ +#ifndef PRISM_CUSTOM_ALLOCATOR_H +#define PRISM_CUSTOM_ALLOCATOR_H + +#if defined(MRC_TARGET_MRUBY) + #include "mruby.h" + extern mrb_state *global_mrb; + + #define xmalloc(size) mrb_malloc(global_mrb, size) + #define xcalloc(nmemb,size) mrb_calloc(global_mrb, nmemb, size) + #define xrealloc(ptr,size) mrb_realloc(global_mrb, ptr, size) + #define xfree(ptr) mrb_free(global_mrb, ptr) + + #define mrc_malloc(c,size) mrb_malloc(c->mrb, size) + #define mrc_calloc(c,nmemb,size) mrb_calloc(c->mrb, nmemb, size) + #define mrc_realloc(c,ptr,size) mrb_realloc(c->mrb, ptr, size) + #define mrc_free(c,ptr) mrb_free(c->mrb, ptr) +#elif defined(MRC_TARGET_MRUBYC) + #include "mrubyc.h" + #if defined(MRBC_ALLOC_LIBC) + #define xmalloc(size) malloc(size) + #define xcalloc(nmemb,size) calloc(nmemb, size) + #define xrealloc(nmemb,size) realloc(nmemb, size) + #define xfree(ptr) free(ptr) + + #define mrc_malloc(c,size) malloc(size) + #define mrc_calloc(c,nmemb,size) calloc(nmemb, size) + #define mrc_realloc(c,ptr,size) realloc(ptr, size) + #define mrc_free(c,ptr) free(ptr) + #else + #define xmalloc(size) mrbc_raw_alloc(size) + #define xcalloc(nmemb,size) mrbc_raw_calloc(nmemb, size) + #define xrealloc(nmemb,size) mrc_raw_realloc(nmemb, size) + #define xfree(ptr) mrc_raw_free(ptr) + + #define mrc_malloc(c,size) mrbc_raw_alloc(size) + #define mrc_calloc(c,nmemb,size) mrbc_raw_calloc(nmemb, size) + #define mrc_realloc(c,ptr,size) mrc_raw_realloc(ptr, size) + #define mrc_free(c,ptr) mrc_raw_free(ptr) + + static inline void mrc_raw_free(void *ptr) + { + /* mrbc_raw_free() warns when ptr=NULL but it should be allowed in C99 */ + if (ptr == NULL) return; + mrbc_raw_free(ptr); + } + + static inline void* + mrc_raw_realloc(void *ptr, unsigned int size) + { + /* mrbc_raw_realloc() fails when ptr=NULL but it should be allowed in C99 */ + if (ptr == NULL) { + return mrbc_raw_alloc(size); + } else { + return mrbc_raw_realloc(ptr, size); + } + } + #endif +#else + + // for picorbc + #define mrc_malloc(c,size) malloc(size) + #define mrc_calloc(c,nmemb,size) calloc(nmemb, size) + #define mrc_realloc(c,ptr,size) realloc(ptr, size) + #define mrc_free(c,ptr) free(ptr) + #define xmalloc(size) malloc(size) + #define xcalloc(nmemb,size) calloc(nmemb, size) + #define xrealloc(ptr,size) realloc(ptr, size) + #define xfree(ptr) free(ptr) + +#endif + +#endif + diff --git a/vendor/mruby-compiler2/lib/prism/CHANGELOG.md b/vendor/mruby-compiler2/lib/prism/CHANGELOG.md new file mode 100644 index 0000000..aa17d50 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/CHANGELOG.md @@ -0,0 +1,684 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.4.0] - 2025-03-18 + +### Added + +- Support `3.5` as a version option. +- Many, many compatibility fixes for the parser translation layer. +- Handle escapes in named capture names. +- The `freeze` option is added to the various `Prism::` APIs to deeply freeze the AST. +- Properly support `it` for the parser and ruby_parser translation layers. +- Track the `then` keyword on `rescue` nodes. +- Add a `multiple_statements?` flag to parentheses nodes to support desired `defined?` behavior. + +### Changed + +- The strings used in the AST are now frozen. +- Fixed handling escaped characters after control sequences in character literals. +- Fix reading off the end of an unterminated global variable. +- Raise a syntax error for defining `[]=` with endless method syntax. +- Increase value of `PRISM_DEPTH_MAXIMUM` to `10000`. +- Freeze `Prism::VERSION`. +- Fix up rescue modifier precedence. + +## [1.3.0] - 2024-12-21 + +### Added + +- Introduce `Prism::StringQuery`. +- Introduce `Prism::Relocation`. +- Track `do` keyword for `WhileNode` and `UntilNode`. +- Change the way the gem is built to rely on `mkmf` instead of `make`. +- Lots more documentation on node fields. + +### Changed + +- Properly add an error for `def @foo; end`. +- Properly add an error for `foo(**, *)`. +- Fix up regression in string parsing in `RubyParser` translation. +- Reject invalid dot method call after match expression. +- Reject invalid operator after match expression. +- Fix up %-literals delimited by newlines. +- Properly add an error for `-> { _1; -> { _1 } }`. +- Reject blocks and keywords in index writes. + +## [1.2.0] - 2024-10-10 + +### Added + +- Introduce `Prism::CodeUnitsCache`. + +### Changed + +- Properly handle lexing global variables that begin with `$-`. +- Properly reject invalid multi writes within parentheses. +- Fix unary `*` binding power. +- Set `contains_keywords` flag for implicit `gets` calls when `-p` is used. +- Properly reject invalid non-associative operator patterns. +- Do not warn about unused variables declared on negative lines. + +## [1.1.0] - 2024-10-02 + +### Added + +- Explicitly type each child node field in the Ruby API. +- Add the `main_script` option to the parse APIs, which controls whether or not shebangs are considered. +- Add the `partial_script` options to the parse APIs, which controls whether or not jumps that would otherwise be considered invalid are allowed. This is useful for parsing things like ERB sources, where you know it will be evaluated in a different context. Note that this functionality is replacing the previous idiom of passing in a list of scopes to indicate an `eval` context, because that behavior has changed upstream in `ruby/ruby`. +- Add `ArgumentsNode#contains_multiple_splats?`. +- Add `ArgumentsNode#contains_forwarding?`. +- Accept all valid Ruby versions for the `version` option on parse APIs. +- Accept version shorthands like `"3.3"` and `"3.4"` for the `version` option on parse APIs. +- Support a max depth to protect against malicious payloads without hitting the stack limit. + +### Changed + +- Fix some token incompatibilities in the `parser` translation. +- Fix up parsing tempfiles on Windows. +- Fix up handling UTF-8 characters in file paths on Windows. +- Do not warn for a `\r` at the end of a shebang on Windows. +- Properly handle erroring for parsing a directory on Windows. +- When a numbered reference is out of range, warn instead of raise. +- Allow returns in default parameter values. +- Reject many more invalid syntax patterns. + +## [1.0.0] - 2024-08-28 + +### Added + +- Add `Node#breadth_first_search`. +- Add `Node#node_id`. +- Add `ArgumentsNode#contains_splat?`. +- Passing the special value `false` for the `encoding` option tells Prism to ignore magic encoding comments. +- Expose flags on every node type (allows checking static literal and newline). +- Implement mismatched indentation warning. +- Add C API for receiving a callback when parsing shebangs with additional flags. + +### Changed + +- **BREAKING**: Some fields are renamed that had illogical names. The previous names all now emit deprecation warnings. + - `CaseMatchNode#consequent` was renamed to `CaseMatchNode#else_clause` + - `CaseNode#consequent` was renamed to `CaseNode#else_clause` + - `IfNode#consequent` was renamed to `IfNode#subsequent` + - `RescueNode#consequent` was renamed to `RescueNode#subsequent` + - `UnlessNode#consequent` was renamed to `UnlessNode#else_clause` +- Block exits are now allowed in loop predicates (e.g., `while _ && break do end`). +- Multi-writes are now disallowed when not at the statement level. +- Ensure that range operators are non-associative. +- (JavaScript) Correctly deserialize encoded strings. +- Properly support parsing regular expressions in extended mode. +- Use gmake on FreeBSD. +- Parsing streams now handles NUL bytes in the middle of the stream. +- Properly detect invalid returns. + +## [0.30.0] - 2024-06-07 + +### Added + +- More correctly raise mixed encoding errors. +- Implement ambiguous binary operator warning. +- Fix up regexp escapes with control and meta characters. +- Fix up support for the `it` implicit local variable. +- Heredoc identifiers now properly disallow CLRF. +- Errors added for void value expressions in begin clauses. +- Many updates to more closely match the `parser` gem in parser translation. +- Many errors added for invalid regular expressions. + +### Changed + +- Handle parser translation missing the `parser` gem. +- Handle ruby_parser translation missing the `ruby_parser` gem. +- Various error messages have been updated to more closely match CRuby. +- `RationalNode` now has a `numerator` and `denominator` field instead of a `numeric` field. For the Ruby API we provide a `RationalNode#numeric` method for backwards-compatibility. + +## [0.29.0] - 2024-05-10 + +### Added + +- Added `Prism::CallNode#full_message_loc`, which gives the location including the `=` if there is one. +- A warning for when `# shareable_constant_value` is not used on its own line. +- An error for invalid implicit local variable writes. +- Implicit hash patterns in array patterns are disallowed. +- We now validate that Unicode escape sequences are not surrogates. + +### Changed + +- All fields named `operator` have been renamed to `binary_operator` for `*OperatorWriteNode` nodes. This is to make it easier to provide C++ support. In the Ruby API, the old fields are aliased to the new fields with a deprecation warning. +- Many updated error messages to more closely match CRuby. +- We ensure keyword parameters do not end in `!` or `?`. +- Fixed some escaping in string literals with control sequences and hex escapes. +- Fix a bug with RBS types when used outside the `ruby/prism` codebase. + +## [0.28.0] - 2024-05-03 + +### Added + +- Nested hashes will now warn for duplicated keys, as in: `{ foo: 1, **{ foo: 2 } }`. +- `Prism::ReturnNode` now has a flag on it to indicate if it is redundant. +- `Prism::Location#slice_lines` and `Prism::Node#slice_lines` are now provided to slice the source code of a node including the content before the node on the same line that it starts on and the content after the node on the same line that it ends on. +- Symbols with invalid byte sequences now give errors. +- You can now pass `"3.3.1"` to the `version:` parameter on all `Prism.*` APIs. +- `Prism::Source#lines`, `Prism::Location#source_lines`, `Prism::Node#source_lines`, and `Prism::Node#script_lines` are now provided, which will all return the source code of the source as an array of strings. +- `Prism::ASCIISource` is now provided, which is a subclass of `Prism::Source` but specialized to increase performance when the source is entirely ASCII. +- Prism now provides errors when parsing Ruby 3.4+ syntax for index expressions with keywords or blocks. +- Prism now provides an error when `**nil` is used after other keyword parameters. +- Prism now provides errors when safe navigation is used in call target expressions, e.g., `foo&.bar, = 1`. +- `Prism::Node#tunnel` is now provided, which returns an array of nodes starting at the current node that contain a given line and column. + +### Changed + +- All translation layers now assume an eval context, which means they will not return errors for invalid jumps like `yield`. +- `Prism::Node#inspect` now uses a queue instead of recursion to avoid stack overflows. +- Prism now more closely mirrors CRuby interpolation semantics, which means you could potentially have a static literal string that directly interpolates another static literal string. +- The shipped RBI sorbet types no longer use generics. +- `Prism::ConstantPathNode#child` and `Prism::ConstantTargetNode#child` are now deprecated, replaced by two new fields on these nodes: `name` and `name_loc`. + +## [0.27.0] - 2024-04-23 + +### Added + +- Implemented `===` for each of the nodes, which will check if equality but ignore the specific ranges of locations. + +### Changed + +- Fix translation of `ItParametersNode` for parser translation. +- Fix translation of `dstr` for ruby_parser translation. +- Do not allow omitted hash values whose keys end with `!` or `?`. +- Split up `Prism::ParseResult` into `Prism::Result` with subclasses `Prism::ParseResult`, `Prism::LexResult`, `Prism::ParseLexResult`, and `Prism::LexCompat::Result`. +- Change reflection classes to have only a single `IntegerField` class and rename `DoubleField` to `FloatField`. +- Fall back to default `AR` and `CC` in `Makefile`. +- Use GC-able symbols for the syntax tree to avoid adding to the global symbol table. +- Fix a bug with karatsuba_multiply that would result in a stack overflow. +- Fix parser translation when looking for tokens with `srange_find`. + +## [0.26.0] - 2024-04-18 + +### Added + +- Add `Prism::Node::fields`, which returns a list of `Prism::Reflection::Field` objects representing the fields of the node class. This is useful in metaprogramming contexts. +- `Prism::Location#chop`, for removing the last byte from a location. +- The void statement warning is now implemented. +- The unreachable statement warning is now implemented. +- A syntax error has been added for block arguments on yields, e.g., `yield(&foo)`. + +### Changed + +- Better fidelity to `parser` when translating heredocs with interpolation. +- Fixed `RBI` and `RBS` types for `Prism::parse_*` signatures. +- Remove some incorrect warnings about unused local variables. +- More closely match CRuby error messages for global variables. +- Fix an issue with `parser` translation when line continuations are found in string literals. + +## [0.25.0] - 2024-04-05 + +### Added + +- `Prism::Translation::Ripper` is now able to mirror all of the Ripper APIs. +- `Prism::Location#leading_comments` and `Prism::Location#trailing_comments` is added. +- `Prism::Comment#slice` is added. +- Warn for writing literal values in conditional predicates. +- Check for `_POSIX_MAPPED_FILES` before using `mmap`. +- `Prism::ItParametersNode` is added, to support `-> { it }`. +- Parse integer and float literal values onto the tree. +- Warn on duplicated hash keys and duplicated when clauses. +- Ship much improved `RBI` and `RBS` types. +- Support for the `-p`, `-n`, `-a`, and `-l` command line switches. +- Warn on integer literals in flip-flops. +- Support BSD make. +- Add `Prism::WhenNode#then_keyword_loc`. +- Support custom allocation functions through the `PRISM_XALLOCATOR` define. +- Warn for certain keywrods at the end of the line. +- Provide `pm_visit_node`, a C visitor API. +- `Prism::parse_stream` is added, which works for any Ruby `IO` object. +- Provide flags for regular expression literals for their derived encoding. +- Provide flags for whether or not an interpolated string literal is frozen. +- Add `Prism::StringNode.mutable?` for when a string is explicitly mutable, to support delineating chilled strings. +- Warn for incorrect character literal syntax. +- Warn for chained comparison operators. +- Warn for `**` interpreted as an argument prefix. +- Warn for `&` interpreted as an argument prefix. +- `Prism::ShareableConstantNode` added to support ractors. +- Warn for frozen string literals found after tokens. +- Support `PRISM_BUILD_MINIMAL` to provide only the minimal necessary functionality to reduce the binary size. +- Handle CLRF inside heredocs, strings, and regular expressions. +- Mark inner strings in interpolated strings as frozen. +- Support the `-x` command line switch. +- Error messages now much more closely mirror CRuby. +- Provide syntax errors for invalid block exits (`break`, `next`, `retry`, and `yield`). +- Warn on unused local variables. +- Do not syntax error on default parameter values that only write to the parameter. + +### Changed + +- Many improvements to the compatibility with the `whitequark/parser` translation. +- Accept newlines before pattern terminators `)` or `]`. +- `Prism::Node#start_offset` and `Prism::Node#end_offset` are now much more efficient. +- Read files using `fread` instead of `mmap` when we're going to keep around the source through the Ruby API. +- Fix `Sexp#line_max` setting in the `seattlerb/ruby_parser` translation layer. +- Allow spaces before the encoding comment. + +## [0.24.0] - 2024-02-15 + +### Added + +- More support for `Prism::Translation::Ripper` is added. +- Support multiple versions for `Prism::Translation::Parser`. +- Improved memory usage in the FFI backend. +- Very large speed and memory improvements for creating the Ruby AST. + +### Changed + +- Fix location for empty symbol in hash key. +- Fix parsing a rescue modifier on the value of an assignment when the LHS is a method call with arguments and no parentheses. + +## [0.23.0] - 2024-02-14 + +### Added + +- More support for `Prism::RipperCompat` is added. +- A significantly faster offset cache for `Prism::Translation::Parser` is added for files with multibyte characters. +- `Prism::Translation::RubyParser` is added. +- `Prism::ConstantPathTarget#full_name` is added. +- `version: "3.4.0"` is added as an option that is an alias for `version: "latest"`. +- Four new APIs are added to `Prism::Location`: + - `Prism::Location#start_code_units_offset` + - `Prism::Location#end_code_units_offset` + - `Prism::Location#start_code_units_column` + - `Prism::Location#end_code_units_column` +- Invalid multibyte characters are now validated within strings, lists, and heredocs. + +### Changed + +- When defining `def !@`, the `name_loc` was previously only pointing to `!`, but now includes the `@`. The `name` is the same. +- `Prism::RipperCompat` has been moved to `Prism::Translation::Ripper`. +- Many of the error messages that prism produces have been changed to match the error messages that CRuby produces. + +## [0.22.0] - 2024-02-07 + +### Added + +- More support for `Prism::RipperCompat` is added. +- Support for Ruby 2.7 has been added, and the minimum Ruby requirement has been lowered to 2.7. + +### Changed + +- The error for an invalid source encoding has a new `:argument` level to indicate it raises an argument error. +- `BeginNode` nodes that are used when a class, singleton class, module, method definition, or block have an inline `rescue`/`ensure`/`else` now have their opening locations set to the beginning of the respective keyword. +- Improved error messages for invalid characters. +- `Prism.parse_file` and similar APIs will raise more appropriate errors when the file does not exist or cannot be mapped. +- Correctly handle the `recover` parameter for `Prism::Translation::Parser`. + +## [0.21.0] - 2024-02-05 + +### Added + +- Add the `pm_constant_pool_find` API for finding a constant. + +### Changed + +- Fixes for `Prism::Translation::Parser`. + - Ensure all errors flow through `parser.diagnostics.process`. + - Fix the find pattern node. + - Fix block forwarding with `NumberedParametersNode`. + - Ensure we can parse strings with invalid bytes for the encoding. + - Fix hash pairs in pattern matching. +- Properly reject operator writes on operator calls, e.g., `a.+ -= b`. +- Fix multi-byte escapes. +- Handle missing body in `begin` within the receiver of a method call. + +## [0.20.0] - 2024-02-01 + +### Added + +- String literal hash keys are now marked as frozen as static literal. +- `IndexTargetNode` now always has the `ATTRIBUTE_WRITE` flag. +- `Call*Node` nodes now have an `IGNORE_VISIBILITY` flag. +- We now support the `it` default parameter. +- Errors and warnings now have levels associated with them. +- Symbols now have correct encoding flags. +- We have now merged `parser-prism` in, which provides translation to the `whitequark/parser` AST. +- We now emit errors for invalid method definition receivers. + +### Changed + +- We now emit errors on invalid pinned local variables. +- When passed scopes, it is now assumed that the innermost scope is the current binding. +- We now provide better error recovery for non terminated heredocs. +- Fix for `RationalNode#value` for non-decimal integers. +- Unary symbols `!@` and `~@` now unescape to `!` and `~`, respectively. +- `frozen_string_literal: false` now works properly. + +### Removed + +- We've removed the `locals_body_index` field. +- We've removed the `verbose` option on the various parse APIs. Warnings are now always emitted with their associated level so that consumers can decide how to handle them. + +## [0.19.0] - 2023-12-14 + +### Added + +- `ArrayNode` now has a `contains_splat?` flag if it has a splatted element in it. +- All of the remaining encodings have been implemented. +- Allow forwarding `&` in a method that has a `...` parameter. +- Many statements that are found in non-statement positions are being properly rejected now. +- Void values are now properly checked. +- Referencing a parameter in its own default value is now properly rejected. +- `DATA`/`__END__` is now parsed as its own field on parse result (`data_loc`) as opposed to as a comment. +- Blank `*` now properly forwards into arrays. +- `ImplicitRestNode` is introduced to represent the implicit rest of a destructure. +- We now support negative start lines. +- `StringNode#heredoc?`, `InterpolatedStringNode#heredoc?`, `XStringNode#heredoc?`, and `InterpolatedXStringNode#heredoc?` are introduced. +- `NumberedParametersNode` is introduced to represent the implicit set of parameters when numbered parameters are used. +- `Prism::parse_success?` and `Prism::parse_failure?` are introduced to bypass reifying the AST. +- We now emit a warning for constant assignments in method definitions. +- We now provide flags on strings and xstrings to indicate the correct encoding. +- The hash pattern `rest` field now more accurately parses `**` and `**nil`. +- The equality operators are now properly parsed as non-associative. + +### Changed + +- **BREAKING**: Many fields have changed positions within their nodes. This impacts the C API and the Ruby API if you are manually creating nodes through the initializer. +- **BREAKING**: Almost all of the error messages have been updated to begin with lowercase characters to match ruby/spec. +- Unterminated strings with only plain content are now always `StringNode` as opposed to `InterpolatedStringNode` +- **BREAKING**: Call node has been split up when it is in the target position into `CallTargetNode` and `IndexTargetNode`. + +## [0.18.0] - 2023-11-21 + +### Added + +- The `ParametersNode#signature` method is added, which returns the same thing as `Method#parameters`. +- Visitor functionality has been added to the JavaScript API. +- The `Node#to_dot` API has been added to convert syntax trees to Graphviz digraphs. +- `IfNode` and `UnlessNode` now have a `then_keyword_loc` field. +- Many more encodings are now supported. +- Some new `Location` APIs have been added for dealing with characters instead of bytes, which are: `start_character_offset`, `end_character_offset`, `start_character_column`, and `end_character_column`. +- A warning has been added for when `END {}` is used within a method. +- `ConstantPathNode#full_name{,_parts}` will now raise an error if the receiver of the constant path is not itself a constant. +- The `in` keyword and the `=>` operator now respect non-associativity. +- The `..` and `...` operators now properly respect non-associativity. + +### Changed + +- Previously `...` in blocks was accepted, but it is now properly rejected. +- **BREAKING**: `librubyparser.*` has been renamed to `libprism.*` in the C API. +- We now properly reject floats with exponent and rational suffixes. +- We now properly reject void value expressions. +- **BREAKING**: The `--disable-static` option has been removed from the C extension. +- The rescue modifier keyword is now properly parsed in terms of precedence. +- We now properly reject defining a numbered parameter method. +- **BREAKING**: `MatchWriteNode` now has a list of `targets`, which are local variable target nodes. This is instead of `locals` which was a constant list. This is to support writing to local variables outside the current scope. It has the added benefit of providing location information for the local variable targets. +- **BREAKING**: `CaseNode` has been split into `CaseNode` and `CaseMatchNode`, the latter is used for `case ... in` expressions. +- **BREAKING**: `StringConcatNode` has been removed in favor of using `InterpolatedStringNode` as a list. + +## [0.17.1] - 2023-11-03 + +### Changed + +- Do not use constant nesting in RBI files. + +## [0.17.0] - 2023-11-03 + +### Added + +- We now properly support forwarding arguments into arrays, like `def foo(*) = [*]`. +- We now have much better documentation for the C and Ruby APIs. +- We now properly provide an error message when attempting to assign to numbered parameters from within regular expression named capture groups, as in `/(?<_1>)/ =~ ""`. + +### Changed + +- **BREAKING**: `KeywordParameterNode` is split into `OptionalKeywordParameterNode` and `RequiredKeywordParameterNode`. `RequiredKeywordParameterNode` has no `value` field. +- **BREAKING**: Most of the `Prism::` APIs now accept a bunch of keyword options. The options we now support are: `filepath`, `encoding`, `line`, `frozen_string_literal`, `verbose`, and `scopes`. See [the pull request](https://github.com/ruby/prism/pull/1763) for more details. +- **BREAKING**: Comments are now split into three different classes instead of a single class, and the `type` field has been removed. They are: `InlineComment`, `EmbDocComment`, and `DATAComment`. + +## [0.16.0] - 2023-10-30 + +### Added + +- `InterpolatedMatchLastLineNode#options` and `MatchLastLineNode#options` are added, which are the same methods as are exposed on `InterpolatedRegularExpressionNode` and `RegularExpressionNode`. +- The project can now be compiled with `wasi-sdk` to expose a WebAssembly interface. +- `ArgumentsNode#keyword_splat?` is added to indicate if the arguments node has a keyword splat. +- The C API `pm_prettyprint` has a much improved output which lines up closely with `Node#inspect`. +- Prism now ships with `RBS` and `RBI` type signatures (in the `/sig` and `/rbi` directories, respectively). +- `Prism::parse_comments` and `Prism::parse_file_comments` APIs are added to extract only the comments from the source code. + +### Changed + +- **BREAKING**: `Multi{Target,Write}Node#targets` is split up now into `lefts`, `rest`, and `rights`. This is to avoid having to scan the list in the case that there are splat nodes. +- Some bugs are fixed on `Multi{Target,Write}Node` accidentally creating additional nesting when not necessary. +- **BREAKING**: `RequiredDestructuredParameterNode` has been removed in favor of using `MultiTargetNode` in those places. +- **BREAKING**: `HashPatternNode#assocs` has been renamed to `HashPatternNode#elements`. `HashPatternNode#kwrest` has been renamed to `HashPatternNode#rest`. + +## [0.15.1] - 2023-10-18 + +### Changed + +- Fix compilation warning on assigning to bitfield. + +## [0.15.0] - 2023-10-18 + +### Added + +- `BackReferenceReadNode#name` is now provided. +- `Index{Operator,And,Or}WriteNode` are introduced, split out from `Call{Operator,And,Or}WriteNode` when the method is `[]`. + +### Changed + +- Ensure `PM_NODE_FLAG_COMMON_MASK` into a constant expression to fix compile errors. +- `super(&arg)` is now fixed. +- Ensure the last encoding flag on regular expressions wins. +- Fix the common whitespace calculation when embedded expressions begin on a line. +- Capture groups in regular expressions now scan the unescaped version to get the correct local variables. +- `*` and `&` are added to the local table when `...` is found in the parameters of a method definition. + +## [0.14.0] - 2023-10-13 + +### Added + +- Syntax errors are added for invalid lambda local semicolon placement. +- Lambda locals are now checked for duplicate names. +- Destructured parameters are now checked for duplicate names. +- `Constant{Read,Path,PathTarget}Node#full_name` and `Constant{Read,Path,PathTarget}Node#full_name_parts` are added to walk constant paths for you to find the full name of the constant. +- Syntax errors are added when assigning to a numbered parameter. +- `Node::type` is added, which matches the `Node#type` API. +- Magic comments are now parsed as part of the parsing process and a new field is added in the form of `ParseResult#magic_comments` to access them. + +### Changed + +- **BREAKING**: `Call*Node#name` methods now return symbols instead of strings. +- **BREAKING**: For loops now have their index value considered as part of the body, so depths of local variable assignments will be increased by 1. +- Tilde heredocs now split up their lines into multiple string nodes to make them easier to dedent. + +## [0.13.0] - 2023-09-29 + +### Added + +- `BEGIN {}` blocks are only allowed at the top-level, and will now provide a syntax error if they are not. +- Numbered parameters are not allowed in block parameters, and will now provide a syntax error if they are. +- Many more Ruby modules and classes are now documented. Also, many have been moved into their own files and autoloaded so that initial boot time of the gem is much faster. +- `PM_TOKEN_METHOD_NAME` is introduced, used to indicate an identifier that if definitely a method name because it has an `!` or `?` at the end. +- In the C API, arrays, assocs, and hashes now can have the `PM_NODE_FLAG_STATIC_LITERAL` flag attached if they can be compiled statically. This is used in CRuby, for example, to determine if a `duphash`/`duparray` instruction can be used as opposed to a `newhash`/`newarray`. +- `Node#type` is introduced, which returns a symbol representing the type of the node. This is useful for case comparisons when you have to compare against multiple types. + +### Changed + +- **BREAKING**: Everything has been renamed to `prism` instead of `yarp`. The `yp_`/`YP_` prefix in the C API has been changed to `pm_`/`PM_`. For the most part, everything should be find/replaceable. +- **BREAKING**: `BlockArgumentNode` nodes now go into the `block` field on `CallNode` nodes, in addition to the `BlockNode` nodes that used to be there. Hopefully this makes it more consistent to compile/deal with in general, but it does mean it can be a surprising breaking change. +- Escaped whitespace in `%w` lists is now properly unescaped. +- `Node#pretty_print` now respects pretty print indentation. +- `Dispatcher` was previously firing `_leave` events in the incorrect order. This has now been fixed. +- **BREAKING**: `Visitor` has now been split into `Visitor` and `Compiler`. The visitor visits nodes but doesn't return anything from the visit methods. It is suitable for taking action based on the tree, but not manipulating the tree itself. The `Compiler` visits nodes and returns the computed value up the tree. It is suitable for compiling the tree into another format. As such, `MutationVisitor` has been renamed to `MutationCompiler`. + +## [0.12.0] - 2023-09-15 + +### Added + +- `RegularExpressionNode#options` and `InterpolatedRegularExpressionNode#options` are now provided. These return integers that match up to the `Regexp#options` API. +- Greatly improved `Node#inspect` and `Node#pretty_print` APIs. +- `MatchLastLineNode` and `InterpolatedMatchLastLineNode` are introduced to represent using a regular expression as the predicate of an `if` or `unless` statement. +- `IntegerNode` now has a base flag on it. +- Heredocs that were previously `InterpolatedStringNode` and `InterpolatedXStringNode` nodes without any actual interpolation are now `StringNode` and `XStringNode`, respectively. +- `StringNode` now has a `frozen?` flag on it, which respects the `frozen_string_literal` magic comment. +- Numbered parameters are now supported, and are properly represented using `LocalVariableReadNode` nodes. +- `ImplicitNode` is introduced, which wraps implicit calls, local variable reads, or constant reads in omitted hash values. +- `YARP::Dispatcher` is introduced, which provides a way for multiple objects to listen for certain events on the AST while it is being walked. This is effectively a way to implement a more efficient visitor pattern when you have many different uses for the AST. + +### Changed + +- **BREAKING**: Flags fields are now marked as private, to ensure we can change their implementation under the hood. Actually querying should be through the accessor methods. +- **BREAKING**: `AliasNode` is now split into `AliasMethodNode` and `AliasGlobalVariableNode`. +- Method definitions on local variables is now correctly handled. +- Unary minus precedence has been fixed. +- Concatenating character literals with string literals is now fixed. +- Many more invalid syntaxes are now properly rejected. +- **BREAKING**: Comments now no longer include their trailing newline. + +## [0.11.0] - 2023-09-08 + +### Added + +- `Node#inspect` is much improved. +- `YARP::Pattern` is introduced, which can construct procs to match against nodes. +- `BlockLocalVariableNode` is introduced to take the place of the locations array on `BlockParametersNode`. +- `ParseResult#attach_comments!` is now provided to attach comments to locations in the tree. +- `MultiTargetNode` is introduced as the target of multi writes and for loops. +- `Node#comment_targets` is introduced to return the list of objects that can have attached comments. + +### Changed + +- **BREAKING**: `GlobalVariable*Node#name` now returns a symbol. +- **BREAKING**: `Constant*Node#name` now returns a symbol. +- **BREAKING**: `BlockParameterNode`, `KeywordParameterNode`, `KeywordRestParameterNode`, `RestParameterNode`, `DefNode` all have their `name` methods returning symbols now. +- **BREAKING**: `ClassNode#name` and `ModuleNode#name` now return symbols. +- **BREAKING**: `Location#end_column` is now exclusive instead of inclusive. +- `Location#slice` now returns a properly encoded string. +- `CallNode#operator_loc` is now `CallNode#call_operator_loc`. +- `CallOperatorAndWriteNode` is renamed to `CallAndWriteNode` and its structure has changed. +- `CallOperatorOrWriteNode` is renamed to `CallOrWriteNode` and its structure has changed. + +## [0.10.0] - 2023-09-01 + +### Added + +- `InstanceVariable*Node` and `ClassVariable*Node` objects now have their `name` returning a Symbol. This is because they are now part of the constant pool. +- `NumberedReferenceReadNode` now has a `number` field, which returns an Integer. + +### Changed + +- **BREAKING**: Various `operator_id` and `constant_id` fields have been renamed to `operator` and `name`, respectively. See [09d0a144](https://github.com/ruby/yarp/commit/09d0a144dfd519c5b5f96f0b6ee95d256e2cb1a6) for details. +- `%w`, `%W`, `%i`, `%I`, `%q`, and `%Q` literals can now span around the contents of a heredoc. +- **BREAKING**: All of the public C APIs that accept the source string now accept `const uint8_t *` as opposed to `const char *`. + +## [0.9.0] - 2023-08-25 + +### Added + +- Regular expressions can now be bound by `\n`, `\r`, and a combination of `\r\n`. +- Strings delimited by `%`, `%q`, and `%Q` can now be bound by `\n`, `\r`, and a combination of `\r\n`. +- `IntegerNode#value` now returns the value of the integer as a Ruby `Integer`. +- `FloatNode#value` now returns the value of the float as a Ruby `Float`. +- `RationalNode#value` now returns the value of the rational as a Ruby `Rational`. +- `ImaginaryNode#value` now returns the value of the imaginary as a Ruby `Complex`. +- `ClassNode#name` is now a string that returns the name of just the class, without the namespace. +- `ModuleNode#name` is now a string that returns the name of just the module, without the namespace. +- Regular expressions and strings found after a heredoc declaration but before the heredoc body are now parsed correctly. +- The serialization API now supports shared strings, which should help reduce the size of the serialized AST. +- `*Node#copy` is introduced, which returns a copy of the node with the given overrides. +- `Location#copy` is introduced, which returns a copy of the location with the given overrides. +- `DesugarVisitor` is introduced, which provides a simpler AST for use in tools that want to process fewer node types. +- `{ClassVariable,Constant,ConstantPath,GlobalVariable,InstanceVariable,LocalVariable}TargetNode` are introduced. These nodes represent the target of writes in locations where a value cannot be provided, like a multi write or a rescue reference. +- `UntilNode#closing_loc` and `WhileNode#closing_loc` are now provided. +- `Location#join` is now provided, which joins two locations together. +- `YARP::parse_lex` and `YARP::parse_lex_file` are introduced to parse and lex in one result. + +### Changed + +- When there is a magic encoding comment, the encoding of the first token's source string is now properly reencoded. +- Constants followed by unary `&` are now properly parsed as a call with a passed block argument. +- Escaping multi-byte characters in a string literal will now properly escape the entire character. +- `YARP.lex_compat` now has more accurate behavior when a byte-order mark is present in the file. +- **BREAKING**: `AndWriteNode`, `OrWriteNode`, and `OperatorWriteNode` have been split back up into their `0.7.0` versions. +- We now properly support spaces between the `encoding` and `=`/`:` in a magic encoding comment. +- We now properly parse `-> foo: bar do end`. + +## [0.8.0] - 2023-08-18 + +### Added + +- Some performance improvements when converting from the C AST to the Ruby AST. +- Two rust crates have been added: `yarp-sys` and `yarp`. They are as yet unpublished. + +### Changed + +- Escaped newlines in strings and heredocs are now handled more correctly. +- Dedenting heredocs that result in empty string nodes will now drop those string nodes from the list. +- Beginless and endless ranges in conditional expressions now properly form a flip flop node. +- `%` at the end of files no longer crashes. +- Location information has been corrected for `if/elsif` chains that have no `else`. +- `__END__` at the very end of the file was previously parsed as an identifier, but is now correct. +- **BREAKING**: Nodes that reference `&&=`, `||=`, and other writing operators have been consolidated. Previously, they were separate individual nodes. Now they are a tree with the target being the left-hand side and the value being the right-hand side with a joining `AndWriteNode`, `OrWriteNode`, or `OperatorWriteNode` in the middle. This impacts all of the nodes that match this pattern: `{ClassVariable,Constant,ConstantPath,GlobalVariable,InstanceVariable,LocalVariable}Operator{And,Or,}WriteNode`. +- **BREAKING**: `BlockParametersNode`, `ClassNode`, `DefNode`, `LambdaNode`, `ModuleNode`, `ParenthesesNode`, and `SingletonClassNode` have had their `statements` field renamed to `body` to give a hint that it might not be a `StatementsNode` (it could also be a `BeginNode`). + +## [0.7.0] - 2023-08-14 + +### Added + +- We now have an explicit `FlipFlopNode`. It has the same flags as `RangeNode`. +- We now have a syntax error when implicit and explicit blocks are passed to a method call. +- `Node#slice` is now implemented, for retrieving the slice of the source code corresponding to a node. +- We now support the `utf8-mac` encoding. +- Predicate methods have been added for nodes that have flags. For example `CallNode#safe_navigation?` and `RangeNode#exclude_end?`. +- The gem now functions on JRuby and TruffleRuby, thanks to a new FFI backend. +- Comments are now part of the serialization API. + +### Changed + +- Autotools has been removed from the build system, so when the gem is installed it will no longer need to go through a configure step. +- The AST for `foo = *bar` has changed to have an explicit array on the right hand side, rather than a splat node. This is more consistent with how other parsers handle this. +- **BREAKING**: `RangeNodeFlags` has been renamed to `RangeFlags`. +- Unary minus on number literals is now parsed as part of the literal, rather than a call to a unary operator. This is more consistent with how other parsers handle this. + +## [0.6.0] - 2023-08-09 + +### Added + +- 🎉 Initial release! 🎉 + +[unreleased]: https://github.com/ruby/prism/compare/v1.4.0...HEAD +[1.4.0]: https://github.com/ruby/prism/compare/v1.3.0...v1.4.0 +[1.3.0]: https://github.com/ruby/prism/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/ruby/prism/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/ruby/prism/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/ruby/prism/compare/v0.30.0...v1.0.0 +[0.30.0]: https://github.com/ruby/prism/compare/v0.29.0...v0.30.0 +[0.29.0]: https://github.com/ruby/prism/compare/v0.28.0...v0.29.0 +[0.28.0]: https://github.com/ruby/prism/compare/v0.27.0...v0.28.0 +[0.27.0]: https://github.com/ruby/prism/compare/v0.26.0...v0.27.0 +[0.26.0]: https://github.com/ruby/prism/compare/v0.25.0...v0.26.0 +[0.25.0]: https://github.com/ruby/prism/compare/v0.24.0...v0.25.0 +[0.24.0]: https://github.com/ruby/prism/compare/v0.23.0...v0.24.0 +[0.23.0]: https://github.com/ruby/prism/compare/v0.22.0...v0.23.0 +[0.22.0]: https://github.com/ruby/prism/compare/v0.21.0...v0.22.0 +[0.21.0]: https://github.com/ruby/prism/compare/v0.20.0...v0.21.0 +[0.20.0]: https://github.com/ruby/prism/compare/v0.19.0...v0.20.0 +[0.19.0]: https://github.com/ruby/prism/compare/v0.18.0...v0.19.0 +[0.18.0]: https://github.com/ruby/prism/compare/v0.17.1...v0.18.0 +[0.17.1]: https://github.com/ruby/prism/compare/v0.17.0...v0.17.1 +[0.17.0]: https://github.com/ruby/prism/compare/v0.16.0...v0.17.0 +[0.16.0]: https://github.com/ruby/prism/compare/v0.15.1...v0.16.0 +[0.15.1]: https://github.com/ruby/prism/compare/v0.15.0...v0.15.1 +[0.15.0]: https://github.com/ruby/prism/compare/v0.14.0...v0.15.0 +[0.14.0]: https://github.com/ruby/prism/compare/v0.13.0...v0.14.0 +[0.13.0]: https://github.com/ruby/prism/compare/v0.12.0...v0.13.0 +[0.12.0]: https://github.com/ruby/prism/compare/v0.11.0...v0.12.0 +[0.11.0]: https://github.com/ruby/prism/compare/v0.10.0...v0.11.0 +[0.10.0]: https://github.com/ruby/prism/compare/v0.9.0...v0.10.0 +[0.9.0]: https://github.com/ruby/prism/compare/v0.8.0...v0.9.0 +[0.8.0]: https://github.com/ruby/prism/compare/v0.7.0...v0.8.0 +[0.7.0]: https://github.com/ruby/prism/compare/v0.6.0...v0.7.0 +[0.6.0]: https://github.com/ruby/prism/compare/d60531...v0.6.0 diff --git a/vendor/mruby-compiler2/lib/prism/CODE_OF_CONDUCT.md b/vendor/mruby-compiler2/lib/prism/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f6ae7f6 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at opensource@shopify.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/vendor/mruby-compiler2/lib/prism/CONTRIBUTING.md b/vendor/mruby-compiler2/lib/prism/CONTRIBUTING.md new file mode 100644 index 0000000..8e6fa3b --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contributing + +Thank you for your interest in contributing to prism! Below are a couple of ways that you can help out. + +## Discussions + +The discussions page on the GitHub repository are open. If you have a question or want to discuss the project, feel free to open a new discussion or comment on an existing one. This is the best place to ask questions about the project. + +## Code + +If you want to contribute code, please first open or contribute to a discussion. A lot of the project is in flux, and we want to make sure that you are contributing to the right place. Once you have a discussion going, you can open a pull request with your changes. We will review your code and get it merged in. + +## Tests + +We could always use more tests! One of the biggest challenges of this project is building up a big test suite. If you want to contribute tests, feel free to open a pull request. These will get merged in as soon as possible. + +The `test` Rake task will not compile libraries or the C extension, and this is intentional (to make testing against an installed version easier). If you want to test your changes, please make sure you're also running either the task: + +``` sh +bundle exec rake +``` + +or explicitly running the `compile` task: + +``` sh +bundle exec rake compile test +# or to just compile the C extension ... +bundle exec rake compile:prism test +``` + +To test the rust bindings (with caveats about setting up your Rust environment properly first): + +``` sh +bundle exec rake compile test:rust +``` + + +## Documentation + +We could always use more documentation! If you want to contribute documentation, feel free to open a pull request. These will get merged in as soon as possible. Documenting functions or methods is always useful, but we also need more guides and tutorials. If you have an idea for a guide or tutorial, feel free to open an issue and we can discuss it. + +## Developing + +To get `clangd` support in the editor for development, generate the compilation database. This command will +create an ignored `compile_commands.json` file at the project root, which is used by clangd to provide functionality. + +You will need `bear` which can be installed on macOS with `brew install bear`. + +```sh +bundle exec rake bear +``` + +## Debugging + +Some useful rake tasks: + +- `test:valgrind` runs the test suite under valgrind to look for illegal memory access or memory leaks +- `test:gdb` and `test:lldb` run the test suite under those debuggers diff --git a/vendor/mruby-compiler2/lib/prism/LICENSE.md b/vendor/mruby-compiler2/lib/prism/LICENSE.md new file mode 100644 index 0000000..2d449f9 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2022-present, Shopify Inc. + +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. diff --git a/vendor/mruby-compiler2/lib/prism/README.md b/vendor/mruby-compiler2/lib/prism/README.md new file mode 100644 index 0000000..b601ee5 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/README.md @@ -0,0 +1,141 @@ +

Prism Ruby parser

+
+ Prism Ruby parser +
+ +This 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. + +## Overview + +The repository contains the infrastructure for both a shared library (libprism) and a native CRuby extension. The shared library has no bindings to CRuby itself, and so can be used by other projects. The native CRuby extension links against `ruby.h`, and so is suitable in the context of CRuby. + +``` +. +├── Makefile configuration to compile the shared library and native tests +├── Rakefile configuration to compile the native extension and run the Ruby tests +├── bin +│   ├── lex runs the lexer on a file or string, prints the tokens, and compares to ripper +│   ├── parse runs the parse on a file or string and prints the AST +│   └── prism a CLI for development and debugging +├── config.yml specification for tokens and nodes in the tree +├── doc documentation website +├── docs markdown documentation about the project +├── ext +│   └── prism +│   ├── extconf.rb configuration to generate the Makefile for the native extension +│   └── extension.c the native extension that interacts with libprism +├── fuzz files related to fuzz testing +├── gemfiles gemfiles used by different Ruby versions in CI +├── include +│   ├── prism header files for the shared library +│   └── prism.h main header file for the shared library +├── java Java bindings for the shared library +├── java-wasm Java WASM bindings for the shared library +├── javascript JavaScript WASM bindings for the shared library +├── lib +│   ├── prism Ruby library files +│   └── prism.rb main entrypoint for the Ruby library +├── rakelib various Rake tasks for the project +├── rbi RBI type signatures for the Ruby library +├── rust +│   ├── ruby-prism Rustified crate for the shared library +│   └── ruby-prism-sys FFI binding for Rust +├── sample +│ └── prism Sample code that uses the Ruby API for documentation purposes +├── sig RBS type signatures for the Ruby library +├── src +│   ├── util various utility files +│   └── prism.c main entrypoint for the shared library +├── templates contains ERB templates generated by templates/template.rb +│   └── template.rb generates code from the nodes and tokens configured by config.yml +└── test + └── prism + ├── fixtures Ruby code used for testing + └── snapshots snapshots of generated syntax trees corresponding to fixtures +``` + +## Getting started + +To compile the shared library, you will need: + +* C99 compiler +* GNU make +* Ruby 2.7.0 or later + +Once you have these dependencies, run: + +``` +bundle install +``` + +to fetch the Ruby dependencies. Finally, run: + +``` +bundle exec rake compile +``` + +to compile the shared library. It will be built in the `build` directory. To test that everything is working, run: + +``` +bin/parse -e "1 + 2" +``` + +to see the syntax tree for the expression `1 + 2`. + +## Contributing + +See the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information. We additionally have documentation about the overall design of the project as well as various subtopics. + +* [Build system](docs/build_system.md) +* [Configuration](docs/configuration.md) +* [CRuby compilation](docs/cruby_compilation.md) +* [Design](docs/design.md) +* [Encoding](docs/encoding.md) +* [Fuzzing](docs/fuzzing.md) +* [Heredocs](docs/heredocs.md) +* [JavaScript](docs/javascript.md) +* [Local variable depth](docs/local_variable_depth.md) +* [Mapping](docs/mapping.md) +* [Parser translation](docs/parser_translation.md) +* [Parsing rules](docs/parsing_rules.md) +* [Releasing](docs/releasing.md) +* [Ripper translation](docs/ripper_translation.md) +* [Ruby API](docs/ruby_api.md) +* [RubyParser translation](docs/ruby_parser_translation.md) +* [Serialization](docs/serialization.md) +* [Testing](docs/testing.md) + +## Examples + +Prism has been integrated into the majority of Ruby runtimes, many libraries, and some applications. Below is a list of some of the projects that use Prism: + +### Runtimes + +* [CRuby](https://github.com/ruby/ruby/pull/7964) (via C) +* [Garnet](https://github.com/camertron/garnet-js) (via WASM) +* [JRuby](https://github.com/jruby/jruby/pull/8103) (via Java) +* [Natalie](https://github.com/natalie-lang/natalie/pull/1213) (via C++ and Ruby) +* [Opal](https://github.com/opal/opal/pull/2642) (via Ruby and WASM) +* [TruffleRuby](https://github.com/oracle/truffleruby/issues/3117) (via Java) + +### Libraries + +* [dispersion](https://github.com/joeldrapper/dispersion) +* [minifyrb](https://github.com/koic/minifyrb) +* [packwerk](https://github.com/Shopify/packwerk/pull/388) (via parser translator) +* [rbi](https://github.com/Shopify/rbi) +* [rails](https://github.com/rails/rails) + * [parsing renders](https://github.com/rails/rails/pull/49438) + * [parsing rdoc](https://github.com/rails/rails/pull/50870) + * [parsing tests](https://github.com/rails/rails/pull/51006) +* [repl_type_completor](https://github.com/ruby/repl_type_completor) +* [rubocop](https://docs.rubocop.org/rubocop/configuration.html#setting-the-parser-engine) (via parser translator) +* [ruby-lsp](https://github.com/Shopify/ruby-lsp) +* [smart_todo](https://github.com/Shopify/smart_todo/pull/69) +* [sorbet-eraser](https://github.com/kddnewton/sorbet-eraser/pull/25) +* [synvert](https://github.com/xinminlabs/synvert-core-ruby) +* [typeprof](https://github.com/ruby/typeprof) + +### Applications + +* [gem.sh](https://github.com/marcoroth/gem.sh/pull/96) diff --git a/vendor/mruby-compiler2/lib/prism/include/prism.h b/vendor/mruby-compiler2/lib/prism/include/prism.h new file mode 100644 index 0000000..317568a --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism.h @@ -0,0 +1,383 @@ +/** + * @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_integer.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/static_literals.h" +#include "prism/version.h" + +#include +#include +#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); + +/** + * 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); + +/** + * This function is used in pm_parse_stream to retrieve a line of input from a + * stream. It closely mirrors that of fgets so that fgets can be used as the + * default implementation. + */ +typedef char * (pm_parse_stream_fgets_t)(char *string, int size, void *stream); + +/** + * Parse a stream of Ruby source and return the tree. + * + * @param parser The parser to use. + * @param buffer The buffer to use. + * @param stream The stream to parse. + * @param stream_fgets The function to use to read from the stream. + * @param options The optional options to use when parsing. + * @return The AST representing the source. + */ +PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const pm_options_t *options); + +// We optionally support serializing to a binary string. For systems that don't +// want or need this functionality, it can be turned off with the +// PRISM_EXCLUDE_SERIALIZATION define. +#ifndef PRISM_EXCLUDE_SERIALIZATION + +/** + * Parse and serialize the AST represented by the source that is read out of the + * given stream into to the given buffer. + * + * @param buffer The buffer to serialize to. + * @param stream The stream to parse. + * @param stream_fgets The function to use to read from the stream. + * @param data The optional data to pass to the parser. + */ +PRISM_EXPORTED_FUNCTION void pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const char *data); + +/** + * 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(const 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); + +#endif + +/** + * Parse the source and return true if it parses without errors or warnings. + * + * @param source The source to parse. + * @param size The size of the source. + * @param data The optional data to pass to the parser. + * @return True if the source parses without errors or warnings. + */ +PRISM_EXPORTED_FUNCTION bool pm_parse_success_p(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_name(pm_token_type_t token_type); + +/** + * Returns the human name of the given token type. + * + * @param token_type The token type to convert to a human name. + * @return The human name of the given token type. + */ +const char * pm_token_type_human(pm_token_type_t token_type); + +// We optionally support dumping to JSON. For systems that don't want or need +// this functionality, it can be turned off with the PRISM_EXCLUDE_JSON define. +#ifndef PRISM_EXCLUDE_JSON + +/** + * Dump JSON to the given buffer. + * + * @param buffer The buffer to serialize to. + * @param parser The parser that parsed the node. + * @param node The node to serialize. + */ +PRISM_EXPORTED_FUNCTION void pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node); + +#endif + +/** + * Represents the results of a slice query. + */ +typedef enum { + /** Returned if the encoding given to a slice query was invalid. */ + PM_STRING_QUERY_ERROR = -1, + + /** Returned if the result of the slice query is false. */ + PM_STRING_QUERY_FALSE, + + /** Returned if the result of the slice query is true. */ + PM_STRING_QUERY_TRUE +} pm_string_query_t; + +/** + * Check that the slice is a valid local variable name. + * + * @param source The source to check. + * @param length The length of the source. + * @param encoding_name The name of the encoding of the source. + * @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if + * the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name); + +/** + * Check that the slice is a valid constant name. + * + * @param source The source to check. + * @param length The length of the source. + * @param encoding_name The name of the encoding of the source. + * @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if + * the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name); + +/** + * Check that the slice is a valid method name. + * + * @param source The source to check. + * @param length The length of the source. + * @param encoding_name The name of the encoding of the source. + * @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if + * the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name); + +/** + * @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(&parser, 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(&parser, root); + * pm_parser_free(&parser); + * } + * ``` + */ + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/ast.h b/vendor/mruby-compiler2/lib/prism/include/prism/ast.h new file mode 100644 index 0000000..9c4ad63 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/ast.h @@ -0,0 +1,7964 @@ +/*----------------------------------------------------------------------------*/ +/* 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_integer.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, + + /** CallTargetNode */ + PM_CALL_TARGET_NODE = 22, + + /** CapturePatternNode */ + PM_CAPTURE_PATTERN_NODE = 23, + + /** CaseMatchNode */ + PM_CASE_MATCH_NODE = 24, + + /** CaseNode */ + PM_CASE_NODE = 25, + + /** ClassNode */ + PM_CLASS_NODE = 26, + + /** ClassVariableAndWriteNode */ + PM_CLASS_VARIABLE_AND_WRITE_NODE = 27, + + /** ClassVariableOperatorWriteNode */ + PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE = 28, + + /** ClassVariableOrWriteNode */ + PM_CLASS_VARIABLE_OR_WRITE_NODE = 29, + + /** ClassVariableReadNode */ + PM_CLASS_VARIABLE_READ_NODE = 30, + + /** ClassVariableTargetNode */ + PM_CLASS_VARIABLE_TARGET_NODE = 31, + + /** ClassVariableWriteNode */ + PM_CLASS_VARIABLE_WRITE_NODE = 32, + + /** ConstantAndWriteNode */ + PM_CONSTANT_AND_WRITE_NODE = 33, + + /** ConstantOperatorWriteNode */ + PM_CONSTANT_OPERATOR_WRITE_NODE = 34, + + /** ConstantOrWriteNode */ + PM_CONSTANT_OR_WRITE_NODE = 35, + + /** ConstantPathAndWriteNode */ + PM_CONSTANT_PATH_AND_WRITE_NODE = 36, + + /** ConstantPathNode */ + PM_CONSTANT_PATH_NODE = 37, + + /** ConstantPathOperatorWriteNode */ + PM_CONSTANT_PATH_OPERATOR_WRITE_NODE = 38, + + /** ConstantPathOrWriteNode */ + PM_CONSTANT_PATH_OR_WRITE_NODE = 39, + + /** ConstantPathTargetNode */ + PM_CONSTANT_PATH_TARGET_NODE = 40, + + /** ConstantPathWriteNode */ + PM_CONSTANT_PATH_WRITE_NODE = 41, + + /** ConstantReadNode */ + PM_CONSTANT_READ_NODE = 42, + + /** ConstantTargetNode */ + PM_CONSTANT_TARGET_NODE = 43, + + /** ConstantWriteNode */ + PM_CONSTANT_WRITE_NODE = 44, + + /** DefNode */ + PM_DEF_NODE = 45, + + /** DefinedNode */ + PM_DEFINED_NODE = 46, + + /** ElseNode */ + PM_ELSE_NODE = 47, + + /** EmbeddedStatementsNode */ + PM_EMBEDDED_STATEMENTS_NODE = 48, + + /** EmbeddedVariableNode */ + PM_EMBEDDED_VARIABLE_NODE = 49, + + /** EnsureNode */ + PM_ENSURE_NODE = 50, + + /** FalseNode */ + PM_FALSE_NODE = 51, + + /** FindPatternNode */ + PM_FIND_PATTERN_NODE = 52, + + /** FlipFlopNode */ + PM_FLIP_FLOP_NODE = 53, + + /** FloatNode */ + PM_FLOAT_NODE = 54, + + /** ForNode */ + PM_FOR_NODE = 55, + + /** ForwardingArgumentsNode */ + PM_FORWARDING_ARGUMENTS_NODE = 56, + + /** ForwardingParameterNode */ + PM_FORWARDING_PARAMETER_NODE = 57, + + /** ForwardingSuperNode */ + PM_FORWARDING_SUPER_NODE = 58, + + /** GlobalVariableAndWriteNode */ + PM_GLOBAL_VARIABLE_AND_WRITE_NODE = 59, + + /** GlobalVariableOperatorWriteNode */ + PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE = 60, + + /** GlobalVariableOrWriteNode */ + PM_GLOBAL_VARIABLE_OR_WRITE_NODE = 61, + + /** GlobalVariableReadNode */ + PM_GLOBAL_VARIABLE_READ_NODE = 62, + + /** GlobalVariableTargetNode */ + PM_GLOBAL_VARIABLE_TARGET_NODE = 63, + + /** GlobalVariableWriteNode */ + PM_GLOBAL_VARIABLE_WRITE_NODE = 64, + + /** HashNode */ + PM_HASH_NODE = 65, + + /** HashPatternNode */ + PM_HASH_PATTERN_NODE = 66, + + /** IfNode */ + PM_IF_NODE = 67, + + /** ImaginaryNode */ + PM_IMAGINARY_NODE = 68, + + /** ImplicitNode */ + PM_IMPLICIT_NODE = 69, + + /** ImplicitRestNode */ + PM_IMPLICIT_REST_NODE = 70, + + /** InNode */ + PM_IN_NODE = 71, + + /** IndexAndWriteNode */ + PM_INDEX_AND_WRITE_NODE = 72, + + /** IndexOperatorWriteNode */ + PM_INDEX_OPERATOR_WRITE_NODE = 73, + + /** IndexOrWriteNode */ + PM_INDEX_OR_WRITE_NODE = 74, + + /** IndexTargetNode */ + PM_INDEX_TARGET_NODE = 75, + + /** InstanceVariableAndWriteNode */ + PM_INSTANCE_VARIABLE_AND_WRITE_NODE = 76, + + /** InstanceVariableOperatorWriteNode */ + PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE = 77, + + /** InstanceVariableOrWriteNode */ + PM_INSTANCE_VARIABLE_OR_WRITE_NODE = 78, + + /** InstanceVariableReadNode */ + PM_INSTANCE_VARIABLE_READ_NODE = 79, + + /** InstanceVariableTargetNode */ + PM_INSTANCE_VARIABLE_TARGET_NODE = 80, + + /** InstanceVariableWriteNode */ + PM_INSTANCE_VARIABLE_WRITE_NODE = 81, + + /** IntegerNode */ + PM_INTEGER_NODE = 82, + + /** InterpolatedMatchLastLineNode */ + PM_INTERPOLATED_MATCH_LAST_LINE_NODE = 83, + + /** InterpolatedRegularExpressionNode */ + PM_INTERPOLATED_REGULAR_EXPRESSION_NODE = 84, + + /** InterpolatedStringNode */ + PM_INTERPOLATED_STRING_NODE = 85, + + /** InterpolatedSymbolNode */ + PM_INTERPOLATED_SYMBOL_NODE = 86, + + /** InterpolatedXStringNode */ + PM_INTERPOLATED_X_STRING_NODE = 87, + + /** ItLocalVariableReadNode */ + PM_IT_LOCAL_VARIABLE_READ_NODE = 88, + + /** ItParametersNode */ + PM_IT_PARAMETERS_NODE = 89, + + /** KeywordHashNode */ + PM_KEYWORD_HASH_NODE = 90, + + /** KeywordRestParameterNode */ + PM_KEYWORD_REST_PARAMETER_NODE = 91, + + /** LambdaNode */ + PM_LAMBDA_NODE = 92, + + /** LocalVariableAndWriteNode */ + PM_LOCAL_VARIABLE_AND_WRITE_NODE = 93, + + /** LocalVariableOperatorWriteNode */ + PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE = 94, + + /** LocalVariableOrWriteNode */ + PM_LOCAL_VARIABLE_OR_WRITE_NODE = 95, + + /** LocalVariableReadNode */ + PM_LOCAL_VARIABLE_READ_NODE = 96, + + /** LocalVariableTargetNode */ + PM_LOCAL_VARIABLE_TARGET_NODE = 97, + + /** LocalVariableWriteNode */ + PM_LOCAL_VARIABLE_WRITE_NODE = 98, + + /** MatchLastLineNode */ + PM_MATCH_LAST_LINE_NODE = 99, + + /** MatchPredicateNode */ + PM_MATCH_PREDICATE_NODE = 100, + + /** MatchRequiredNode */ + PM_MATCH_REQUIRED_NODE = 101, + + /** MatchWriteNode */ + PM_MATCH_WRITE_NODE = 102, + + /** MissingNode */ + PM_MISSING_NODE = 103, + + /** ModuleNode */ + PM_MODULE_NODE = 104, + + /** MultiTargetNode */ + PM_MULTI_TARGET_NODE = 105, + + /** MultiWriteNode */ + PM_MULTI_WRITE_NODE = 106, + + /** NextNode */ + PM_NEXT_NODE = 107, + + /** NilNode */ + PM_NIL_NODE = 108, + + /** NoKeywordsParameterNode */ + PM_NO_KEYWORDS_PARAMETER_NODE = 109, + + /** NumberedParametersNode */ + PM_NUMBERED_PARAMETERS_NODE = 110, + + /** NumberedReferenceReadNode */ + PM_NUMBERED_REFERENCE_READ_NODE = 111, + + /** OptionalKeywordParameterNode */ + PM_OPTIONAL_KEYWORD_PARAMETER_NODE = 112, + + /** OptionalParameterNode */ + PM_OPTIONAL_PARAMETER_NODE = 113, + + /** OrNode */ + PM_OR_NODE = 114, + + /** ParametersNode */ + PM_PARAMETERS_NODE = 115, + + /** ParenthesesNode */ + PM_PARENTHESES_NODE = 116, + + /** PinnedExpressionNode */ + PM_PINNED_EXPRESSION_NODE = 117, + + /** PinnedVariableNode */ + PM_PINNED_VARIABLE_NODE = 118, + + /** PostExecutionNode */ + PM_POST_EXECUTION_NODE = 119, + + /** PreExecutionNode */ + PM_PRE_EXECUTION_NODE = 120, + + /** ProgramNode */ + PM_PROGRAM_NODE = 121, + + /** RangeNode */ + PM_RANGE_NODE = 122, + + /** RationalNode */ + PM_RATIONAL_NODE = 123, + + /** RedoNode */ + PM_REDO_NODE = 124, + + /** RegularExpressionNode */ + PM_REGULAR_EXPRESSION_NODE = 125, + + /** RequiredKeywordParameterNode */ + PM_REQUIRED_KEYWORD_PARAMETER_NODE = 126, + + /** RequiredParameterNode */ + PM_REQUIRED_PARAMETER_NODE = 127, + + /** RescueModifierNode */ + PM_RESCUE_MODIFIER_NODE = 128, + + /** RescueNode */ + PM_RESCUE_NODE = 129, + + /** RestParameterNode */ + PM_REST_PARAMETER_NODE = 130, + + /** RetryNode */ + PM_RETRY_NODE = 131, + + /** ReturnNode */ + PM_RETURN_NODE = 132, + + /** SelfNode */ + PM_SELF_NODE = 133, + + /** ShareableConstantNode */ + PM_SHAREABLE_CONSTANT_NODE = 134, + + /** SingletonClassNode */ + PM_SINGLETON_CLASS_NODE = 135, + + /** SourceEncodingNode */ + PM_SOURCE_ENCODING_NODE = 136, + + /** SourceFileNode */ + PM_SOURCE_FILE_NODE = 137, + + /** SourceLineNode */ + PM_SOURCE_LINE_NODE = 138, + + /** SplatNode */ + PM_SPLAT_NODE = 139, + + /** StatementsNode */ + PM_STATEMENTS_NODE = 140, + + /** StringNode */ + PM_STRING_NODE = 141, + + /** SuperNode */ + PM_SUPER_NODE = 142, + + /** SymbolNode */ + PM_SYMBOL_NODE = 143, + + /** TrueNode */ + PM_TRUE_NODE = 144, + + /** UndefNode */ + PM_UNDEF_NODE = 145, + + /** UnlessNode */ + PM_UNLESS_NODE = 146, + + /** UntilNode */ + PM_UNTIL_NODE = 147, + + /** WhenNode */ + PM_WHEN_NODE = 148, + + /** WhileNode */ + PM_WHILE_NODE = 149, + + /** XStringNode */ + PM_X_STRING_NODE = 150, + + /** YieldNode */ + PM_YIELD_NODE = 151, + + /** 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. + */ +static const pm_node_flags_t PM_NODE_FLAG_NEWLINE = 0x1; +static const pm_node_flags_t PM_NODE_FLAG_STATIC_LITERAL = 0x2; + +/** + * 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)) + +/** + * Return true if the given flag is set on the given node. + */ +#define PM_NODE_FLAG_P(node, flag) ((((pm_node_t *)(node))->flags & (flag)) != 0) + +/** + * 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; + + /** + * The unique identifier for this node, which is deterministic based on the + * source. It is used to identify unique nodes across parses. + */ + uint32_t node_id; + + /** + * 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 + * + * Represents the use of the `alias` keyword to alias a global variable. + * + * alias $foo $bar + * ^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents the new name of the global variable that can be used after aliasing. + * + * alias $foo $bar + * ^^^^ + */ + struct pm_node *new_name; + + /** + * AliasGlobalVariableNode#old_name + * + * Represents the old name of the global variable that can be used before aliasing. + * + * alias $foo $bar + * ^^^^ + */ + struct pm_node *old_name; + + /** + * AliasGlobalVariableNode#keyword_loc + * + * The location of the `alias` keyword. + * + * alias $foo $bar + * ^^^^^ + */ + pm_location_t keyword_loc; +} pm_alias_global_variable_node_t; + +/** + * AliasMethodNode + * + * Represents the use of the `alias` keyword to alias a method. + * + * alias foo bar + * ^^^^^^^^^^^^^ + * + * 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 + * + * Represents the new name of the method that will be aliased. + * + * alias foo bar + * ^^^ + * + * alias :foo :bar + * ^^^^ + * + * alias :"#{foo}" :"#{bar}" + * ^^^^^^^^^ + */ + struct pm_node *new_name; + + /** + * AliasMethodNode#old_name + * + * Represents the old name of the method that will be aliased. + * + * alias foo bar + * ^^^ + * + * alias :foo :bar + * ^^^^ + * + * alias :"#{foo}" :"#{bar}" + * ^^^^^^^^^ + */ + struct pm_node *old_name; + + /** + * AliasMethodNode#keyword_loc + * + * Represents the location of the `alias` keyword. + * + * alias foo bar + * ^^^^^ + */ + pm_location_t keyword_loc; +} pm_alias_method_node_t; + +/** + * AlternationPatternNode + * + * Represents an alternation pattern in pattern matching. + * + * foo => bar | baz + * ^^^^^^^^^ + * + * 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 + * + * Represents the left side of the expression. + * + * foo => bar | baz + * ^^^ + */ + struct pm_node *left; + + /** + * AlternationPatternNode#right + * + * Represents the right side of the expression. + * + * foo => bar | baz + * ^^^ + */ + struct pm_node *right; + + /** + * AlternationPatternNode#operator_loc + * + * Represents the alternation operator location. + * + * foo => bar | baz + * ^ + */ + pm_location_t operator_loc; +} pm_alternation_pattern_node_t; + +/** + * AndNode + * + * Represents the use of the `&&` operator or the `and` keyword. + * + * left and right + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_AND_NODE + * + * @extends pm_node_t + */ +typedef struct pm_and_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * AndNode#left + * + * Represents the left side of the expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * left and right + * ^^^^ + * + * 1 && 2 + * ^ + */ + struct pm_node *left; + + /** + * AndNode#right + * + * Represents the right side of the expression. + * + * left && right + * ^^^^^ + * + * 1 and 2 + * ^ + */ + struct pm_node *right; + + /** + * AndNode#operator_loc + * + * The location of the `and` keyword or the `&&` operator. + * + * left and right + * ^^^ + */ + pm_location_t operator_loc; +} pm_and_node_t; + +/** + * ArgumentsNode + * + * Represents a set of arguments to a method or a keyword. + * + * return foo, bar, baz + * ^^^^^^^^^^^^^ + * + * Type: ::PM_ARGUMENTS_NODE + + * Flags (#pm_arguments_node_flags): + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS + * + * @extends pm_node_t + */ +typedef struct pm_arguments_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ArgumentsNode#arguments + * + * The list of arguments, if present. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo(bar, baz) + * ^^^^^^^^ + */ + struct pm_node_list arguments; +} pm_arguments_node_t; + +/** + * ArrayNode + * + * Represents an array literal. This can be a regular array using brackets or a special array using % like %w or %i. + * + * [1, 2, 3] + * ^^^^^^^^^ + * + * Type: ::PM_ARRAY_NODE + + * Flags (#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 + * + * Represent the list of zero or more [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression) within the array. + */ + struct pm_node_list elements; + + /** + * ArrayNode#opening_loc + * + * Represents the optional source location for the opening token. + * + * [1,2,3] # "[" + * %w[foo bar baz] # "%w[" + * %I(apple orange banana) # "%I(" + * foo = 1, 2, 3 # nil + */ + pm_location_t opening_loc; + + /** + * ArrayNode#closing_loc + * + * Represents the optional source location for the closing token. + * + * [1,2,3] # "]" + * %w[foo bar baz] # "]" + * %I(apple orange banana) # ")" + * foo = 1, 2, 3 # nil + */ + pm_location_t closing_loc; +} pm_array_node_t; + +/** + * ArrayPatternNode + * + * Represents an array pattern in pattern matching. + * + * foo in 1, 2 + * ^^^^^^^^^^^ + * + * foo in [1, 2] + * ^^^^^^^^^^^^^ + * + * foo in *bar + * ^^^^^^^^^^^ + * + * foo in Bar[] + * ^^^^^^^^^^^^ + * + * foo in Bar[1, 2, 3] + * ^^^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents the required elements of the array pattern. + * + * foo in [1, 2] + * ^ ^ + */ + struct pm_node_list requireds; + + /** + * ArrayPatternNode#rest + * + * Represents the rest element of the array pattern. + * + * foo in *bar + * ^^^^ + */ + struct pm_node *rest; + + /** + * ArrayPatternNode#posts + * + * Represents the elements after the rest element of the array pattern. + * + * foo in *bar, baz + * ^^^ + */ + struct pm_node_list posts; + + /** + * ArrayPatternNode#opening_loc + * + * Represents the opening location of the array pattern. + * + * foo in [1, 2] + * ^ + */ + pm_location_t opening_loc; + + /** + * ArrayPatternNode#closing_loc + * + * Represents the closing location of the array pattern. + * + * foo in [1, 2] + * ^ + */ + pm_location_t closing_loc; +} pm_array_pattern_node_t; + +/** + * AssocNode + * + * Represents a hash key/value pair. + * + * { a => b } + * ^^^^^^ + * + * Type: ::PM_ASSOC_NODE + * + * @extends pm_node_t + */ +typedef struct pm_assoc_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * AssocNode#key + * + * The key of the association. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * { a: b } + * ^ + * + * { foo => bar } + * ^^^ + * + * { def a; end => 1 } + * ^^^^^^^^^^ + */ + struct pm_node *key; + + /** + * AssocNode#value + * + * The value of the association, if present. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * { foo => bar } + * ^^^ + * + * { x: 1 } + * ^ + */ + struct pm_node *value; + + /** + * AssocNode#operator_loc + * + * The location of the `=>` operator, if present. + * + * { foo => bar } + * ^^ + */ + pm_location_t operator_loc; +} pm_assoc_node_t; + +/** + * AssocSplatNode + * + * Represents a splat in a hash literal. + * + * { **foo } + * ^^^^^ + * + * 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 + * + * The value to be splatted, if present. Will be missing when keyword rest argument forwarding is used. + * + * { **foo } + * ^^^ + */ + struct pm_node *value; + + /** + * AssocSplatNode#operator_loc + * + * The location of the `**` operator. + * + * { **x } + * ^^ + */ + pm_location_t operator_loc; +} pm_assoc_splat_node_t; + +/** + * BackReferenceReadNode + * + * Represents reading a reference to a field in the previous match. + * + * $' + * ^^ + * + * 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 + * + * The name of the back-reference variable, including the leading `$`. + * + * $& # name `:$&` + * + * $+ # name `:$+` + */ + pm_constant_id_t name; +} pm_back_reference_read_node_t; + +/** + * BeginNode + * + * Represents a begin statement. + * + * begin + * foo + * end + * ^^^^^ + * + * 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 + * + * Represents the location of the `begin` keyword. + * + * begin x end + * ^^^^^ + */ + pm_location_t begin_keyword_loc; + + /** + * BeginNode#statements + * + * Represents the statements within the begin block. + * + * begin x end + * ^ + */ + struct pm_statements_node *statements; + + /** + * BeginNode#rescue_clause + * + * Represents the rescue clause within the begin block. + * + * begin x; rescue y; end + * ^^^^^^^^ + */ + struct pm_rescue_node *rescue_clause; + + /** + * BeginNode#else_clause + * + * Represents the else clause within the begin block. + * + * begin x; rescue y; else z; end + * ^^^^^^ + */ + struct pm_else_node *else_clause; + + /** + * BeginNode#ensure_clause + * + * Represents the ensure clause within the begin block. + * + * begin x; ensure y; end + * ^^^^^^^^ + */ + struct pm_ensure_node *ensure_clause; + + /** + * BeginNode#end_keyword_loc + * + * Represents the location of the `end` keyword. + * + * begin x end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_begin_node_t; + +/** + * BlockArgumentNode + * + * Represents a block argument using `&`. + * + * bar(&args) + * ^^^^^^^^^^ + * + * 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 + * + * The expression that is being passed as a block argument. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo(&args) + * ^^^^^ + */ + struct pm_node *expression; + + /** + * BlockArgumentNode#operator_loc + * + * Represents the location of the `&` operator. + * + * foo(&args) + * ^ + */ + pm_location_t operator_loc; +} pm_block_argument_node_t; + +/** + * BlockLocalVariableNode + * + * Represents a block local variable. + * + * a { |; b| } + * ^ + * + * Type: ::PM_BLOCK_LOCAL_VARIABLE_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_block_local_variable_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BlockLocalVariableNode#name + * + * The name of the block local variable. + * + * a { |; b| } # name `:b` + * ^ + */ + pm_constant_id_t name; +} pm_block_local_variable_node_t; + +/** + * BlockNode + * + * Represents a block of ruby code. + * + * [1, 2, 3].each { |i| puts x } + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_BLOCK_NODE + * + * @extends pm_node_t + */ +typedef struct pm_block_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BlockNode#locals + * + * The local variables declared in the block. + * + * [1, 2, 3].each { |i| puts x } # locals: [:i] + * ^ + */ + pm_constant_id_list_t locals; + + /** + * BlockNode#parameters + * + * The parameters of the block. + * + * [1, 2, 3].each { |i| puts x } + * ^^^ + * [1, 2, 3].each { puts _1 } + * ^^^^^^^^^^^ + * [1, 2, 3].each { puts it } + * ^^^^^^^^^^^ + */ + struct pm_node *parameters; + + /** + * BlockNode#body + * + * The body of the block. + * + * [1, 2, 3].each { |i| puts x } + * ^^^^^^ + */ + struct pm_node *body; + + /** + * BlockNode#opening_loc + * + * Represents the location of the opening `|`. + * + * [1, 2, 3].each { |i| puts x } + * ^ + */ + pm_location_t opening_loc; + + /** + * BlockNode#closing_loc + * + * Represents the location of the closing `|`. + * + * [1, 2, 3].each { |i| puts x } + * ^ + */ + pm_location_t closing_loc; +} pm_block_node_t; + +/** + * BlockParameterNode + * + * Represents a block parameter of a method, block, or lambda definition. + * + * def a(&b) + * ^^ + * end + * + * Type: ::PM_BLOCK_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_block_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BlockParameterNode#name + * + * The name of the block parameter. + * + * def a(&b) # name `:b` + * ^ + * end + */ + pm_constant_id_t name; + + /** + * BlockParameterNode#name_loc + * + * Represents the location of the block parameter name. + * + * def a(&b) + * ^ + */ + pm_location_t name_loc; + + /** + * BlockParameterNode#operator_loc + * + * Represents the location of the `&` operator. + * + * def a(&b) + * ^ + * end + */ + pm_location_t operator_loc; +} pm_block_parameter_node_t; + +/** + * BlockParametersNode + * + * Represents a block's parameters declaration. + * + * -> (a, b = 1; local) { } + * ^^^^^^^^^^^^^^^^^ + * + * foo do |a, b = 1; local| + * ^^^^^^^^^^^^^^^^^ + * end + * + * 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 + * + * Represents the parameters of the block. + * + * -> (a, b = 1; local) { } + * ^^^^^^^^ + * + * foo do |a, b = 1; local| + * ^^^^^^^^ + * end + */ + struct pm_parameters_node *parameters; + + /** + * BlockParametersNode#locals + * + * Represents the local variables of the block. + * + * -> (a, b = 1; local) { } + * ^^^^^ + * + * foo do |a, b = 1; local| + * ^^^^^ + * end + */ + struct pm_node_list locals; + + /** + * BlockParametersNode#opening_loc + * + * Represents the opening location of the block parameters. + * + * -> (a, b = 1; local) { } + * ^ + * + * foo do |a, b = 1; local| + * ^ + * end + */ + pm_location_t opening_loc; + + /** + * BlockParametersNode#closing_loc + * + * Represents the closing location of the block parameters. + * + * -> (a, b = 1; local) { } + * ^ + * + * foo do |a, b = 1; local| + * ^ + * end + */ + pm_location_t closing_loc; +} pm_block_parameters_node_t; + +/** + * BreakNode + * + * Represents the use of the `break` keyword. + * + * break foo + * ^^^^^^^^^ + * + * Type: ::PM_BREAK_NODE + * + * @extends pm_node_t + */ +typedef struct pm_break_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BreakNode#arguments + * + * The arguments to the break statement, if present. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * break foo + * ^^^ + */ + struct pm_arguments_node *arguments; + + /** + * BreakNode#keyword_loc + * + * The location of the `break` keyword. + * + * break foo + * ^^^^^ + */ + pm_location_t keyword_loc; +} pm_break_node_t; + +/** + * CallAndWriteNode + * + * Represents the use of the `&&=` operator on a call. + * + * foo.bar &&= value + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CALL_AND_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallAndWriteNode#receiver + * + * The object that the method is being called on. This can be either `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo.bar &&= value + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallAndWriteNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar &&= value + * ^ + */ + pm_location_t call_operator_loc; + + /** + * CallAndWriteNode#message_loc + * + * Represents the location of the message. + * + * foo.bar &&= value + * ^^^ + */ + pm_location_t message_loc; + + /** + * CallAndWriteNode#read_name + * + * Represents the name of the method being called. + * + * foo.bar &&= value # read_name `:bar` + * ^^^ + */ + pm_constant_id_t read_name; + + /** + * CallAndWriteNode#write_name + * + * Represents the name of the method being written to. + * + * foo.bar &&= value # write_name `:bar=` + * ^^^ + */ + pm_constant_id_t write_name; + + /** + * CallAndWriteNode#operator_loc + * + * Represents the location of the operator. + * + * foo.bar &&= value + * ^^^ + */ + pm_location_t operator_loc; + + /** + * CallAndWriteNode#value + * + * Represents the value being assigned. + * + * foo.bar &&= value + * ^^^^^ + */ + struct pm_node *value; +} pm_call_and_write_node_t; + +/** + * CallNode + * + * Represents a method call, in all of the various forms that can take. + * + * foo + * ^^^ + * + * foo() + * ^^^^^ + * + * +foo + * ^^^^ + * + * foo + bar + * ^^^^^^^^^ + * + * foo.bar + * ^^^^^^^ + * + * foo&.bar + * ^^^^^^^^ + * + * Type: ::PM_CALL_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallNode#receiver + * + * The object that the method is being called on. This can be either `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo.bar + * ^^^ + * + * +foo + * ^^^ + * + * foo + bar + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar + * ^ + * + * foo&.bar + * ^^ + */ + pm_location_t call_operator_loc; + + /** + * CallNode#name + * + * Represents the name of the method being called. + * + * foo.bar # name `:foo` + * ^^^ + */ + pm_constant_id_t name; + + /** + * CallNode#message_loc + * + * Represents the location of the message. + * + * foo.bar + * ^^^ + */ + pm_location_t message_loc; + + /** + * CallNode#opening_loc + * + * Represents the location of the left parenthesis. + * foo(bar) + * ^ + */ + pm_location_t opening_loc; + + /** + * CallNode#arguments + * + * Represents the arguments to the method call. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo(bar) + * ^^^ + */ + struct pm_arguments_node *arguments; + + /** + * CallNode#closing_loc + * + * Represents the location of the right parenthesis. + * + * foo(bar) + * ^ + */ + pm_location_t closing_loc; + + /** + * CallNode#block + * + * Represents the block that is being passed to the method. + * + * foo { |a| a } + * ^^^^^^^^^ + */ + struct pm_node *block; +} pm_call_node_t; + +/** + * CallOperatorWriteNode + * + * Represents the use of an assignment operator on a call. + * + * foo.bar += baz + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_CALL_OPERATOR_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallOperatorWriteNode#receiver + * + * The object that the method is being called on. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo.bar += value + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallOperatorWriteNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar += value + * ^ + */ + pm_location_t call_operator_loc; + + /** + * CallOperatorWriteNode#message_loc + * + * Represents the location of the message. + * + * foo.bar += value + * ^^^ + */ + pm_location_t message_loc; + + /** + * CallOperatorWriteNode#read_name + * + * Represents the name of the method being called. + * + * foo.bar += value # read_name `:bar` + * ^^^ + */ + pm_constant_id_t read_name; + + /** + * CallOperatorWriteNode#write_name + * + * Represents the name of the method being written to. + * + * foo.bar += value # write_name `:bar=` + * ^^^ + */ + pm_constant_id_t write_name; + + /** + * CallOperatorWriteNode#binary_operator + * + * Represents the binary operator being used. + * + * foo.bar += value # binary_operator `:+` + * ^ + */ + pm_constant_id_t binary_operator; + + /** + * CallOperatorWriteNode#binary_operator_loc + * + * Represents the location of the binary operator. + * + * foo.bar += value + * ^^ + */ + pm_location_t binary_operator_loc; + + /** + * CallOperatorWriteNode#value + * + * Represents the value being assigned. + * + * foo.bar += value + * ^^^^^ + */ + struct pm_node *value; +} pm_call_operator_write_node_t; + +/** + * CallOrWriteNode + * + * Represents the use of the `||=` operator on a call. + * + * foo.bar ||= value + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CALL_OR_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallOrWriteNode#receiver + * + * The object that the method is being called on. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo.bar ||= value + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallOrWriteNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar ||= value + * ^ + */ + pm_location_t call_operator_loc; + + /** + * CallOrWriteNode#message_loc + * + * Represents the location of the message. + * + * foo.bar ||= value + * ^^^ + */ + pm_location_t message_loc; + + /** + * CallOrWriteNode#read_name + * + * Represents the name of the method being called. + * + * foo.bar ||= value # read_name `:bar` + * ^^^ + */ + pm_constant_id_t read_name; + + /** + * CallOrWriteNode#write_name + * + * Represents the name of the method being written to. + * + * foo.bar ||= value # write_name `:bar=` + * ^^^ + */ + pm_constant_id_t write_name; + + /** + * CallOrWriteNode#operator_loc + * + * Represents the location of the operator. + * + * foo.bar ||= value + * ^^^ + */ + pm_location_t operator_loc; + + /** + * CallOrWriteNode#value + * + * Represents the value being assigned. + * + * foo.bar ||= value + * ^^^^^ + */ + struct pm_node *value; +} pm_call_or_write_node_t; + +/** + * CallTargetNode + * + * Represents assigning to a method call. + * + * foo.bar, = 1 + * ^^^^^^^ + * + * begin + * rescue => foo.bar + * ^^^^^^^ + * end + * + * for foo.bar in baz do end + * ^^^^^^^ + * + * Type: ::PM_CALL_TARGET_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallTargetNode#receiver + * + * The object that the method is being called on. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo.bar = 1 + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallTargetNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar = 1 + * ^ + */ + pm_location_t call_operator_loc; + + /** + * CallTargetNode#name + * + * Represents the name of the method being called. + * + * foo.bar = 1 # name `:foo` + * ^^^ + */ + pm_constant_id_t name; + + /** + * CallTargetNode#message_loc + * + * Represents the location of the message. + * + * foo.bar = 1 + * ^^^ + */ + pm_location_t message_loc; +} pm_call_target_node_t; + +/** + * CapturePatternNode + * + * Represents assigning to a local variable in pattern matching. + * + * foo => [bar => baz] + * ^^^^^^^^^^^^ + * + * 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 + * + * Represents the value to capture. + * + * foo => bar + * ^^^ + */ + struct pm_node *value; + + /** + * CapturePatternNode#target + * + * Represents the target of the capture. + * + * foo => bar + * ^^^ + */ + struct pm_local_variable_target_node *target; + + /** + * CapturePatternNode#operator_loc + * + * Represents the location of the `=>` operator. + * + * foo => bar + * ^^ + */ + pm_location_t operator_loc; +} pm_capture_pattern_node_t; + +/** + * CaseMatchNode + * + * Represents the use of a case statement for pattern matching. + * + * case true + * in false + * end + * ^^^^^^^^^ + * + * 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 + * + * Represents the predicate of the case match. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * case true; in false; end + * ^^^^ + */ + struct pm_node *predicate; + + /** + * CaseMatchNode#conditions + * + * Represents the conditions of the case match. + * + * case true; in false; end + * ^^^^^^^^ + */ + struct pm_node_list conditions; + + /** + * CaseMatchNode#else_clause + * + * Represents the else clause of the case match. + * + * case true; in false; else; end + * ^^^^ + */ + struct pm_else_node *else_clause; + + /** + * CaseMatchNode#case_keyword_loc + * + * Represents the location of the `case` keyword. + * + * case true; in false; end + * ^^^^ + */ + pm_location_t case_keyword_loc; + + /** + * CaseMatchNode#end_keyword_loc + * + * Represents the location of the `end` keyword. + * + * case true; in false; end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_case_match_node_t; + +/** + * CaseNode + * + * Represents the use of a case statement. + * + * case true + * when false + * end + * ^^^^^^^^^^ + * + * Type: ::PM_CASE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_case_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CaseNode#predicate + * + * Represents the predicate of the case statement. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * case true; when false; end + * ^^^^ + */ + struct pm_node *predicate; + + /** + * CaseNode#conditions + * + * Represents the conditions of the case statement. + * + * case true; when false; end + * ^^^^^^^^^^ + */ + struct pm_node_list conditions; + + /** + * CaseNode#else_clause + * + * Represents the else clause of the case statement. + * + * case true; when false; else; end + * ^^^^ + */ + struct pm_else_node *else_clause; + + /** + * CaseNode#case_keyword_loc + * + * Represents the location of the `case` keyword. + * + * case true; when false; end + * ^^^^ + */ + pm_location_t case_keyword_loc; + + /** + * CaseNode#end_keyword_loc + * + * Represents the location of the `end` keyword. + * + * case true; when false; end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_case_node_t; + +/** + * ClassNode + * + * Represents a class declaration involving the `class` keyword. + * + * class Foo end + * ^^^^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `&&=` operator for assignment to a class variable. + * + * @@target &&= value + * ^^^^^^^^^^^^^^^^^^ + * + * 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 + * + * The name of the class variable, which is a `@@` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers). + * + * @@target &&= value # name `:@@target` + * ^^^^^^^^ + */ + pm_constant_id_t name; + + /** + * ClassVariableAndWriteNode#name_loc + * + * Represents the location of the variable name. + * + * @@target &&= value + * ^^^^^^^^ + */ + pm_location_t name_loc; + + /** + * ClassVariableAndWriteNode#operator_loc + * + * Represents the location of the `&&=` operator. + * + * @@target &&= value + * ^^^ + */ + pm_location_t operator_loc; + + /** + * ClassVariableAndWriteNode#value + * + * Represents the value being assigned. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * @@target &&= value + * ^^^^^ + */ + struct pm_node *value; +} pm_class_variable_and_write_node_t; + +/** + * ClassVariableOperatorWriteNode + * + * Represents assigning to a class variable using an operator that isn't `=`. + * + * @@target += value + * ^^^^^^^^^^^^^^^^^ + * + * 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#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * ClassVariableOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * ClassVariableOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_class_variable_operator_write_node_t; + +/** + * ClassVariableOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a class variable. + * + * @@target ||= value + * ^^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents referencing a class variable. + * + * @@foo + * ^^^^^ + * + * 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 + * + * The name of the class variable, which is a `@@` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers). + * + * @@abc # name `:@@abc` + * + * @@_test # name `:@@_test` + */ + pm_constant_id_t name; +} pm_class_variable_read_node_t; + +/** + * ClassVariableTargetNode + * + * Represents writing to a class variable in a context that doesn't have an explicit value. + * + * @@foo, @@bar = baz + * ^^^^^ ^^^^^ + * + * 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 + * + * Represents writing to a class variable. + * + * @@foo = 1 + * ^^^^^^^^^ + * + * 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 + * + * The name of the class variable, which is a `@@` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers). + * + * @@abc = 123 # name `@@abc` + * + * @@_test = :test # name `@@_test` + */ + pm_constant_id_t name; + + /** + * ClassVariableWriteNode#name_loc + * + * The location of the variable name. + * + * @@foo = :bar + * ^^^^^ + */ + pm_location_t name_loc; + + /** + * ClassVariableWriteNode#value + * + * The value to write to the class variable. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * @@foo = :bar + * ^^^^ + * + * @@_xyz = 123 + * ^^^ + */ + struct pm_node *value; + + /** + * ClassVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * @@foo = :bar + * ^ + */ + pm_location_t operator_loc; +} pm_class_variable_write_node_t; + +/** + * ConstantAndWriteNode + * + * Represents the use of the `&&=` operator for assignment to a constant. + * + * Target &&= value + * ^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents assigning to a constant using an operator that isn't `=`. + * + * Target += value + * ^^^^^^^^^^^^^^^ + * + * 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#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * ConstantOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * ConstantOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_constant_operator_write_node_t; + +/** + * ConstantOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a constant. + * + * Target ||= value + * ^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `&&=` operator for assignment to a constant path. + * + * Parent::Child &&= value + * ^^^^^^^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents accessing a constant through a path of `::` operators. + * + * Foo::Bar + * ^^^^^^^^ + * + * 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 + * + * The left-hand node of the path, if present. It can be `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). It will be `nil` when the constant lookup is at the root of the module tree. + * + * Foo::Bar + * ^^^ + * + * self::Test + * ^^^^ + * + * a.b::C + * ^^^ + */ + struct pm_node *parent; + + /** + * ConstantPathNode#name + * + * The name of the constant being accessed. This could be `nil` in the event of a syntax error. + */ + pm_constant_id_t name; + + /** + * ConstantPathNode#delimiter_loc + * + * The location of the `::` delimiter. + * + * ::Foo + * ^^ + * + * One::Two + * ^^ + */ + pm_location_t delimiter_loc; + + /** + * ConstantPathNode#name_loc + * + * The location of the name of the constant. + * + * ::Foo + * ^^^ + * + * One::Two + * ^^^ + */ + pm_location_t name_loc; +} pm_constant_path_node_t; + +/** + * ConstantPathOperatorWriteNode + * + * Represents assigning to a constant path using an operator that isn't `=`. + * + * Parent::Child += value + * ^^^^^^^^^^^^^^^^^^^^^^ + * + * 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#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * ConstantPathOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * ConstantPathOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_constant_path_operator_write_node_t; + +/** + * ConstantPathOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a constant path. + * + * Parent::Child ||= value + * ^^^^^^^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents writing to a constant path in a context that doesn't have an explicit value. + * + * Foo::Foo, Bar::Bar = baz + * ^^^^^^^^ ^^^^^^^^ + * + * 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#name + */ + pm_constant_id_t name; + + /** + * ConstantPathTargetNode#delimiter_loc + */ + pm_location_t delimiter_loc; + + /** + * ConstantPathTargetNode#name_loc + */ + pm_location_t name_loc; +} pm_constant_path_target_node_t; + +/** + * ConstantPathWriteNode + * + * Represents writing to a constant path. + * + * ::Foo = 1 + * ^^^^^^^^^ + * + * Foo::Bar = 1 + * ^^^^^^^^^^^^ + * + * ::Foo::Bar = 1 + * ^^^^^^^^^^^^^^ + * + * 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 + * + * A node representing the constant path being written to. + * + * Foo::Bar = 1 + * ^^^^^^^^ + * + * ::Foo = :abc + * ^^^^^ + */ + struct pm_constant_path_node *target; + + /** + * ConstantPathWriteNode#operator_loc + * + * The location of the `=` operator. + * + * ::ABC = 123 + * ^ + */ + pm_location_t operator_loc; + + /** + * ConstantPathWriteNode#value + * + * The value to write to the constant path. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * FOO::BAR = :abc + * ^^^^ + */ + struct pm_node *value; +} pm_constant_path_write_node_t; + +/** + * ConstantReadNode + * + * Represents referencing a constant. + * + * Foo + * ^^^ + * + * 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 + * + * The name of the [constant](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#constants). + * + * X # name `:X` + * + * SOME_CONSTANT # name `:SOME_CONSTANT` + */ + pm_constant_id_t name; +} pm_constant_read_node_t; + +/** + * ConstantTargetNode + * + * Represents writing to a constant in a context that doesn't have an explicit value. + * + * Foo, Bar = baz + * ^^^ ^^^ + * + * 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 + * + * Represents writing to a constant. + * + * Foo = 1 + * ^^^^^^^ + * + * 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 + * + * The name of the [constant](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#constants). + * + * Foo = :bar # name `:Foo` + * + * XYZ = 1 # name `:XYZ` + */ + pm_constant_id_t name; + + /** + * ConstantWriteNode#name_loc + * + * The location of the constant name. + * + * FOO = 1 + * ^^^ + */ + pm_location_t name_loc; + + /** + * ConstantWriteNode#value + * + * The value to write to the constant. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * FOO = :bar + * ^^^^ + * + * MyClass = Class.new + * ^^^^^^^^^ + */ + struct pm_node *value; + + /** + * ConstantWriteNode#operator_loc + * + * The location of the `=` operator. + * + * FOO = :bar + * ^ + */ + pm_location_t operator_loc; +} pm_constant_write_node_t; + +/** + * DefNode + * + * Represents a method definition. + * + * def method + * end + * ^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `defined?` keyword. + * + * defined?(a) + * ^^^^^^^^^^^ + * + * 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 + * + * Represents an `else` clause in a `case`, `if`, or `unless` statement. + * + * if a then b else c end + * ^^^^^^^^^^ + * + * 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 + * + * Represents an interpolated set of statements. + * + * "foo #{bar}" + * ^^^^^^ + * + * 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 + * + * Represents an interpolated variable. + * + * "foo #@bar" + * ^^^^^ + * + * 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 + * + * Represents an `ensure` clause in a `begin` statement. + * + * begin + * foo + * ensure + * ^^^^^^ + * bar + * end + * + * 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 + * + * Represents the use of the literal `false` keyword. + * + * false + * ^^^^^ + * + * 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 + * + * Represents a find pattern in pattern matching. + * + * foo in *bar, baz, *qux + * ^^^^^^^^^^^^^^^ + * + * foo in [*bar, baz, *qux] + * ^^^^^^^^^^^^^^^^^ + * + * foo in Foo(*bar, baz, *qux) + * ^^^^^^^^^^^^^^^^^^^^ + * + * 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_splat_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 + * + * Represents the use of the `..` or `...` operators to create flip flops. + * + * baz if foo .. bar + * ^^^^^^^^^^ + * + * Type: ::PM_FLIP_FLOP_NODE + + * Flags (#pm_range_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 + * + * Represents a floating point number literal. + * + * 1.0 + * ^^^ + * + * Type: ::PM_FLOAT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_float_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * FloatNode#value + * + * The value of the floating point number as a Float. + */ + double value; +} pm_float_node_t; + +/** + * ForNode + * + * Represents the use of the `for` keyword. + * + * for i in a end + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_FOR_NODE + * + * @extends pm_node_t + */ +typedef struct pm_for_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ForNode#index + * + * The index expression for `for` loops. + * + * for i in a end + * ^ + */ + struct pm_node *index; + + /** + * ForNode#collection + * + * The collection to iterate over. + * + * for i in a end + * ^ + */ + struct pm_node *collection; + + /** + * ForNode#statements + * + * Represents the body of statements to execute for each iteration of the loop. + * + * for i in a + * foo(i) + * ^^^^^^ + * end + */ + struct pm_statements_node *statements; + + /** + * ForNode#for_keyword_loc + * + * The location of the `for` keyword. + * + * for i in a end + * ^^^ + */ + pm_location_t for_keyword_loc; + + /** + * ForNode#in_keyword_loc + * + * The location of the `in` keyword. + * + * for i in a end + * ^^ + */ + pm_location_t in_keyword_loc; + + /** + * ForNode#do_keyword_loc + * + * The location of the `do` keyword, if present. + * + * for i in a do end + * ^^ + */ + pm_location_t do_keyword_loc; + + /** + * ForNode#end_keyword_loc + * + * The location of the `end` keyword. + * + * for i in a end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_for_node_t; + +/** + * ForwardingArgumentsNode + * + * Represents forwarding all arguments to this method to another method. + * + * def foo(...) + * bar(...) + * ^^^ + * end + * + * 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 + * + * Represents the use of the forwarding parameter in a method, block, or lambda declaration. + * + * def foo(...) + * ^^^ + * end + * + * 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 + * + * Represents the use of the `super` keyword without parentheses or arguments. + * + * super + * ^^^^^ + * + * 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 + * + * Represents the use of the `&&=` operator for assignment to a global variable. + * + * $target &&= value + * ^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents assigning to a global variable using an operator that isn't `=`. + * + * $target += value + * ^^^^^^^^^^^^^^^^ + * + * 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#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * GlobalVariableOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * GlobalVariableOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_global_variable_operator_write_node_t; + +/** + * GlobalVariableOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a global variable. + * + * $target ||= value + * ^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents referencing a global variable. + * + * $foo + * ^^^^ + * + * 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 + * + * The name of the global variable, which is a `$` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifier). Alternatively, it can be one of the special global variables designated by a symbol. + * + * $foo # name `:$foo` + * + * $_Test # name `:$_Test` + */ + pm_constant_id_t name; +} pm_global_variable_read_node_t; + +/** + * GlobalVariableTargetNode + * + * Represents writing to a global variable in a context that doesn't have an explicit value. + * + * $foo, $bar = baz + * ^^^^ ^^^^ + * + * 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 + * + * Represents writing to a global variable. + * + * $foo = 1 + * ^^^^^^^^ + * + * 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 + * + * The name of the global variable, which is a `$` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifier). Alternatively, it can be one of the special global variables designated by a symbol. + * + * $foo = :bar # name `:$foo` + * + * $_Test = 123 # name `:$_Test` + */ + pm_constant_id_t name; + + /** + * GlobalVariableWriteNode#name_loc + * + * The location of the global variable's name. + * + * $foo = :bar + * ^^^^ + */ + pm_location_t name_loc; + + /** + * GlobalVariableWriteNode#value + * + * The value to write to the global variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * $foo = :bar + * ^^^^ + * + * $-xyz = 123 + * ^^^ + */ + struct pm_node *value; + + /** + * GlobalVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * $foo = :bar + * ^ + */ + pm_location_t operator_loc; +} pm_global_variable_write_node_t; + +/** + * HashNode + * + * Represents a hash literal. + * + * { a => b } + * ^^^^^^^^^^ + * + * Type: ::PM_HASH_NODE + * + * @extends pm_node_t + */ +typedef struct pm_hash_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * HashNode#opening_loc + * + * The location of the opening brace. + * + * { a => b } + * ^ + */ + pm_location_t opening_loc; + + /** + * HashNode#elements + * + * The elements of the hash. These can be either `AssocNode`s or `AssocSplatNode`s. + * + * { a: b } + * ^^^^ + * + * { **foo } + * ^^^^^ + */ + struct pm_node_list elements; + + /** + * HashNode#closing_loc + * + * The location of the closing brace. + * + * { a => b } + * ^ + */ + pm_location_t closing_loc; +} pm_hash_node_t; + +/** + * HashPatternNode + * + * Represents a hash pattern in pattern matching. + * + * foo => { a: 1, b: 2 } + * ^^^^^^^^^^^^^^ + * + * foo => { a: 1, b: 2, **c } + * ^^^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `if` keyword, either in the block form or the modifier form, or a ternary expression. + * + * bar if foo + * ^^^^^^^^^^ + * + * if foo then bar end + * ^^^^^^^^^^^^^^^^^^^ + * + * foo ? bar : baz + * ^^^^^^^^^^^^^^^ + * + * 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 + * + * The location of the `if` keyword if present. + * + * bar if foo + * ^^ + * + * The `if_keyword_loc` field will be `nil` when the `IfNode` represents a ternary expression. + */ + pm_location_t if_keyword_loc; + + /** + * IfNode#predicate + * + * The node for the condition the `IfNode` is testing. + * + * if foo + * ^^^ + * bar + * end + * + * bar if foo + * ^^^ + * + * foo ? bar : baz + * ^^^ + */ + struct pm_node *predicate; + + /** + * IfNode#then_keyword_loc + * + * The location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise. + * + * if foo then bar end + * ^^^^ + * + * a ? b : c + * ^ + */ + pm_location_t then_keyword_loc; + + /** + * IfNode#statements + * + * Represents the body of statements that will be executed when the predicate is evaluated as truthy. Will be `nil` when no body is provided. + * + * if foo + * bar + * ^^^ + * baz + * ^^^ + * end + */ + struct pm_statements_node *statements; + + /** + * IfNode#subsequent + * + * Represents an `ElseNode` or an `IfNode` when there is an `else` or an `elsif` in the `if` statement. + * + * if foo + * bar + * elsif baz + * ^^^^^^^^^ + * qux + * ^^^ + * end + * ^^^ + * + * if foo then bar else baz end + * ^^^^^^^^^^^^ + */ + struct pm_node *subsequent; + + /** + * IfNode#end_keyword_loc + * + * The location of the `end` keyword if present, `nil` otherwise. + * + * if foo + * bar + * end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_if_node_t; + +/** + * ImaginaryNode + * + * Represents an imaginary number literal. + * + * 1.0i + * ^^^^ + * + * 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 + * + * Represents a node that is implicitly being added to the tree but doesn't correspond directly to a node in the source. + * + * { foo: } + * ^^^^ + * + * { Foo: } + * ^^^^ + * + * foo in { bar: } + * ^^^^ + * + * 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 + * + * Represents using a trailing comma to indicate an implicit rest parameter. + * + * foo { |bar,| } + * ^ + * + * foo in [bar,] + * ^ + * + * for foo, in bar do end + * ^ + * + * foo, = bar + * ^ + * + * 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 + * + * Represents the use of the `in` keyword in a case statement. + * + * case a; in b then c end + * ^^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `&&=` operator on a call to the `[]` method. + * + * foo.bar[baz] &&= value + * ^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INDEX_AND_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @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_block_argument_node *block; + + /** + * IndexAndWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * IndexAndWriteNode#value + */ + struct pm_node *value; +} pm_index_and_write_node_t; + +/** + * IndexOperatorWriteNode + * + * Represents the use of an assignment operator on a call to `[]`. + * + * foo.bar[baz] += value + * ^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INDEX_OPERATOR_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @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_block_argument_node *block; + + /** + * IndexOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; + + /** + * IndexOperatorWriteNode#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * IndexOperatorWriteNode#value + */ + struct pm_node *value; +} pm_index_operator_write_node_t; + +/** + * IndexOrWriteNode + * + * Represents the use of the `||=` operator on a call to `[]`. + * + * foo.bar[baz] ||= value + * ^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INDEX_OR_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @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_block_argument_node *block; + + /** + * IndexOrWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * IndexOrWriteNode#value + */ + struct pm_node *value; +} pm_index_or_write_node_t; + +/** + * IndexTargetNode + * + * Represents assigning to an index. + * + * foo[bar], = 1 + * ^^^^^^^^ + * + * begin + * rescue => foo[bar] + * ^^^^^^^^ + * end + * + * for foo[bar] in baz do end + * ^^^^^^^^ + * + * Type: ::PM_INDEX_TARGET_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_index_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * IndexTargetNode#receiver + */ + struct pm_node *receiver; + + /** + * IndexTargetNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * IndexTargetNode#arguments + */ + struct pm_arguments_node *arguments; + + /** + * IndexTargetNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * IndexTargetNode#block + */ + struct pm_block_argument_node *block; +} pm_index_target_node_t; + +/** + * InstanceVariableAndWriteNode + * + * Represents the use of the `&&=` operator for assignment to an instance variable. + * + * @target &&= value + * ^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents assigning to an instance variable using an operator that isn't `=`. + * + * @target += value + * ^^^^^^^^^^^^^^^^ + * + * 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#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * InstanceVariableOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * InstanceVariableOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_instance_variable_operator_write_node_t; + +/** + * InstanceVariableOrWriteNode + * + * Represents the use of the `||=` operator for assignment to an instance variable. + * + * @target ||= value + * ^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents referencing an instance variable. + * + * @foo + * ^^^^ + * + * 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 + * + * The name of the instance variable, which is a `@` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers). + * + * @x # name `:@x` + * + * @_test # name `:@_test` + */ + pm_constant_id_t name; +} pm_instance_variable_read_node_t; + +/** + * InstanceVariableTargetNode + * + * Represents writing to an instance variable in a context that doesn't have an explicit value. + * + * @foo, @bar = baz + * ^^^^ ^^^^ + * + * 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 + * + * Represents writing to an instance variable. + * + * @foo = 1 + * ^^^^^^^^ + * + * 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 + * + * The name of the instance variable, which is a `@` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers). + * + * @x = :y # name `:@x` + * + * @_foo = "bar" # name `@_foo` + */ + pm_constant_id_t name; + + /** + * InstanceVariableWriteNode#name_loc + * + * The location of the variable name. + * + * @_x = 1 + * ^^^ + */ + pm_location_t name_loc; + + /** + * InstanceVariableWriteNode#value + * + * The value to write to the instance variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * @foo = :bar + * ^^^^ + * + * @_x = 1234 + * ^^^^ + */ + struct pm_node *value; + + /** + * InstanceVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * @x = y + * ^ + */ + pm_location_t operator_loc; +} pm_instance_variable_write_node_t; + +/** + * IntegerNode + * + * Represents an integer number literal. + * + * 1 + * ^ + * + * Type: ::PM_INTEGER_NODE + + * Flags (#pm_integer_base_flags): + * * ::PM_INTEGER_BASE_FLAGS_BINARY + * * ::PM_INTEGER_BASE_FLAGS_DECIMAL + * * ::PM_INTEGER_BASE_FLAGS_OCTAL + * * ::PM_INTEGER_BASE_FLAGS_HEXADECIMAL + * + * @extends pm_node_t + */ +typedef struct pm_integer_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * IntegerNode#value + * + * The value of the integer literal as a number. + */ + pm_integer_t value; +} pm_integer_node_t; + +/** + * InterpolatedMatchLastLineNode + * + * Represents a regular expression literal that contains interpolation that is being used in the predicate of a conditional to implicitly match against the last line read by an IO object. + * + * if /foo #{bar} baz/ then end + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INTERPOLATED_MATCH_LAST_LINE_NODE + + * Flags (#pm_regular_expression_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 + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING + * + * @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 + * + * Represents a regular expression literal that contains interpolation. + * + * /foo #{bar} baz/ + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INTERPOLATED_REGULAR_EXPRESSION_NODE + + * Flags (#pm_regular_expression_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 + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING + * + * @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 + * + * Represents a string literal that contains interpolation. + * + * "foo #{bar} baz" + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INTERPOLATED_STRING_NODE + + * Flags (#pm_interpolated_string_node_flags): + * * ::PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN + * * ::PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE + * + * @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 + * + * Represents a symbol literal that contains interpolation. + * + * :"foo #{bar} baz" + * ^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents an xstring literal that contains interpolation. + * + * `foo #{bar} baz` + * ^^^^^^^^^^^^^^^^ + * + * 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; + +/** + * ItLocalVariableReadNode + * + * Represents reading from the implicit `it` local variable. + * + * -> { it } + * ^^ + * + * Type: ::PM_IT_LOCAL_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_it_local_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_it_local_variable_read_node_t; + +/** + * ItParametersNode + * + * Represents an implicit set of parameters through the use of the `it` keyword within a block or lambda. + * + * -> { it + it } + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_IT_PARAMETERS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_it_parameters_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_it_parameters_node_t; + +/** + * KeywordHashNode + * + * Represents a hash literal without opening and closing braces. + * + * foo(a: b) + * ^^^^ + * + * Type: ::PM_KEYWORD_HASH_NODE + + * Flags (#pm_keyword_hash_node_flags): + * * ::PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS + * + * @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 + * + * Represents a keyword rest parameter to a method, block, or lambda definition. + * + * def a(**b) + * ^^^ + * end + * + * Type: ::PM_KEYWORD_REST_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @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 + * + * Represents using a lambda literal (not the lambda method call). + * + * ->(value) { value * 2 } + * ^^^^^^^^^^^^^^^^^^^^^^^ + * + * 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_node *parameters; + + /** + * LambdaNode#body + */ + struct pm_node *body; +} pm_lambda_node_t; + +/** + * LocalVariableAndWriteNode + * + * Represents the use of the `&&=` operator for assignment to a local variable. + * + * target &&= value + * ^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents assigning to a local variable using an operator that isn't `=`. + * + * target += value + * ^^^^^^^^^^^^^^^ + * + * 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#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * LocalVariableOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * LocalVariableOperatorWriteNode#name + */ + pm_constant_id_t name; + + /** + * LocalVariableOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; + + /** + * LocalVariableOperatorWriteNode#depth + */ + uint32_t depth; +} pm_local_variable_operator_write_node_t; + +/** + * LocalVariableOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a local variable. + * + * target ||= value + * ^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents reading a local variable. Note that this requires that a local variable of the same name has already been written to in the same scope, otherwise it is parsed as a method call. + * + * foo + * ^^^ + * + * 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 + * + * The name of the local variable, which is an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers). + * + * x # name `:x` + * + * _Test # name `:_Test` + * + * Note that this can also be an underscore followed by a number for the default block parameters. + * + * _1 # name `:_1` + */ + pm_constant_id_t name; + + /** + * LocalVariableReadNode#depth + * + * The number of visible scopes that should be searched to find the origin of this local variable. + * + * foo = 1; foo # depth 0 + * + * bar = 2; tap { bar } # depth 1 + * + * The specific rules for calculating the depth may differ from individual Ruby implementations, as they are not specified by the language. For more information, see [the Prism documentation](https://github.com/ruby/prism/blob/main/docs/local_variable_depth.md). + */ + uint32_t depth; +} pm_local_variable_read_node_t; + +/** + * LocalVariableTargetNode + * + * Represents writing to a local variable in a context that doesn't have an explicit value. + * + * foo, bar = baz + * ^^^ ^^^ + * + * 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 + * + * Represents writing to a local variable. + * + * foo = 1 + * ^^^^^^^ + * + * 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 + * + * The name of the local variable, which is an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers). + * + * foo = :bar # name `:foo` + * + * abc = 123 # name `:abc` + */ + pm_constant_id_t name; + + /** + * LocalVariableWriteNode#depth + * + * The number of semantic scopes we have to traverse to find the declaration of this variable. + * + * foo = 1 # depth 0 + * + * tap { foo = 1 } # depth 1 + * + * The specific rules for calculating the depth may differ from individual Ruby implementations, as they are not specified by the language. For more information, see [the Prism documentation](https://github.com/ruby/prism/blob/main/docs/local_variable_depth.md). + */ + uint32_t depth; + + /** + * LocalVariableWriteNode#name_loc + * + * The location of the variable name. + * + * foo = :bar + * ^^^ + */ + pm_location_t name_loc; + + /** + * LocalVariableWriteNode#value + * + * The value to write to the local variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo = :bar + * ^^^^ + * + * abc = 1234 + * ^^^^ + * + * Note that since the name of a local variable is known before the value is parsed, it is valid for a local variable to appear within the value of its own write. + * + * foo = foo + */ + struct pm_node *value; + + /** + * LocalVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * x = :y + * ^ + */ + pm_location_t operator_loc; +} pm_local_variable_write_node_t; + +/** + * MatchLastLineNode + * + * Represents a regular expression literal used in the predicate of a conditional to implicitly match against the last line read by an IO object. + * + * if /foo/i then end + * ^^^^^^ + * + * Type: ::PM_MATCH_LAST_LINE_NODE + + * Flags (#pm_regular_expression_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 + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING + * + * @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 + * + * Represents the use of the modifier `in` operator. + * + * foo in bar + * ^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `=>` operator. + * + * foo => bar + * ^^^^^^^^^^ + * + * 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 + * + * Represents writing local variables using a regular expression match with named capture groups. + * + * /(?bar)/ =~ baz + * ^^^^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents a node that is missing from the source and results in a syntax error. + * + * 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 + * + * Represents a module declaration involving the `module` keyword. + * + * module Foo end + * ^^^^^^^^^^^^^^ + * + * 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 + * + * Represents a multi-target expression. + * + * a, (b, c) = 1, 2, 3 + * ^^^^^^ + * + * This can be a part of `MultiWriteNode` as above, or the target of a `for` loop + * + * for a, b in [[1, 2], [3, 4]] + * ^^^^ + * + * 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 + * + * Represents the targets expressions before a splat node. + * + * a, (b, c, *) = 1, 2, 3, 4, 5 + * ^^^^ + * + * The splat node can be absent, in that case all target expressions are in the left field. + * + * a, (b, c) = 1, 2, 3, 4, 5 + * ^^^^ + */ + struct pm_node_list lefts; + + /** + * MultiTargetNode#rest + * + * Represents a splat node in the target expression. + * + * a, (b, *c) = 1, 2, 3, 4 + * ^^ + * + * The variable can be empty, this results in a `SplatNode` with a `nil` expression field. + * + * a, (b, *) = 1, 2, 3, 4 + * ^ + * + * If the `*` is omitted, this field will contain an `ImplicitRestNode` + * + * a, (b,) = 1, 2, 3, 4 + * ^ + */ + struct pm_node *rest; + + /** + * MultiTargetNode#rights + * + * Represents the targets expressions after a splat node. + * + * a, (*, b, c) = 1, 2, 3, 4, 5 + * ^^^^ + */ + struct pm_node_list rights; + + /** + * MultiTargetNode#lparen_loc + * + * The location of the opening parenthesis. + * + * a, (b, c) = 1, 2, 3 + * ^ + */ + pm_location_t lparen_loc; + + /** + * MultiTargetNode#rparen_loc + * + * The location of the closing parenthesis. + * + * a, (b, c) = 1, 2, 3 + * ^ + */ + pm_location_t rparen_loc; +} pm_multi_target_node_t; + +/** + * MultiWriteNode + * + * Represents a write to a multi-target expression. + * + * a, b, c = 1, 2, 3 + * ^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents the targets expressions before a splat node. + * + * a, b, * = 1, 2, 3, 4, 5 + * ^^^^ + * + * The splat node can be absent, in that case all target expressions are in the left field. + * + * a, b, c = 1, 2, 3, 4, 5 + * ^^^^^^^ + */ + struct pm_node_list lefts; + + /** + * MultiWriteNode#rest + * + * Represents a splat node in the target expression. + * + * a, b, *c = 1, 2, 3, 4 + * ^^ + * + * The variable can be empty, this results in a `SplatNode` with a `nil` expression field. + * + * a, b, * = 1, 2, 3, 4 + * ^ + * + * If the `*` is omitted, this field will contain an `ImplicitRestNode` + * + * a, b, = 1, 2, 3, 4 + * ^ + */ + struct pm_node *rest; + + /** + * MultiWriteNode#rights + * + * Represents the targets expressions after a splat node. + * + * a, *, b, c = 1, 2, 3, 4, 5 + * ^^^^ + */ + struct pm_node_list rights; + + /** + * MultiWriteNode#lparen_loc + * + * The location of the opening parenthesis. + * + * (a, b, c) = 1, 2, 3 + * ^ + */ + pm_location_t lparen_loc; + + /** + * MultiWriteNode#rparen_loc + * + * The location of the closing parenthesis. + * + * (a, b, c) = 1, 2, 3 + * ^ + */ + pm_location_t rparen_loc; + + /** + * MultiWriteNode#operator_loc + * + * The location of the operator. + * + * a, b, c = 1, 2, 3 + * ^ + */ + pm_location_t operator_loc; + + /** + * MultiWriteNode#value + * + * The value to write to the targets. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * a, b, c = 1, 2, 3 + * ^^^^^^^ + */ + struct pm_node *value; +} pm_multi_write_node_t; + +/** + * NextNode + * + * Represents the use of the `next` keyword. + * + * next 1 + * ^^^^^^ + * + * 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 + * + * Represents the use of the `nil` keyword. + * + * nil + * ^^^ + * + * 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 + * + * Represents the use of `**nil` inside method arguments. + * + * def a(**nil) + * ^^^^^ + * end + * + * 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; + +/** + * NumberedParametersNode + * + * Represents an implicit set of parameters through the use of numbered parameters within a block or lambda. + * + * -> { _1 + _2 } + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_NUMBERED_PARAMETERS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_numbered_parameters_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * NumberedParametersNode#maximum + */ + uint8_t maximum; +} pm_numbered_parameters_node_t; + +/** + * NumberedReferenceReadNode + * + * Represents reading a numbered reference to a capture in the previous match. + * + * $1 + * ^^ + * + * 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 + * + * The (1-indexed, from the left) number of the capture group. Numbered references that are too large result in this value being `0`. + * + * $1 # number `1` + * + * $5432 # number `5432` + * + * $4294967296 # number `0` + */ + uint32_t number; +} pm_numbered_reference_read_node_t; + +/** + * OptionalKeywordParameterNode + * + * Represents an optional keyword parameter to a method, block, or lambda definition. + * + * def a(b: 1) + * ^^^^ + * end + * + * Type: ::PM_OPTIONAL_KEYWORD_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @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 + * + * Represents an optional parameter to a method, block, or lambda definition. + * + * def a(b = 1) + * ^^^^^ + * end + * + * Type: ::PM_OPTIONAL_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @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 + * + * Represents the use of the `||` operator or the `or` keyword. + * + * left or right + * ^^^^^^^^^^^^^ + * + * Type: ::PM_OR_NODE + * + * @extends pm_node_t + */ +typedef struct pm_or_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * OrNode#left + * + * Represents the left side of the expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * left or right + * ^^^^ + * + * 1 || 2 + * ^ + */ + struct pm_node *left; + + /** + * OrNode#right + * + * Represents the right side of the expression. + * + * left || right + * ^^^^^ + * + * 1 or 2 + * ^ + */ + struct pm_node *right; + + /** + * OrNode#operator_loc + * + * The location of the `or` keyword or the `||` operator. + * + * left or right + * ^^ + */ + pm_location_t operator_loc; +} pm_or_node_t; + +/** + * ParametersNode + * + * Represents the list of parameters on a method, block, or lambda definition. + * + * def a(b, c, d) + * ^^^^^^^ + * end + * + * 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 + * + * Represents a parenthesized expression + * + * (10 + 34) + * ^^^^^^^^^ + * + * Type: ::PM_PARENTHESES_NODE + + * Flags (#pm_parentheses_node_flags): + * * ::PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS + * + * @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 + * + * Represents the use of the `^` operator for pinning an expression in a pattern matching expression. + * + * foo in ^(bar) + * ^^^^^^ + * + * 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 + * + * Represents the use of the `^` operator for pinning a variable in a pattern matching expression. + * + * foo in ^bar + * ^^^^ + * + * 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 + * + * Represents the use of the `END` keyword. + * + * END { foo } + * ^^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `BEGIN` keyword. + * + * BEGIN { foo } + * ^^^^^^^^^^^^^ + * + * 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 + * + * The top level node of any parse tree. + * + * 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 + * + * Represents the use of the `..` or `...` operators. + * + * 1..2 + * ^^^^ + * + * c if a =~ /left/ ... b =~ /right/ + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_RANGE_NODE + + * Flags (#pm_range_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 + * + * The left-hand side of the range, if present. It can be either `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * 1... + * ^ + * + * hello...goodbye + * ^^^^^ + */ + struct pm_node *left; + + /** + * RangeNode#right + * + * The right-hand side of the range, if present. It can be either `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * ..5 + * ^ + * + * 1...foo + * ^^^ + * If neither right-hand or left-hand side was included, this will be a MissingNode. + */ + struct pm_node *right; + + /** + * RangeNode#operator_loc + * + * The location of the `..` or `...` operator. + */ + pm_location_t operator_loc; +} pm_range_node_t; + +/** + * RationalNode + * + * Represents a rational number literal. + * + * 1.0r + * ^^^^ + * + * Type: ::PM_RATIONAL_NODE + + * Flags (#pm_integer_base_flags): + * * ::PM_INTEGER_BASE_FLAGS_BINARY + * * ::PM_INTEGER_BASE_FLAGS_DECIMAL + * * ::PM_INTEGER_BASE_FLAGS_OCTAL + * * ::PM_INTEGER_BASE_FLAGS_HEXADECIMAL + * + * @extends pm_node_t + */ +typedef struct pm_rational_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * RationalNode#numerator + * + * The numerator of the rational number. + * + * 1.5r # numerator 3 + */ + pm_integer_t numerator; + + /** + * RationalNode#denominator + * + * The denominator of the rational number. + * + * 1.5r # denominator 2 + */ + pm_integer_t denominator; +} pm_rational_node_t; + +/** + * RedoNode + * + * Represents the use of the `redo` keyword. + * + * redo + * ^^^^ + * + * 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 + * + * Represents a regular expression literal with no interpolation. + * + * /foo/i + * ^^^^^^ + * + * Type: ::PM_REGULAR_EXPRESSION_NODE + + * Flags (#pm_regular_expression_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 + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING + * + * @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 + * + * Represents a required keyword parameter to a method, block, or lambda definition. + * + * def a(b: ) + * ^^ + * end + * + * Type: ::PM_REQUIRED_KEYWORD_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @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 + * + * Represents a required parameter to a method, block, or lambda definition. + * + * def a(b) + * ^ + * end + * + * Type: ::PM_REQUIRED_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @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 + * + * Represents an expression modified with a rescue. + * + * foo rescue nil + * ^^^^^^^^^^^^^^ + * + * 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 + * + * Represents a rescue statement. + * + * begin + * rescue Foo, *splat, Bar => ex + * foo + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * end + * + * `Foo, *splat, Bar` are in the `exceptions` field. `ex` is in the `reference` field. + * + * 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#then_keyword_loc + */ + pm_location_t then_keyword_loc; + + /** + * RescueNode#statements + */ + struct pm_statements_node *statements; + + /** + * RescueNode#subsequent + */ + struct pm_rescue_node *subsequent; +} pm_rescue_node_t; + +/** + * RestParameterNode + * + * Represents a rest parameter to a method, block, or lambda definition. + * + * def a(*b) + * ^^ + * end + * + * Type: ::PM_REST_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @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 + * + * Represents the use of the `retry` keyword. + * + * retry + * ^^^^^ + * + * 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 + * + * Represents the use of the `return` keyword. + * + * return 1 + * ^^^^^^^^ + * + * 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 + * + * Represents the `self` keyword. + * + * self + * ^^^^ + * + * 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; + +/** + * ShareableConstantNode + * + * This node wraps a constant write to indicate that when the value is written, it should have its shareability state modified. + * + * # shareable_constant_value: literal + * C = { a: 1 } + * ^^^^^^^^^^^^ + * + * Type: ::PM_SHAREABLE_CONSTANT_NODE + + * Flags (#pm_shareable_constant_node_flags): + * * ::PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL + * * ::PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING + * * ::PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY + * + * @extends pm_node_t + */ +typedef struct pm_shareable_constant_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ShareableConstantNode#write + * + * The constant write that should be modified with the shareability state. + */ + struct pm_node *write; +} pm_shareable_constant_node_t; + +/** + * SingletonClassNode + * + * Represents a singleton class declaration involving the `class` keyword. + * + * class << self end + * ^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `__ENCODING__` keyword. + * + * __ENCODING__ + * ^^^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `__FILE__` keyword. + * + * __FILE__ + * ^^^^^^^^ + * + * Type: ::PM_SOURCE_FILE_NODE + + * Flags (#pm_string_flags): + * * ::PM_STRING_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_STRING_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_STRING_FLAGS_FROZEN + * * ::PM_STRING_FLAGS_MUTABLE + * + * @extends pm_node_t + */ +typedef struct pm_source_file_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * SourceFileNode#filepath + * + * Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism::parse*` APIs. + */ + pm_string_t filepath; +} pm_source_file_node_t; + +/** + * SourceLineNode + * + * Represents the use of the `__LINE__` keyword. + * + * __LINE__ + * ^^^^^^^^ + * + * 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 + * + * Represents the use of the splat operator. + * + * [*a] + * ^^ + * + * 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 + * + * Represents a set of statements contained within some scope. + * + * foo; bar; baz + * ^^^^^^^^^^^^^ + * + * 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 + * + * Represents a string literal, a string contained within a `%w` list, or plain string content within an interpolated string. + * + * "foo" + * ^^^^^ + * + * %w[foo] + * ^^^ + * + * "foo #{bar} baz" + * ^^^^ ^^^^ + * + * Type: ::PM_STRING_NODE + + * Flags (#pm_string_flags): + * * ::PM_STRING_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_STRING_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_STRING_FLAGS_FROZEN + * * ::PM_STRING_FLAGS_MUTABLE + * + * @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 + * + * Represents the use of the `super` keyword with parentheses or arguments. + * + * super() + * ^^^^^^^ + * + * super foo, bar + * ^^^^^^^^^^^^^^ + * + * 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 + * + * Represents a symbol literal or a symbol contained within a `%i` list. + * + * :foo + * ^^^^ + * + * %i[foo] + * ^^^ + * + * Type: ::PM_SYMBOL_NODE + + * Flags (#pm_symbol_flags): + * * ::PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING + * + * @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 + * + * Represents the use of the literal `true` keyword. + * + * true + * ^^^^ + * + * 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 + * + * Represents the use of the `undef` keyword. + * + * undef :foo, :bar, :baz + * ^^^^^^^^^^^^^^^^^^^^^^ + * + * 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 + * + * Represents the use of the `unless` keyword, either in the block form or the modifier form. + * + * bar unless foo + * ^^^^^^^^^^^^^^ + * + * unless foo then bar end + * ^^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_UNLESS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_unless_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * UnlessNode#keyword_loc + * + * The location of the `unless` keyword. + * + * unless cond then bar end + * ^^^^^^ + * + * bar unless cond + * ^^^^^^ + */ + pm_location_t keyword_loc; + + /** + * UnlessNode#predicate + * + * The condition to be evaluated for the unless expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * unless cond then bar end + * ^^^^ + * + * bar unless cond + * ^^^^ + */ + struct pm_node *predicate; + + /** + * UnlessNode#then_keyword_loc + * + * The location of the `then` keyword, if present. + * + * unless cond then bar end + * ^^^^ + */ + pm_location_t then_keyword_loc; + + /** + * UnlessNode#statements + * + * The body of statements that will executed if the unless condition is + * falsey. Will be `nil` if no body is provided. + * + * unless cond then bar end + * ^^^ + */ + struct pm_statements_node *statements; + + /** + * UnlessNode#else_clause + * + * The else clause of the unless expression, if present. + * + * unless cond then bar else baz end + * ^^^^^^^^ + */ + struct pm_else_node *else_clause; + + /** + * UnlessNode#end_keyword_loc + * + * The location of the `end` keyword, if present. + * + * unless cond then bar end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_unless_node_t; + +/** + * UntilNode + * + * Represents the use of the `until` keyword, either in the block form or the modifier form. + * + * bar until foo + * ^^^^^^^^^^^^^ + * + * until foo do bar end + * ^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_UNTIL_NODE + + * Flags (#pm_loop_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#do_keyword_loc + */ + pm_location_t do_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 + * + * Represents the use of the `when` keyword within a case statement. + * + * case true + * when true + * ^^^^^^^^^ + * end + * + * 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#then_keyword_loc + */ + pm_location_t then_keyword_loc; + + /** + * WhenNode#statements + */ + struct pm_statements_node *statements; +} pm_when_node_t; + +/** + * WhileNode + * + * Represents the use of the `while` keyword, either in the block form or the modifier form. + * + * bar while foo + * ^^^^^^^^^^^^^ + * + * while foo do bar end + * ^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_WHILE_NODE + + * Flags (#pm_loop_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#do_keyword_loc + */ + pm_location_t do_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 + * + * Represents an xstring literal with no interpolation. + * + * `foo` + * ^^^^^ + * + * Type: ::PM_X_STRING_NODE + + * Flags (#pm_encoding_flags): + * * ::PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING + * + * @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 + * + * Represents the use of the `yield` keyword. + * + * yield 1 + * ^^^^^^^ + * + * 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 the arguments contain forwarding */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING = 4, + + /** if the arguments contain keywords */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS = 8, + + /** if the arguments contain a keyword splat */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT = 16, + + /** if the arguments contain a splat */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT = 32, + + /** if the arguments contain multiple splats */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS = 64, +} 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 = 4, +} pm_array_node_flags_t; + +/** + * Flags for call nodes. + */ +typedef enum pm_call_node_flags { + /** &. operator */ + PM_CALL_NODE_FLAGS_SAFE_NAVIGATION = 4, + + /** a call that could have been a local variable */ + PM_CALL_NODE_FLAGS_VARIABLE_CALL = 8, + + /** a call that is an attribute write, so the value being written should be returned */ + PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE = 16, + + /** a call that ignores method visibility */ + PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY = 32, +} pm_call_node_flags_t; + +/** + * Flags for nodes that have unescaped content. + */ +typedef enum pm_encoding_flags { + /** internal bytes forced the encoding to UTF-8 */ + PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING = 4, + + /** internal bytes forced the encoding to binary */ + PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING = 8, +} pm_encoding_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 = 4, + + /** 0d or no prefix */ + PM_INTEGER_BASE_FLAGS_DECIMAL = 8, + + /** 0o or 0 prefix */ + PM_INTEGER_BASE_FLAGS_OCTAL = 16, + + /** 0x prefix */ + PM_INTEGER_BASE_FLAGS_HEXADECIMAL = 32, +} pm_integer_base_flags_t; + +/** + * Flags for interpolated string nodes that indicated mutability if they are also marked as literals. + */ +typedef enum pm_interpolated_string_node_flags { + /** frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` */ + PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN = 4, + + /** mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` */ + PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE = 8, +} pm_interpolated_string_node_flags_t; + +/** + * Flags for keyword hash nodes. + */ +typedef enum pm_keyword_hash_node_flags { + /** a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments */ + PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS = 4, +} pm_keyword_hash_node_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 = 4, +} pm_loop_flags_t; + +/** + * Flags for parameter nodes. + */ +typedef enum pm_parameter_flags { + /** a parameter name that has been repeated in the method signature */ + PM_PARAMETER_FLAGS_REPEATED_PARAMETER = 4, +} pm_parameter_flags_t; + +/** + * Flags for parentheses nodes. + */ +typedef enum pm_parentheses_node_flags { + /** parentheses that contain multiple potentially void statements */ + PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS = 4, +} pm_parentheses_node_flags_t; + +/** + * Flags for range and flip-flop nodes. + */ +typedef enum pm_range_flags { + /** ... operator */ + PM_RANGE_FLAGS_EXCLUDE_END = 4, +} 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 = 4, + + /** x - ignores whitespace and allows comments in regular expressions */ + PM_REGULAR_EXPRESSION_FLAGS_EXTENDED = 8, + + /** m - allows $ to match the end of lines within strings */ + PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE = 16, + + /** o - only interpolates values into the regular expression once */ + PM_REGULAR_EXPRESSION_FLAGS_ONCE = 32, + + /** e - forces the EUC-JP encoding */ + PM_REGULAR_EXPRESSION_FLAGS_EUC_JP = 64, + + /** n - forces the ASCII-8BIT encoding */ + PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT = 128, + + /** s - forces the Windows-31J encoding */ + PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J = 256, + + /** u - forces the UTF-8 encoding */ + PM_REGULAR_EXPRESSION_FLAGS_UTF_8 = 512, + + /** internal bytes forced the encoding to UTF-8 */ + PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING = 1024, + + /** internal bytes forced the encoding to binary */ + PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING = 2048, + + /** internal bytes forced the encoding to US-ASCII */ + PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING = 4096, +} pm_regular_expression_flags_t; + +/** + * Flags for shareable constant nodes. + */ +typedef enum pm_shareable_constant_node_flags { + /** constant writes that should be modified with shareable constant value literal */ + PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL = 4, + + /** constant writes that should be modified with shareable constant value experimental everything */ + PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING = 8, + + /** constant writes that should be modified with shareable constant value experimental copy */ + PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY = 16, +} pm_shareable_constant_node_flags_t; + +/** + * Flags for string nodes. + */ +typedef enum pm_string_flags { + /** internal bytes forced the encoding to UTF-8 */ + PM_STRING_FLAGS_FORCED_UTF8_ENCODING = 4, + + /** internal bytes forced the encoding to binary */ + PM_STRING_FLAGS_FORCED_BINARY_ENCODING = 8, + + /** frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` */ + PM_STRING_FLAGS_FROZEN = 16, + + /** mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` */ + PM_STRING_FLAGS_MUTABLE = 32, +} pm_string_flags_t; + +/** + * Flags for symbol nodes. + */ +typedef enum pm_symbol_flags { + /** internal bytes forced the encoding to UTF-8 */ + PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING = 4, + + /** internal bytes forced the encoding to binary */ + PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING = 8, + + /** internal bytes forced the encoding to US-ASCII */ + PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING = 16, +} pm_symbol_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 0 + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/defines.h b/vendor/mruby-compiler2/lib/prism/include/prism/defines.h new file mode 100644 index 0000000..e31429c --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/defines.h @@ -0,0 +1,260 @@ +/** + * @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 +#include +#include + +/** + * We want to be able to use the PRI* macros for printing out integers, but on + * some platforms they aren't included unless this is already defined. + */ +#define __STDC_FORMAT_MACROS +// Include sys/types.h before inttypes.h to work around issue with +// certain versions of GCC and newlib which causes omission of PRIx64 +#include +#include + +/** + * When we are parsing using recursive descent, we want to protect against + * malicious payloads that could attempt to crash our parser. We do this by + * specifying a maximum depth to which we are allowed to recurse. + */ +#ifndef PRISM_DEPTH_MAXIMUM + #define PRISM_DEPTH_MAXIMUM 10000 +#endif + +/** + * 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__) +# if defined(__MINGW_PRINTF_FORMAT) +# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, argument_index))) +# else +# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((format(printf, string_index, argument_index))) +# endif +#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 + +/** + * A simple utility macro to concatenate two tokens together, necessary when one + * of the tokens is itself a macro. + */ +#define PM_CONCATENATE(left, right) left ## right + +/** + * We want to be able to use static assertions, but they weren't standardized + * until C11. As such, we polyfill it here by making a hacky typedef that will + * fail to compile due to a negative array size if the condition is false. + */ +#if defined(_Static_assert) +# define PM_STATIC_ASSERT(line, condition, message) _Static_assert(condition, message) +#else +# define PM_STATIC_ASSERT(line, condition, message) typedef char PM_CONCATENATE(static_assert_, line)[(condition) ? 1 : -1] +#endif + +/** + * In general, libc for embedded systems does not support memory-mapped files. + * If the target platform is POSIX or Windows, we can map a file in memory and + * read it in a more efficient manner. + */ +#ifdef _WIN32 +# define PRISM_HAS_MMAP +#else +# include +# ifdef _POSIX_MAPPED_FILES +# define PRISM_HAS_MMAP +# endif +#endif + +/** + * If PRISM_HAS_NO_FILESYSTEM is defined, then we want to exclude all filesystem + * related code from the library. All filesystem related code should be guarded + * by PRISM_HAS_FILESYSTEM. + */ +#ifndef PRISM_HAS_NO_FILESYSTEM +# define PRISM_HAS_FILESYSTEM +#endif + +/** + * isinf on POSIX systems it accepts a float, a double, or a long double. + * But mingw didn't provide an isinf macro, only an isinf function that only + * accepts floats, so we need to use _finite instead. + */ +#ifdef __MINGW64__ + #include + #define PRISM_ISINF(x) (!_finite(x)) +#else + #define PRISM_ISINF(x) isinf(x) +#endif + +/** + * If you build prism with a custom allocator, configure it with + * "-D PRISM_XALLOCATOR" to use your own allocator that defines xmalloc, + * xrealloc, xcalloc, and xfree. + * + * For example, your `prism_xallocator.h` file could look like this: + * + * ``` + * #ifndef PRISM_XALLOCATOR_H + * #define PRISM_XALLOCATOR_H + * #define xmalloc my_malloc + * #define xrealloc my_realloc + * #define xcalloc my_calloc + * #define xfree my_free + * #endif + * ``` + */ +#ifdef PRISM_XALLOCATOR + #include "prism_xallocator.h" +#else + #ifndef xmalloc + /** + * The malloc function that should be used. This can be overridden with + * the PRISM_XALLOCATOR define. + */ + #define xmalloc malloc + #endif + + #ifndef xrealloc + /** + * The realloc function that should be used. This can be overridden with + * the PRISM_XALLOCATOR define. + */ + #define xrealloc realloc + #endif + + #ifndef xcalloc + /** + * The calloc function that should be used. This can be overridden with + * the PRISM_XALLOCATOR define. + */ + #define xcalloc calloc + #endif + + #ifndef xfree + /** + * The free function that should be used. This can be overridden with the + * PRISM_XALLOCATOR define. + */ + #define xfree free + #endif +#endif + +/** + * If PRISM_BUILD_MINIMAL is defined, then we're going to define every possible + * switch that will turn off certain features of prism. + */ +#ifdef PRISM_BUILD_MINIMAL + /** Exclude the serialization API. */ + #define PRISM_EXCLUDE_SERIALIZATION + + /** Exclude the JSON serialization API. */ + #define PRISM_EXCLUDE_JSON + + /** Exclude the Array#pack parser API. */ + #define PRISM_EXCLUDE_PACK + + /** Exclude the prettyprint API. */ + #define PRISM_EXCLUDE_PRETTYPRINT + + /** Exclude the full set of encodings, using the minimal only. */ + #define PRISM_ENCODING_EXCLUDE_FULL +#endif + +/** + * Support PRISM_LIKELY and PRISM_UNLIKELY to help the compiler optimize its + * branch predication. + */ +#if defined(__GNUC__) || defined(__clang__) + /** The compiler should predicate that this branch will be taken. */ + #define PRISM_LIKELY(x) __builtin_expect(!!(x), 1) + + /** The compiler should predicate that this branch will not be taken. */ + #define PRISM_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else + /** Void because this platform does not support branch prediction hints. */ + #define PRISM_LIKELY(x) (x) + + /** Void because this platform does not support branch prediction hints. */ + #define PRISM_UNLIKELY(x) (x) +#endif + +/** + * We use -Wimplicit-fallthrough to guard potentially unintended fall-through between cases of a switch. + * Use PRISM_FALLTHROUGH to explicitly annotate cases where the fallthrough is intentional. + */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L // C23 or later + #define PRISM_FALLTHROUGH [[fallthrough]]; +#elif defined(__GNUC__) || defined(__clang__) + #define PRISM_FALLTHROUGH __attribute__((fallthrough)); +#elif defined(_MSC_VER) + #define PRISM_FALLTHROUGH __fallthrough; +#else + #define PRISM_FALLTHROUGH +#endif + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/diagnostic.h b/vendor/mruby-compiler2/lib/prism/include/prism/diagnostic.h new file mode 100644 index 0000000..2b1aa9b --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/diagnostic.h @@ -0,0 +1,451 @@ +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/include/prism/diagnostic.h.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +/** + * @file diagnostic.h + * + * A list of diagnostics generated during parsing. + */ +#ifndef PRISM_DIAGNOSTIC_H +#define PRISM_DIAGNOSTIC_H + +#include "prism/ast.h" +#include "prism/defines.h" +#include "prism/util/pm_list.h" + +#include +#include +#include + +/** + * The diagnostic IDs of all of the diagnostics, used to communicate the types + * of errors between the parser and the user. + */ +typedef enum { + // These are the error diagnostics. + PM_ERR_ALIAS_ARGUMENT, + PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE, + 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_FORWARDING, + PM_ERR_ARGUMENT_BLOCK_MULTI, + PM_ERR_ARGUMENT_CONFLICT_AMPERSAND, + PM_ERR_ARGUMENT_CONFLICT_STAR, + PM_ERR_ARGUMENT_CONFLICT_STAR_STAR, + 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_AMPERSAND, + PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES, + PM_ERR_ARGUMENT_NO_FORWARDING_STAR, + PM_ERR_ARGUMENT_NO_FORWARDING_STAR_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_CLASS_VARIABLE_BARE, + 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_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_LIST, + PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL, + PM_ERR_ESCAPE_INVALID_UNICODE_LONG, + PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, + 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_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_OPERATOR, + PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, + PM_ERR_EXPECT_EXPRESSION_AFTER_QUESTION, + PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT, + PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, + PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, + PM_ERR_EXPECT_FOR_DELIMITER, + PM_ERR_EXPECT_IDENT_REQ_PARAMETER, + PM_ERR_EXPECT_IN_DELIMITER, + PM_ERR_EXPECT_LPAREN_REQ_PARAMETER, + PM_ERR_EXPECT_MESSAGE, + PM_ERR_EXPECT_RBRACKET, + PM_ERR_EXPECT_RPAREN, + PM_ERR_EXPECT_RPAREN_AFTER_MULTI, + PM_ERR_EXPECT_RPAREN_REQ_PARAMETER, + PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, + PM_ERR_EXPECT_STRING_CONTENT, + PM_ERR_EXPECT_WHEN_DELIMITER, + PM_ERR_EXPRESSION_BARE_HASH, + PM_ERR_EXPRESSION_NOT_WRITABLE, + PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING, + PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE, + PM_ERR_EXPRESSION_NOT_WRITABLE_FILE, + PM_ERR_EXPRESSION_NOT_WRITABLE_LINE, + PM_ERR_EXPRESSION_NOT_WRITABLE_NIL, + PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED, + PM_ERR_EXPRESSION_NOT_WRITABLE_SELF, + PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE, + PM_ERR_FLOAT_PARSE, + PM_ERR_FOR_COLLECTION, + PM_ERR_FOR_IN, + PM_ERR_FOR_INDEX, + PM_ERR_FOR_TERM, + PM_ERR_GLOBAL_VARIABLE_BARE, + 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_IDENTIFIER, + PM_ERR_HEREDOC_TERM, + PM_ERR_INCOMPLETE_QUESTION_MARK, + PM_ERR_INCOMPLETE_VARIABLE_CLASS, + PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3, + PM_ERR_INCOMPLETE_VARIABLE_INSTANCE, + PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3, + PM_ERR_INSTANCE_VARIABLE_BARE, + PM_ERR_INVALID_BLOCK_EXIT, + PM_ERR_INVALID_CHARACTER, + PM_ERR_INVALID_COMMA, + PM_ERR_INVALID_ENCODING_MAGIC_COMMENT, + PM_ERR_INVALID_ESCAPE_CHARACTER, + PM_ERR_INVALID_FLOAT_EXPONENT, + PM_ERR_INVALID_LOCAL_VARIABLE_READ, + PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, + PM_ERR_INVALID_MULTIBYTE_CHAR, + PM_ERR_INVALID_MULTIBYTE_CHARACTER, + PM_ERR_INVALID_MULTIBYTE_ESCAPE, + PM_ERR_INVALID_NUMBER_BINARY, + PM_ERR_INVALID_NUMBER_DECIMAL, + PM_ERR_INVALID_NUMBER_FRACTION, + PM_ERR_INVALID_NUMBER_HEXADECIMAL, + PM_ERR_INVALID_NUMBER_OCTAL, + PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER, + PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING, + PM_ERR_INVALID_PERCENT, + PM_ERR_INVALID_PERCENT_EOF, + PM_ERR_INVALID_PRINTABLE_CHARACTER, + PM_ERR_INVALID_RETRY_AFTER_ELSE, + PM_ERR_INVALID_RETRY_AFTER_ENSURE, + PM_ERR_INVALID_RETRY_WITHOUT_RESCUE, + PM_ERR_INVALID_SYMBOL, + PM_ERR_INVALID_VARIABLE_GLOBAL, + PM_ERR_INVALID_VARIABLE_GLOBAL_3_3, + PM_ERR_INVALID_YIELD, + PM_ERR_IT_NOT_ALLOWED_NUMBERED, + PM_ERR_IT_NOT_ALLOWED_ORDINARY, + 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_MIXED_ENCODING, + PM_ERR_MODULE_IN_METHOD, + PM_ERR_MODULE_NAME, + PM_ERR_MODULE_TERM, + PM_ERR_MULTI_ASSIGN_MULTI_SPLATS, + PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST, + PM_ERR_NESTING_TOO_DEEP, + PM_ERR_NO_LOCAL_VARIABLE, + PM_ERR_NON_ASSOCIATIVE_OPERATOR, + PM_ERR_NOT_EXPRESSION, + PM_ERR_NUMBER_LITERAL_UNDERSCORE, + PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK, + PM_ERR_NUMBERED_PARAMETER_IT, + PM_ERR_NUMBERED_PARAMETER_ORDINARY, + PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK, + 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_FORWARDING_AFTER_REST, + PM_ERR_PARAMETER_METHOD_NAME, + PM_ERR_PARAMETER_NAME_DUPLICATED, + 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_UNEXPECTED_NO_KW, + PM_ERR_PARAMETER_WILD_LOOSE_COMMA, + PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS, + PM_ERR_PATTERN_CAPTURE_DUPLICATE, + PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, + PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, + PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET, + 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_EXPRESSION_AFTER_REST, + PM_ERR_PATTERN_FIND_MISSING_INNER, + PM_ERR_PATTERN_HASH_IMPLICIT, + PM_ERR_PATTERN_HASH_KEY, + PM_ERR_PATTERN_HASH_KEY_DUPLICATE, + PM_ERR_PATTERN_HASH_KEY_INTERPOLATED, + PM_ERR_PATTERN_HASH_KEY_LABEL, + PM_ERR_PATTERN_HASH_KEY_LOCALS, + 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_ENCODING_OPTION_MISMATCH, + PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING, + PM_ERR_REGEXP_INVALID_UNICODE_RANGE, + PM_ERR_REGEXP_NON_ESCAPED_MBC, + PM_ERR_REGEXP_PARSE_ERROR, + PM_ERR_REGEXP_TERM, + PM_ERR_REGEXP_UNKNOWN_OPTIONS, + PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP, + PM_ERR_RESCUE_EXPRESSION, + PM_ERR_RESCUE_MODIFIER_VALUE, + PM_ERR_RESCUE_TERM, + PM_ERR_RESCUE_VARIABLE, + PM_ERR_RETURN_INVALID, + PM_ERR_SCRIPT_NOT_FOUND, + PM_ERR_SINGLETON_FOR_LITERALS, + 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_EOF, + 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_DISALLOWED, + PM_ERR_UNARY_RECEIVER, + PM_ERR_UNDEF_ARGUMENT, + PM_ERR_UNEXPECTED_BLOCK_ARGUMENT, + PM_ERR_UNEXPECTED_INDEX_BLOCK, + PM_ERR_UNEXPECTED_INDEX_KEYWORDS, + PM_ERR_UNEXPECTED_LABEL, + PM_ERR_UNEXPECTED_MULTI_WRITE, + PM_ERR_UNEXPECTED_RANGE_OPERATOR, + PM_ERR_UNEXPECTED_SAFE_NAVIGATION, + PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, + PM_ERR_UNEXPECTED_TOKEN_IGNORE, + PM_ERR_UNTIL_TERM, + PM_ERR_VOID_EXPRESSION, + PM_ERR_WHILE_TERM, + PM_ERR_WRITE_TARGET_IN_METHOD, + PM_ERR_WRITE_TARGET_READONLY, + PM_ERR_WRITE_TARGET_UNEXPECTED, + PM_ERR_XSTRING_TERM, + + // These are the warning diagnostics. + PM_WARN_AMBIGUOUS_BINARY_OPERATOR, + PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS, + PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS, + PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND, + PM_WARN_AMBIGUOUS_PREFIX_STAR, + PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR, + PM_WARN_AMBIGUOUS_SLASH, + PM_WARN_COMPARISON_AFTER_COMPARISON, + PM_WARN_DOT_DOT_DOT_EOL, + PM_WARN_EQUAL_IN_CONDITIONAL, + PM_WARN_EQUAL_IN_CONDITIONAL_3_3, + PM_WARN_END_IN_METHOD, + PM_WARN_DUPLICATED_HASH_KEY, + PM_WARN_DUPLICATED_WHEN_CLAUSE, + PM_WARN_FLOAT_OUT_OF_RANGE, + PM_WARN_IGNORED_FROZEN_STRING_LITERAL, + PM_WARN_INDENTATION_MISMATCH, + PM_WARN_INTEGER_IN_FLIP_FLOP, + PM_WARN_INVALID_CHARACTER, + PM_WARN_INVALID_MAGIC_COMMENT_VALUE, + PM_WARN_INVALID_NUMBERED_REFERENCE, + PM_WARN_KEYWORD_EOL, + PM_WARN_LITERAL_IN_CONDITION_DEFAULT, + PM_WARN_LITERAL_IN_CONDITION_VERBOSE, + PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE, + PM_WARN_SHEBANG_CARRIAGE_RETURN, + PM_WARN_UNEXPECTED_CARRIAGE_RETURN, + PM_WARN_UNREACHABLE_STATEMENT, + PM_WARN_UNUSED_LOCAL_VARIABLE, + PM_WARN_VOID_STATEMENT, +} pm_diagnostic_id_t; + +/** + * This struct represents a diagnostic generated during parsing. + * + * @extends pm_list_node_t + */ +typedef struct { + /** The embedded base node. */ + pm_list_node_t node; + + /** The location of the diagnostic in the source. */ + pm_location_t location; + + /** The ID of the diagnostic. */ + pm_diagnostic_id_t diag_id; + + /** 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; + + /** + * The level of the diagnostic, see `pm_error_level_t` and + * `pm_warning_level_t` for possible values. + */ + uint8_t level; +} pm_diagnostic_t; + +/** + * The levels of errors generated during parsing. + */ +typedef enum { + /** For errors that should raise a syntax error. */ + PM_ERROR_LEVEL_SYNTAX = 0, + + /** For errors that should raise an argument error. */ + PM_ERROR_LEVEL_ARGUMENT = 1, + + /** For errors that should raise a load error. */ + PM_ERROR_LEVEL_LOAD = 2 +} pm_error_level_t; + +/** + * The levels of warnings generated during parsing. + */ +typedef enum { + /** For warnings which should be emitted if $VERBOSE != nil. */ + PM_WARNING_LEVEL_DEFAULT = 0, + + /** For warnings which should be emitted if $VERBOSE == true. */ + PM_WARNING_LEVEL_VERBOSE = 1 +} pm_warning_level_t; + +/** + * Get the human-readable name of the given diagnostic ID. + * + * @param diag_id The diagnostic ID. + * @return The human-readable name of the diagnostic ID. + */ +const char * pm_diagnostic_id_human(pm_diagnostic_id_t diag_id); + +/** + * 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/vendor/mruby-compiler2/lib/prism/include/prism/encoding.h b/vendor/mruby-compiler2/lib/prism/include/prism/encoding.h new file mode 100644 index 0000000..5f77248 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/encoding.h @@ -0,0 +1,283 @@ +/** + * @file encoding.h + * + * The encoding interface and implementations used by the parser. + */ +#ifndef PRISM_ENCODING_H +#define PRISM_ENCODING_H + +#include "prism/defines.h" +#include "prism/util/pm_strncasecmp.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 UTF-8 encoding. + * + * @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_char_width(const uint8_t *b, 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]; + +/** + * These are all of the encodings that prism supports. + */ +typedef enum { + PM_ENCODING_UTF_8 = 0, + PM_ENCODING_US_ASCII, + PM_ENCODING_ASCII_8BIT, + PM_ENCODING_EUC_JP, + PM_ENCODING_WINDOWS_31J, + +// We optionally support excluding the full set of encodings to only support the +// minimum necessary to process Ruby code without encoding comments. +#ifndef PRISM_ENCODING_EXCLUDE_FULL + PM_ENCODING_BIG5, + PM_ENCODING_BIG5_HKSCS, + PM_ENCODING_BIG5_UAO, + PM_ENCODING_CESU_8, + PM_ENCODING_CP51932, + PM_ENCODING_CP850, + PM_ENCODING_CP852, + PM_ENCODING_CP855, + PM_ENCODING_CP949, + PM_ENCODING_CP950, + PM_ENCODING_CP951, + PM_ENCODING_EMACS_MULE, + PM_ENCODING_EUC_JP_MS, + PM_ENCODING_EUC_JIS_2004, + PM_ENCODING_EUC_KR, + PM_ENCODING_EUC_TW, + PM_ENCODING_GB12345, + PM_ENCODING_GB18030, + PM_ENCODING_GB1988, + PM_ENCODING_GB2312, + PM_ENCODING_GBK, + PM_ENCODING_IBM437, + PM_ENCODING_IBM720, + PM_ENCODING_IBM737, + PM_ENCODING_IBM775, + PM_ENCODING_IBM852, + PM_ENCODING_IBM855, + PM_ENCODING_IBM857, + PM_ENCODING_IBM860, + PM_ENCODING_IBM861, + PM_ENCODING_IBM862, + PM_ENCODING_IBM863, + PM_ENCODING_IBM864, + PM_ENCODING_IBM865, + PM_ENCODING_IBM866, + PM_ENCODING_IBM869, + PM_ENCODING_ISO_8859_1, + PM_ENCODING_ISO_8859_2, + PM_ENCODING_ISO_8859_3, + PM_ENCODING_ISO_8859_4, + PM_ENCODING_ISO_8859_5, + PM_ENCODING_ISO_8859_6, + PM_ENCODING_ISO_8859_7, + PM_ENCODING_ISO_8859_8, + PM_ENCODING_ISO_8859_9, + PM_ENCODING_ISO_8859_10, + PM_ENCODING_ISO_8859_11, + PM_ENCODING_ISO_8859_13, + PM_ENCODING_ISO_8859_14, + PM_ENCODING_ISO_8859_15, + PM_ENCODING_ISO_8859_16, + PM_ENCODING_KOI8_R, + PM_ENCODING_KOI8_U, + PM_ENCODING_MAC_CENT_EURO, + PM_ENCODING_MAC_CROATIAN, + PM_ENCODING_MAC_CYRILLIC, + PM_ENCODING_MAC_GREEK, + PM_ENCODING_MAC_ICELAND, + PM_ENCODING_MAC_JAPANESE, + PM_ENCODING_MAC_ROMAN, + PM_ENCODING_MAC_ROMANIA, + PM_ENCODING_MAC_THAI, + PM_ENCODING_MAC_TURKISH, + PM_ENCODING_MAC_UKRAINE, + PM_ENCODING_SHIFT_JIS, + PM_ENCODING_SJIS_DOCOMO, + PM_ENCODING_SJIS_KDDI, + PM_ENCODING_SJIS_SOFTBANK, + PM_ENCODING_STATELESS_ISO_2022_JP, + PM_ENCODING_STATELESS_ISO_2022_JP_KDDI, + PM_ENCODING_TIS_620, + PM_ENCODING_UTF8_MAC, + PM_ENCODING_UTF8_DOCOMO, + PM_ENCODING_UTF8_KDDI, + PM_ENCODING_UTF8_SOFTBANK, + PM_ENCODING_WINDOWS_1250, + PM_ENCODING_WINDOWS_1251, + PM_ENCODING_WINDOWS_1252, + PM_ENCODING_WINDOWS_1253, + PM_ENCODING_WINDOWS_1254, + PM_ENCODING_WINDOWS_1255, + PM_ENCODING_WINDOWS_1256, + PM_ENCODING_WINDOWS_1257, + PM_ENCODING_WINDOWS_1258, + PM_ENCODING_WINDOWS_874, +#endif + + PM_ENCODING_MAXIMUM +} pm_encoding_type_t; + +/** + * This is the table of all of the encodings that prism supports. + */ +extern const pm_encoding_t pm_encodings[PM_ENCODING_MAXIMUM]; + +/** + * This is the default UTF-8 encoding. We need a reference to it to quickly + * create parsers. + */ +#define PM_ENCODING_UTF_8_ENTRY (&pm_encodings[PM_ENCODING_UTF_8]) + +/** + * This is the US-ASCII encoding. We need a reference to it to be able to + * compare against it when a string is being created because it could possibly + * need to fall back to ASCII-8BIT. + */ +#define PM_ENCODING_US_ASCII_ENTRY (&pm_encodings[PM_ENCODING_US_ASCII]) + +/** + * This is the ASCII-8BIT encoding. We need a reference to it so that pm_strpbrk + * can compare against it because invalid multibyte characters are not a thing + * in this encoding. It is also needed for handling Regexp encoding flags. + */ +#define PM_ENCODING_ASCII_8BIT_ENTRY (&pm_encodings[PM_ENCODING_ASCII_8BIT]) + +/** + * This is the EUC-JP encoding. We need a reference to it to quickly process + * regular expression modifiers. + */ +#define PM_ENCODING_EUC_JP_ENTRY (&pm_encodings[PM_ENCODING_EUC_JP]) + +/** + * This is the Windows-31J encoding. We need a reference to it to quickly + * process regular expression modifiers. + */ +#define PM_ENCODING_WINDOWS_31J_ENTRY (&pm_encodings[PM_ENCODING_WINDOWS_31J]) + +/** + * Parse the given name of an encoding and return a pointer to the corresponding + * encoding struct if one can be found, otherwise return NULL. + * + * @param start A pointer to the first byte of the name. + * @param end A pointer to the last byte of the name. + * @returns A pointer to the encoding struct if one is found, otherwise NULL. + */ +const pm_encoding_t * pm_encoding_find(const uint8_t *start, const uint8_t *end); + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/node.h b/vendor/mruby-compiler2/lib/prism/include/prism/node.h new file mode 100644 index 0000000..e8686a3 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/node.h @@ -0,0 +1,129 @@ +/** + * @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" +#include "prism/util/pm_buffer.h" + +/** + * Loop through each node in the node list, writing each node to the given + * pm_node_t pointer. + */ +#define PM_NODE_LIST_FOREACH(list, index, node) \ + for (size_t index = 0; index < (list)->size && ((node) = (list)->nodes[index]); index++) + +/** + * 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); + +/** + * Prepend a new node onto the beginning of the node list. + * + * @param list The list to prepend to. + * @param node The node to prepend. + */ +void pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node); + +/** + * Concatenate the given node list onto the end of the other node list. + * + * @param list The list to concatenate onto. + * @param other The list to concatenate. + */ +void pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other); + +/** + * Free the internal memory associated with the given node list. + * + * @param list The list to free. + */ +void pm_node_list_free(pm_node_list_t *list); + +/** + * 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); + +/** + * 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); + +/** + * Visit each of the nodes in this subtree using the given visitor callback. The + * callback function will be called for each node in the subtree. If it returns + * false, then that node's children will not be visited. If it returns true, + * then the children will be visited. The data parameter is treated as an opaque + * pointer and is passed to the visitor callback for consumers to use as they + * see fit. + * + * As an example: + * + * ```c + * #include "prism.h" + * + * bool visit(const pm_node_t *node, void *data) { + * size_t *indent = (size_t *) data; + * for (size_t i = 0; i < *indent * 2; i++) putc(' ', stdout); + * printf("%s\n", pm_node_type_to_str(node->type)); + * + * size_t next_indent = *indent + 1; + * size_t *next_data = &next_indent; + * pm_visit_child_nodes(node, visit, next_data); + * + * return false; + * } + * + * int main(void) { + * const char *source = "1 + 2; 3 + 4"; + * size_t size = strlen(source); + * + * pm_parser_t parser; + * pm_options_t options = { 0 }; + * pm_parser_init(&parser, (const uint8_t *) source, size, &options); + * + * size_t indent = 0; + * pm_node_t *node = pm_parse(&parser); + * + * size_t *data = &indent; + * pm_visit_node(node, visit, data); + * + * pm_node_destroy(&parser, node); + * pm_parser_free(&parser); + * return EXIT_SUCCESS; + * } + * ``` + * + * @param node The root node to start visiting from. + * @param visitor The callback to call for each node in the subtree. + * @param data An opaque pointer that is passed to the visitor callback. + */ +PRISM_EXPORTED_FUNCTION void pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data); + +/** + * Visit the children of the given node with the given callback. This is the + * default behavior for walking the tree that is called from pm_visit_node if + * the callback returns true. + * + * @param node The node to visit the children of. + * @param visitor The callback to call for each child node. + * @param data An opaque pointer that is passed to the visitor callback. + */ +PRISM_EXPORTED_FUNCTION void pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data); + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/options.h b/vendor/mruby-compiler2/lib/prism/include/prism/options.h new file mode 100644 index 0000000..2f64701 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/options.h @@ -0,0 +1,442 @@ +/** + * @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_char.h" +#include "prism/util/pm_string.h" + +#include +#include +#include + +/** + * String literals should be made frozen. + */ +#define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED ((int8_t) -1) + +/** + * String literals may be frozen or mutable depending on the implementation + * default. + */ +#define PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET ((int8_t) 0) + +/** + * String literals should be made mutable. + */ +#define PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED ((int8_t) 1) + +/** + * 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; + + /** Flags for the set of forwarding parameters in this scope. */ + uint8_t forwarding; +} pm_options_scope_t; + +/** The default value for parameters. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_NONE = 0x0; + +/** When the scope is fowarding with the * parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS = 0x1; + +/** When the scope is fowarding with the ** parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS = 0x2; + +/** When the scope is fowarding with the & parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_BLOCK = 0x4; + +/** When the scope is fowarding with the ... parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_ALL = 0x8; + +// Forward declaration needed by the callback typedef. +struct pm_options; + +/** + * The callback called when additional switches are found in a shebang comment + * that need to be processed by the runtime. + * + * @param options The options struct that may be updated by this callback. + * Certain fields will be checked for changes, specifically encoding, + * command_line, and frozen_string_literal. + * @param source The source of the shebang comment. + * @param length The length of the source. + * @param shebang_callback_data Any additional data that should be passed along + * to the callback. + */ +typedef void (*pm_options_shebang_callback_t)(struct pm_options *options, const uint8_t *source, size_t length, void *shebang_callback_data); + +/** + * The version of Ruby syntax that we should be parsing with. This is used to + * allow consumers to specify which behavior they want in case they need to + * parse in the same way as a specific version of CRuby would have. + */ +typedef enum { + /** The current version of prism. */ + PM_OPTIONS_VERSION_LATEST = 0, + + /** The vendored version of prism in CRuby 3.3.x. */ + PM_OPTIONS_VERSION_CRUBY_3_3 = 1, + + /** The vendored version of prism in CRuby 3.4.x. */ + PM_OPTIONS_VERSION_CRUBY_3_4 = 2 +} pm_options_version_t; + +/** + * The options that can be passed to the parser. + */ +typedef struct pm_options { + /** + * The callback to call when additional switches are found in a shebang + * comment. + */ + pm_options_shebang_callback_t shebang_callback; + + /** + * Any additional data that should be passed along to the shebang callback + * if one was set. + */ + void *shebang_callback_data; + + /** 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 + * 1-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. Scopes are ordered from the outermost scope to the + * innermost one. + */ + pm_options_scope_t *scopes; + + /** + * The version of prism that we should be parsing with. This is used to + * allow consumers to specify which behavior they want in case they need to + * parse exactly as a specific version of CRuby. + */ + pm_options_version_t version; + + /** A bitset of the various options that were set on the command line. */ + uint8_t command_line; + + /** + * Whether or not the frozen string literal option has been set. + * May be: + * - PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED + * - PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED + * - PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET + */ + int8_t frozen_string_literal; + + /** + * Whether or not the encoding magic comments should be respected. This is a + * niche use-case where you want to parse a file with a specific encoding + * but ignore any encoding magic comments at the top of the file. + */ + bool encoding_locked; + + /** + * When the file being parsed is the main script, the shebang will be + * considered for command-line flags (or for implicit -x). The caller needs + * to pass this information to the parser so that it can behave correctly. + */ + bool main_script; + + /** + * When the file being parsed is considered a "partial" script, jumps will + * not be marked as errors if they are not contained within loops/blocks. + * This is used in the case that you're parsing a script that you know will + * be embedded inside another script later, but you do not have that context + * yet. For example, when parsing an ERB template that will be evaluated + * inside another script. + */ + bool partial_script; + + /** + * Whether or not the parser should freeze the nodes that it creates. This + * makes it possible to have a deeply frozen AST that is safe to share + * between concurrency primitives. + */ + bool freeze; +} pm_options_t; + +/** + * A bit representing whether or not the command line -a option was set. -a + * splits the input line $_ into $F. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_A = 0x1; + +/** + * A bit representing whether or not the command line -e option was set. -e + * allow the user to specify a script to be executed. This is necessary for + * prism to know because certain warnings are not generated when -e is used. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_E = 0x2; + +/** + * A bit representing whether or not the command line -l option was set. -l + * chomps the input line by default. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_L = 0x4; + +/** + * A bit representing whether or not the command line -n option was set. -n + * wraps the script in a while gets loop. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_N = 0x8; + +/** + * A bit representing whether or not the command line -p option was set. -p + * prints the value of $_ at the end of each loop. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_P = 0x10; + +/** + * A bit representing whether or not the command line -x option was set. -x + * searches the input file for a shebang that matches the current Ruby engine. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_X = 0x20; + +/** + * Set the shebang callback option on the given options struct. + * + * @param options The options struct to set the shebang callback on. + * @param shebang_callback The shebang callback to set. + * @param shebang_callback_data Any additional data that should be passed along + * to the callback. + */ +PRISM_EXPORTED_FUNCTION void pm_options_shebang_callback_set(pm_options_t *options, pm_options_shebang_callback_t shebang_callback, void *shebang_callback_data); + +/** + * 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 encoding_locked option on the given options struct. + * + * @param options The options struct to set the encoding_locked value on. + * @param encoding_locked The encoding_locked value to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked); + +/** + * 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); + +/** + * Sets the command line option on the given options struct. + * + * @param options The options struct to set the command line option on. + * @param command_line The command_line value to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options, uint8_t command_line); + +/** + * Set the version option on the given options struct by parsing the given + * string. If the string contains an invalid option, this returns false. + * Otherwise, it returns true. + * + * @param options The options struct to set the version on. + * @param version The version to set. + * @param length The length of the version string. + * @return Whether or not the version was parsed successfully. + */ +PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length); + +/** + * Set the main script option on the given options struct. + * + * @param options The options struct to set the main script value on. + * @param main_script The main script value to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_main_script_set(pm_options_t *options, bool main_script); + +/** + * Set the partial script option on the given options struct. + * + * @param options The options struct to set the partial script value on. + * @param partial_script The partial script value to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_partial_script_set(pm_options_t *options, bool partial_script); + +/** + * Set the freeze option on the given options struct. + * + * @param options The options struct to set the freeze value on. + * @param freeze The freeze value to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_freeze_set(pm_options_t *options, bool freeze); + +/** + * 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. + * @return Whether or not the scopes array was initialized successfully. + */ +PRISM_EXPORTED_FUNCTION bool 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. + * @return Whether or not the scope was initialized successfully. + */ +PRISM_EXPORTED_FUNCTION bool 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); + +/** + * Set the forwarding option on the given scope struct. + * + * @param scope The scope struct to set the forwarding on. + * @param forwarding The forwarding value to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding); + +/** + * 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` | -p command line option | + * | `1` | -n command line option | + * | `1` | -l command line option | + * | `1` | -a command line option | + * | `1` | the version | + * | `1` | encoding locked | + * | `1` | main script | + * | `1` | partial script | + * | `1` | freeze | + * | `4` | the number of scopes | + * | ... | the scopes | + * + * The version field is an enum, so it should be one of the following values: + * + * | value | version | + * | ----- | ------------------------- | + * | `0` | use the latest version of prism | + * | `1` | use the version of prism that is vendored in CRuby 3.3.0 | + * + * Each scope is laid out as follows: + * + * | # bytes | field | + * | ------- | -------------------------- | + * | `4` | the number of locals | + * | `1` | the forwarding flags | + * | ... | the locals | + * + * Each local is laid 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, encoding locked, main script, and partial script + * 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/vendor/mruby-compiler2/lib/prism/include/prism/pack.h b/vendor/mruby-compiler2/lib/prism/include/prism/pack.h new file mode 100644 index 0000000..0b0b4b1 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/pack.h @@ -0,0 +1,163 @@ +/** + * @file pack.h + * + * A pack template string parser. + */ +#ifndef PRISM_PACK_H +#define PRISM_PACK_H + +#include "prism/defines.h" + +// We optionally support parsing String#pack templates. For systems that don't +// want or need this functionality, it can be turned off with the +// PRISM_EXCLUDE_PACK define. +#ifdef PRISM_EXCLUDE_PACK + +void pm_pack_parse(void); + +#else + +#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 + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/parser.h b/vendor/mruby-compiler2/lib/prism/include/prism/parser.h new file mode 100644 index 0000000..992729d --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/parser.h @@ -0,0 +1,933 @@ +/** + * @file parser.h + * + * The parser used to parse Ruby source. + */ +#ifndef PRISM_PARSER_H +#define PRISM_PARSER_H + +#include "prism/defines.h" +#include "prism/ast.h" +#include "prism/encoding.h" +#include "prism/options.h" +#include "prism/static_literals.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_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; + +/** + * All of the information necessary to store to lexing a heredoc. + */ +typedef 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; +} pm_heredoc_lex_mode_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[7]; + } 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[7]; + } string; + + struct { + /** + * All of the data necessary to lex a heredoc. + */ + pm_heredoc_lex_mode_t base; + + /** + * 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; + + /** True if the previous token ended with a line continuation. */ + bool line_continuation; + } 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 null context, used for returning a value from a function */ + PM_CONTEXT_NONE = 0, + + /** a begin statement */ + PM_CONTEXT_BEGIN, + + /** an ensure statement with an explicit begin */ + PM_CONTEXT_BEGIN_ENSURE, + + /** a rescue else statement with an explicit begin */ + PM_CONTEXT_BEGIN_ELSE, + + /** a rescue statement with an explicit begin */ + PM_CONTEXT_BEGIN_RESCUE, + + /** expressions in block arguments using braces */ + PM_CONTEXT_BLOCK_BRACES, + + /** expressions in block arguments using do..end */ + PM_CONTEXT_BLOCK_KEYWORDS, + + /** an ensure statement within a do..end block */ + PM_CONTEXT_BLOCK_ENSURE, + + /** a rescue else statement within a do..end block */ + PM_CONTEXT_BLOCK_ELSE, + + /** a rescue statement within a do..end block */ + PM_CONTEXT_BLOCK_RESCUE, + + /** a case when statements */ + PM_CONTEXT_CASE_WHEN, + + /** a case in statements */ + PM_CONTEXT_CASE_IN, + + /** a class declaration */ + PM_CONTEXT_CLASS, + + /** an ensure statement within a class statement */ + PM_CONTEXT_CLASS_ENSURE, + + /** a rescue else statement within a class statement */ + PM_CONTEXT_CLASS_ELSE, + + /** a rescue statement within a class statement */ + PM_CONTEXT_CLASS_RESCUE, + + /** a method definition */ + PM_CONTEXT_DEF, + + /** an ensure statement within a method definition */ + PM_CONTEXT_DEF_ENSURE, + + /** a rescue else statement within a method definition */ + PM_CONTEXT_DEF_ELSE, + + /** a rescue statement within a method definition */ + PM_CONTEXT_DEF_RESCUE, + + /** a method definition's parameters */ + PM_CONTEXT_DEF_PARAMS, + + /** a defined? expression */ + PM_CONTEXT_DEFINED, + + /** 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, + + /** 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, + + /** an ensure statement within a lambda expression */ + PM_CONTEXT_LAMBDA_ENSURE, + + /** a rescue else statement within a lambda expression */ + PM_CONTEXT_LAMBDA_ELSE, + + /** a rescue statement within a lambda expression */ + PM_CONTEXT_LAMBDA_RESCUE, + + /** the predicate clause of a loop statement */ + PM_CONTEXT_LOOP_PREDICATE, + + /** the top level context */ + PM_CONTEXT_MAIN, + + /** a module declaration */ + PM_CONTEXT_MODULE, + + /** an ensure statement within a module statement */ + PM_CONTEXT_MODULE_ENSURE, + + /** a rescue else statement within a module statement */ + PM_CONTEXT_MODULE_ELSE, + + /** a rescue statement within a module statement */ + PM_CONTEXT_MODULE_RESCUE, + + /** a multiple target expression */ + PM_CONTEXT_MULTI_TARGET, + + /** 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 modifier rescue clause */ + PM_CONTEXT_RESCUE_MODIFIER, + + /** a singleton class definition */ + PM_CONTEXT_SCLASS, + + /** an ensure statement with a singleton class */ + PM_CONTEXT_SCLASS_ENSURE, + + /** a rescue else statement with a singleton class */ + PM_CONTEXT_SCLASS_ELSE, + + /** a rescue statement with a singleton class */ + PM_CONTEXT_SCLASS_RESCUE, + + /** a ternary expression */ + PM_CONTEXT_TERNARY, + + /** 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; + + /** The location of the comment in the source. */ + pm_location_t location; + + /** 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 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; + +/** The type of shareable constant value that can be set. */ +typedef uint8_t pm_shareable_constant_value_t; +static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_NONE = 0x0; +static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_LITERAL = PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL; +static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING = PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING; +static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY = PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY; + +/** + * This tracks an individual local variable in a certain lexical context, as + * well as the number of times is it read. + */ +typedef struct { + /** The name of the local variable. */ + pm_constant_id_t name; + + /** The location of the local variable in the source. */ + pm_location_t location; + + /** The index of the local variable in the local table. */ + uint32_t index; + + /** The number of times the local variable is read. */ + uint32_t reads; + + /** The hash of the local variable. */ + uint32_t hash; +} pm_local_t; + +/** + * This is a set of local variables in a certain lexical context (method, class, + * module, etc.). We need to track how many times these variables are read in + * order to warn if they only get written. + */ +typedef struct pm_locals { + /** The number of local variables in the set. */ + uint32_t size; + + /** The capacity of the local variables set. */ + uint32_t capacity; + + /** The nullable allocated memory for the local variables in the set. */ + pm_local_t *locals; +} pm_locals_t; + +/** The flags about scope parameters that can be set. */ +typedef uint8_t pm_scope_parameters_t; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_NONE = 0x0; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS = 0x1; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS = 0x2; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_BLOCK = 0x4; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_ALL = 0x8; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED = 0x10; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_NUMBERED_INNER = 0x20; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_NUMBERED_FOUND = 0x40; + +/** + * 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 { + /** A pointer to the previous scope in the linked list. */ + struct pm_scope *previous; + + /** The IDs of the locals in the given scope. */ + pm_locals_t locals; + + /** + * This is a list of the implicit parameters contained within the block. + * These will be processed after the block is parsed to determine the kind + * of parameters node that should be used and to check if any errors need to + * be added. + */ + pm_node_list_t implicit_parameters; + + /** + * This is a bitfield that indicates the parameters that are being used in + * this scope. It is a combination of the PM_SCOPE_PARAMETERS_* constants. + * There are three different kinds of parameters that can be used in a + * scope: + * + * - Ordinary parameters (e.g., def foo(bar); end) + * - Numbered parameters (e.g., def foo; _1; end) + * - The it parameter (e.g., def foo; it; end) + * + * If ordinary parameters are being used, then certain parameters can be + * forwarded to another method/structure. Those are indicated by four + * additional bits in the params field. For example, some combinations of: + * + * - def foo(*); end + * - def foo(**); end + * - def foo(&); end + * - def foo(...); end + */ + pm_scope_parameters_t parameters; + + /** + * The current state of constant shareability for this scope. This is + * changed by magic shareable_constant_value comments. + */ + pm_shareable_constant_value_t shareable_constant; + + /** + * 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; +} pm_scope_t; + +/** + * A struct that represents a stack of boolean values. + */ +typedef uint32_t pm_state_stack_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 next node identifier that will be assigned. This is a unique + * identifier used to track nodes such that the syntax tree can be dropped + * but the node can be found through another parse. + */ + uint32_t node_id; + + /** 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 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; + + /** + * An optional location that represents the location of the __END__ marker + * and the rest of the content of the file. This content is loaded into the + * DATA constant when the file being parsed is the main file being executed. + */ + 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 hash keys for the hash that is currently being parsed. This is not + * usually necessary because it can pass it down the various call chains, + * but in the event that you're parsing a hash that is being directly + * pushed into another hash with **, we need to share the hash keys so that + * we can warn for the nested hash as well. + */ + pm_static_literals_t *current_hash_keys; + + /** + * 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. + */ + const 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; + + /** + * 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; + + /** + * 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; + + /** + * When a string-like expression is being lexed, any byte or escape sequence + * that resolves to a value whose top bit is set (i.e., >= 0x80) will + * explicitly set the encoding to the same encoding as the source. + * Alternatively, if a unicode escape sequence is used (e.g., \\u{80}) that + * resolves to a value whose top bit is set, then the encoding will be + * explicitly set to UTF-8. + * + * The _next_ time this happens, if the encoding that is about to become the + * explicitly set encoding does not match the previously set explicit + * encoding, a mixed encoding error will be emitted. + * + * When the expression is finished being lexed, the explicit encoding + * controls the encoding of the expression. For the most part this means + * that the expression will either be encoded in the source encoding or + * UTF-8. This holds for all encodings except US-ASCII. If the source is + * US-ASCII and an explicit encoding was set that was _not_ UTF-8, then the + * expression will be encoded as ASCII-8BIT. + * + * Note that if the expression is a list, different elements within the same + * list can have different encodings, so this will get reset between each + * element. Furthermore all of this only applies to lists that support + * interpolation, because otherwise escapes that could change the encoding + * are ignored. + * + * At first glance, it may make more sense for this to live on the lexer + * mode, but we need it here to communicate back to the parser for character + * literals that do not push a new lexer mode. + */ + const pm_encoding_t *explicit_encoding; + + /** + * When parsing block exits (e.g., break, next, redo), we need to validate + * that they are in correct contexts. For the most part we can do this by + * looking at our parent contexts. However, modifier while and until + * expressions can change that context to make block exits valid. In these + * cases, we need to keep track of the block exits and then validate them + * after the expression has been parsed. + * + * We use a pointer here because we don't want to keep a whole list attached + * since this will only be used in the context of begin/end expressions. + */ + pm_node_list_t *current_block_exits; + + /** The version of prism that we should use to parse. */ + pm_options_version_t version; + + /** The command line flags given from the options. */ + uint8_t command_line; + + /** + * Whether or not we have found a frozen_string_literal magic comment with + * a true or false value. + * May be: + * - PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED + * - PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED + * - PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET + */ + int8_t frozen_string_literal; + + /** + * Whether or not we are parsing an eval string. This impacts whether or not + * we should evaluate if block exits/yields are valid. + */ + bool parsing_eval; + + /** + * Whether or not we are parsing a "partial" script, which is a script that + * will be evaluated in the context of another script, so we should not + * check jumps (next/break/etc.) for validity. + */ + bool partial_script; + + /** 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; + + /** + * This is very specialized behavior for when you want to parse in a context + * that does not respect encoding comments. Its main use case is translating + * into the whitequark/parser AST which re-encodes source files in UTF-8 + * before they are parsed and ignores encoding comments. + */ + bool encoding_locked; + + /** + * 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; + + /** + * 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; + + /** + * True if the current regular expression being lexed contains only ASCII + * characters. + */ + bool current_regular_expression_ascii_only; + + /** + * By default, Ruby always warns about mismatched indentation. This can be + * toggled with a magic comment. + */ + bool warn_mismatched_indentation; +}; + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/prettyprint.h b/vendor/mruby-compiler2/lib/prism/include/prism/prettyprint.h new file mode 100644 index 0000000..5a52b2b --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/prettyprint.h @@ -0,0 +1,34 @@ +/** + * @file prettyprint.h + * + * An AST node pretty-printer. + */ +#ifndef PRISM_PRETTYPRINT_H +#define PRISM_PRETTYPRINT_H + +#include "prism/defines.h" + +#ifdef PRISM_EXCLUDE_PRETTYPRINT + +void pm_prettyprint(void); + +#else + +#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 + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/regexp.h b/vendor/mruby-compiler2/lib/prism/include/prism/regexp.h new file mode 100644 index 0000000..c0b3163 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/regexp.h @@ -0,0 +1,43 @@ +/** + * @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/encoding.h" +#include "prism/util/pm_memchr.h" +#include "prism/util/pm_string.h" + +#include +#include +#include + +/** + * This callback is called when a named capture group is found. + */ +typedef void (*pm_regexp_name_callback_t)(const pm_string_t *name, void *data); + +/** + * This callback is called when a parse error is found. + */ +typedef void (*pm_regexp_error_callback_t)(const uint8_t *start, const uint8_t *end, const char *message, void *data); + +/** + * Parse a regular expression. + * + * @param parser The parser that is currently being used. + * @param source The source code to parse. + * @param size The size of the source code. + * @param extended_mode Whether to parse the regular expression in extended mode. + * @param name_callback The optional callback to call when a named capture group is found. + * @param name_data The optional data to pass to the name callback. + * @param error_callback The callback to call when a parse error is found. + * @param error_data The data to pass to the error callback. + */ +PRISM_EXPORTED_FUNCTION void pm_regexp_parse(pm_parser_t *parser, const uint8_t *source, size_t size, bool extended_mode, pm_regexp_name_callback_t name_callback, void *name_data, pm_regexp_error_callback_t error_callback, void *error_data); + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/static_literals.h b/vendor/mruby-compiler2/lib/prism/include/prism/static_literals.h new file mode 100644 index 0000000..bd29761 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/static_literals.h @@ -0,0 +1,121 @@ +/** + * @file static_literals.h + * + * A set of static literal nodes that can be checked for duplicates. + */ +#ifndef PRISM_STATIC_LITERALS_H +#define PRISM_STATIC_LITERALS_H + +#include "prism/defines.h" +#include "prism/ast.h" +#include "prism/util/pm_newline_list.h" + +#include +#include + +/** + * An internal hash table for a set of nodes. + */ +typedef struct { + /** The array of nodes in the hash table. */ + pm_node_t **nodes; + + /** The size of the hash table. */ + uint32_t size; + + /** The space that has been allocated in the hash table. */ + uint32_t capacity; +} pm_node_hash_t; + +/** + * Certain sets of nodes (hash keys and when clauses) check for duplicate nodes + * to alert the user of potential issues. To do this, we keep a set of the nodes + * that have been seen so far, and compare whenever we find a new node. + * + * We bucket the nodes based on their type to minimize the number of comparisons + * that need to be performed. + */ +typedef struct { + /** + * This is the set of IntegerNode and SourceLineNode instances. + */ + pm_node_hash_t integer_nodes; + + /** + * This is the set of FloatNode instances. + */ + pm_node_hash_t float_nodes; + + /** + * This is the set of RationalNode and ImaginaryNode instances. + */ + pm_node_hash_t number_nodes; + + /** + * This is the set of StringNode and SourceFileNode instances. + */ + pm_node_hash_t string_nodes; + + /** + * This is the set of RegularExpressionNode instances. + */ + pm_node_hash_t regexp_nodes; + + /** + * This is the set of SymbolNode instances. + */ + pm_node_hash_t symbol_nodes; + + /** + * A pointer to the last TrueNode instance that was inserted, or NULL. + */ + pm_node_t *true_node; + + /** + * A pointer to the last FalseNode instance that was inserted, or NULL. + */ + pm_node_t *false_node; + + /** + * A pointer to the last NilNode instance that was inserted, or NULL. + */ + pm_node_t *nil_node; + + /** + * A pointer to the last SourceEncodingNode instance that was inserted, or + * NULL. + */ + pm_node_t *source_encoding_node; +} pm_static_literals_t; + +/** + * Add a node to the set of static literals. + * + * @param newline_list The list of newline offsets to use to calculate lines. + * @param start_line The line number that the parser starts on. + * @param literals The set of static literals to add the node to. + * @param node The node to add to the set. + * @param replace Whether to replace the previous node if one already exists. + * @return A pointer to the node that is being overwritten, if there is one. + */ +pm_node_t * pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node, bool replace); + +/** + * Free the internal memory associated with the given static literals set. + * + * @param literals The set of static literals to free. + */ +void pm_static_literals_free(pm_static_literals_t *literals); + +/** + * Create a string-based representation of the given static literal. + * + * @param buffer The buffer to write the string to. + * @param newline_list The list of newline offsets to use to calculate lines. + * @param start_line The line number that the parser starts on. + * @param encoding_name The name of the encoding of the source being parsed. + * @param node The node to create a string representation of. + */ +void pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node); + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_buffer.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_buffer.h new file mode 100644 index 0000000..f3c20ab --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_buffer.h @@ -0,0 +1,228 @@ +/** + * @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 "prism/util/pm_char.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(const 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(const 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); + +/** + * Append a double to the buffer. + * + * @param buffer The buffer to append to. + * @param value The double to append. + */ +void pm_buffer_append_double(pm_buffer_t *buffer, double value); + +/** + * Append a unicode codepoint to the buffer. + * + * @param buffer The buffer to append to. + * @param value The character to append. + * @returns True if the codepoint was valid and appended successfully, false + * otherwise. + */ +bool pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t value); + +/** + * The different types of escaping that can be performed by the buffer when + * appending a slice of Ruby source code. + */ +typedef enum { + PM_BUFFER_ESCAPING_RUBY, + PM_BUFFER_ESCAPING_JSON +} pm_buffer_escaping_t; + +/** + * Append a slice of source code to the buffer. + * + * @param buffer The buffer to append to. + * @param source The source code to append. + * @param length The length of the source code to append. + * @param escaping The type of escaping to perform. + */ +void pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping); + +/** + * Prepend the given string to the buffer. + * + * @param buffer The buffer to prepend to. + * @param value The string to prepend. + * @param length The length of the string to prepend. + */ +void pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length); + +/** + * 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); + +/** + * Clear the buffer by reducing its size to 0. This does not free the allocated + * memory, but it does allow the buffer to be reused. + * + * @param buffer The buffer to clear. + */ +void pm_buffer_clear(pm_buffer_t *buffer); + +/** + * Strip the whitespace from the end of the buffer. + * + * @param buffer The buffer to strip. + */ +void pm_buffer_rstrip(pm_buffer_t *buffer); + +/** + * Checks if the buffer includes the given value. + * + * @param buffer The buffer to check. + * @param value The value to check for. + * @returns The index of the first occurrence of the value in the buffer, or + * SIZE_MAX if the value is not found. + */ +size_t pm_buffer_index(const pm_buffer_t *buffer, char value); + +/** + * Insert the given string into the buffer at the given index. + * + * @param buffer The buffer to insert into. + * @param index The index to insert at. + * @param value The string to insert. + * @param length The length of the string to insert. + */ +void pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length); + +/** + * 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/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_char.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_char.h new file mode 100644 index 0000000..deeafd6 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_char.h @@ -0,0 +1,204 @@ +/** + * @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/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_constant_pool.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_constant_pool.h new file mode 100644 index 0000000..6df23f8 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_constant_pool.h @@ -0,0 +1,218 @@ +/** + * @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 + +/** + * When we allocate constants into the pool, we reserve 0 to mean that the slot + * is not yet filled. This constant is reused in other places to indicate the + * lack of a constant id. + */ +#define PM_CONSTANT_ID_UNSET 0 + +/** + * 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); + +/** + * Initialize a list of constant ids with a given capacity. + * + * @param list The list to initialize. + * @param capacity The initial capacity of the list. + */ +void pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity); + +/** + * 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); + +/** + * Insert a constant id into a list of constant ids at the specified index. + * + * @param list The list to insert into. + * @param index The index at which to insert. + * @param id The id to insert. + */ +void pm_constant_id_list_insert(pm_constant_id_list_t *list, size_t index, 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); + +/** + * 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); + +/** + * Find a constant in a constant pool. Returns the id of the constant, or 0 if + * the constant is not found. + * + * @param pool The pool to find the constant in. + * @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_find(const pm_constant_pool_t *pool, const uint8_t *start, size_t length); + +/** + * 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, 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/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_integer.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_integer.h new file mode 100644 index 0000000..a9e2966 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_integer.h @@ -0,0 +1,126 @@ +/** + * @file pm_integer.h + * + * This module provides functions for working with arbitrary-sized integers. + */ +#ifndef PRISM_NUMBER_H +#define PRISM_NUMBER_H + +#include "prism/defines.h" +#include "prism/util/pm_buffer.h" + +#include +#include +#include +#include + +/** + * A structure represents an arbitrary-sized integer. + */ +typedef struct { + /** + * The number of allocated values. length is set to 0 if the integer fits + * into uint32_t. + */ + size_t length; + + /** + * List of 32-bit integers. Set to NULL if the integer fits into uint32_t. + */ + uint32_t *values; + + /** + * Embedded value for small integer. This value is set to 0 if the value + * does not fit into uint32_t. + */ + uint32_t value; + + /** + * Whether or not the integer is negative. It is stored this way so that a + * zeroed pm_integer_t is always positive zero. + */ + bool negative; +} pm_integer_t; + +/** + * An enum controlling the base of an integer. It is expected that the base is + * already known before parsing the integer, even though it could be derived + * from the string itself. + */ +typedef enum { + /** The default decimal base, with no prefix. Leading 0s will be ignored. */ + PM_INTEGER_BASE_DEFAULT, + + /** The binary base, indicated by a 0b or 0B prefix. */ + PM_INTEGER_BASE_BINARY, + + /** The octal base, indicated by a 0, 0o, or 0O prefix. */ + PM_INTEGER_BASE_OCTAL, + + /** The decimal base, indicated by a 0d, 0D, or empty prefix. */ + PM_INTEGER_BASE_DECIMAL, + + /** The hexadecimal base, indicated by a 0x or 0X prefix. */ + PM_INTEGER_BASE_HEXADECIMAL, + + /** + * An unknown base, in which case pm_integer_parse will derive it based on + * the content of the string. This is less efficient and does more + * comparisons, so if callers know the base ahead of time, they should use + * that instead. + */ + PM_INTEGER_BASE_UNKNOWN +} pm_integer_base_t; + +/** + * Parse an integer from a string. This assumes that the format of the integer + * has already been validated, as internal validation checks are not performed + * here. + * + * @param integer The integer to parse into. + * @param base The base of the integer. + * @param start The start of the string. + * @param end The end of the string. + */ +void pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end); + +/** + * Compare two integers. This function returns -1 if the left integer is less + * than the right integer, 0 if they are equal, and 1 if the left integer is + * greater than the right integer. + * + * @param left The left integer to compare. + * @param right The right integer to compare. + * @return The result of the comparison. + */ +int pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right); + +/** + * Reduce a ratio of integers to its simplest form. + * + * If either the numerator or denominator do not fit into a 32-bit integer, then + * this function is a no-op. In the future, we may consider reducing even the + * larger numbers, but for now we're going to keep it simple. + * + * @param numerator The numerator of the ratio. + * @param denominator The denominator of the ratio. + */ +void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator); + +/** + * Convert an integer to a decimal string. + * + * @param buffer The buffer to append the string to. + * @param integer The integer to convert to a string. + */ +PRISM_EXPORTED_FUNCTION void pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer); + +/** + * Free the internal memory of an integer. This memory will only be allocated if + * the integer exceeds the size of a single node in the linked list. + * + * @param integer The integer to free. + */ +PRISM_EXPORTED_FUNCTION void pm_integer_free(pm_integer_t *integer); + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_list.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_list.h new file mode 100644 index 0000000..3512dee --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/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 = xmalloc(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/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_memchr.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_memchr.h new file mode 100644 index 0000000..e0671ea --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/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/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, const pm_encoding_t *encoding); + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_newline_list.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_newline_list.h new file mode 100644 index 0000000..406abe8 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_newline_list.h @@ -0,0 +1,113 @@ +/** + * @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. */ + int32_t line; + + /** The column number. */ + uint32_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); + +/** + * Clear out the newlines that have been appended to the list. + * + * @param list The list to clear. + */ +void +pm_newline_list_clear(pm_newline_list_t *list); + +/** + * 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); + +/** + * Returns the line of the given offset. If the offset is not in the list, the + * line of the closest offset less than the given offset is returned. + * + * @param list The list to search. + * @param cursor A pointer to the offset to search for. + * @param start_line The line to start counting from. + * @return The line of the given offset. + */ +int32_t pm_newline_list_line(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line); + +/** + * 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. + * @param start_line The line to start counting from. + * @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, int32_t start_line); + +/** + * 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/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_string.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_string.h new file mode 100644 index 0000000..f99f1ab --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_string.h @@ -0,0 +1,190 @@ +/** + * @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 +#include + +// The following headers are necessary to read files using demand paging. +#ifdef _WIN32 +#include +#elif defined(_POSIX_MAPPED_FILES) +#include +#include +#include +#elif defined(PRISM_HAS_FILESYSTEM) +#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, + +#ifdef PRISM_HAS_MMAP + /** This string is a memory-mapped file, and should be freed using `pm_string_free`. */ + PM_STRING_MAPPED +#endif + } 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); + +/** + * Represents the result of calling pm_string_mapped_init or + * pm_string_file_init. We need this additional information because there is + * not a platform-agnostic way to indicate that the file that was attempted to + * be opened was a directory. + */ +typedef enum { + /** Indicates that the string was successfully initialized. */ + PM_STRING_INIT_SUCCESS = 0, + /** + * Indicates a generic error from a string_*_init function, where the type + * of error should be read from `errno` or `GetLastError()`. + */ + PM_STRING_INIT_ERROR_GENERIC = 1, + /** + * Indicates that the file that was attempted to be opened was a directory. + */ + PM_STRING_INIT_ERROR_DIRECTORY = 2 +} pm_string_init_result_t; + +/** + * 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 The success of the read, indicated by the value of the enum. + */ +PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_mapped_init(pm_string_t *string, const char *filepath); + +/** + * 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. + * + * @param string The string to initialize. + * @param filepath The filepath to read. + * @return The success of the read, indicated by the value of the enum. + */ +PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_file_init(pm_string_t *string, const char *filepath); + +/** + * 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); + +/** + * Compare the underlying lengths and bytes of two strings. Returns 0 if the + * strings are equal, a negative number if the left string is less than the + * right string, and a positive number if the left string is greater than the + * right string. + * + * @param left The left string to compare. + * @param right The right string to compare. + * @return The comparison result. + */ +int pm_string_compare(const pm_string_t *left, const pm_string_t *right); + +/** + * 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/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_strncasecmp.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_strncasecmp.h new file mode 100644 index 0000000..5cb88cb --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/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/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_strpbrk.h b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_strpbrk.h new file mode 100644 index 0000000..f387bd5 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_strpbrk.h @@ -0,0 +1,46 @@ +/** + * @file pm_strpbrk.h + * + * A custom strpbrk implementation. + */ +#ifndef PRISM_STRPBRK_H +#define PRISM_STRPBRK_H + +#include "prism/defines.h" +#include "prism/diagnostic.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 to search. + * @param charset The charset to search for. + * @param length The maximum number of bytes to search. + * @param validate Whether to validate that the source string is valid in the + * current encoding of the parser. + * @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, bool validate); + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/version.h b/vendor/mruby-compiler2/lib/prism/include/prism/version.h new file mode 100644 index 0000000..0a2a8c8 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/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 1 + +/** + * The minor version of the Prism library as an int. + */ +#define PRISM_VERSION_MINOR 4 + +/** + * 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 "1.4.0" + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/prism.gemspec b/vendor/mruby-compiler2/lib/prism/prism.gemspec new file mode 100644 index 0000000..1947eed --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/prism.gemspec @@ -0,0 +1,170 @@ +# frozen_string_literal: true + +Gem::Specification.new do |spec| + spec.name = "prism" + spec.version = "1.4.0" + spec.authors = ["Shopify"] + spec.email = ["ruby@shopify.com"] + + spec.summary = "Prism Ruby parser" + spec.homepage = "https://github.com/ruby/prism" + spec.license = "MIT" + + spec.required_ruby_version = ">= 2.7.0" + + spec.require_paths = ["lib"] + spec.files = [ + "BSDmakefile", + "CHANGELOG.md", + "CODE_OF_CONDUCT.md", + "CONTRIBUTING.md", + "LICENSE.md", + "Makefile", + "README.md", + "config.yml", + "docs/build_system.md", + "docs/configuration.md", + "docs/cruby_compilation.md", + "docs/design.md", + "docs/encoding.md", + "docs/fuzzing.md", + "docs/heredocs.md", + "docs/javascript.md", + "docs/local_variable_depth.md", + "docs/mapping.md", + "docs/parser_translation.md", + "docs/parsing_rules.md", + "docs/releasing.md", + "docs/relocation.md", + "docs/ripper_translation.md", + "docs/ruby_api.md", + "docs/ruby_parser_translation.md", + "docs/serialization.md", + "docs/testing.md", + "ext/prism/api_node.c", + "ext/prism/api_pack.c", + "ext/prism/extension.c", + "ext/prism/extension.h", + "include/prism.h", + "include/prism/ast.h", + "include/prism/defines.h", + "include/prism/diagnostic.h", + "include/prism/encoding.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/static_literals.h", + "include/prism/util/pm_buffer.h", + "include/prism/util/pm_char.h", + "include/prism/util/pm_constant_pool.h", + "include/prism/util/pm_integer.h", + "include/prism/util/pm_list.h", + "include/prism/util/pm_memchr.h", + "include/prism/util/pm_newline_list.h", + "include/prism/util/pm_strncasecmp.h", + "include/prism/util/pm_string.h", + "include/prism/util/pm_strpbrk.h", + "include/prism/version.h", + "lib/prism.rb", + "lib/prism/compiler.rb", + "lib/prism/desugar_compiler.rb", + "lib/prism/dispatcher.rb", + "lib/prism/dot_visitor.rb", + "lib/prism/dsl.rb", + "lib/prism/ffi.rb", + "lib/prism/inspect_visitor.rb", + "lib/prism/lex_compat.rb", + "lib/prism/mutation_compiler.rb", + "lib/prism/node_ext.rb", + "lib/prism/node.rb", + "lib/prism/pack.rb", + "lib/prism/parse_result.rb", + "lib/prism/parse_result/comments.rb", + "lib/prism/parse_result/errors.rb", + "lib/prism/parse_result/newlines.rb", + "lib/prism/pattern.rb", + "lib/prism/polyfill/append_as_bytes.rb", + "lib/prism/polyfill/byteindex.rb", + "lib/prism/polyfill/unpack1.rb", + "lib/prism/reflection.rb", + "lib/prism/relocation.rb", + "lib/prism/serialize.rb", + "lib/prism/string_query.rb", + "lib/prism/translation.rb", + "lib/prism/translation/parser.rb", + "lib/prism/translation/parser33.rb", + "lib/prism/translation/parser34.rb", + "lib/prism/translation/parser35.rb", + "lib/prism/translation/parser/builder.rb", + "lib/prism/translation/parser/compiler.rb", + "lib/prism/translation/parser/lexer.rb", + "lib/prism/translation/ripper.rb", + "lib/prism/translation/ripper/sexp.rb", + "lib/prism/translation/ripper/shim.rb", + "lib/prism/translation/ruby_parser.rb", + "lib/prism/visitor.rb", + "prism.gemspec", + "rbi/prism.rbi", + "rbi/prism/compiler.rbi", + "rbi/prism/dsl.rbi", + "rbi/prism/inspect_visitor.rbi", + "rbi/prism/node_ext.rbi", + "rbi/prism/node.rbi", + "rbi/prism/parse_result.rbi", + "rbi/prism/reflection.rbi", + "rbi/prism/string_query.rbi", + "rbi/prism/translation/parser.rbi", + "rbi/prism/translation/parser33.rbi", + "rbi/prism/translation/parser34.rbi", + "rbi/prism/translation/parser35.rbi", + "rbi/prism/translation/ripper.rbi", + "rbi/prism/visitor.rbi", + "sig/prism.rbs", + "sig/prism/compiler.rbs", + "sig/prism/dispatcher.rbs", + "sig/prism/dot_visitor.rbs", + "sig/prism/dsl.rbs", + "sig/prism/inspect_visitor.rbs", + "sig/prism/lex_compat.rbs", + "sig/prism/mutation_compiler.rbs", + "sig/prism/node_ext.rbs", + "sig/prism/node.rbs", + "sig/prism/pack.rbs", + "sig/prism/parse_result.rbs", + "sig/prism/pattern.rbs", + "sig/prism/reflection.rbs", + "sig/prism/relocation.rbs", + "sig/prism/serialize.rbs", + "sig/prism/string_query.rbs", + "sig/prism/visitor.rbs", + "src/diagnostic.c", + "src/encoding.c", + "src/node.c", + "src/options.c", + "src/pack.c", + "src/prettyprint.c", + "src/prism.c", + "src/regexp.c", + "src/serialize.c", + "src/static_literals.c", + "src/token_type.c", + "src/util/pm_buffer.c", + "src/util/pm_char.c", + "src/util/pm_constant_pool.c", + "src/util/pm_integer.c", + "src/util/pm_list.c", + "src/util/pm_memchr.c", + "src/util/pm_newline_list.c", + "src/util/pm_string.c", + "src/util/pm_strncasecmp.c", + "src/util/pm_strpbrk.c" + ] + + spec.extensions = ["ext/prism/extconf.rb"] + spec.metadata["allowed_push_host"] = "https://rubygems.org" + spec.metadata["source_code_uri"] = "https://github.com/ruby/prism" + spec.metadata["changelog_uri"] = "https://github.com/ruby/prism/blob/main/CHANGELOG.md" +end diff --git a/vendor/mruby-compiler2/lib/prism/src/diagnostic.c b/vendor/mruby-compiler2/lib/prism/src/diagnostic.c new file mode 100644 index 0000000..be1bd71 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/diagnostic.c @@ -0,0 +1,842 @@ +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/diagnostic.c.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +#include "prism/diagnostic.h" + +#define PM_DIAGNOSTIC_ID_MAX 319 + +/** This struct holds the data for each diagnostic. */ +typedef struct { + /** The message associated with the diagnostic. */ + const char* message; + + /** The level associated with the diagnostic. */ + uint8_t level; +} pm_diagnostic_data_t; + +/** + * ## 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. + * + * ## Level + * + * For errors, they are: + * + * * `PM_ERROR_LEVEL_SYNTAX` - Errors that should raise SyntaxError. + * * `PM_ERROR_LEVEL_ARGUMENT` - Errors that should raise ArgumentError. + * * `PM_ERROR_LEVEL_LOAD` - Errors that should raise LoadError. + * + * For warnings, they are: + * + * * `PM_WARNING_LEVEL_DEFAULT` - Warnings that appear for `ruby -c -e 'code'`. + * * `PM_WARNING_LEVEL_VERBOSE` - Warnings that appear with `-w`, as in `ruby -w -c -e 'code'`. + */ +static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { + // Special error that can be replaced + [PM_ERR_CANNOT_PARSE_EXPRESSION] = { "cannot parse the expression", PM_ERROR_LEVEL_SYNTAX }, + + // Errors that should raise argument errors + [PM_ERR_INVALID_ENCODING_MAGIC_COMMENT] = { "unknown or invalid encoding in the magic comment", PM_ERROR_LEVEL_ARGUMENT }, + + // Errors that should raise load errors + [PM_ERR_SCRIPT_NOT_FOUND] = { "no Ruby script found in input", PM_ERROR_LEVEL_LOAD }, + + // Errors that should raise syntax errors + [PM_ERR_ALIAS_ARGUMENT] = { "invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE] = { "invalid argument being passed to `alias`; can't make alias for the number variables", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_AMPAMPEQ_MULTI_ASSIGN] = { "unexpected `&&=` in a multiple assignment", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_AFTER_BLOCK] = { "unexpected argument after a block argument", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES] = { "unexpected argument after `...`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_BARE_HASH] = { "unexpected bare hash argument", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_BLOCK_MULTI] = { "both block arg and actual block given; only one block is allowed", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_CONFLICT_AMPERSAND] = { "unexpected `&`; anonymous block parameter is also used within block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_CONFLICT_STAR] = { "unexpected `*`; anonymous rest parameter is also used within block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_CONFLICT_STAR_STAR] = { "unexpected `**`; anonymous keyword rest parameter is also used within block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_FORMAL_CLASS] = { "invalid formal argument; formal argument cannot be a class variable", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_FORMAL_CONSTANT] = { "invalid formal argument; formal argument cannot be a constant", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_FORMAL_GLOBAL] = { "invalid formal argument; formal argument cannot be a global variable", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_FORMAL_IVAR] = { "invalid formal argument; formal argument cannot be an instance variable", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_FORWARDING_UNBOUND] = { "unexpected `...` in an non-parenthesized call", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND] = { "unexpected `&`; no anonymous block parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = { "unexpected ... when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_STAR] = { "unexpected `*`; no anonymous rest parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR] = { "unexpected `**`; no anonymous keyword rest parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT] = { "unexpected `*` splat argument after a `**` keyword splat argument", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT] = { "unexpected `*` splat argument after a `*` splat argument", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_TERM_PAREN] = { "unexpected %s; expected a `)` to close the arguments", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_UNEXPECTED_BLOCK] = { "unexpected '{' after a method call without parenthesis", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARRAY_ELEMENT] = { "expected an element for the array", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARRAY_EXPRESSION] = { "expected an expression for the array element", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARRAY_EXPRESSION_AFTER_STAR] = { "expected an expression after `*` in the array", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARRAY_SEPARATOR] = { "unexpected %s; expected a `,` separator for the array elements", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARRAY_TERM] = { "unexpected %s; expected a `]` to close the array", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_BEGIN_LONELY_ELSE] = { "unexpected `else` in `begin` block; else without rescue is useless", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_BEGIN_TERM] = { "expected an `end` to close the `begin` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_BEGIN_UPCASE_BRACE] = { "expected a `{` after `BEGIN`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_BEGIN_UPCASE_TERM] = { "expected a `}` to close the `BEGIN` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_BEGIN_UPCASE_TOPLEVEL] = { "BEGIN is permitted only at toplevel", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE] = { "expected a local variable name in the block parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_BLOCK_PARAM_PIPE_TERM] = { "expected the block parameters to end with `|`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_BLOCK_TERM_BRACE] = { "expected a block beginning with `{` to end with `}`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_BLOCK_TERM_END] = { "expected a block beginning with `do` to end with `end`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CANNOT_PARSE_STRING_PART] = { "cannot parse the string part", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CASE_EXPRESSION_AFTER_CASE] = { "expected an expression after `case`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CASE_EXPRESSION_AFTER_WHEN] = { "expected an expression after `when`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CASE_MATCH_MISSING_PREDICATE] = { "expected a predicate for a case matching statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CASE_MISSING_CONDITIONS] = { "expected a `when` or `in` clause after `case`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CASE_TERM] = { "expected an `end` to close the `case` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CLASS_IN_METHOD] = { "unexpected class definition in method body", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CLASS_NAME] = { "unexpected constant path after `class`; class/module name must be CONSTANT", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CLASS_SUPERCLASS] = { "expected a superclass after `<`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CLASS_TERM] = { "expected an `end` to close the `class` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CLASS_UNEXPECTED_END] = { "unexpected `end`, expecting ';' or '\\n'", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CLASS_VARIABLE_BARE] = { "'@@' without identifiers is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CONDITIONAL_ELSIF_PREDICATE] = { "expected a predicate expression for the `elsif` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CONDITIONAL_IF_PREDICATE] = { "expected a predicate expression for the `if` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CONDITIONAL_PREDICATE_TERM] = { "expected `then` or `;` or '\\n'", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CONDITIONAL_TERM] = { "expected an `end` to close the conditional clause", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CONDITIONAL_TERM_ELSE] = { "expected an `end` to close the `else` clause", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CONDITIONAL_UNLESS_PREDICATE] = { "expected a predicate expression for the `unless` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CONDITIONAL_UNTIL_PREDICATE] = { "expected a predicate expression for the `until` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CONDITIONAL_WHILE_PREDICATE] = { "expected a predicate expression for the `while` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT] = { "expected a constant after the `::` operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_ENDLESS] = { "could not parse the endless method body", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_ENDLESS_SETTER] = { "invalid method name; a setter method cannot be defined in an endless method definition", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_NAME] = { "unexpected %s; expected a method name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_PARAMS_TERM] = { "expected a delimiter to close the parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_PARAMS_TERM_PAREN] = { "unexpected %s; expected a `)` to close the parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_RECEIVER] = { "expected a receiver for the method definition", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_RECEIVER_TERM] = { "expected a `.` or `::` after the receiver in a method definition", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_TERM] = { "expected an `end` to close the `def` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEFINED_EXPRESSION] = { "expected an expression after `defined?`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EMBDOC_TERM] = { "embedded document meets end of file", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EMBEXPR_END] = { "expected a `}` to close the embedded expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EMBVAR_INVALID] = { "invalid embedded variable", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_END_UPCASE_BRACE] = { "expected a `{` after `END`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_END_UPCASE_TERM] = { "expected a `}` to close the `END` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_CONTROL] = { "Invalid escape character syntax", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT] = { "invalid control escape sequence; control cannot be repeated", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_HEXADECIMAL] = { "invalid hex escape sequence", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_META] = { "Invalid escape character syntax", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_META_REPEAT] = { "invalid meta escape sequence; meta cannot be repeated", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE] = { "invalid Unicode escape sequence", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS] = { "invalid Unicode escape sequence; Unicode cannot be combined with control or meta flags", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_LIST] = { "invalid Unicode list: %.*s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL] = { "invalid Unicode escape sequence; Multiple codepoints at single character literal are disallowed", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_LONG] = { "invalid Unicode escape sequence; maximum length is 6 digits", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_SHORT] = { "too short escape sequence: %.*s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_TERM] = { "unterminated Unicode escape", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_ARGUMENT] = { "unexpected %s; expected an argument", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EOL_AFTER_STATEMENT] = { "unexpected %s, expecting end-of-input", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ] = { "expected an expression after `&&=`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ] = { "expected an expression after `||=`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA] = { "expected an expression after `,`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL] = { "expected an expression after `=`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS] = { "expected an expression after `<<`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_LPAREN] = { "expected an expression after `(`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR] = { "unexpected %s; expected an expression after the operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT] = { "expected an expression after `*` splat in an argument", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH] = { "expected an expression after `**` in a hash", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_STAR] = { "expected an expression after `*`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_FOR_DELIMITER] = { "unexpected %s; expected a 'do', newline, or ';' after the 'for' loop collection", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_IDENT_REQ_PARAMETER] = { "expected an identifier for the required parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_IN_DELIMITER] = { "expected a delimiter after the patterns of an `in` clause", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_LPAREN_REQ_PARAMETER] = { "expected a `(` to start a required parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_MESSAGE] = { "unexpected %s; expecting a message to send to the receiver", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_RBRACKET] = { "expected a matching `]`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_RPAREN] = { "expected a matching `)`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_RPAREN_AFTER_MULTI] = { "expected a `)` after multiple assignment", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_RPAREN_REQ_PARAMETER] = { "expected a `)` to end a required parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER] = { "unexpected %s; expected a newline or a ';' after the singleton class", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_STRING_CONTENT] = { "expected string content after opening string delimiter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_WHEN_DELIMITER] = { "expected a delimiter after the predicates of a `when` clause", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_BARE_HASH] = { "unexpected bare hash in expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE] = { "unexpected '='; target cannot be written", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING] = { "Can't assign to __ENCODING__", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE] = { "Can't assign to false", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_FILE] = { "Can't assign to __FILE__", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_LINE] = { "Can't assign to __LINE__", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_NIL] = { "Can't assign to nil", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED] = { "Can't assign to numbered parameter %.2s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_SELF] = { "Can't change the value of self", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE] = { "Can't assign to true", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_FLOAT_PARSE] = { "could not parse the float '%.*s'", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_FOR_COLLECTION] = { "expected a collection after the `in` in a `for` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_FOR_INDEX] = { "expected an index after `for`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_FOR_IN] = { "expected an `in` after the index in a `for` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_FOR_TERM] = { "expected an `end` to close the `for` loop", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_GLOBAL_VARIABLE_BARE] = { "'$' without identifiers is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HASH_EXPRESSION_AFTER_LABEL] = { "expected an expression after the label in a hash", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HASH_KEY] = { "unexpected %s, expecting '}' or a key in the hash literal", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HASH_ROCKET] = { "expected a `=>` between the hash key and value", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HASH_TERM] = { "expected a `}` to close the hash literal", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HASH_VALUE] = { "unexpected %s; expected a value in the hash literal", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HEREDOC_IDENTIFIER] = { "unterminated here document identifier", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HEREDOC_TERM] = { "unterminated heredoc; can't find string \"%.*s\" anywhere before EOF", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INCOMPLETE_QUESTION_MARK] = { "incomplete expression at `?`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3] = { "`%.*s' is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INCOMPLETE_VARIABLE_CLASS] = { "'%.*s' is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3] = { "`%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "'%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INSTANCE_VARIABLE_BARE] = { "'@' without identifiers is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_BLOCK_EXIT] = { "Invalid %s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_COMMA] = { "invalid comma", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_ESCAPE_CHARACTER] = { "Invalid escape character syntax", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_LOCAL_VARIABLE_READ] = { "identifier %.*s is not valid to get", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_LOCAL_VARIABLE_WRITE] = { "identifier %.*s is not valid to set", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_FRACTION] = { "unexpected fraction part after numeric literal", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_HEXADECIMAL] = { "invalid hexadecimal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_OCTAL] = { "invalid octal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER] = { "invalid underscore placement in number", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING] = { "trailing '_' in number", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_CHARACTER] = { "Invalid char '\\x%02X' in expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_MULTIBYTE_CHAR] = { "invalid multibyte char (%s)", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_MULTIBYTE_CHARACTER] = { "invalid multibyte character 0x%X", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_MULTIBYTE_ESCAPE] = { "invalid multibyte escape: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_PERCENT] = { "unknown type of %string", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_PERCENT_EOF] = { "unterminated quoted string meets end of file", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_RETRY_AFTER_ELSE] = { "Invalid retry after else", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_RETRY_AFTER_ENSURE] = { "Invalid retry after ensure", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_RETRY_WITHOUT_RESCUE] = { "Invalid retry without rescue", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_SYMBOL] = { "invalid symbol", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_VARIABLE_GLOBAL_3_3] = { "`%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_VARIABLE_GLOBAL] = { "'%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_YIELD] = { "Invalid yield", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_IT_NOT_ALLOWED_NUMBERED] = { "'it' is not allowed when a numbered parameter is already used", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_IT_NOT_ALLOWED_ORDINARY] = { "'it' is not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LAMBDA_OPEN] = { "expected a `do` keyword or a `{` to open the lambda block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LAMBDA_TERM_BRACE] = { "expected a lambda block beginning with `{` to end with `}`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LAMBDA_TERM_END] = { "expected a lambda block beginning with `do` to end with `end`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_I_LOWER_ELEMENT] = { "expected a symbol in a `%i` list", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_I_LOWER_TERM] = { "unterminated list; expected a closing delimiter for the `%i`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_I_UPPER_ELEMENT] = { "expected a symbol in a `%I` list", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_I_UPPER_TERM] = { "unterminated list; expected a closing delimiter for the `%I`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_W_LOWER_ELEMENT] = { "expected a string in a `%w` list", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_W_LOWER_TERM] = { "unterminated list; expected a closing delimiter for the `%w`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_W_UPPER_ELEMENT] = { "expected a string in a `%W` list", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_W_UPPER_TERM] = { "unterminated list; expected a closing delimiter for the `%W`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_MALLOC_FAILED] = { "failed to allocate memory", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_MIXED_ENCODING] = { "UTF-8 mixed within %s source", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_MODULE_IN_METHOD] = { "unexpected module definition in method body", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_MODULE_NAME] = { "unexpected constant path after `module`; class/module name must be CONSTANT", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_MODULE_TERM] = { "expected an `end` to close the `module` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_MULTI_ASSIGN_MULTI_SPLATS] = { "multiple splats in multiple assignment", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST] = { "unexpected '%.*s' resulting in multiple splats in multiple assignment", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NESTING_TOO_DEEP] = { "nesting too deep", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NO_LOCAL_VARIABLE] = { "%.*s: no such local variable", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NON_ASSOCIATIVE_OPERATOR] = { "unexpected %s; %s is a non-associative operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NOT_EXPRESSION] = { "expected an expression after `not`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NUMBER_LITERAL_UNDERSCORE] = { "number literal ending with a `_`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK] = { "numbered parameter is already used in inner block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NUMBERED_PARAMETER_IT] = { "numbered parameters are not allowed when 'it' is already used", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NUMBERED_PARAMETER_ORDINARY] = { "numbered parameters are not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK] = { "numbered parameter is already used in outer block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_OPERATOR_MULTI_ASSIGN] = { "unexpected operator for a multiple assignment", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_OPERATOR_WRITE_ARGUMENTS] = { "unexpected operator after a call with arguments", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_OPERATOR_WRITE_BLOCK] = { "unexpected operator after a call with a block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI] = { "unexpected multiple `**` splat parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_BLOCK_MULTI] = { "multiple block parameters; only one block is allowed", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_CIRCULAR] = { "circular argument reference - %.*s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_FORWARDING_AFTER_REST] = { "... after rest argument", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_METHOD_NAME] = { "unexpected name for a parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_NAME_DUPLICATED] = { "duplicated argument name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_NO_DEFAULT] = { "expected a default value for the parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_NO_DEFAULT_KW] = { "expected a default value for the keyword parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_NUMBERED_RESERVED] = { "%.2s is reserved for numbered parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_ORDER] = { "unexpected parameter order", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_SPLAT_MULTI] = { "unexpected multiple `*` splat parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_STAR] = { "unexpected parameter `*`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_UNEXPECTED_FWD] = { "unexpected `...` in parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_WILD_LOOSE_COMMA] = { "unexpected `,` in parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_UNEXPECTED_NO_KW] = { "unexpected **nil; no keywords marker disallowed after keywords", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS] = { "unexpected multiple '*' rest patterns in an array pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_CAPTURE_DUPLICATE] = { "duplicated variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET] = { "expected a pattern expression after the `[` operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA] = { "expected a pattern expression after `,`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET] = { "expected a pattern expression after `=>`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_IN] = { "expected a pattern expression after the `in` keyword", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_KEY] = { "expected a pattern expression after the key", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN] = { "expected a pattern expression after the `(` operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_PIN] = { "expected a pattern expression after the `^` pin operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE] = { "expected a pattern expression after the `|` operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE] = { "expected a pattern expression after the range operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_EXPRESSION_AFTER_REST] = { "unexpected pattern expression after the `**` expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_FIND_MISSING_INNER] = { "find patterns need at least one required inner pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_IMPLICIT] = { "unexpected implicit hash in pattern; use '{' to delineate", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY] = { "unexpected %s; expected a key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY_DUPLICATE] = { "duplicated key name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY_INTERPOLATED] = { "symbol literal with interpolation is not allowed", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY_LABEL] = { "expected a label as the key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY_LOCALS] = { "key must be valid as local variables", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_IDENT_AFTER_HROCKET] = { "expected an identifier after the `=>` operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_LABEL_AFTER_COMMA] = { "expected a label after the `,` in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_REST] = { "unexpected rest pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_TERM_BRACE] = { "expected a `}` to close the pattern expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_TERM_BRACKET] = { "expected a `]` to close the pattern expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_TERM_PAREN] = { "expected a `)` to close the pattern expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN] = { "unexpected `||=` in a multiple assignment", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH] = { "regexp encoding option '%c' differs from source encoding '%s'", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING] = { "incompatible character encoding: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_NON_ESCAPED_MBC] = { "/.../n has a non escaped non ASCII character in non ASCII-8BIT script: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_INVALID_UNICODE_RANGE] = { "invalid Unicode range: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_PARSE_ERROR] = { "%s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_UNKNOWN_OPTIONS] = { "unknown regexp %s - %.*s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_TERM] = { "unterminated regexp meets end of file; expected a closing delimiter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP] = { "UTF-8 character in non UTF-8 regexp: /%s/", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_RESCUE_EXPRESSION] = { "expected a rescued expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_RESCUE_MODIFIER_VALUE] = { "expected a value after the `rescue` modifier", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_RESCUE_TERM] = { "expected a closing delimiter for the `rescue` clause", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_RESCUE_VARIABLE] = { "expected an exception variable after `=>` in a rescue statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_RETURN_INVALID] = { "Invalid return in class/module body", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_SINGLETON_FOR_LITERALS] = { "cannot define singleton method for literals", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STATEMENT_ALIAS] = { "unexpected an `alias` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STATEMENT_POSTEXE_END] = { "unexpected an `END` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STATEMENT_PREEXE_BEGIN] = { "unexpected a `BEGIN` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STATEMENT_UNDEF] = { "unexpected an `undef` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STRING_CONCATENATION] = { "expected a string for concatenation", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STRING_INTERPOLATED_TERM] = { "unterminated string; expected a closing delimiter for the interpolated string", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STRING_LITERAL_EOF] = { "unterminated string meets end of file", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STRING_LITERAL_TERM] = { "unexpected %s, expected a string literal terminator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_SYMBOL_INVALID] = { "invalid symbol", PM_ERROR_LEVEL_SYNTAX }, // TODO expected symbol? prism.c ~9719 + [PM_ERR_SYMBOL_TERM_DYNAMIC] = { "unterminated quoted string; expected a closing delimiter for the dynamic symbol", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_SYMBOL_TERM_INTERPOLATED] = { "unterminated symbol; expected a closing delimiter for the interpolated symbol", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_TERNARY_COLON] = { "expected a `:` after the true expression of a ternary operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_TERNARY_EXPRESSION_FALSE] = { "expected an expression after `:` in the ternary operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_TERNARY_EXPRESSION_TRUE] = { "expected an expression after `?` in the ternary operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNARY_RECEIVER] = { "unexpected %s, expected a receiver for unary `%c`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNARY_DISALLOWED] = { "unexpected %s; unary calls are not allowed in this context", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNDEF_ARGUMENT] = { "invalid argument being passed to `undef`; expected a bare word, constant, or symbol argument", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_BLOCK_ARGUMENT] = { "block argument should not be given", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_INDEX_BLOCK] = { "unexpected block arg given in index assignment; blocks are not allowed in index assignment expressions", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_INDEX_KEYWORDS] = { "unexpected keyword arg given in index assignment; keywords are not allowed in index assignment expressions", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_LABEL] = { "unexpected label", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_MULTI_WRITE] = { "unexpected multiple assignment; multiple assignment is not allowed in this context", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_RANGE_OPERATOR] = { "unexpected range operator; .. and ... are non-associative and cannot be chained", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_SAFE_NAVIGATION] = { "&. inside multiple assignment destination", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT] = { "unexpected %s, assuming it is closing the parent %s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_TOKEN_IGNORE] = { "unexpected %s, ignoring it", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNTIL_TERM] = { "expected an `end` to close the `until` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_VOID_EXPRESSION] = { "unexpected void value expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_WHILE_TERM] = { "expected an `end` to close the `while` statement", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_WRITE_TARGET_IN_METHOD] = { "dynamic constant assignment", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_WRITE_TARGET_READONLY] = { "Can't set variable %.*s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_WRITE_TARGET_UNEXPECTED] = { "unexpected write target", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_XSTRING_TERM] = { "expected a closing delimiter for the `%x` or backtick string", PM_ERROR_LEVEL_SYNTAX }, + + // Warnings + [PM_WARN_AMBIGUOUS_BINARY_OPERATOR] = { "'%s' after local variable or literal is interpreted as binary operator even though it seems like %s", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS] = { "ambiguous first argument; put parentheses or a space even after `-` operator", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS] = { "ambiguous first argument; put parentheses or a space even after `+` operator", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND] = { "ambiguous `&` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_AMBIGUOUS_PREFIX_STAR] = { "ambiguous `*` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR] = { "ambiguous `**` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_AMBIGUOUS_SLASH] = { "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_COMPARISON_AFTER_COMPARISON] = { "comparison '%.*s' after comparison", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_DOT_DOT_DOT_EOL] = { "... at EOL, should be parenthesized?", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_DUPLICATED_HASH_KEY] = { "key %.*s is duplicated and overwritten on line %" PRIi32, PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_DUPLICATED_WHEN_CLAUSE] = { "'when' clause on line %" PRIi32 " duplicates 'when' clause on line %" PRIi32 " and is ignored", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_EQUAL_IN_CONDITIONAL_3_3] = { "found `= literal' in conditional, should be ==", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_EQUAL_IN_CONDITIONAL] = { "found '= literal' in conditional, should be ==", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_END_IN_METHOD] = { "END in method; use at_exit", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_FLOAT_OUT_OF_RANGE] = { "Float %.*s%s out of range", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_IGNORED_FROZEN_STRING_LITERAL] = { "'frozen_string_literal' is ignored after any tokens", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_INDENTATION_MISMATCH] = { "mismatched indentations at '%.*s' with '%.*s' at %" PRIi32, PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_INTEGER_IN_FLIP_FLOP] = { "integer literal in flip-flop", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_INVALID_CHARACTER] = { "invalid character syntax; use %s%s%s", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_INVALID_MAGIC_COMMENT_VALUE] = { "invalid value for %.*s: %.*s", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_INVALID_NUMBERED_REFERENCE] = { "'%.*s' is too big for a number variable, always nil", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_KEYWORD_EOL] = { "`%.*s` at the end of line without an expression", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_LITERAL_IN_CONDITION_DEFAULT] = { "%sliteral in %s", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_LITERAL_IN_CONDITION_VERBOSE] = { "%sliteral in %s", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE] = { "'shareable_constant_value' is ignored unless in comment-only line", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_SHEBANG_CARRIAGE_RETURN] = { "shebang line ending with \\r may cause problems", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_UNEXPECTED_CARRIAGE_RETURN] = { "encountered \\r in middle of line, treated as a mere space", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_UNREACHABLE_STATEMENT] = { "statement not reached", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_UNUSED_LOCAL_VARIABLE] = { "assigned but unused variable - %.*s", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_VOID_STATEMENT] = { "possibly useless use of %.*s in void context", PM_WARNING_LEVEL_VERBOSE } +}; + +/** + * Get the human-readable name of the given diagnostic ID. + */ +const char * +pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { + switch (diag_id) { + case PM_ERR_ALIAS_ARGUMENT: return "alias_argument"; + case PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE: return "alias_argument_numbered_reference"; + case PM_ERR_AMPAMPEQ_MULTI_ASSIGN: return "ampampeq_multi_assign"; + case PM_ERR_ARGUMENT_AFTER_BLOCK: return "argument_after_block"; + case PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES: return "argument_after_forwarding_ellipses"; + case PM_ERR_ARGUMENT_BARE_HASH: return "argument_bare_hash"; + case PM_ERR_ARGUMENT_BLOCK_FORWARDING: return "argument_block_forwarding"; + case PM_ERR_ARGUMENT_BLOCK_MULTI: return "argument_block_multi"; + case PM_ERR_ARGUMENT_CONFLICT_AMPERSAND: return "argument_conflict_ampersand"; + case PM_ERR_ARGUMENT_CONFLICT_STAR: return "argument_conflict_star"; + case PM_ERR_ARGUMENT_CONFLICT_STAR_STAR: return "argument_conflict_star_star"; + case PM_ERR_ARGUMENT_FORMAL_CLASS: return "argument_formal_class"; + case PM_ERR_ARGUMENT_FORMAL_CONSTANT: return "argument_formal_constant"; + case PM_ERR_ARGUMENT_FORMAL_GLOBAL: return "argument_formal_global"; + case PM_ERR_ARGUMENT_FORMAL_IVAR: return "argument_formal_ivar"; + case PM_ERR_ARGUMENT_FORWARDING_UNBOUND: return "argument_forwarding_unbound"; + case PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND: return "argument_no_forwarding_ampersand"; + case PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES: return "argument_no_forwarding_ellipses"; + case PM_ERR_ARGUMENT_NO_FORWARDING_STAR: return "argument_no_forwarding_star"; + case PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR: return "argument_no_forwarding_star_star"; + case PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT: return "argument_splat_after_assoc_splat"; + case PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT: return "argument_splat_after_splat"; + case PM_ERR_ARGUMENT_TERM_PAREN: return "argument_term_paren"; + case PM_ERR_ARGUMENT_UNEXPECTED_BLOCK: return "argument_unexpected_block"; + case PM_ERR_ARRAY_ELEMENT: return "array_element"; + case PM_ERR_ARRAY_EXPRESSION: return "array_expression"; + case PM_ERR_ARRAY_EXPRESSION_AFTER_STAR: return "array_expression_after_star"; + case PM_ERR_ARRAY_SEPARATOR: return "array_separator"; + case PM_ERR_ARRAY_TERM: return "array_term"; + case PM_ERR_BEGIN_LONELY_ELSE: return "begin_lonely_else"; + case PM_ERR_BEGIN_TERM: return "begin_term"; + case PM_ERR_BEGIN_UPCASE_BRACE: return "begin_upcase_brace"; + case PM_ERR_BEGIN_UPCASE_TERM: return "begin_upcase_term"; + case PM_ERR_BEGIN_UPCASE_TOPLEVEL: return "begin_upcase_toplevel"; + case PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE: return "block_param_local_variable"; + case PM_ERR_BLOCK_PARAM_PIPE_TERM: return "block_param_pipe_term"; + case PM_ERR_BLOCK_TERM_BRACE: return "block_term_brace"; + case PM_ERR_BLOCK_TERM_END: return "block_term_end"; + case PM_ERR_CANNOT_PARSE_EXPRESSION: return "cannot_parse_expression"; + case PM_ERR_CANNOT_PARSE_STRING_PART: return "cannot_parse_string_part"; + case PM_ERR_CASE_EXPRESSION_AFTER_CASE: return "case_expression_after_case"; + case PM_ERR_CASE_EXPRESSION_AFTER_WHEN: return "case_expression_after_when"; + case PM_ERR_CASE_MATCH_MISSING_PREDICATE: return "case_match_missing_predicate"; + case PM_ERR_CASE_MISSING_CONDITIONS: return "case_missing_conditions"; + case PM_ERR_CASE_TERM: return "case_term"; + case PM_ERR_CLASS_IN_METHOD: return "class_in_method"; + case PM_ERR_CLASS_NAME: return "class_name"; + case PM_ERR_CLASS_SUPERCLASS: return "class_superclass"; + case PM_ERR_CLASS_TERM: return "class_term"; + case PM_ERR_CLASS_UNEXPECTED_END: return "class_unexpected_end"; + case PM_ERR_CLASS_VARIABLE_BARE: return "class_variable_bare"; + case PM_ERR_CONDITIONAL_ELSIF_PREDICATE: return "conditional_elsif_predicate"; + case PM_ERR_CONDITIONAL_IF_PREDICATE: return "conditional_if_predicate"; + case PM_ERR_CONDITIONAL_PREDICATE_TERM: return "conditional_predicate_term"; + case PM_ERR_CONDITIONAL_TERM: return "conditional_term"; + case PM_ERR_CONDITIONAL_TERM_ELSE: return "conditional_term_else"; + case PM_ERR_CONDITIONAL_UNLESS_PREDICATE: return "conditional_unless_predicate"; + case PM_ERR_CONDITIONAL_UNTIL_PREDICATE: return "conditional_until_predicate"; + case PM_ERR_CONDITIONAL_WHILE_PREDICATE: return "conditional_while_predicate"; + case PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT: return "constant_path_colon_colon_constant"; + case PM_ERR_DEF_ENDLESS: return "def_endless"; + case PM_ERR_DEF_ENDLESS_SETTER: return "def_endless_setter"; + case PM_ERR_DEF_NAME: return "def_name"; + case PM_ERR_DEF_PARAMS_TERM: return "def_params_term"; + case PM_ERR_DEF_PARAMS_TERM_PAREN: return "def_params_term_paren"; + case PM_ERR_DEF_RECEIVER: return "def_receiver"; + case PM_ERR_DEF_RECEIVER_TERM: return "def_receiver_term"; + case PM_ERR_DEF_TERM: return "def_term"; + case PM_ERR_DEFINED_EXPRESSION: return "defined_expression"; + case PM_ERR_EMBDOC_TERM: return "embdoc_term"; + case PM_ERR_EMBEXPR_END: return "embexpr_end"; + case PM_ERR_EMBVAR_INVALID: return "embvar_invalid"; + case PM_ERR_END_UPCASE_BRACE: return "end_upcase_brace"; + case PM_ERR_END_UPCASE_TERM: return "end_upcase_term"; + case PM_ERR_ESCAPE_INVALID_CONTROL: return "escape_invalid_control"; + case PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT: return "escape_invalid_control_repeat"; + case PM_ERR_ESCAPE_INVALID_HEXADECIMAL: return "escape_invalid_hexadecimal"; + case PM_ERR_ESCAPE_INVALID_META: return "escape_invalid_meta"; + case PM_ERR_ESCAPE_INVALID_META_REPEAT: return "escape_invalid_meta_repeat"; + case PM_ERR_ESCAPE_INVALID_UNICODE: return "escape_invalid_unicode"; + case PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS: return "escape_invalid_unicode_cm_flags"; + case PM_ERR_ESCAPE_INVALID_UNICODE_LIST: return "escape_invalid_unicode_list"; + case PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL: return "escape_invalid_unicode_literal"; + case PM_ERR_ESCAPE_INVALID_UNICODE_LONG: return "escape_invalid_unicode_long"; + case PM_ERR_ESCAPE_INVALID_UNICODE_SHORT: return "escape_invalid_unicode_short"; + case PM_ERR_ESCAPE_INVALID_UNICODE_TERM: return "escape_invalid_unicode_term"; + case PM_ERR_EXPECT_ARGUMENT: return "expect_argument"; + case PM_ERR_EXPECT_EOL_AFTER_STATEMENT: return "expect_eol_after_statement"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ: return "expect_expression_after_ampampeq"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA: return "expect_expression_after_comma"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL: return "expect_expression_after_equal"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS: return "expect_expression_after_less_less"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_LPAREN: return "expect_expression_after_lparen"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: return "expect_expression_after_operator"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ: return "expect_expression_after_pipepipeeq"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_QUESTION: return "expect_expression_after_question"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT: return "expect_expression_after_splat"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH: return "expect_expression_after_splat_hash"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_STAR: return "expect_expression_after_star"; + case PM_ERR_EXPECT_FOR_DELIMITER: return "expect_for_delimiter"; + case PM_ERR_EXPECT_IDENT_REQ_PARAMETER: return "expect_ident_req_parameter"; + case PM_ERR_EXPECT_IN_DELIMITER: return "expect_in_delimiter"; + case PM_ERR_EXPECT_LPAREN_REQ_PARAMETER: return "expect_lparen_req_parameter"; + case PM_ERR_EXPECT_MESSAGE: return "expect_message"; + case PM_ERR_EXPECT_RBRACKET: return "expect_rbracket"; + case PM_ERR_EXPECT_RPAREN: return "expect_rparen"; + case PM_ERR_EXPECT_RPAREN_AFTER_MULTI: return "expect_rparen_after_multi"; + case PM_ERR_EXPECT_RPAREN_REQ_PARAMETER: return "expect_rparen_req_parameter"; + case PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER: return "expect_singleton_class_delimiter"; + case PM_ERR_EXPECT_STRING_CONTENT: return "expect_string_content"; + case PM_ERR_EXPECT_WHEN_DELIMITER: return "expect_when_delimiter"; + case PM_ERR_EXPRESSION_BARE_HASH: return "expression_bare_hash"; + case PM_ERR_EXPRESSION_NOT_WRITABLE: return "expression_not_writable"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING: return "expression_not_writable_encoding"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE: return "expression_not_writable_false"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_FILE: return "expression_not_writable_file"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_LINE: return "expression_not_writable_line"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_NIL: return "expression_not_writable_nil"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED: return "expression_not_writable_numbered"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF: return "expression_not_writable_self"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE: return "expression_not_writable_true"; + case PM_ERR_FLOAT_PARSE: return "float_parse"; + case PM_ERR_FOR_COLLECTION: return "for_collection"; + case PM_ERR_FOR_IN: return "for_in"; + case PM_ERR_FOR_INDEX: return "for_index"; + case PM_ERR_FOR_TERM: return "for_term"; + case PM_ERR_GLOBAL_VARIABLE_BARE: return "global_variable_bare"; + case PM_ERR_HASH_EXPRESSION_AFTER_LABEL: return "hash_expression_after_label"; + case PM_ERR_HASH_KEY: return "hash_key"; + case PM_ERR_HASH_ROCKET: return "hash_rocket"; + case PM_ERR_HASH_TERM: return "hash_term"; + case PM_ERR_HASH_VALUE: return "hash_value"; + case PM_ERR_HEREDOC_IDENTIFIER: return "heredoc_identifier"; + case PM_ERR_HEREDOC_TERM: return "heredoc_term"; + case PM_ERR_INCOMPLETE_QUESTION_MARK: return "incomplete_question_mark"; + case PM_ERR_INCOMPLETE_VARIABLE_CLASS: return "incomplete_variable_class"; + case PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3: return "incomplete_variable_class_3_3"; + case PM_ERR_INCOMPLETE_VARIABLE_INSTANCE: return "incomplete_variable_instance"; + case PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3: return "incomplete_variable_instance_3_3"; + case PM_ERR_INSTANCE_VARIABLE_BARE: return "instance_variable_bare"; + case PM_ERR_INVALID_BLOCK_EXIT: return "invalid_block_exit"; + case PM_ERR_INVALID_CHARACTER: return "invalid_character"; + case PM_ERR_INVALID_COMMA: return "invalid_comma"; + case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT: return "invalid_encoding_magic_comment"; + case PM_ERR_INVALID_ESCAPE_CHARACTER: return "invalid_escape_character"; + case PM_ERR_INVALID_FLOAT_EXPONENT: return "invalid_float_exponent"; + case PM_ERR_INVALID_LOCAL_VARIABLE_READ: return "invalid_local_variable_read"; + case PM_ERR_INVALID_LOCAL_VARIABLE_WRITE: return "invalid_local_variable_write"; + case PM_ERR_INVALID_MULTIBYTE_CHAR: return "invalid_multibyte_char"; + case PM_ERR_INVALID_MULTIBYTE_CHARACTER: return "invalid_multibyte_character"; + case PM_ERR_INVALID_MULTIBYTE_ESCAPE: return "invalid_multibyte_escape"; + case PM_ERR_INVALID_NUMBER_BINARY: return "invalid_number_binary"; + case PM_ERR_INVALID_NUMBER_DECIMAL: return "invalid_number_decimal"; + case PM_ERR_INVALID_NUMBER_FRACTION: return "invalid_number_fraction"; + case PM_ERR_INVALID_NUMBER_HEXADECIMAL: return "invalid_number_hexadecimal"; + case PM_ERR_INVALID_NUMBER_OCTAL: return "invalid_number_octal"; + case PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER: return "invalid_number_underscore_inner"; + case PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING: return "invalid_number_underscore_trailing"; + case PM_ERR_INVALID_PERCENT: return "invalid_percent"; + case PM_ERR_INVALID_PERCENT_EOF: return "invalid_percent_eof"; + case PM_ERR_INVALID_PRINTABLE_CHARACTER: return "invalid_printable_character"; + case PM_ERR_INVALID_RETRY_AFTER_ELSE: return "invalid_retry_after_else"; + case PM_ERR_INVALID_RETRY_AFTER_ENSURE: return "invalid_retry_after_ensure"; + case PM_ERR_INVALID_RETRY_WITHOUT_RESCUE: return "invalid_retry_without_rescue"; + case PM_ERR_INVALID_SYMBOL: return "invalid_symbol"; + case PM_ERR_INVALID_VARIABLE_GLOBAL: return "invalid_variable_global"; + case PM_ERR_INVALID_VARIABLE_GLOBAL_3_3: return "invalid_variable_global_3_3"; + case PM_ERR_INVALID_YIELD: return "invalid_yield"; + case PM_ERR_IT_NOT_ALLOWED_NUMBERED: return "it_not_allowed_numbered"; + case PM_ERR_IT_NOT_ALLOWED_ORDINARY: return "it_not_allowed_ordinary"; + case PM_ERR_LAMBDA_OPEN: return "lambda_open"; + case PM_ERR_LAMBDA_TERM_BRACE: return "lambda_term_brace"; + case PM_ERR_LAMBDA_TERM_END: return "lambda_term_end"; + case PM_ERR_LIST_I_LOWER_ELEMENT: return "list_i_lower_element"; + case PM_ERR_LIST_I_LOWER_TERM: return "list_i_lower_term"; + case PM_ERR_LIST_I_UPPER_ELEMENT: return "list_i_upper_element"; + case PM_ERR_LIST_I_UPPER_TERM: return "list_i_upper_term"; + case PM_ERR_LIST_W_LOWER_ELEMENT: return "list_w_lower_element"; + case PM_ERR_LIST_W_LOWER_TERM: return "list_w_lower_term"; + case PM_ERR_LIST_W_UPPER_ELEMENT: return "list_w_upper_element"; + case PM_ERR_LIST_W_UPPER_TERM: return "list_w_upper_term"; + case PM_ERR_MALLOC_FAILED: return "malloc_failed"; + case PM_ERR_MIXED_ENCODING: return "mixed_encoding"; + case PM_ERR_MODULE_IN_METHOD: return "module_in_method"; + case PM_ERR_MODULE_NAME: return "module_name"; + case PM_ERR_MODULE_TERM: return "module_term"; + case PM_ERR_MULTI_ASSIGN_MULTI_SPLATS: return "multi_assign_multi_splats"; + case PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST: return "multi_assign_unexpected_rest"; + case PM_ERR_NESTING_TOO_DEEP: return "nesting_too_deep"; + case PM_ERR_NO_LOCAL_VARIABLE: return "no_local_variable"; + case PM_ERR_NON_ASSOCIATIVE_OPERATOR: return "non_associative_operator"; + case PM_ERR_NOT_EXPRESSION: return "not_expression"; + case PM_ERR_NUMBER_LITERAL_UNDERSCORE: return "number_literal_underscore"; + case PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK: return "numbered_parameter_inner_block"; + case PM_ERR_NUMBERED_PARAMETER_IT: return "numbered_parameter_it"; + case PM_ERR_NUMBERED_PARAMETER_ORDINARY: return "numbered_parameter_ordinary"; + case PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK: return "numbered_parameter_outer_block"; + case PM_ERR_OPERATOR_MULTI_ASSIGN: return "operator_multi_assign"; + case PM_ERR_OPERATOR_WRITE_ARGUMENTS: return "operator_write_arguments"; + case PM_ERR_OPERATOR_WRITE_BLOCK: return "operator_write_block"; + case PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI: return "parameter_assoc_splat_multi"; + case PM_ERR_PARAMETER_BLOCK_MULTI: return "parameter_block_multi"; + case PM_ERR_PARAMETER_CIRCULAR: return "parameter_circular"; + case PM_ERR_PARAMETER_FORWARDING_AFTER_REST: return "parameter_forwarding_after_rest"; + case PM_ERR_PARAMETER_METHOD_NAME: return "parameter_method_name"; + case PM_ERR_PARAMETER_NAME_DUPLICATED: return "parameter_name_duplicated"; + case PM_ERR_PARAMETER_NO_DEFAULT: return "parameter_no_default"; + case PM_ERR_PARAMETER_NO_DEFAULT_KW: return "parameter_no_default_kw"; + case PM_ERR_PARAMETER_NUMBERED_RESERVED: return "parameter_numbered_reserved"; + case PM_ERR_PARAMETER_ORDER: return "parameter_order"; + case PM_ERR_PARAMETER_SPLAT_MULTI: return "parameter_splat_multi"; + case PM_ERR_PARAMETER_STAR: return "parameter_star"; + case PM_ERR_PARAMETER_UNEXPECTED_FWD: return "parameter_unexpected_fwd"; + case PM_ERR_PARAMETER_UNEXPECTED_NO_KW: return "parameter_unexpected_no_kw"; + case PM_ERR_PARAMETER_WILD_LOOSE_COMMA: return "parameter_wild_loose_comma"; + case PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS: return "pattern_array_multiple_rests"; + case PM_ERR_PATTERN_CAPTURE_DUPLICATE: return "pattern_capture_duplicate"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET: return "pattern_expression_after_bracket"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA: return "pattern_expression_after_comma"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET: return "pattern_expression_after_hrocket"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_IN: return "pattern_expression_after_in"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_KEY: return "pattern_expression_after_key"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN: return "pattern_expression_after_paren"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_PIN: return "pattern_expression_after_pin"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE: return "pattern_expression_after_pipe"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE: return "pattern_expression_after_range"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_REST: return "pattern_expression_after_rest"; + case PM_ERR_PATTERN_FIND_MISSING_INNER: return "pattern_find_missing_inner"; + case PM_ERR_PATTERN_HASH_IMPLICIT: return "pattern_hash_implicit"; + case PM_ERR_PATTERN_HASH_KEY: return "pattern_hash_key"; + case PM_ERR_PATTERN_HASH_KEY_DUPLICATE: return "pattern_hash_key_duplicate"; + case PM_ERR_PATTERN_HASH_KEY_INTERPOLATED: return "pattern_hash_key_interpolated"; + case PM_ERR_PATTERN_HASH_KEY_LABEL: return "pattern_hash_key_label"; + case PM_ERR_PATTERN_HASH_KEY_LOCALS: return "pattern_hash_key_locals"; + case PM_ERR_PATTERN_IDENT_AFTER_HROCKET: return "pattern_ident_after_hrocket"; + case PM_ERR_PATTERN_LABEL_AFTER_COMMA: return "pattern_label_after_comma"; + case PM_ERR_PATTERN_REST: return "pattern_rest"; + case PM_ERR_PATTERN_TERM_BRACE: return "pattern_term_brace"; + case PM_ERR_PATTERN_TERM_BRACKET: return "pattern_term_bracket"; + case PM_ERR_PATTERN_TERM_PAREN: return "pattern_term_paren"; + case PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN: return "pipepipeeq_multi_assign"; + case PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH: return "regexp_encoding_option_mismatch"; + case PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING: return "regexp_incompat_char_encoding"; + case PM_ERR_REGEXP_INVALID_UNICODE_RANGE: return "regexp_invalid_unicode_range"; + case PM_ERR_REGEXP_NON_ESCAPED_MBC: return "regexp_non_escaped_mbc"; + case PM_ERR_REGEXP_PARSE_ERROR: return "regexp_parse_error"; + case PM_ERR_REGEXP_TERM: return "regexp_term"; + case PM_ERR_REGEXP_UNKNOWN_OPTIONS: return "regexp_unknown_options"; + case PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP: return "regexp_utf8_char_non_utf8_regexp"; + case PM_ERR_RESCUE_EXPRESSION: return "rescue_expression"; + case PM_ERR_RESCUE_MODIFIER_VALUE: return "rescue_modifier_value"; + case PM_ERR_RESCUE_TERM: return "rescue_term"; + case PM_ERR_RESCUE_VARIABLE: return "rescue_variable"; + case PM_ERR_RETURN_INVALID: return "return_invalid"; + case PM_ERR_SCRIPT_NOT_FOUND: return "script_not_found"; + case PM_ERR_SINGLETON_FOR_LITERALS: return "singleton_for_literals"; + case PM_ERR_STATEMENT_ALIAS: return "statement_alias"; + case PM_ERR_STATEMENT_POSTEXE_END: return "statement_postexe_end"; + case PM_ERR_STATEMENT_PREEXE_BEGIN: return "statement_preexe_begin"; + case PM_ERR_STATEMENT_UNDEF: return "statement_undef"; + case PM_ERR_STRING_CONCATENATION: return "string_concatenation"; + case PM_ERR_STRING_INTERPOLATED_TERM: return "string_interpolated_term"; + case PM_ERR_STRING_LITERAL_EOF: return "string_literal_eof"; + case PM_ERR_STRING_LITERAL_TERM: return "string_literal_term"; + case PM_ERR_SYMBOL_INVALID: return "symbol_invalid"; + case PM_ERR_SYMBOL_TERM_DYNAMIC: return "symbol_term_dynamic"; + case PM_ERR_SYMBOL_TERM_INTERPOLATED: return "symbol_term_interpolated"; + case PM_ERR_TERNARY_COLON: return "ternary_colon"; + case PM_ERR_TERNARY_EXPRESSION_FALSE: return "ternary_expression_false"; + case PM_ERR_TERNARY_EXPRESSION_TRUE: return "ternary_expression_true"; + case PM_ERR_UNARY_DISALLOWED: return "unary_disallowed"; + case PM_ERR_UNARY_RECEIVER: return "unary_receiver"; + case PM_ERR_UNDEF_ARGUMENT: return "undef_argument"; + case PM_ERR_UNEXPECTED_BLOCK_ARGUMENT: return "unexpected_block_argument"; + case PM_ERR_UNEXPECTED_INDEX_BLOCK: return "unexpected_index_block"; + case PM_ERR_UNEXPECTED_INDEX_KEYWORDS: return "unexpected_index_keywords"; + case PM_ERR_UNEXPECTED_LABEL: return "unexpected_label"; + case PM_ERR_UNEXPECTED_MULTI_WRITE: return "unexpected_multi_write"; + case PM_ERR_UNEXPECTED_RANGE_OPERATOR: return "unexpected_range_operator"; + case PM_ERR_UNEXPECTED_SAFE_NAVIGATION: return "unexpected_safe_navigation"; + case PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT: return "unexpected_token_close_context"; + case PM_ERR_UNEXPECTED_TOKEN_IGNORE: return "unexpected_token_ignore"; + case PM_ERR_UNTIL_TERM: return "until_term"; + case PM_ERR_VOID_EXPRESSION: return "void_expression"; + case PM_ERR_WHILE_TERM: return "while_term"; + case PM_ERR_WRITE_TARGET_IN_METHOD: return "write_target_in_method"; + case PM_ERR_WRITE_TARGET_READONLY: return "write_target_readonly"; + case PM_ERR_WRITE_TARGET_UNEXPECTED: return "write_target_unexpected"; + case PM_ERR_XSTRING_TERM: return "xstring_term"; + case PM_WARN_AMBIGUOUS_BINARY_OPERATOR: return "ambiguous_binary_operator"; + case PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS: return "ambiguous_first_argument_minus"; + case PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS: return "ambiguous_first_argument_plus"; + case PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND: return "ambiguous_prefix_ampersand"; + case PM_WARN_AMBIGUOUS_PREFIX_STAR: return "ambiguous_prefix_star"; + case PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR: return "ambiguous_prefix_star_star"; + case PM_WARN_AMBIGUOUS_SLASH: return "ambiguous_slash"; + case PM_WARN_COMPARISON_AFTER_COMPARISON: return "comparison_after_comparison"; + case PM_WARN_DOT_DOT_DOT_EOL: return "dot_dot_dot_eol"; + case PM_WARN_EQUAL_IN_CONDITIONAL: return "equal_in_conditional"; + case PM_WARN_EQUAL_IN_CONDITIONAL_3_3: return "equal_in_conditional_3_3"; + case PM_WARN_END_IN_METHOD: return "end_in_method"; + case PM_WARN_DUPLICATED_HASH_KEY: return "duplicated_hash_key"; + case PM_WARN_DUPLICATED_WHEN_CLAUSE: return "duplicated_when_clause"; + case PM_WARN_FLOAT_OUT_OF_RANGE: return "float_out_of_range"; + case PM_WARN_IGNORED_FROZEN_STRING_LITERAL: return "ignored_frozen_string_literal"; + case PM_WARN_INDENTATION_MISMATCH: return "indentation_mismatch"; + case PM_WARN_INTEGER_IN_FLIP_FLOP: return "integer_in_flip_flop"; + case PM_WARN_INVALID_CHARACTER: return "invalid_character"; + case PM_WARN_INVALID_MAGIC_COMMENT_VALUE: return "invalid_magic_comment_value"; + case PM_WARN_INVALID_NUMBERED_REFERENCE: return "invalid_numbered_reference"; + case PM_WARN_KEYWORD_EOL: return "keyword_eol"; + case PM_WARN_LITERAL_IN_CONDITION_DEFAULT: return "literal_in_condition_default"; + case PM_WARN_LITERAL_IN_CONDITION_VERBOSE: return "literal_in_condition_verbose"; + case PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE: return "shareable_constant_value_line"; + case PM_WARN_SHEBANG_CARRIAGE_RETURN: return "shebang_carriage_return"; + case PM_WARN_UNEXPECTED_CARRIAGE_RETURN: return "unexpected_carriage_return"; + case PM_WARN_UNREACHABLE_STATEMENT: return "unreachable_statement"; + case PM_WARN_UNUSED_LOCAL_VARIABLE: return "unused_local_variable"; + case PM_WARN_VOID_STATEMENT: return "void_statement"; + } + + assert(false && "unreachable"); + return ""; +} + +static inline const char * +pm_diagnostic_message(pm_diagnostic_id_t diag_id) { + assert(diag_id < PM_DIAGNOSTIC_ID_MAX); + + const char *message = diagnostic_messages[diag_id].message; + assert(message); + + return message; +} + +static inline uint8_t +pm_diagnostic_level(pm_diagnostic_id_t diag_id) { + assert(diag_id < PM_DIAGNOSTIC_ID_MAX); + + return (uint8_t) diagnostic_messages[diag_id].level; +} + +/** + * 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 *) xcalloc(1, sizeof(pm_diagnostic_t)); + if (diagnostic == NULL) return false; + + *diagnostic = (pm_diagnostic_t) { + .location = { start, end }, + .diag_id = diag_id, + .message = pm_diagnostic_message(diag_id), + .owned = false, + .level = pm_diagnostic_level(diag_id) + }; + + 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 *) xcalloc(1, sizeof(pm_diagnostic_t)); + if (diagnostic == NULL) { + return false; + } + + size_t length = (size_t) (result + 1); + char *message = (char *) xmalloc(length); + if (message == NULL) { + xfree(diagnostic); + return false; + } + + va_start(arguments, diag_id); + vsnprintf(message, length, format, arguments); + va_end(arguments); + + *diagnostic = (pm_diagnostic_t) { + .location = { start, end }, + .diag_id = diag_id, + .message = message, + .owned = true, + .level = pm_diagnostic_level(diag_id) + }; + + 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_diagnostic_t *node = (pm_diagnostic_t *) list->head; + + while (node != NULL) { + pm_diagnostic_t *next = (pm_diagnostic_t *) node->node.next; + + if (node->owned) xfree((void *) node->message); + xfree(node); + + node = next; + } +} diff --git a/vendor/mruby-compiler2/lib/prism/src/encoding.c b/vendor/mruby-compiler2/lib/prism/src/encoding.c new file mode 100644 index 0000000..a4aeed1 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/encoding.c @@ -0,0 +1,5235 @@ +#include "prism/encoding.h" + +typedef uint32_t pm_unicode_codepoint_t; + +#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 1302 +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, 0x1C5, + 0x1C7, 0x1C8, + 0x1CA, 0x1CB, + 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, 0x1F2, + 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, + 0x1F88, 0x1F8F, + 0x1F98, 0x1F9F, + 0x1FA8, 0x1FAF, + 0x1FB8, 0x1FBC, + 0x1FC8, 0x1FCC, + 0x1FD8, 0x1FDB, + 0x1FE8, 0x1FEC, + 0x1FF8, 0x1FFC, + 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, +}; + +/** + * 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 +}; + +/** + * 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 >= 0); + + size_t maximum = (n > 4) ? 4 : ((size_t) n); + uint32_t codepoint; + uint32_t state = 0; + + for (size_t index = 0; 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 == 0) { + *width = index + 1; + return (pm_unicode_codepoint_t) codepoint; + } + } + + *width = 0; + return 0; +} + +/** + * Return the size of the next character in the UTF-8 encoding. + */ +size_t +pm_encoding_utf_8_char_width(const uint8_t *b, ptrdiff_t n) { + assert(n >= 0); + + size_t maximum = (n > 4) ? 4 : ((size_t) n); + uint32_t state = 0; + + for (size_t index = 0; index < maximum; index++) { + state = pm_utf_8_dfa[256 + (state * 16) + pm_utf_8_dfa[b[index]]]; + if (state == 0) return index + 1; + } + + return 0; +} + +/** + * 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; + } +} + +#ifndef PRISM_ENCODING_EXCLUDE_FULL + +static pm_unicode_codepoint_t +pm_cesu_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) { + if (b[0] < 0x80) { + *width = 1; + return (pm_unicode_codepoint_t) b[0]; + } + + if (n > 1 && b[0] >= 0xC2 && b[0] <= 0xDF && b[1] >= 0x80 && b[1] <= 0xBF) { + *width = 2; + + // 110xxxxx 10xxxxxx + return (pm_unicode_codepoint_t) (((b[0] & 0x1F) << 6) | (b[1] & 0x3F)); + } + + if (n > 5 && b[0] == 0xED && b[1] >= 0xA0 && b[1] <= 0xAF && b[2] >= 0x80 && b[2] <= 0xBF && b[3] == 0xED && b[4] >= 0xB0 && b[4] <= 0xBF && b[5] >= 0x80 && b[5] <= 0xBF) { + *width = 6; + + // 11101101 1010xxxx 10xxxxxx 11101101 1011xxxx 10xxxxxx + return (pm_unicode_codepoint_t) (0x10000 + (((b[1] & 0xF) << 16) | ((b[2] & 0x3F) << 10) | ((b[4] & 0xF) << 6) | (b[5] & 0x3F))); + } + + if (n > 2 && b[0] == 0xED && b[1] >= 0xA0 && b[1] <= 0xBF) { + *width = 3; + + // 11101101 1010xxxx 10xxxxx + return (pm_unicode_codepoint_t) (0x10000 + (((b[0] & 0x03) << 16) | ((b[1] & 0x3F) << 10) | (b[2] & 0x3F))); + } + + if (n > 2 && ((b[0] == 0xE0 && b[1] >= 0xA0) || (b[0] >= 0xE1 && b[0] <= 0xEF && b[1] >= 0x80)) && b[1] <= 0xBF && b[2] >= 0x80 && b[2] <= 0xBF) { + *width = 3; + + // 1110xxxx 10xxxxxx 10xxxxx + return (pm_unicode_codepoint_t) (((b[0] & 0xF) << 12) | ((b[1] & 0x3F) << 6) | (b[2] & 0x3F)); + } + + *width = 0; + return 0; +} + +static size_t +pm_encoding_cesu_8_char_width(const uint8_t *b, ptrdiff_t n) { + size_t width; + pm_cesu_8_codepoint(b, n, &width); + return width; +} + +static size_t +pm_encoding_cesu_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_cesu_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; + } +} + +static size_t +pm_encoding_cesu_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_cesu_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; + } +} + +static bool +pm_encoding_cesu_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_cesu_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; + } +} + +#endif + +#undef UNICODE_ALPHA_CODEPOINTS_LENGTH +#undef UNICODE_ALNUM_CODEPOINTS_LENGTH +#undef UNICODE_ISUPPER_CODEPOINTS_LENGTH + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding US-ASCII character. + */ +static const 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 +}; + +#ifndef PRISM_ENCODING_EXCLUDE_FULL + +/** + * Each element of the following table contains a bitfield that indicates a + * piece of information about the corresponding CP850 character. + */ +static const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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, 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, 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 const 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 const 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, 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, 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 +}; + +#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 +#endif + +/** + * 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. + */ +static 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); +} + +/** + * Certain encodings are equivalent to ASCII below 0x80, so it works for our + * purposes to have a function here that first checks the bounds and then falls + * back to checking the ASCII lookup table. + */ +static size_t +pm_encoding_ascii_alpha_char_7bit(const uint8_t *b, ptrdiff_t n) { + return (*b < 0x80) ? pm_encoding_ascii_alpha_char(b, n) : 0; +} + +/** + * Return the size of the next character in the ASCII encoding if it is an + * alphanumeric character. + */ +static 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; +} + +/** + * Certain encodings are equivalent to ASCII below 0x80, so it works for our + * purposes to have a function here that first checks the bounds and then falls + * back to checking the ASCII lookup table. + */ +static size_t +pm_encoding_ascii_alnum_char_7bit(const uint8_t *b, ptrdiff_t n) { + return (*b < 0x80) ? pm_encoding_ascii_alnum_char(b, n) : 0; +} + +/** + * Return true if the next character in the ASCII encoding if it is an uppercase + * character. + */ +static 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 EUC-JP encoding, or 0 if a + * character cannot be decoded from the given bytes. + */ +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[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; +} + +/** + * Returns the size of the next character in the EUC-JP encoding if it is an + * uppercase character. + */ +static bool +pm_encoding_euc_jp_isupper_char(const uint8_t *b, ptrdiff_t n) { + size_t width = pm_encoding_euc_jp_char_width(b, n); + + if (width == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else if (width == 2) { + return ( + (b[0] == 0xA3 && b[1] >= 0xC1 && b[1] <= 0xDA) || + (b[0] == 0xA6 && b[1] >= 0xA1 && b[1] <= 0xB8) || + (b[0] == 0xA7 && b[1] >= 0xA1 && b[1] <= 0xC1) + ); + } else { + return false; + } +} + +/** + * Returns the size of the next character in the Shift_JIS encoding, or 0 if a + * character cannot be decoded from the given bytes. + */ +static size_t +pm_encoding_shift_jis_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the single byte characters. + if (b[0] < 0x80 || (b[0] >= 0xA1 && b[0] <= 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 && b[1] != 0x7F)) { + return 2; + } + + return 0; +} + +/** + * Returns the size of the next character in the Shift_JIS encoding if it is an + * alphanumeric character. + */ +static size_t +pm_encoding_shift_jis_alnum_char(const uint8_t *b, ptrdiff_t n) { + size_t width = pm_encoding_shift_jis_char_width(b, n); + return width == 1 ? ((b[0] >= 0x80) || pm_encoding_ascii_alnum_char(b, n)) : width; +} + +/** + * Returns the size of the next character in the Shift_JIS encoding if it is an + * alphabetical character. + */ +static size_t +pm_encoding_shift_jis_alpha_char(const uint8_t *b, ptrdiff_t n) { + size_t width = pm_encoding_shift_jis_char_width(b, n); + return width == 1 ? ((b[0] >= 0x80) || pm_encoding_ascii_alpha_char(b, n)) : width; +} + +/** + * Returns the size of the next character in the Shift_JIS encoding if it is an + * uppercase character. + */ +static bool +pm_encoding_shift_jis_isupper_char(const uint8_t *b, ptrdiff_t n) { + size_t width = pm_encoding_shift_jis_char_width(b, n); + + if (width == 1) { + return pm_encoding_ascii_isupper_char(b, n); + } else if (width == 2) { + return ( + ((b[0] == 0x82) && (b[1] >= 0x60 && b[1] <= 0x79)) || + ((b[0] == 0x83) && (b[1] >= 0x9F && b[1] <= 0xB6)) || + ((b[0] == 0x84) && (b[1] >= 0x40 && b[1] <= 0x60)) + ); + } else { + return width; + } +} + +#ifndef PRISM_ENCODING_EXCLUDE_FULL + +/** + * Certain encodings are equivalent to ASCII below 0x80, so it works for our + * purposes to have a function here that first checks the bounds and then falls + * back to checking the ASCII lookup table. + */ +static bool +pm_encoding_ascii_isupper_char_7bit(const uint8_t *b, ptrdiff_t n) { + return (*b < 0x80) && pm_encoding_ascii_isupper_char(b, n); +} + +/** + * Returns the size of the next character in the Big5 encoding, or 0 if a + * character cannot be decoded from the given bytes. + */ +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] <= 0x7E) || (b[1] >= 0xA1 && b[1] <= 0xFE))) { + return 2; + } + + return 0; +} + +/** + * Returns the size of the next character in the CP949 encoding, or 0 if a + * character cannot be decoded from the given bytes. + */ +static size_t +pm_encoding_cp949_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] >= 0x41 && b[1] <= 0x5A) || (b[1] >= 0x61 && b[1] <= 0x7A) || (b[1] >= 0x81 && b[1] <= 0xFE))) { + return 2; + } + + return 0; +} + +/** + * Returns the size of the next character in the Emacs MULE encoding, or 0 if a + * character cannot be decoded from the given bytes. + */ +static size_t +pm_encoding_emacs_mule_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the 1 byte characters. + if (*b < 0x80) { + return 1; + } + + // These are the 2 byte characters. + if ((n > 1) && (b[0] >= 0x81 && b[0] <= 0x8F) && (b[1] >= 0xA0)) { + return 2; + } + + // These are the 3 byte characters. + if ( + (n > 2) && + ( + ((b[0] >= 0x90 && b[0] <= 0x99) && (b[1] >= 0xA0)) || + ((b[0] == 0x9A || b[0] == 0x9B) && (b[1] >= 0xE0 && b[1] <= 0xEF)) + ) && + (b[2] >= 0xA0) + ) { + return 3; + } + + // These are the 4 byte characters. + if ( + (n > 3) && + ( + ((b[0] == 0x9C) && (b[1] >= 0xF0) && (b[1] <= 0xF4)) || + ((b[0] == 0x9D) && (b[1] >= 0xF5) && (b[1] <= 0xFE)) + ) && + (b[2] >= 0xA0) && (b[3] >= 0xA0) + ) { + return 4; + } + + return 0; +} + +/** + * Returns the size of the next character in the EUC-KR encoding, or 0 if a + * character cannot be decoded from the given bytes. + */ +static size_t +pm_encoding_euc_kr_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] >= 0xA1 && b[1] <= 0xFE)) { + return 2; + } + + return 0; +} + +/** + * Returns the size of the next character in the EUC-TW encoding, or 0 if a + * character cannot be decoded from the given bytes. + */ +static size_t +pm_encoding_euc_tw_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] >= 0xA1) && (b[1] <= 0xFE)) { + return 2; + } + + // These are the quadruple byte characters. + if ((n > 3) && (b[0] == 0x8E) && (b[1] >= 0xA1) && (b[1] <= 0xB0) && (b[2] >= 0xA1) && (b[2] <= 0xFE) && (b[3] >= 0xA1) && (b[3] <= 0xFE)) { + return 4; + } + + return 0; +} + +/** + * Returns the size of the next character in the GB18030 encoding, or 0 if a + * character cannot be decoded from the given bytes. + */ +static size_t +pm_encoding_gb18030_char_width(const uint8_t *b, ptrdiff_t n) { + // These are the 1 byte characters. + if (*b < 0x80) { + return 1; + } + + // These are the 2 byte characters. + if ((n > 1) && (b[0] >= 0x81 && b[0] <= 0xFE) && (b[1] >= 0x40 && b[1] <= 0xFE && b[1] != 0x7F)) { + return 2; + } + + // These are the 4 byte characters. + if ((n > 3) && ((b[0] >= 0x81 && b[0] <= 0xFE) && (b[1] >= 0x30 && b[1] <= 0x39) && (b[2] >= 0x81 && b[2] <= 0xFE) && (b[3] >= 0x30 && b[3] <= 0x39))) { + return 4; + } + + return 0; +} + +/** + * Returns the size of the next character in the GBK encoding, or 0 if a + * character cannot be decoded from the given bytes. + */ +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; +} + +#endif + +/** + * This is the table of all of the encodings that prism supports. + */ +const pm_encoding_t pm_encodings[] = { + [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 + }, + [PM_ENCODING_US_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 + }, + [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 + }, + [PM_ENCODING_EUC_JP] = { + .name = "EUC-JP", + .char_width = pm_encoding_euc_jp_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_euc_jp_isupper_char, + .multibyte = true + }, + [PM_ENCODING_WINDOWS_31J] = { + .name = "Windows-31J", + .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 + }, + +#ifndef PRISM_ENCODING_EXCLUDE_FULL + [PM_ENCODING_BIG5] = { + .name = "Big5", + .char_width = pm_encoding_big5_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_BIG5_HKSCS] = { + .name = "Big5-HKSCS", + .char_width = pm_encoding_big5_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_BIG5_UAO] = { + .name = "Big5-UAO", + .char_width = pm_encoding_big5_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_CESU_8] = { + .name = "CESU-8", + .char_width = pm_encoding_cesu_8_char_width, + .alnum_char = pm_encoding_cesu_8_alnum_char, + .alpha_char = pm_encoding_cesu_8_alpha_char, + .isupper_char = pm_encoding_cesu_8_isupper_char, + .multibyte = true + }, + [PM_ENCODING_CP51932] = { + .name = "CP51932", + .char_width = pm_encoding_euc_jp_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_euc_jp_isupper_char, + .multibyte = true + }, + [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 + }, + [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 + }, + [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 + }, + [PM_ENCODING_CP949] = { + .name = "CP949", + .char_width = pm_encoding_cp949_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_CP950] = { + .name = "CP950", + .char_width = pm_encoding_big5_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_CP951] = { + .name = "CP951", + .char_width = pm_encoding_big5_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_EMACS_MULE] = { + .name = "Emacs-Mule", + .char_width = pm_encoding_emacs_mule_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_EUC_JP_MS] = { + .name = "eucJP-ms", + .char_width = pm_encoding_euc_jp_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_euc_jp_isupper_char, + .multibyte = true + }, + [PM_ENCODING_EUC_JIS_2004] = { + .name = "EUC-JIS-2004", + .char_width = pm_encoding_euc_jp_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_euc_jp_isupper_char, + .multibyte = true + }, + [PM_ENCODING_EUC_KR] = { + .name = "EUC-KR", + .char_width = pm_encoding_euc_kr_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_EUC_TW] = { + .name = "EUC-TW", + .char_width = pm_encoding_euc_tw_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_GB12345] = { + .name = "GB12345", + .char_width = pm_encoding_euc_kr_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_GB18030] = { + .name = "GB18030", + .char_width = pm_encoding_gb18030_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [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 + }, + [PM_ENCODING_GB2312] = { + .name = "GB2312", + .char_width = pm_encoding_euc_kr_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_GBK] = { + .name = "GBK", + .char_width = pm_encoding_gbk_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [PM_ENCODING_KOI8_R] = { + .name = "KOI8-R", + .char_width = pm_encoding_single_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 + }, + [PM_ENCODING_KOI8_U] = { + .name = "KOI8-U", + .char_width = pm_encoding_single_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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [PM_ENCODING_MAC_JAPANESE] = { + .name = "MacJapanese", + .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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [PM_ENCODING_SJIS_DOCOMO] = { + .name = "SJIS-DoCoMo", + .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 + }, + [PM_ENCODING_SJIS_KDDI] = { + .name = "SJIS-KDDI", + .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 + }, + [PM_ENCODING_SJIS_SOFTBANK] = { + .name = "SJIS-SoftBank", + .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 + }, + [PM_ENCODING_STATELESS_ISO_2022_JP] = { + .name = "stateless-ISO-2022-JP", + .char_width = pm_encoding_emacs_mule_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [PM_ENCODING_STATELESS_ISO_2022_JP_KDDI] = { + .name = "stateless-ISO-2022-JP-KDDI", + .char_width = pm_encoding_emacs_mule_char_width, + .alnum_char = pm_encoding_ascii_alnum_char_7bit, + .alpha_char = pm_encoding_ascii_alpha_char_7bit, + .isupper_char = pm_encoding_ascii_isupper_char_7bit, + .multibyte = true + }, + [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 + }, + [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 + }, + [PM_ENCODING_UTF8_DOCOMO] = { + .name = "UTF8-DoCoMo", + .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 + }, + [PM_ENCODING_UTF8_KDDI] = { + .name = "UTF8-KDDI", + .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 + }, + [PM_ENCODING_UTF8_SOFTBANK] = { + .name = "UTF8-SoftBank", + .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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + }, + [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 + } +#endif +}; + +/** + * Parse the given name of an encoding and return a pointer to the corresponding + * encoding struct if one can be found, otherwise return NULL. + */ +const pm_encoding_t * +pm_encoding_find(const uint8_t *start, const uint8_t *end) { + size_t width = (size_t) (end - start); + + // First, 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)) { +#ifndef PRISM_ENCODING_EXCLUDE_FULL + // 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)) { + return &pm_encodings[PM_ENCODING_UTF8_MAC]; + } +#endif + + // Otherwise we'll return the default UTF-8 encoding. + return PM_ENCODING_UTF_8_ENTRY; + } + + // 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(name, encoding) if (width == sizeof(name) - 1 && pm_strncasecmp(start, (const uint8_t *) name, width) == 0) return &pm_encodings[encoding]; +#define ENCODING2(name1, name2, encoding) ENCODING1(name1, encoding) ENCODING1(name2, encoding) + + if (width >= 3) { + switch (*start) { + case 'A': case 'a': + ENCODING1("ASCII", PM_ENCODING_US_ASCII); + ENCODING1("ASCII-8BIT", PM_ENCODING_ASCII_8BIT); + ENCODING1("ANSI_X3.4-1968", PM_ENCODING_US_ASCII); + break; + case 'B': case 'b': + ENCODING1("BINARY", PM_ENCODING_ASCII_8BIT); +#ifndef PRISM_ENCODING_EXCLUDE_FULL + ENCODING1("Big5", PM_ENCODING_BIG5); + ENCODING2("Big5-HKSCS", "Big5-HKSCS:2008", PM_ENCODING_BIG5_HKSCS); + ENCODING1("Big5-UAO", PM_ENCODING_BIG5_UAO); +#endif + break; + case 'C': case 'c': + ENCODING1("CP65001", PM_ENCODING_UTF_8); + ENCODING2("CP932", "csWindows31J", PM_ENCODING_WINDOWS_31J); +#ifndef PRISM_ENCODING_EXCLUDE_FULL + ENCODING1("CESU-8", PM_ENCODING_CESU_8); + 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); + ENCODING1("CP936", PM_ENCODING_GBK); + ENCODING1("CP949", PM_ENCODING_CP949); + ENCODING1("CP950", PM_ENCODING_CP950); + ENCODING1("CP951", PM_ENCODING_CP951); + 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); +#endif + break; + case 'E': case 'e': + ENCODING2("EUC-JP", "eucJP", PM_ENCODING_EUC_JP); +#ifndef PRISM_ENCODING_EXCLUDE_FULL + ENCODING2("eucJP-ms", "euc-jp-ms", PM_ENCODING_EUC_JP_MS); + ENCODING2("EUC-JIS-2004", "EUC-JISX0213", PM_ENCODING_EUC_JIS_2004); + ENCODING2("EUC-KR", "eucKR", PM_ENCODING_EUC_KR); + ENCODING2("EUC-CN", "eucCN", PM_ENCODING_GB2312); + ENCODING2("EUC-TW", "eucTW", PM_ENCODING_EUC_TW); + ENCODING1("Emacs-Mule", PM_ENCODING_EMACS_MULE); +#endif + break; + case 'G': case 'g': +#ifndef PRISM_ENCODING_EXCLUDE_FULL + ENCODING1("GBK", PM_ENCODING_GBK); + ENCODING1("GB12345", PM_ENCODING_GB12345); + ENCODING1("GB18030", PM_ENCODING_GB18030); + ENCODING1("GB1988", PM_ENCODING_GB1988); + ENCODING1("GB2312", PM_ENCODING_GB2312); +#endif + break; + case 'I': case 'i': +#ifndef PRISM_ENCODING_EXCLUDE_FULL + 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); +#endif + break; + case 'K': case 'k': +#ifndef PRISM_ENCODING_EXCLUDE_FULL + ENCODING1("KOI8-R", PM_ENCODING_KOI8_R); + ENCODING1("KOI8-U", PM_ENCODING_KOI8_U); +#endif + break; + case 'M': case 'm': +#ifndef PRISM_ENCODING_EXCLUDE_FULL + 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); +#endif + break; + case 'P': case 'p': + ENCODING1("PCK", PM_ENCODING_WINDOWS_31J); + break; + case 'S': case 's': + ENCODING1("SJIS", PM_ENCODING_WINDOWS_31J); +#ifndef PRISM_ENCODING_EXCLUDE_FULL + ENCODING1("Shift_JIS", PM_ENCODING_SHIFT_JIS); + ENCODING1("SJIS-DoCoMo", PM_ENCODING_SJIS_DOCOMO); + ENCODING1("SJIS-KDDI", PM_ENCODING_SJIS_KDDI); + ENCODING1("SJIS-SoftBank", PM_ENCODING_SJIS_SOFTBANK); + ENCODING1("stateless-ISO-2022-JP", PM_ENCODING_STATELESS_ISO_2022_JP); + ENCODING1("stateless-ISO-2022-JP-KDDI", PM_ENCODING_STATELESS_ISO_2022_JP_KDDI); +#endif + break; + case 'T': case 't': +#ifndef PRISM_ENCODING_EXCLUDE_FULL + ENCODING1("TIS-620", PM_ENCODING_TIS_620); +#endif + break; + case 'U': case 'u': + ENCODING1("US-ASCII", PM_ENCODING_US_ASCII); +#ifndef PRISM_ENCODING_EXCLUDE_FULL + ENCODING2("UTF8-MAC", "UTF-8-HFS", PM_ENCODING_UTF8_MAC); + ENCODING1("UTF8-DoCoMo", PM_ENCODING_UTF8_DOCOMO); + ENCODING1("UTF8-KDDI", PM_ENCODING_UTF8_KDDI); + ENCODING1("UTF8-SoftBank", PM_ENCODING_UTF8_SOFTBANK); +#endif + break; + case 'W': case 'w': + ENCODING1("Windows-31J", PM_ENCODING_WINDOWS_31J); +#ifndef PRISM_ENCODING_EXCLUDE_FULL + 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); +#endif + break; + case '6': + ENCODING1("646", PM_ENCODING_US_ASCII); + break; + } + } + +#undef ENCODING2 +#undef ENCODING1 + + // If we didn't match any encodings, return NULL. + return NULL; +} diff --git a/vendor/mruby-compiler2/lib/prism/src/node.c b/vendor/mruby-compiler2/lib/prism/src/node.c new file mode 100644 index 0000000..9c666b8 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/node.c @@ -0,0 +1,8674 @@ +/*----------------------------------------------------------------------------*/ +/* 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 "prism/templates/src/node.c.erb" +#include "prism/node.h" + +/** + * Attempts to grow the node list to the next size. If there is already + * capacity in the list, this function does nothing. Otherwise it reallocates + * the list to be twice as large as it was before. If the reallocation fails, + * this function returns false, otherwise it returns true. + */ +static bool +pm_node_list_grow(pm_node_list_t *list, size_t size) { + size_t requested_size = list->size + size; + + // If the requested size caused overflow, return false. + if (requested_size < list->size) return false; + + // If the requested size is within the existing capacity, return true. + if (requested_size < list->capacity) return true; + + // Otherwise, reallocate the list to be twice as large as it was before. + size_t next_capacity = list->capacity == 0 ? 4 : list->capacity * 2; + + // If multiplying by 2 caused overflow, return false. + if (next_capacity < list->capacity) return false; + + // If we didn't get enough by doubling, keep doubling until we do. + while (requested_size > next_capacity) { + size_t double_capacity = next_capacity * 2; + + // Ensure we didn't overflow by multiplying by 2. + if (double_capacity < next_capacity) return false; + next_capacity = double_capacity; + } + + pm_node_t **nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * next_capacity); + if (nodes == NULL) return false; + + list->nodes = nodes; + list->capacity = next_capacity; + return true; +} + +/** + * 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 (pm_node_list_grow(list, 1)) { + list->nodes[list->size++] = node; + } +} + +/** + * Prepend a new node onto the beginning of the node list. + */ +void +pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node) { + if (pm_node_list_grow(list, 1)) { + memmove(list->nodes + 1, list->nodes, list->size * sizeof(pm_node_t *)); + list->nodes[0] = node; + list->size++; + } +} + +/** + * Concatenate the given node list onto the end of the other node list. + */ +void +pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other) { + if (other->size > 0 && pm_node_list_grow(list, other->size)) { + memcpy(list->nodes + list->size, other->nodes, other->size * sizeof(pm_node_t *)); + list->size += other->size; + } +} + +/** + * Free the internal memory associated with the given node list. + */ +void +pm_node_list_free(pm_node_list_t *list) { + if (list->capacity > 0) { + xfree(list->nodes); + *list = (pm_node_list_t) { 0 }; + } +} + +PRISM_EXPORTED_FUNCTION void +pm_node_destroy(pm_parser_t *parser, pm_node_t *node); + +/** + * Destroy the nodes that are contained within the given node list. + */ +static void +pm_node_list_destroy(pm_parser_t *parser, pm_node_list_t *list) { + pm_node_t *node; + PM_NODE_LIST_FOREACH(list, index, node) pm_node_destroy(parser, node); + pm_node_list_free(list); +} + +/** + * 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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_ARGUMENTS_NODE: { + pm_arguments_node_t *cast = (pm_arguments_node_t *) node; + pm_node_list_destroy(parser, &cast->arguments); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ARRAY_NODE: { + pm_array_node_t *cast = (pm_array_node_t *) node; + pm_node_list_destroy(parser, &cast->elements); + break; + } +#line 110 "prism/templates/src/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_destroy(parser, &cast->requireds); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_destroy(parser, &cast->posts); + break; + } +#line 110 "prism/templates/src/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); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_BACK_REFERENCE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_BLOCK_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/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_destroy(parser, &cast->locals); + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_CALL_TARGET_NODE: { + pm_call_target_node_t *cast = (pm_call_target_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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_destroy(parser, &cast->conditions); + if (cast->else_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->else_clause); + } + break; + } +#line 110 "prism/templates/src/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_destroy(parser, &cast->conditions); + if (cast->else_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->else_clause); + } + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_CLASS_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CLASS_VARIABLE_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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); + } + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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); + } + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_FALSE_NODE: { + break; + } +#line 110 "prism/templates/src/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_destroy(parser, &cast->requireds); + pm_node_destroy(parser, (pm_node_t *)cast->right); + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_FLOAT_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_FORWARDING_ARGUMENTS_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_FORWARDING_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_GLOBAL_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_HASH_NODE: { + pm_hash_node_t *cast = (pm_hash_node_t *) node; + pm_node_list_destroy(parser, &cast->elements); + break; + } +#line 110 "prism/templates/src/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_destroy(parser, &cast->elements); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + break; + } +#line 110 "prism/templates/src/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->subsequent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->subsequent); + } + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_IMPLICIT_REST_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_INDEX_TARGET_NODE: { + pm_index_target_node_t *cast = (pm_index_target_node_t *) node; + 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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_INSTANCE_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_INTEGER_NODE: { + pm_integer_node_t *cast = (pm_integer_node_t *) node; + pm_integer_free(&cast->value); + break; + } +#line 110 "prism/templates/src/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_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/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_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INTERPOLATED_STRING_NODE: { + pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; + pm_node_list_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; + pm_node_list_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/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_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_IT_PARAMETERS_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_KEYWORD_HASH_NODE: { + pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; + pm_node_list_destroy(parser, &cast->elements); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_KEYWORD_REST_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_LOCAL_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_LOCAL_VARIABLE_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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_destroy(parser, &cast->targets); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MISSING_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_MULTI_TARGET_NODE: { + pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; + pm_node_list_destroy(parser, &cast->lefts); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_destroy(parser, &cast->rights); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MULTI_WRITE_NODE: { + pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; + pm_node_list_destroy(parser, &cast->lefts); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_destroy(parser, &cast->rights); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_NIL_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_NO_KEYWORDS_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_NUMBERED_PARAMETERS_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_NUMBERED_REFERENCE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_PARAMETERS_NODE: { + pm_parameters_node_t *cast = (pm_parameters_node_t *) node; + pm_node_list_destroy(parser, &cast->requireds); + pm_node_list_destroy(parser, &cast->optionals); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_destroy(parser, &cast->posts); + pm_node_list_destroy(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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_RATIONAL_NODE: { + pm_rational_node_t *cast = (pm_rational_node_t *) node; + pm_integer_free(&cast->numerator); + pm_integer_free(&cast->denominator); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_REDO_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_REQUIRED_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_RESCUE_NODE: { + pm_rescue_node_t *cast = (pm_rescue_node_t *) node; + pm_node_list_destroy(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->subsequent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->subsequent); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_REST_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_RETRY_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_SELF_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SHAREABLE_CONSTANT_NODE: { + pm_shareable_constant_node_t *cast = (pm_shareable_constant_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->write); + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_SOURCE_ENCODING_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_SOURCE_LINE_NODE: { + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + pm_node_list_destroy(parser, &cast->body); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_STRING_NODE: { + pm_string_node_t *cast = (pm_string_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_SYMBOL_NODE: { + pm_symbol_node_t *cast = (pm_symbol_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_TRUE_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_UNDEF_NODE: { + pm_undef_node_t *cast = (pm_undef_node_t *) node; + pm_node_list_destroy(parser, &cast->names); + break; + } +#line 110 "prism/templates/src/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->else_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->else_clause); + } + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/node.c.erb" + case PM_WHEN_NODE: { + pm_when_node_t *cast = (pm_when_node_t *) node; + pm_node_list_destroy(parser, &cast->conditions); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/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 110 "prism/templates/src/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 110 "prism/templates/src/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 139 "prism/templates/src/node.c.erb" + default: + assert(false && "unreachable"); + break; + } + xfree(node); +} + +/** + * 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_CALL_TARGET_NODE: + return "PM_CALL_TARGET_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_INDEX_TARGET_NODE: + return "PM_INDEX_TARGET_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_IT_LOCAL_VARIABLE_READ_NODE: + return "PM_IT_LOCAL_VARIABLE_READ_NODE"; + case PM_IT_PARAMETERS_NODE: + return "PM_IT_PARAMETERS_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_PARAMETERS_NODE: + return "PM_NUMBERED_PARAMETERS_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_SHAREABLE_CONSTANT_NODE: + return "PM_SHAREABLE_CONSTANT_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 ""; +} + +/** + * Visit each of the nodes in this subtree using the given visitor callback. The + * callback function will be called for each node in the subtree. If it returns + * false, then that node's children will not be visited. If it returns true, + * then the children will be visited. The data parameter is treated as an opaque + * pointer and is passed to the visitor callback for consumers to use as they + * see fit. + */ +PRISM_EXPORTED_FUNCTION void +pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) { + if (visitor(node, data)) pm_visit_child_nodes(node, visitor, data); +} + +/** + * Visit the children of the given node with the given callback. This is the + * default behavior for walking the tree that is called from pm_visit_node if + * the callback returns true. + */ +PRISM_EXPORTED_FUNCTION void +pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) { + switch (PM_NODE_TYPE(node)) { + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + const pm_alias_global_variable_node_t *cast = (const pm_alias_global_variable_node_t *) node; + + // Visit the new_name field + pm_visit_node((const pm_node_t *) cast->new_name, visitor, data); + + // Visit the old_name field + pm_visit_node((const pm_node_t *) cast->old_name, visitor, data); + + break; + } + case PM_ALIAS_METHOD_NODE: { + const pm_alias_method_node_t *cast = (const pm_alias_method_node_t *) node; + + // Visit the new_name field + pm_visit_node((const pm_node_t *) cast->new_name, visitor, data); + + // Visit the old_name field + pm_visit_node((const pm_node_t *) cast->old_name, visitor, data); + + break; + } + case PM_ALTERNATION_PATTERN_NODE: { + const pm_alternation_pattern_node_t *cast = (const pm_alternation_pattern_node_t *) node; + + // Visit the left field + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + + // Visit the right field + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + + break; + } + case PM_AND_NODE: { + const pm_and_node_t *cast = (const pm_and_node_t *) node; + + // Visit the left field + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + + // Visit the right field + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + + break; + } + case PM_ARGUMENTS_NODE: { + const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node; + + // Visit the arguments field + const pm_node_list_t *arguments = &cast->arguments; + for (size_t index = 0; index < arguments->size; index++) { + pm_visit_node(arguments->nodes[index], visitor, data); + } + + break; + } + case PM_ARRAY_NODE: { + const pm_array_node_t *cast = (const pm_array_node_t *) node; + + // Visit the elements field + const pm_node_list_t *elements = &cast->elements; + for (size_t index = 0; index < elements->size; index++) { + pm_visit_node(elements->nodes[index], visitor, data); + } + + break; + } + case PM_ARRAY_PATTERN_NODE: { + const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node; + + // Visit the constant field + if (cast->constant != NULL) { + pm_visit_node((const pm_node_t *) cast->constant, visitor, data); + } + + // Visit the requireds field + const pm_node_list_t *requireds = &cast->requireds; + for (size_t index = 0; index < requireds->size; index++) { + pm_visit_node(requireds->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + // Visit the posts field + const pm_node_list_t *posts = &cast->posts; + for (size_t index = 0; index < posts->size; index++) { + pm_visit_node(posts->nodes[index], visitor, data); + } + + break; + } + case PM_ASSOC_NODE: { + const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node; + + // Visit the key field + pm_visit_node((const pm_node_t *) cast->key, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_ASSOC_SPLAT_NODE: { + const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node; + + // Visit the value field + if (cast->value != NULL) { + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + } + + break; + } + case PM_BACK_REFERENCE_READ_NODE: + break; + case PM_BEGIN_NODE: { + const pm_begin_node_t *cast = (const pm_begin_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + // Visit the rescue_clause field + if (cast->rescue_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->rescue_clause, visitor, data); + } + + // Visit the else_clause field + if (cast->else_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->else_clause, visitor, data); + } + + // Visit the ensure_clause field + if (cast->ensure_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->ensure_clause, visitor, data); + } + + break; + } + case PM_BLOCK_ARGUMENT_NODE: { + const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node; + + // Visit the expression field + if (cast->expression != NULL) { + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + } + + break; + } + case PM_BLOCK_LOCAL_VARIABLE_NODE: + break; + case PM_BLOCK_NODE: { + const pm_block_node_t *cast = (const pm_block_node_t *) node; + + // Visit the parameters field + if (cast->parameters != NULL) { + pm_visit_node((const pm_node_t *) cast->parameters, visitor, data); + } + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_BLOCK_PARAMETER_NODE: + break; + case PM_BLOCK_PARAMETERS_NODE: { + const pm_block_parameters_node_t *cast = (const pm_block_parameters_node_t *) node; + + // Visit the parameters field + if (cast->parameters != NULL) { + pm_visit_node((const pm_node_t *) cast->parameters, visitor, data); + } + + // Visit the locals field + const pm_node_list_t *locals = &cast->locals; + for (size_t index = 0; index < locals->size; index++) { + pm_visit_node(locals->nodes[index], visitor, data); + } + + break; + } + case PM_BREAK_NODE: { + const pm_break_node_t *cast = (const pm_break_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + break; + } + case PM_CALL_AND_WRITE_NODE: { + const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CALL_NODE: { + const pm_call_node_t *cast = (const pm_call_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: { + const pm_call_operator_write_node_t *cast = (const pm_call_operator_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CALL_OR_WRITE_NODE: { + const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CALL_TARGET_NODE: { + const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node; + + // Visit the receiver field + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + + break; + } + case PM_CAPTURE_PATTERN_NODE: { + const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + break; + } + case PM_CASE_MATCH_NODE: { + const pm_case_match_node_t *cast = (const pm_case_match_node_t *) node; + + // Visit the predicate field + if (cast->predicate != NULL) { + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + } + + // Visit the conditions field + const pm_node_list_t *conditions = &cast->conditions; + for (size_t index = 0; index < conditions->size; index++) { + pm_visit_node(conditions->nodes[index], visitor, data); + } + + // Visit the else_clause field + if (cast->else_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->else_clause, visitor, data); + } + + break; + } + case PM_CASE_NODE: { + const pm_case_node_t *cast = (const pm_case_node_t *) node; + + // Visit the predicate field + if (cast->predicate != NULL) { + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + } + + // Visit the conditions field + const pm_node_list_t *conditions = &cast->conditions; + for (size_t index = 0; index < conditions->size; index++) { + pm_visit_node(conditions->nodes[index], visitor, data); + } + + // Visit the else_clause field + if (cast->else_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->else_clause, visitor, data); + } + + break; + } + case PM_CLASS_NODE: { + const pm_class_node_t *cast = (const pm_class_node_t *) node; + + // Visit the constant_path field + pm_visit_node((const pm_node_t *) cast->constant_path, visitor, data); + + // Visit the superclass field + if (cast->superclass != NULL) { + pm_visit_node((const pm_node_t *) cast->superclass, visitor, data); + } + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + const pm_class_variable_and_write_node_t *cast = (const pm_class_variable_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + const pm_class_variable_operator_write_node_t *cast = (const pm_class_variable_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + const pm_class_variable_or_write_node_t *cast = (const pm_class_variable_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CLASS_VARIABLE_READ_NODE: + break; + case PM_CLASS_VARIABLE_TARGET_NODE: + break; + case PM_CLASS_VARIABLE_WRITE_NODE: { + const pm_class_variable_write_node_t *cast = (const pm_class_variable_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_AND_WRITE_NODE: { + const pm_constant_and_write_node_t *cast = (const pm_constant_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + const pm_constant_operator_write_node_t *cast = (const pm_constant_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_OR_WRITE_NODE: { + const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + const pm_constant_path_and_write_node_t *cast = (const pm_constant_path_and_write_node_t *) node; + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_PATH_NODE: { + const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node; + + // Visit the parent field + if (cast->parent != NULL) { + pm_visit_node((const pm_node_t *) cast->parent, visitor, data); + } + + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + const pm_constant_path_operator_write_node_t *cast = (const pm_constant_path_operator_write_node_t *) node; + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + const pm_constant_path_or_write_node_t *cast = (const pm_constant_path_or_write_node_t *) node; + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_PATH_TARGET_NODE: { + const pm_constant_path_target_node_t *cast = (const pm_constant_path_target_node_t *) node; + + // Visit the parent field + if (cast->parent != NULL) { + pm_visit_node((const pm_node_t *) cast->parent, visitor, data); + } + + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: { + const pm_constant_path_write_node_t *cast = (const pm_constant_path_write_node_t *) node; + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_READ_NODE: + break; + case PM_CONSTANT_TARGET_NODE: + break; + case PM_CONSTANT_WRITE_NODE: { + const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_DEF_NODE: { + const pm_def_node_t *cast = (const pm_def_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the parameters field + if (cast->parameters != NULL) { + pm_visit_node((const pm_node_t *) cast->parameters, visitor, data); + } + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_DEFINED_NODE: { + const pm_defined_node_t *cast = (const pm_defined_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_ELSE_NODE: { + const pm_else_node_t *cast = (const pm_else_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: { + const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: { + const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node; + + // Visit the variable field + pm_visit_node((const pm_node_t *) cast->variable, visitor, data); + + break; + } + case PM_ENSURE_NODE: { + const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_FALSE_NODE: + break; + case PM_FIND_PATTERN_NODE: { + const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node; + + // Visit the constant field + if (cast->constant != NULL) { + pm_visit_node((const pm_node_t *) cast->constant, visitor, data); + } + + // Visit the left field + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + + // Visit the requireds field + const pm_node_list_t *requireds = &cast->requireds; + for (size_t index = 0; index < requireds->size; index++) { + pm_visit_node(requireds->nodes[index], visitor, data); + } + + // Visit the right field + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + + break; + } + case PM_FLIP_FLOP_NODE: { + const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node; + + // Visit the left field + if (cast->left != NULL) { + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + } + + // Visit the right field + if (cast->right != NULL) { + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + } + + break; + } + case PM_FLOAT_NODE: + break; + case PM_FOR_NODE: { + const pm_for_node_t *cast = (const pm_for_node_t *) node; + + // Visit the index field + pm_visit_node((const pm_node_t *) cast->index, visitor, data); + + // Visit the collection field + pm_visit_node((const pm_node_t *) cast->collection, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_FORWARDING_ARGUMENTS_NODE: + break; + case PM_FORWARDING_PARAMETER_NODE: + break; + case PM_FORWARDING_SUPER_NODE: { + const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node; + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + const pm_global_variable_and_write_node_t *cast = (const pm_global_variable_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + const pm_global_variable_operator_write_node_t *cast = (const pm_global_variable_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + const pm_global_variable_or_write_node_t *cast = (const pm_global_variable_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_GLOBAL_VARIABLE_READ_NODE: + break; + case PM_GLOBAL_VARIABLE_TARGET_NODE: + break; + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + const pm_global_variable_write_node_t *cast = (const pm_global_variable_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_HASH_NODE: { + const pm_hash_node_t *cast = (const pm_hash_node_t *) node; + + // Visit the elements field + const pm_node_list_t *elements = &cast->elements; + for (size_t index = 0; index < elements->size; index++) { + pm_visit_node(elements->nodes[index], visitor, data); + } + + break; + } + case PM_HASH_PATTERN_NODE: { + const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node; + + // Visit the constant field + if (cast->constant != NULL) { + pm_visit_node((const pm_node_t *) cast->constant, visitor, data); + } + + // Visit the elements field + const pm_node_list_t *elements = &cast->elements; + for (size_t index = 0; index < elements->size; index++) { + pm_visit_node(elements->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + break; + } + case PM_IF_NODE: { + const pm_if_node_t *cast = (const pm_if_node_t *) node; + + // Visit the predicate field + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + // Visit the subsequent field + if (cast->subsequent != NULL) { + pm_visit_node((const pm_node_t *) cast->subsequent, visitor, data); + } + + break; + } + case PM_IMAGINARY_NODE: { + const pm_imaginary_node_t *cast = (const pm_imaginary_node_t *) node; + + // Visit the numeric field + pm_visit_node((const pm_node_t *) cast->numeric, visitor, data); + + break; + } + case PM_IMPLICIT_NODE: { + const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_IMPLICIT_REST_NODE: + break; + case PM_IN_NODE: { + const pm_in_node_t *cast = (const pm_in_node_t *) node; + + // Visit the pattern field + pm_visit_node((const pm_node_t *) cast->pattern, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_INDEX_AND_WRITE_NODE: { + const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INDEX_OPERATOR_WRITE_NODE: { + const pm_index_operator_write_node_t *cast = (const pm_index_operator_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INDEX_OR_WRITE_NODE: { + const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INDEX_TARGET_NODE: { + const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node; + + // Visit the receiver field + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + const pm_instance_variable_and_write_node_t *cast = (const pm_instance_variable_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + const pm_instance_variable_operator_write_node_t *cast = (const pm_instance_variable_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + const pm_instance_variable_or_write_node_t *cast = (const pm_instance_variable_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INSTANCE_VARIABLE_READ_NODE: + break; + case PM_INSTANCE_VARIABLE_TARGET_NODE: + break; + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + const pm_instance_variable_write_node_t *cast = (const pm_instance_variable_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INTEGER_NODE: + break; + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + const pm_interpolated_match_last_line_node_t *cast = (const pm_interpolated_match_last_line_node_t *) node; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) node; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_INTERPOLATED_STRING_NODE: { + const pm_interpolated_string_node_t *cast = (const pm_interpolated_string_node_t *) node; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_INTERPOLATED_SYMBOL_NODE: { + const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_INTERPOLATED_X_STRING_NODE: { + const pm_interpolated_x_string_node_t *cast = (const pm_interpolated_x_string_node_t *) node; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: + break; + case PM_IT_PARAMETERS_NODE: + break; + case PM_KEYWORD_HASH_NODE: { + const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node; + + // Visit the elements field + const pm_node_list_t *elements = &cast->elements; + for (size_t index = 0; index < elements->size; index++) { + pm_visit_node(elements->nodes[index], visitor, data); + } + + break; + } + case PM_KEYWORD_REST_PARAMETER_NODE: + break; + case PM_LAMBDA_NODE: { + const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node; + + // Visit the parameters field + if (cast->parameters != NULL) { + pm_visit_node((const pm_node_t *) cast->parameters, visitor, data); + } + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + const pm_local_variable_and_write_node_t *cast = (const pm_local_variable_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + const pm_local_variable_operator_write_node_t *cast = (const pm_local_variable_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + const pm_local_variable_or_write_node_t *cast = (const pm_local_variable_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_LOCAL_VARIABLE_READ_NODE: + break; + case PM_LOCAL_VARIABLE_TARGET_NODE: + break; + case PM_LOCAL_VARIABLE_WRITE_NODE: { + const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_MATCH_LAST_LINE_NODE: + break; + case PM_MATCH_PREDICATE_NODE: { + const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + // Visit the pattern field + pm_visit_node((const pm_node_t *) cast->pattern, visitor, data); + + break; + } + case PM_MATCH_REQUIRED_NODE: { + const pm_match_required_node_t *cast = (const pm_match_required_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + // Visit the pattern field + pm_visit_node((const pm_node_t *) cast->pattern, visitor, data); + + break; + } + case PM_MATCH_WRITE_NODE: { + const pm_match_write_node_t *cast = (const pm_match_write_node_t *) node; + + // Visit the call field + pm_visit_node((const pm_node_t *) cast->call, visitor, data); + + // Visit the targets field + const pm_node_list_t *targets = &cast->targets; + for (size_t index = 0; index < targets->size; index++) { + pm_visit_node(targets->nodes[index], visitor, data); + } + + break; + } + case PM_MISSING_NODE: + break; + case PM_MODULE_NODE: { + const pm_module_node_t *cast = (const pm_module_node_t *) node; + + // Visit the constant_path field + pm_visit_node((const pm_node_t *) cast->constant_path, visitor, data); + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_MULTI_TARGET_NODE: { + const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node; + + // Visit the lefts field + const pm_node_list_t *lefts = &cast->lefts; + for (size_t index = 0; index < lefts->size; index++) { + pm_visit_node(lefts->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + // Visit the rights field + const pm_node_list_t *rights = &cast->rights; + for (size_t index = 0; index < rights->size; index++) { + pm_visit_node(rights->nodes[index], visitor, data); + } + + break; + } + case PM_MULTI_WRITE_NODE: { + const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node; + + // Visit the lefts field + const pm_node_list_t *lefts = &cast->lefts; + for (size_t index = 0; index < lefts->size; index++) { + pm_visit_node(lefts->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + // Visit the rights field + const pm_node_list_t *rights = &cast->rights; + for (size_t index = 0; index < rights->size; index++) { + pm_visit_node(rights->nodes[index], visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_NEXT_NODE: { + const pm_next_node_t *cast = (const pm_next_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + break; + } + case PM_NIL_NODE: + break; + case PM_NO_KEYWORDS_PARAMETER_NODE: + break; + case PM_NUMBERED_PARAMETERS_NODE: + break; + case PM_NUMBERED_REFERENCE_READ_NODE: + break; + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + const pm_optional_keyword_parameter_node_t *cast = (const pm_optional_keyword_parameter_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_OPTIONAL_PARAMETER_NODE: { + const pm_optional_parameter_node_t *cast = (const pm_optional_parameter_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_OR_NODE: { + const pm_or_node_t *cast = (const pm_or_node_t *) node; + + // Visit the left field + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + + // Visit the right field + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + + break; + } + case PM_PARAMETERS_NODE: { + const pm_parameters_node_t *cast = (const pm_parameters_node_t *) node; + + // Visit the requireds field + const pm_node_list_t *requireds = &cast->requireds; + for (size_t index = 0; index < requireds->size; index++) { + pm_visit_node(requireds->nodes[index], visitor, data); + } + + // Visit the optionals field + const pm_node_list_t *optionals = &cast->optionals; + for (size_t index = 0; index < optionals->size; index++) { + pm_visit_node(optionals->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + // Visit the posts field + const pm_node_list_t *posts = &cast->posts; + for (size_t index = 0; index < posts->size; index++) { + pm_visit_node(posts->nodes[index], visitor, data); + } + + // Visit the keywords field + const pm_node_list_t *keywords = &cast->keywords; + for (size_t index = 0; index < keywords->size; index++) { + pm_visit_node(keywords->nodes[index], visitor, data); + } + + // Visit the keyword_rest field + if (cast->keyword_rest != NULL) { + pm_visit_node((const pm_node_t *) cast->keyword_rest, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_PARENTHESES_NODE: { + const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node; + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_PINNED_EXPRESSION_NODE: { + const pm_pinned_expression_node_t *cast = (const pm_pinned_expression_node_t *) node; + + // Visit the expression field + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + + break; + } + case PM_PINNED_VARIABLE_NODE: { + const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node; + + // Visit the variable field + pm_visit_node((const pm_node_t *) cast->variable, visitor, data); + + break; + } + case PM_POST_EXECUTION_NODE: { + const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_PRE_EXECUTION_NODE: { + const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_PROGRAM_NODE: { + const pm_program_node_t *cast = (const pm_program_node_t *) node; + + // Visit the statements field + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + + break; + } + case PM_RANGE_NODE: { + const pm_range_node_t *cast = (const pm_range_node_t *) node; + + // Visit the left field + if (cast->left != NULL) { + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + } + + // Visit the right field + if (cast->right != NULL) { + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + } + + break; + } + case PM_RATIONAL_NODE: + break; + case PM_REDO_NODE: + break; + case PM_REGULAR_EXPRESSION_NODE: + break; + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: + break; + case PM_REQUIRED_PARAMETER_NODE: + break; + case PM_RESCUE_MODIFIER_NODE: { + const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node; + + // Visit the expression field + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + + // Visit the rescue_expression field + pm_visit_node((const pm_node_t *) cast->rescue_expression, visitor, data); + + break; + } + case PM_RESCUE_NODE: { + const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node; + + // Visit the exceptions field + const pm_node_list_t *exceptions = &cast->exceptions; + for (size_t index = 0; index < exceptions->size; index++) { + pm_visit_node(exceptions->nodes[index], visitor, data); + } + + // Visit the reference field + if (cast->reference != NULL) { + pm_visit_node((const pm_node_t *) cast->reference, visitor, data); + } + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + // Visit the subsequent field + if (cast->subsequent != NULL) { + pm_visit_node((const pm_node_t *) cast->subsequent, visitor, data); + } + + break; + } + case PM_REST_PARAMETER_NODE: + break; + case PM_RETRY_NODE: + break; + case PM_RETURN_NODE: { + const pm_return_node_t *cast = (const pm_return_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + break; + } + case PM_SELF_NODE: + break; + case PM_SHAREABLE_CONSTANT_NODE: { + const pm_shareable_constant_node_t *cast = (const pm_shareable_constant_node_t *) node; + + // Visit the write field + pm_visit_node((const pm_node_t *) cast->write, visitor, data); + + break; + } + case PM_SINGLETON_CLASS_NODE: { + const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node; + + // Visit the expression field + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_SOURCE_ENCODING_NODE: + break; + case PM_SOURCE_FILE_NODE: + break; + case PM_SOURCE_LINE_NODE: + break; + case PM_SPLAT_NODE: { + const pm_splat_node_t *cast = (const pm_splat_node_t *) node; + + // Visit the expression field + if (cast->expression != NULL) { + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + } + + break; + } + case PM_STATEMENTS_NODE: { + const pm_statements_node_t *cast = (const pm_statements_node_t *) node; + + // Visit the body field + const pm_node_list_t *body = &cast->body; + for (size_t index = 0; index < body->size; index++) { + pm_visit_node(body->nodes[index], visitor, data); + } + + break; + } + case PM_STRING_NODE: + break; + case PM_SUPER_NODE: { + const pm_super_node_t *cast = (const pm_super_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_SYMBOL_NODE: + break; + case PM_TRUE_NODE: + break; + case PM_UNDEF_NODE: { + const pm_undef_node_t *cast = (const pm_undef_node_t *) node; + + // Visit the names field + const pm_node_list_t *names = &cast->names; + for (size_t index = 0; index < names->size; index++) { + pm_visit_node(names->nodes[index], visitor, data); + } + + break; + } + case PM_UNLESS_NODE: { + const pm_unless_node_t *cast = (const pm_unless_node_t *) node; + + // Visit the predicate field + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + // Visit the else_clause field + if (cast->else_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->else_clause, visitor, data); + } + + break; + } + case PM_UNTIL_NODE: { + const pm_until_node_t *cast = (const pm_until_node_t *) node; + + // Visit the predicate field + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_WHEN_NODE: { + const pm_when_node_t *cast = (const pm_when_node_t *) node; + + // Visit the conditions field + const pm_node_list_t *conditions = &cast->conditions; + for (size_t index = 0; index < conditions->size; index++) { + pm_visit_node(conditions->nodes[index], visitor, data); + } + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_WHILE_NODE: { + const pm_while_node_t *cast = (const pm_while_node_t *) node; + + // Visit the predicate field + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_X_STRING_NODE: + break; + case PM_YIELD_NODE: { + const pm_yield_node_t *cast = (const pm_yield_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + break; + } + case PM_SCOPE_NODE: + break; + } +} + +// We optionally support dumping to JSON. For systems that don't want or need +// this functionality, it can be turned off with the PRISM_EXCLUDE_JSON define. +#ifndef PRISM_EXCLUDE_JSON + +static void +pm_dump_json_constant(pm_buffer_t *buffer, const pm_parser_t *parser, pm_constant_id_t constant_id) { + const pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, constant_id); + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, constant->start, constant->length, PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); +} + +static void +pm_dump_json_location(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_location_t *location) { + uint32_t start = (uint32_t) (location->start - parser->start); + uint32_t end = (uint32_t) (location->end - parser->start); + pm_buffer_append_format(buffer, "{\"start\":%" PRIu32 ",\"end\":%" PRIu32 "}", start, end); +} + +/** + * Dump JSON to the given buffer. + */ +PRISM_EXPORTED_FUNCTION void +pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AliasGlobalVariableNode\",\"location\":", 45); + + const pm_alias_global_variable_node_t *cast = (const pm_alias_global_variable_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the new_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"new_name\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->new_name); + + // Dump the old_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"old_name\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->old_name); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ALIAS_METHOD_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AliasMethodNode\",\"location\":", 37); + + const pm_alias_method_node_t *cast = (const pm_alias_method_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the new_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"new_name\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->new_name); + + // Dump the old_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"old_name\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->old_name); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ALTERNATION_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AlternationPatternNode\",\"location\":", 44); + + const pm_alternation_pattern_node_t *cast = (const pm_alternation_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_AND_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AndNode\",\"location\":", 29); + + const pm_and_node_t *cast = (const pm_and_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ARGUMENTS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ArgumentsNode\",\"location\":", 35); + + const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ArgumentsNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ArgumentsNodeFlags\":", 21); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_FORWARDING\"", 21); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_KEYWORDS\"", 19); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_KEYWORD_SPLAT\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_SPLAT\"", 16); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_MULTIPLE_SPLATS\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + const pm_node_list_t *arguments = &cast->arguments; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < arguments->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, arguments->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ARRAY_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ArrayNode\",\"location\":", 31); + + const pm_array_node_t *cast = (const pm_array_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ArrayNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ArrayNodeFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_SPLAT\"", 16); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the elements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"elements\":", 11); + const pm_node_list_t *elements = &cast->elements; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < elements->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, elements->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ARRAY_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ArrayPatternNode\",\"location\":", 38); + + const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the constant field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant\":", 11); + if (cast->constant != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the requireds field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"requireds\":", 12); + const pm_node_list_t *requireds = &cast->requireds; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < requireds->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, requireds->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the posts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"posts\":", 8); + const pm_node_list_t *posts = &cast->posts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < posts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, posts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ASSOC_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AssocNode\",\"location\":", 31); + + const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the key field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"key\":", 6); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->key); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + if (cast->operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ASSOC_SPLAT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AssocSplatNode\",\"location\":", 36); + + const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + if (cast->value != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BACK_REFERENCE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BackReferenceReadNode\",\"location\":", 43); + + const pm_back_reference_read_node_t *cast = (const pm_back_reference_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BEGIN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BeginNode\",\"location\":", 31); + + const pm_begin_node_t *cast = (const pm_begin_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the begin_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"begin_keyword_loc\":", 20); + if (cast->begin_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->begin_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rescue_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rescue_clause\":", 16); + if (cast->rescue_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rescue_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the else_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_clause\":", 14); + if (cast->else_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->else_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the ensure_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ensure_clause\":", 16); + if (cast->ensure_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->ensure_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_ARGUMENT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockArgumentNode\",\"location\":", 39); + + const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + if (cast->expression != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockLocalVariableNode\",\"location\":", 44); + + const pm_block_local_variable_node_t *cast = (const pm_block_local_variable_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockNode\",\"location\":", 31); + + const pm_block_node_t *cast = (const pm_block_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the parameters field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parameters\":", 13); + if (cast->parameters != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parameters); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockParameterNode\",\"location\":", 40); + + const pm_block_parameter_node_t *cast = (const pm_block_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + if (cast->name_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->name_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_PARAMETERS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockParametersNode\",\"location\":", 41); + + const pm_block_parameters_node_t *cast = (const pm_block_parameters_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the parameters field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parameters\":", 13); + if (cast->parameters != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parameters); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_node_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, locals->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BREAK_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BreakNode\",\"location\":", 31); + + const pm_break_node_t *cast = (const pm_break_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallAndWriteNode\",\"location\":", 38); + + const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + if (cast->message_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->message_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the read_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"read_name\":", 12); + pm_dump_json_constant(buffer, parser, cast->read_name); + + // Dump the write_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"write_name\":", 13); + pm_dump_json_constant(buffer, parser, cast->write_name); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallNode\",\"location\":", 30); + + const pm_call_node_t *cast = (const pm_call_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + if (cast->message_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->message_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallOperatorWriteNode\",\"location\":", 43); + + const pm_call_operator_write_node_t *cast = (const pm_call_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + if (cast->message_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->message_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the read_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"read_name\":", 12); + pm_dump_json_constant(buffer, parser, cast->read_name); + + // Dump the write_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"write_name\":", 13); + pm_dump_json_constant(buffer, parser, cast->write_name); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallOrWriteNode\",\"location\":", 37); + + const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + if (cast->message_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->message_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the read_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"read_name\":", 12); + pm_dump_json_constant(buffer, parser, cast->read_name); + + // Dump the write_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"write_name\":", 13); + pm_dump_json_constant(buffer, parser, cast->write_name); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallTargetNode\",\"location\":", 36); + + const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->message_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CAPTURE_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CapturePatternNode\",\"location\":", 40); + + const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CASE_MATCH_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CaseMatchNode\",\"location\":", 35); + + const pm_case_match_node_t *cast = (const pm_case_match_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + if (cast->predicate != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the conditions field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"conditions\":", 13); + const pm_node_list_t *conditions = &cast->conditions; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < conditions->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, conditions->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the else_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_clause\":", 14); + if (cast->else_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->else_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the case_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"case_keyword_loc\":", 19); + pm_dump_json_location(buffer, parser, &cast->case_keyword_loc); + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CASE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CaseNode\",\"location\":", 30); + + const pm_case_node_t *cast = (const pm_case_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + if (cast->predicate != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the conditions field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"conditions\":", 13); + const pm_node_list_t *conditions = &cast->conditions; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < conditions->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, conditions->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the else_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_clause\":", 14); + if (cast->else_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->else_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the case_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"case_keyword_loc\":", 19); + pm_dump_json_location(buffer, parser, &cast->case_keyword_loc); + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassNode\",\"location\":", 31); + + const pm_class_node_t *cast = (const pm_class_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the class_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"class_keyword_loc\":", 20); + pm_dump_json_location(buffer, parser, &cast->class_keyword_loc); + + // Dump the constant_path field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant_path\":", 16); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant_path); + + // Dump the inheritance_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"inheritance_operator_loc\":", 27); + if (cast->inheritance_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->inheritance_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the superclass field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"superclass\":", 13); + if (cast->superclass != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->superclass); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableAndWriteNode\",\"location\":", 47); + + const pm_class_variable_and_write_node_t *cast = (const pm_class_variable_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableOperatorWriteNode\",\"location\":", 52); + + const pm_class_variable_operator_write_node_t *cast = (const pm_class_variable_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableOrWriteNode\",\"location\":", 46); + + const pm_class_variable_or_write_node_t *cast = (const pm_class_variable_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableReadNode\",\"location\":", 43); + + const pm_class_variable_read_node_t *cast = (const pm_class_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableTargetNode\",\"location\":", 45); + + const pm_class_variable_target_node_t *cast = (const pm_class_variable_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableWriteNode\",\"location\":", 44); + + const pm_class_variable_write_node_t *cast = (const pm_class_variable_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantAndWriteNode\",\"location\":", 42); + + const pm_constant_and_write_node_t *cast = (const pm_constant_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantOperatorWriteNode\",\"location\":", 47); + + const pm_constant_operator_write_node_t *cast = (const pm_constant_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantOrWriteNode\",\"location\":", 41); + + const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathAndWriteNode\",\"location\":", 46); + + const pm_constant_path_and_write_node_t *cast = (const pm_constant_path_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathNode\",\"location\":", 38); + + const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the parent field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parent\":", 9); + if (cast->parent != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parent); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the delimiter_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"delimiter_loc\":", 16); + pm_dump_json_location(buffer, parser, &cast->delimiter_loc); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathOperatorWriteNode\",\"location\":", 51); + + const pm_constant_path_operator_write_node_t *cast = (const pm_constant_path_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathOrWriteNode\",\"location\":", 45); + + const pm_constant_path_or_write_node_t *cast = (const pm_constant_path_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathTargetNode\",\"location\":", 44); + + const pm_constant_path_target_node_t *cast = (const pm_constant_path_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the parent field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parent\":", 9); + if (cast->parent != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parent); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the delimiter_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"delimiter_loc\":", 16); + pm_dump_json_location(buffer, parser, &cast->delimiter_loc); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathWriteNode\",\"location\":", 43); + + const pm_constant_path_write_node_t *cast = (const pm_constant_path_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantReadNode\",\"location\":", 38); + + const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantTargetNode\",\"location\":", 40); + + const pm_constant_target_node_t *cast = (const pm_constant_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantWriteNode\",\"location\":", 39); + + const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_DEF_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"DefNode\",\"location\":", 29); + + const pm_def_node_t *cast = (const pm_def_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the parameters field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parameters\":", 13); + if (cast->parameters != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parameters); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the def_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"def_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->def_keyword_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + if (cast->operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the equal_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"equal_loc\":", 12); + if (cast->equal_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->equal_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_DEFINED_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"DefinedNode\",\"location\":", 33); + + const pm_defined_node_t *cast = (const pm_defined_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ELSE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ElseNode\",\"location\":", 30); + + const pm_else_node_t *cast = (const pm_else_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the else_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_keyword_loc\":", 19); + pm_dump_json_location(buffer, parser, &cast->else_keyword_loc); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"EmbeddedStatementsNode\",\"location\":", 44); + + const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_EMBEDDED_VARIABLE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"EmbeddedVariableNode\",\"location\":", 42); + + const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the variable field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"variable\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->variable); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ENSURE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"EnsureNode\",\"location\":", 32); + + const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ensure_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ensure_keyword_loc\":", 21); + pm_dump_json_location(buffer, parser, &cast->ensure_keyword_loc); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FALSE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"FalseNode\",\"location\":", 31); + + const pm_false_node_t *cast = (const pm_false_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FIND_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"FindPatternNode\",\"location\":", 37); + + const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the constant field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant\":", 11); + if (cast->constant != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + + // Dump the requireds field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"requireds\":", 12); + const pm_node_list_t *requireds = &cast->requireds; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < requireds->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, requireds->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FLIP_FLOP_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"FlipFlopNode\",\"location\":", 34); + + const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RangeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RangeFlags\":", 13); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXCLUDE_END\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + if (cast->left != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + if (cast->right != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FLOAT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"FloatNode\",\"location\":", 31); + + const pm_float_node_t *cast = (const pm_float_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_buffer_append_format(buffer, "%f", cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FOR_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ForNode\",\"location\":", 29); + + const pm_for_node_t *cast = (const pm_for_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the index field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"index\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->index); + + // Dump the collection field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"collection\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->collection); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the for_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"for_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->for_keyword_loc); + + // Dump the in_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"in_keyword_loc\":", 17); + pm_dump_json_location(buffer, parser, &cast->in_keyword_loc); + + // Dump the do_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"do_keyword_loc\":", 17); + if (cast->do_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->do_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FORWARDING_ARGUMENTS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ForwardingArgumentsNode\",\"location\":", 45); + + const pm_forwarding_arguments_node_t *cast = (const pm_forwarding_arguments_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FORWARDING_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ForwardingParameterNode\",\"location\":", 45); + + const pm_forwarding_parameter_node_t *cast = (const pm_forwarding_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FORWARDING_SUPER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ForwardingSuperNode\",\"location\":", 41); + + const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableAndWriteNode\",\"location\":", 48); + + const pm_global_variable_and_write_node_t *cast = (const pm_global_variable_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableOperatorWriteNode\",\"location\":", 53); + + const pm_global_variable_operator_write_node_t *cast = (const pm_global_variable_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableOrWriteNode\",\"location\":", 47); + + const pm_global_variable_or_write_node_t *cast = (const pm_global_variable_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableReadNode\",\"location\":", 44); + + const pm_global_variable_read_node_t *cast = (const pm_global_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableTargetNode\",\"location\":", 46); + + const pm_global_variable_target_node_t *cast = (const pm_global_variable_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableWriteNode\",\"location\":", 45); + + const pm_global_variable_write_node_t *cast = (const pm_global_variable_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_HASH_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"HashNode\",\"location\":", 30); + + const pm_hash_node_t *cast = (const pm_hash_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the elements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"elements\":", 11); + const pm_node_list_t *elements = &cast->elements; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < elements->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, elements->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_HASH_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"HashPatternNode\",\"location\":", 37); + + const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the constant field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant\":", 11); + if (cast->constant != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the elements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"elements\":", 11); + const pm_node_list_t *elements = &cast->elements; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < elements->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, elements->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IF_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IfNode\",\"location\":", 28); + + const pm_if_node_t *cast = (const pm_if_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the if_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"if_keyword_loc\":", 17); + if (cast->if_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->if_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + + // Dump the then_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_keyword_loc\":", 19); + if (cast->then_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the subsequent field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"subsequent\":", 13); + if (cast->subsequent != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->subsequent); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IMAGINARY_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ImaginaryNode\",\"location\":", 35); + + const pm_imaginary_node_t *cast = (const pm_imaginary_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the numeric field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"numeric\":", 10); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->numeric); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IMPLICIT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ImplicitNode\",\"location\":", 34); + + const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IMPLICIT_REST_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ImplicitRestNode\",\"location\":", 38); + + const pm_implicit_rest_node_t *cast = (const pm_implicit_rest_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InNode\",\"location\":", 28); + + const pm_in_node_t *cast = (const pm_in_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the pattern field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"pattern\":", 10); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->pattern); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the in_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"in_loc\":", 9); + pm_dump_json_location(buffer, parser, &cast->in_loc); + + // Dump the then_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_loc\":", 11); + if (cast->then_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INDEX_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IndexAndWriteNode\",\"location\":", 39); + + const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IndexOperatorWriteNode\",\"location\":", 44); + + const pm_index_operator_write_node_t *cast = (const pm_index_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INDEX_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IndexOrWriteNode\",\"location\":", 38); + + const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INDEX_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IndexTargetNode\",\"location\":", 37); + + const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableAndWriteNode\",\"location\":", 50); + + const pm_instance_variable_and_write_node_t *cast = (const pm_instance_variable_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableOperatorWriteNode\",\"location\":", 55); + + const pm_instance_variable_operator_write_node_t *cast = (const pm_instance_variable_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableOrWriteNode\",\"location\":", 49); + + const pm_instance_variable_or_write_node_t *cast = (const pm_instance_variable_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableReadNode\",\"location\":", 46); + + const pm_instance_variable_read_node_t *cast = (const pm_instance_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableTargetNode\",\"location\":", 48); + + const pm_instance_variable_target_node_t *cast = (const pm_instance_variable_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableWriteNode\",\"location\":", 47); + + const pm_instance_variable_write_node_t *cast = (const pm_instance_variable_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTEGER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IntegerNode\",\"location\":", 33); + + const pm_integer_node_t *cast = (const pm_integer_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the IntegerBaseFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IntegerBaseFlags\":", 19); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_BINARY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"BINARY\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_DECIMAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"DECIMAL\"", 9); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_OCTAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"OCTAL\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_HEXADECIMAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"HEXADECIMAL\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_integer_string(buffer, &cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedMatchLastLineNode\",\"location\":", 51); + + const pm_interpolated_match_last_line_node_t *cast = (const pm_interpolated_match_last_line_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RegularExpressionFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RegularExpressionFlags\":", 25); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_CASE\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXTENDED\"", 10); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTI_LINE\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ONCE\"", 6); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EUC_JP\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ASCII_8BIT\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"WINDOWS_31J\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"UTF_8\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedRegularExpressionNode\",\"location\":", 55); + + const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RegularExpressionFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RegularExpressionFlags\":", 25); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_CASE\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXTENDED\"", 10); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTI_LINE\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ONCE\"", 6); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EUC_JP\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ASCII_8BIT\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"WINDOWS_31J\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"UTF_8\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_STRING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedStringNode\",\"location\":", 44); + + const pm_interpolated_string_node_t *cast = (const pm_interpolated_string_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the InterpolatedStringNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"InterpolatedStringNodeFlags\":", 30); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FROZEN\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MUTABLE\"", 9); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedSymbolNode\",\"location\":", 44); + + const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_X_STRING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedXStringNode\",\"location\":", 45); + + const pm_interpolated_x_string_node_t *cast = (const pm_interpolated_x_string_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ItLocalVariableReadNode\",\"location\":", 45); + + const pm_it_local_variable_read_node_t *cast = (const pm_it_local_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IT_PARAMETERS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ItParametersNode\",\"location\":", 38); + + const pm_it_parameters_node_t *cast = (const pm_it_parameters_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_KEYWORD_HASH_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"KeywordHashNode\",\"location\":", 37); + + const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the KeywordHashNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"KeywordHashNodeFlags\":", 23); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SYMBOL_KEYS\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the elements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"elements\":", 11); + const pm_node_list_t *elements = &cast->elements; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < elements->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, elements->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_KEYWORD_REST_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"KeywordRestParameterNode\",\"location\":", 46); + + const pm_keyword_rest_parameter_node_t *cast = (const pm_keyword_rest_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + if (cast->name_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->name_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LAMBDA_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LambdaNode\",\"location\":", 32); + + const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the parameters field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parameters\":", 13); + if (cast->parameters != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parameters); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableAndWriteNode\",\"location\":", 47); + + const pm_local_variable_and_write_node_t *cast = (const pm_local_variable_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableOperatorWriteNode\",\"location\":", 52); + + const pm_local_variable_operator_write_node_t *cast = (const pm_local_variable_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableOrWriteNode\",\"location\":", 46); + + const pm_local_variable_or_write_node_t *cast = (const pm_local_variable_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableReadNode\",\"location\":", 43); + + const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableTargetNode\",\"location\":", 45); + + const pm_local_variable_target_node_t *cast = (const pm_local_variable_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableWriteNode\",\"location\":", 44); + + const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MATCH_LAST_LINE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MatchLastLineNode\",\"location\":", 39); + + const pm_match_last_line_node_t *cast = (const pm_match_last_line_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RegularExpressionFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RegularExpressionFlags\":", 25); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_CASE\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXTENDED\"", 10); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTI_LINE\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ONCE\"", 6); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EUC_JP\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ASCII_8BIT\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"WINDOWS_31J\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"UTF_8\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the content_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"content_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->content_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MATCH_PREDICATE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MatchPredicateNode\",\"location\":", 40); + + const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the pattern field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"pattern\":", 10); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->pattern); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MATCH_REQUIRED_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MatchRequiredNode\",\"location\":", 39); + + const pm_match_required_node_t *cast = (const pm_match_required_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the pattern field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"pattern\":", 10); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->pattern); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MATCH_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MatchWriteNode\",\"location\":", 36); + + const pm_match_write_node_t *cast = (const pm_match_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the call field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->call); + + // Dump the targets field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"targets\":", 10); + const pm_node_list_t *targets = &cast->targets; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < targets->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, targets->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MISSING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MissingNode\",\"location\":", 33); + + const pm_missing_node_t *cast = (const pm_missing_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MODULE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ModuleNode\",\"location\":", 32); + + const pm_module_node_t *cast = (const pm_module_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the module_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"module_keyword_loc\":", 21); + pm_dump_json_location(buffer, parser, &cast->module_keyword_loc); + + // Dump the constant_path field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant_path\":", 16); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant_path); + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MULTI_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MultiTargetNode\",\"location\":", 37); + + const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the lefts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lefts\":", 8); + const pm_node_list_t *lefts = &cast->lefts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < lefts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, lefts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rights field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rights\":", 9); + const pm_node_list_t *rights = &cast->rights; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < rights->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, rights->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MULTI_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MultiWriteNode\",\"location\":", 36); + + const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the lefts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lefts\":", 8); + const pm_node_list_t *lefts = &cast->lefts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < lefts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, lefts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rights field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rights\":", 9); + const pm_node_list_t *rights = &cast->rights; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < rights->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, rights->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NEXT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NextNode\",\"location\":", 30); + + const pm_next_node_t *cast = (const pm_next_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NIL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NilNode\",\"location\":", 29); + + const pm_nil_node_t *cast = (const pm_nil_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NO_KEYWORDS_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NoKeywordsParameterNode\",\"location\":", 45); + + const pm_no_keywords_parameter_node_t *cast = (const pm_no_keywords_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NUMBERED_PARAMETERS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NumberedParametersNode\",\"location\":", 44); + + const pm_numbered_parameters_node_t *cast = (const pm_numbered_parameters_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the maximum field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"maximum\":", 10); + pm_buffer_append_format(buffer, "%" PRIu8, cast->maximum); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NUMBERED_REFERENCE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NumberedReferenceReadNode\",\"location\":", 47); + + const pm_numbered_reference_read_node_t *cast = (const pm_numbered_reference_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the number field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"number\":", 9); + pm_buffer_append_format(buffer, "%" PRIu32, cast->number); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"OptionalKeywordParameterNode\",\"location\":", 50); + + const pm_optional_keyword_parameter_node_t *cast = (const pm_optional_keyword_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_OPTIONAL_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"OptionalParameterNode\",\"location\":", 43); + + const pm_optional_parameter_node_t *cast = (const pm_optional_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_OR_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"OrNode\",\"location\":", 28); + + const pm_or_node_t *cast = (const pm_or_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PARAMETERS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ParametersNode\",\"location\":", 36); + + const pm_parameters_node_t *cast = (const pm_parameters_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the requireds field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"requireds\":", 12); + const pm_node_list_t *requireds = &cast->requireds; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < requireds->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, requireds->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the optionals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"optionals\":", 12); + const pm_node_list_t *optionals = &cast->optionals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < optionals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, optionals->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the posts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"posts\":", 8); + const pm_node_list_t *posts = &cast->posts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < posts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, posts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keywords field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keywords\":", 11); + const pm_node_list_t *keywords = &cast->keywords; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < keywords->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, keywords->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keyword_rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_rest\":", 15); + if (cast->keyword_rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->keyword_rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PARENTHESES_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ParenthesesNode\",\"location\":", 37); + + const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParenthesesNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParenthesesNodeFlags\":", 23); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTIPLE_STATEMENTS\"", 21); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PINNED_EXPRESSION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"PinnedExpressionNode\",\"location\":", 42); + + const pm_pinned_expression_node_t *cast = (const pm_pinned_expression_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PINNED_VARIABLE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"PinnedVariableNode\",\"location\":", 40); + + const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the variable field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"variable\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->variable); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_POST_EXECUTION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"PostExecutionNode\",\"location\":", 39); + + const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PRE_EXECUTION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"PreExecutionNode\",\"location\":", 38); + + const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PROGRAM_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ProgramNode\",\"location\":", 33); + + const pm_program_node_t *cast = (const pm_program_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RANGE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RangeNode\",\"location\":", 31); + + const pm_range_node_t *cast = (const pm_range_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RangeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RangeFlags\":", 13); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXCLUDE_END\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + if (cast->left != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + if (cast->right != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RATIONAL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RationalNode\",\"location\":", 34); + + const pm_rational_node_t *cast = (const pm_rational_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the IntegerBaseFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IntegerBaseFlags\":", 19); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_BINARY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"BINARY\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_DECIMAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"DECIMAL\"", 9); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_OCTAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"OCTAL\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_HEXADECIMAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"HEXADECIMAL\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the numerator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"numerator\":", 12); + pm_integer_string(buffer, &cast->numerator); + + // Dump the denominator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"denominator\":", 14); + pm_integer_string(buffer, &cast->denominator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REDO_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RedoNode\",\"location\":", 30); + + const pm_redo_node_t *cast = (const pm_redo_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REGULAR_EXPRESSION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RegularExpressionNode\",\"location\":", 43); + + const pm_regular_expression_node_t *cast = (const pm_regular_expression_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RegularExpressionFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RegularExpressionFlags\":", 25); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_CASE\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXTENDED\"", 10); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTI_LINE\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ONCE\"", 6); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EUC_JP\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ASCII_8BIT\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"WINDOWS_31J\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"UTF_8\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the content_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"content_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->content_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RequiredKeywordParameterNode\",\"location\":", 50); + + const pm_required_keyword_parameter_node_t *cast = (const pm_required_keyword_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REQUIRED_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RequiredParameterNode\",\"location\":", 43); + + const pm_required_parameter_node_t *cast = (const pm_required_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RESCUE_MODIFIER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RescueModifierNode\",\"location\":", 40); + + const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the rescue_expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rescue_expression\":", 20); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rescue_expression); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RESCUE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RescueNode\",\"location\":", 32); + + const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the exceptions field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"exceptions\":", 13); + const pm_node_list_t *exceptions = &cast->exceptions; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < exceptions->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, exceptions->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + if (cast->operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the reference field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"reference\":", 12); + if (cast->reference != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->reference); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the then_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_keyword_loc\":", 19); + if (cast->then_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the subsequent field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"subsequent\":", 13); + if (cast->subsequent != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->subsequent); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REST_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RestParameterNode\",\"location\":", 39); + + const pm_rest_parameter_node_t *cast = (const pm_rest_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + if (cast->name_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->name_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RETRY_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RetryNode\",\"location\":", 31); + + const pm_retry_node_t *cast = (const pm_retry_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RETURN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ReturnNode\",\"location\":", 32); + + const pm_return_node_t *cast = (const pm_return_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SELF_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SelfNode\",\"location\":", 30); + + const pm_self_node_t *cast = (const pm_self_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SHAREABLE_CONSTANT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ShareableConstantNode\",\"location\":", 43); + + const pm_shareable_constant_node_t *cast = (const pm_shareable_constant_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ShareableConstantNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ShareableConstantNodeFlags\":", 29); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"LITERAL\"", 9); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXPERIMENTAL_EVERYTHING\"", 25); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXPERIMENTAL_COPY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the write field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"write\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->write); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SINGLETON_CLASS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SingletonClassNode\",\"location\":", 40); + + const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the class_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"class_keyword_loc\":", 20); + pm_dump_json_location(buffer, parser, &cast->class_keyword_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SOURCE_ENCODING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SourceEncodingNode\",\"location\":", 40); + + const pm_source_encoding_node_t *cast = (const pm_source_encoding_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SOURCE_FILE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SourceFileNode\",\"location\":", 36); + + const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the StringFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"StringFlags\":", 14); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FROZEN\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MUTABLE\"", 9); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the filepath field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"filepath\":", 11); + const pm_string_t *filepath = &cast->filepath; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(filepath), pm_string_length(filepath), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SOURCE_LINE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SourceLineNode\",\"location\":", 36); + + const pm_source_line_node_t *cast = (const pm_source_line_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SPLAT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SplatNode\",\"location\":", 31); + + const pm_splat_node_t *cast = (const pm_splat_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + if (cast->expression != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_STATEMENTS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"StatementsNode\",\"location\":", 36); + + const pm_statements_node_t *cast = (const pm_statements_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + const pm_node_list_t *body = &cast->body; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < body->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, body->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_STRING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"StringNode\",\"location\":", 32); + + const pm_string_node_t *cast = (const pm_string_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the StringFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"StringFlags\":", 14); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FROZEN\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MUTABLE\"", 9); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the content_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"content_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->content_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SUPER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SuperNode\",\"location\":", 31); + + const pm_super_node_t *cast = (const pm_super_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SYMBOL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SymbolNode\",\"location\":", 32); + + const pm_symbol_node_t *cast = (const pm_symbol_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the SymbolFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SymbolFlags\":", 14); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the value_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value_loc\":", 12); + if (cast->value_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->value_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_TRUE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"TrueNode\",\"location\":", 30); + + const pm_true_node_t *cast = (const pm_true_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_UNDEF_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"UndefNode\",\"location\":", 31); + + const pm_undef_node_t *cast = (const pm_undef_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the names field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"names\":", 8); + const pm_node_list_t *names = &cast->names; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < names->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, names->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_UNLESS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"UnlessNode\",\"location\":", 32); + + const pm_unless_node_t *cast = (const pm_unless_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + + // Dump the then_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_keyword_loc\":", 19); + if (cast->then_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the else_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_clause\":", 14); + if (cast->else_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->else_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_UNTIL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"UntilNode\",\"location\":", 31); + + const pm_until_node_t *cast = (const pm_until_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the LoopFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"LoopFlags\":", 12); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_LOOP_FLAGS_BEGIN_MODIFIER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"BEGIN_MODIFIER\"", 16); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the do_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"do_keyword_loc\":", 17); + if (cast->do_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->do_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_WHEN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"WhenNode\",\"location\":", 30); + + const pm_when_node_t *cast = (const pm_when_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the conditions field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"conditions\":", 13); + const pm_node_list_t *conditions = &cast->conditions; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < conditions->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, conditions->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the then_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_keyword_loc\":", 19); + if (cast->then_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_WHILE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"WhileNode\",\"location\":", 31); + + const pm_while_node_t *cast = (const pm_while_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the LoopFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"LoopFlags\":", 12); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_LOOP_FLAGS_BEGIN_MODIFIER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"BEGIN_MODIFIER\"", 16); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the do_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"do_keyword_loc\":", 17); + if (cast->do_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->do_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_X_STRING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"XStringNode\",\"location\":", 33); + + const pm_x_string_node_t *cast = (const pm_x_string_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the EncodingFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EncodingFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the content_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"content_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->content_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_YIELD_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"YieldNode\",\"location\":", 31); + + const pm_yield_node_t *cast = (const pm_yield_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SCOPE_NODE: + break; + } +} + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/src/options.c b/vendor/mruby-compiler2/lib/prism/src/options.c new file mode 100644 index 0000000..a457178 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/options.c @@ -0,0 +1,328 @@ +#include "prism/options.h" + +/** + * Set the shebang callback option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_shebang_callback_set(pm_options_t *options, pm_options_shebang_callback_t shebang_callback, void *shebang_callback_data) { + options->shebang_callback = shebang_callback; + options->shebang_callback_data = shebang_callback_data; +} + +/** + * 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 encoding_locked option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked) { + options->encoding_locked = encoding_locked; +} + +/** + * 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 ? PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED : PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED; +} + +/** + * Sets the command line option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_command_line_set(pm_options_t *options, uint8_t command_line) { + options->command_line = command_line; +} + +/** + * Checks if the given slice represents a number. + */ +static inline bool +is_number(const char *string, size_t length) { + return pm_strspn_decimal_digit((const uint8_t *) string, (ptrdiff_t) length) == length; +} + +/** + * Set the version option on the given options struct by parsing the given + * string. If the string contains an invalid option, this returns false. + * Otherwise, it returns true. + */ +PRISM_EXPORTED_FUNCTION bool +pm_options_version_set(pm_options_t *options, const char *version, size_t length) { + if (version == NULL) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + + if (length == 3) { + if (strncmp(version, "3.3", 3) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_3; + return true; + } + + if (strncmp(version, "3.4", 3) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_4; + return true; + } + + if (strncmp(version, "3.5", 3) == 0) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + + return false; + } + + if (length >= 4) { + if (strncmp(version, "3.3.", 4) == 0 && is_number(version + 4, length - 4)) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_3; + return true; + } + + if (strncmp(version, "3.4.", 4) == 0 && is_number(version + 4, length - 4)) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_4; + return true; + } + + if (strncmp(version, "3.5.", 4) == 0 && is_number(version + 4, length - 4)) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + } + + if (length >= 6) { + if (strncmp(version, "latest", 7) == 0) { // 7 to compare the \0 as well + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + } + + return false; +} + +/** + * Set the main script option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_main_script_set(pm_options_t *options, bool main_script) { + options->main_script = main_script; +} + +/** + * Set the partial script option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_partial_script_set(pm_options_t *options, bool partial_script) { + options->partial_script = partial_script; +} + +/** + * Set the freeze option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_freeze_set(pm_options_t *options, bool freeze) { + options->freeze = freeze; +} + +// For some reason, GCC analyzer thinks we're leaking allocated scopes and +// locals here, even though we definitely aren't. This is a false positive. +// Ideally we wouldn't need to suppress this. +#if defined(__GNUC__) && (__GNUC__ >= 10) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak" +#endif + +/** + * Allocate and zero out the scopes array on the given options struct. + */ +PRISM_EXPORTED_FUNCTION bool +pm_options_scopes_init(pm_options_t *options, size_t scopes_count) { + options->scopes_count = scopes_count; + options->scopes = xcalloc(scopes_count, sizeof(pm_options_scope_t)); + return options->scopes != NULL; +} + +/** + * 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 bool +pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count) { + scope->locals_count = locals_count; + scope->locals = xcalloc(locals_count, sizeof(pm_string_t)); + scope->forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE; + return scope->locals != NULL; +} + +/** + * 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]; +} + +/** + * Set the forwarding option on the given scope struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding) { + scope->forwarding = forwarding; +} + +/** + * 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]); + } + + xfree(scope->locals); + } + + xfree(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 = (int8_t) *data++; + options->command_line = (uint8_t) *data++; + options->version = (pm_options_version_t) *data++; + options->encoding_locked = ((uint8_t) *data++) > 0; + options->main_script = ((uint8_t) *data++) > 0; + options->partial_script = ((uint8_t) *data++) > 0; + options->freeze = ((uint8_t) *data++) > 0; + + uint32_t scopes_count = pm_options_read_u32(data); + data += 4; + + if (scopes_count > 0) { + if (!pm_options_scopes_init(options, scopes_count)) return; + + 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]; + if (!pm_options_scope_init(scope, locals_count)) { + pm_options_free(options); + return; + } + + uint8_t forwarding = (uint8_t) *data++; + pm_options_scope_forwarding_set(&options->scopes[scope_index], forwarding); + + 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; + } + } + } +} + +#if defined(__GNUC__) && (__GNUC__ >= 10) +#pragma GCC diagnostic pop +#endif diff --git a/vendor/mruby-compiler2/lib/prism/src/pack.c b/vendor/mruby-compiler2/lib/prism/src/pack.c new file mode 100644 index 0000000..1388ca8 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/pack.c @@ -0,0 +1,509 @@ +#include "prism/pack.h" + +// We optionally support parsing String#pack templates. For systems that don't +// want or need this functionality, it can be turned off with the +// PRISM_EXCLUDE_PACK define. +#ifdef PRISM_EXCLUDE_PACK + +void pm_pack_parse(void) {} + +#else + +#include +#include + +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; +} + +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; + } +} + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/src/prettyprint.c b/vendor/mruby-compiler2/lib/prism/src/prettyprint.c new file mode 100644 index 0000000..d322400 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/prettyprint.c @@ -0,0 +1,8939 @@ +/*----------------------------------------------------------------------------*/ +/* 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" + +// We optionally support pretty printing nodes. For systems that don't want or +// need this functionality, it can be turned off with the +// PRISM_EXCLUDE_PRETTYPRINT define. +#ifdef PRISM_EXCLUDE_PRETTYPRINT + +void pm_prettyprint(void) {} + +#else + +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, parser->start_line); + pm_line_column_t end = pm_newline_list_line_column(&parser->newline_list, location->end, parser->start_line); + pm_buffer_append_format(output_buffer, "(%" PRIi32 ",%" PRIu32 ")-(%" PRIi32 ",%" PRIu32 ")", start.line, start.column, end.line, 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, "+-- new_name:", 13); + 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->new_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // old_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- old_name:", 13); + 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->old_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- new_name:", 13); + 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->new_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // old_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- old_name:", 13); + 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->old_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- left:", 9); + 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->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + 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->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- left:", 9); + 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->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + 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->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // ArgumentsNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ArgumentsNodeFlags:", 23); + bool found = false; + if (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_forwarding", 20); + found = true; + } + if (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_keywords", 18); + found = true; + } + 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 (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_splat", 15); + found = true; + } + if (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_multiple_splats", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + 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); + + // ArrayNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ArrayNodeFlags:", 19); + 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, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- elements:", 13); + 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); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + 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, "+-- constant:", 13); + if (cast->constant == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->constant, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // requireds + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- requireds:", 14); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- rest:", 9); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // posts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- posts:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- key:", 8); + 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->key, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- value:", 10); + if (cast->value == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- name:", 9); + 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, "+-- begin_keyword_loc:", 22); + pm_location_t *location = &cast->begin_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // rescue_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rescue_clause:", 18); + if (cast->rescue_clause == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->rescue_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // else_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- else_clause:", 16); + if (cast->else_clause == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->else_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // ensure_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ensure_clause:", 18); + if (cast->ensure_clause == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->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, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- expression:", 15); + if (cast->expression == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- locals:", 11); + 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, "+-- parameters:", 15); + if (cast->parameters == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + 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); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\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, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- parameters:", 15); + if (cast->parameters == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + 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 (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // read_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- read_name:", 14); + 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, "+-- write_name:", 15); + 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, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + 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 (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " nil\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_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); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + 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 (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // read_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- read_name:", 14); + 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, "+-- write_name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->write_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + 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 (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // read_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- read_name:", 14); + 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, "+-- write_name:", 15); + 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, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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_TARGET_NODE: { + pm_call_target_node_t *cast = (pm_call_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallTargetNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + 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 (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + 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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + 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, "+-- value:", 10); + 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; + } + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- target:", 11); + 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->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- predicate:", 14); + if (cast->predicate == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // conditions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- conditions:", 15); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->conditions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // else_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- else_clause:", 16); + if (cast->else_clause == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->else_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // case_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- case_keyword_loc:", 21); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- predicate:", 14); + if (cast->predicate == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // conditions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- conditions:", 15); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->conditions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // else_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- else_clause:", 16); + if (cast->else_clause == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->else_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // case_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- case_keyword_loc:", 21); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- locals:", 11); + 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, "+-- class_keyword_loc:", 22); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // constant_path + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- constant_path:", 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->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, "+-- inheritance_operator_loc:", 29); + pm_location_t *location = &cast->inheritance_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // superclass + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- superclass:", 15); + if (cast->superclass == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->superclass, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- name:", 9); + 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, "+-- name:", 9); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- target:", 11); + 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->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- parent:", 11); + if (cast->parent == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->parent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // delimiter_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- delimiter_loc:", 18); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- target:", 11); + 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->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_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, "+-- target:", 11); + 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->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- parent:", 11); + if (cast->parent == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->parent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // delimiter_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- delimiter_loc:", 18); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- target:", 11); + 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->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- name:", 9); + 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, "+-- name:", 9); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parameters:", 15); + if (cast->parameters == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + 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, "+-- def_keyword_loc:", 20); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // equal_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- equal_loc:", 14); + pm_location_t *location = &cast->equal_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- else_keyword_loc:", 21); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // variable + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- variable:", 13); + 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, "+-- ensure_keyword_loc:", 23); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- constant:", 13); + if (cast->constant == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->constant, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- left:", 9); + 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->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // requireds + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- requireds:", 14); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- right:", 10); + 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->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // RangeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RangeFlags:", 15); + 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, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- left:", 9); + if (cast->left == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + if (cast->right == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_FLOAT_NODE: { + pm_float_node_t *cast = (pm_float_node_t *) 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); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_format(output_buffer, " %f\n", cast->value); + } + + 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, "+-- index:", 10); + 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->index, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // collection + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- collection:", 15); + 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->collection, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // for_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- for_keyword_loc:", 20); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // in_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- in_keyword_loc:", 19); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // do_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- do_keyword_loc:", 19); + pm_location_t *location = &cast->do_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- block:", 10); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " nil\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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- name:", 9); + 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, "+-- name:", 9); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- elements:", 13); + 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); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- constant:", 13); + if (cast->constant == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->constant, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- elements:", 13); + 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); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- rest:", 9); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- if_keyword_loc:", 19); + pm_location_t *location = &cast->if_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + 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->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_keyword_loc:", 21); + pm_location_t *location = &cast->then_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // subsequent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- subsequent:", 15); + if (cast->subsequent == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->subsequent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- numeric:", 12); + 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, "+-- value:", 10); + 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, "+-- pattern:", 12); + 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->pattern, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // in_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- in_loc:", 11); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // then_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_loc:", 13); + pm_location_t *location = &cast->then_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + 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 (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + 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 (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + 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 (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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_TARGET_NODE: { + pm_index_target_node_t *cast = (pm_index_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IndexTargetNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + 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 (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + 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->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " nil\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_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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- name:", 9); + 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, "+-- name:", 9); + 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, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // IntegerBaseFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- IntegerBaseFlags:", 21); + 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_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_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_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, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + const pm_integer_t *integer = &cast->value; + pm_buffer_append_byte(output_buffer, ' '); + pm_integer_string(output_buffer, integer); + 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); + + // RegularExpressionFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RegularExpressionFlags:", 27); + 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 (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + 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); + + // RegularExpressionFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RegularExpressionFlags:", 27); + 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 (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + 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); + + // InterpolatedStringNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- InterpolatedStringNodeFlags:", 32); + bool found = false; + if (cast->base.flags & PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " frozen", 7); + found = true; + } + if (cast->base.flags & PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " mutable", 8); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_string(output_buffer, "@ ItLocalVariableReadNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_IT_PARAMETERS_NODE: { + pm_buffer_append_string(output_buffer, "@ ItParametersNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + 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); + + // KeywordHashNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- KeywordHashNodeFlags:", 25); + bool found = false; + if (cast->base.flags & PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " symbol_keys", 12); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- elements:", 13); + 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); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\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, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- locals:", 11); + 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, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parameters:", 15); + if (cast->parameters == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\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, "+-- name:", 9); + 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, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\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, "+-- name:", 9); + 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, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\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, "+-- name:", 9); + 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, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast->depth); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // RegularExpressionFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RegularExpressionFlags:", 27); + 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 (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- content_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + 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, "+-- value:", 10); + 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; + } + + // pattern + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- pattern:", 12); + 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->pattern, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- value:", 10); + 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; + } + + // pattern + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- pattern:", 12); + 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->pattern, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- call:", 9); + 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->call, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // targets + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- targets:", 12); + 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); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- locals:", 11); + 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, "+-- module_keyword_loc:", 23); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // constant_path + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- constant_path:", 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->constant_path, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- lefts:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- rest:", 9); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rights + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rights:", 11); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- lefts:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- rest:", 9); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rights + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rights:", 11); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_NUMBERED_PARAMETERS_NODE: { + pm_numbered_parameters_node_t *cast = (pm_numbered_parameters_node_t *) node; + pm_buffer_append_string(output_buffer, "@ NumberedParametersNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // maximum + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- maximum:", 12); + pm_buffer_append_format(output_buffer, " %" PRIu8 "\n", cast->maximum); + } + + 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, "+-- number:", 11); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\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); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + 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, "+-- left:", 9); + 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->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + 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->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- requireds:", 14); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- optionals:", 14); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- rest:", 9); + if (cast->rest == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // posts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- posts:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- keywords:", 13); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- keyword_rest:", 17); + if (cast->keyword_rest == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->keyword_rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " nil\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); + + // ParenthesesNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParenthesesNodeFlags:", 25); + bool found = false; + if (cast->base.flags & PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multiple_statements", 20); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- expression:", 15); + 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; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- variable:", 13); + 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; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- locals:", 11); + 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, "+-- statements:", 15); + 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); + + // RangeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RangeFlags:", 15); + 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, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- left:", 9); + if (cast->left == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + if (cast->right == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + 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); + + // IntegerBaseFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- IntegerBaseFlags:", 21); + 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_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_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_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, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // numerator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- numerator:", 14); + const pm_integer_t *integer = &cast->numerator; + pm_buffer_append_byte(output_buffer, ' '); + pm_integer_string(output_buffer, integer); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // denominator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- denominator:", 16); + const pm_integer_t *integer = &cast->denominator; + pm_buffer_append_byte(output_buffer, ' '); + pm_integer_string(output_buffer, integer); + pm_buffer_append_byte(output_buffer, '\n'); + } + + 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); + + // RegularExpressionFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RegularExpressionFlags:", 27); + 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 (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- content_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + 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); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- name_loc:", 13); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + 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, "+-- expression:", 15); + 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; + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // rescue_expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rescue_expression:", 22); + 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, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // exceptions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- exceptions:", 15); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // reference + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- reference:", 14); + if (cast->reference == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->reference, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_keyword_loc:", 21); + pm_location_t *location = &cast->then_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // subsequent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- subsequent:", 15); + if (cast->subsequent == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->subsequent, 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); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\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, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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_SHAREABLE_CONSTANT_NODE: { + pm_shareable_constant_node_t *cast = (pm_shareable_constant_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ShareableConstantNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ShareableConstantNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ShareableConstantNodeFlags:", 31); + bool found = false; + if (cast->base.flags & PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " literal", 8); + found = true; + } + if (cast->base.flags & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " experimental_everything", 24); + found = true; + } + if (cast->base.flags & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " experimental_copy", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // write + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- write:", 10); + 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->write, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + 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, "+-- locals:", 11); + 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, "+-- class_keyword_loc:", 22); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- expression:", 15); + 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; + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // StringFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- StringFlags:", 16); + bool found = false; + if (cast->base.flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + 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 (cast->base.flags & PM_STRING_FLAGS_MUTABLE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " mutable", 8); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // filepath + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- filepath:", 13); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->filepath), pm_string_length(&cast->filepath), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- operator_loc:", 17); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- expression:", 15); + if (cast->expression == NULL) { + pm_buffer_append_string(output_buffer, " nil\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, "+-- body:", 9); + 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); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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); + + // StringFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- StringFlags:", 16); + bool found = false; + if (cast->base.flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + 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 (cast->base.flags & PM_STRING_FLAGS_MUTABLE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " mutable", 8); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- content_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == NULL) { + pm_buffer_append_string(output_buffer, " nil\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); + + // SymbolFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- SymbolFlags:", 16); + bool found = false; + if (cast->base.flags & PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // value_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value_loc:", 14); + pm_location_t *location = &cast->value_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- names:", 10); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + 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, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + 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->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_keyword_loc:", 21); + pm_location_t *location = &cast->then_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // else_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- else_clause:", 16); + if (cast->else_clause == NULL) { + pm_buffer_append_string(output_buffer, " nil\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->else_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); + + // LoopFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- LoopFlags:", 14); + 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, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // do_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- do_keyword_loc:", 19); + pm_location_t *location = &cast->do_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + 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->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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_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, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // conditions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- conditions:", 15); + 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ", 4); + pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->conditions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_keyword_loc:", 21); + pm_location_t *location = &cast->then_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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); + + // LoopFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- LoopFlags:", 14); + 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, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // do_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- do_keyword_loc:", 19); + pm_location_t *location = &cast->do_keyword_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + 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->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == NULL) { + pm_buffer_append_string(output_buffer, " nil\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_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); + + // EncodingFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- EncodingFlags:", 18); + bool found = false; + if (cast->base.flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- content_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + 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, "+-- keyword_loc:", 16); + 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); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == NULL) { + pm_buffer_append_string(output_buffer, " nil\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; + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + if (location->start == NULL) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_location(output_buffer, parser, location); + pm_buffer_append_string(output_buffer, " = \"", 4); + pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY); + 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); +} + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/src/prism.c b/vendor/mruby-compiler2/lib/prism/src/prism.c new file mode 100644 index 0000000..c4cab8f --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/prism.c @@ -0,0 +1,23184 @@ +#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 + +// 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 *) xmalloc(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)); + size_t index = 7; + + // Now we'll add the terminator to the list of breakpoints. If the + // terminator is not already a NULL byte, add it to the list. + if (terminator != '\0') { + 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; + } + + parser->explicit_encoding = NULL; + 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, "\r\n\\#\0\0", sizeof(lex_mode.as.regexp.breakpoints)); + size_t index = 4; + + // First we'll add the terminator. + if (terminator != '\0') { + breakpoints[index++] = terminator; + } + + // Next, if there is an incrementor, then we'll check for that as well. + if (incrementor != '\0') { + breakpoints[index++] = incrementor; + } + + parser->explicit_encoding = NULL; + 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, "\r\n\\\0\0\0", sizeof(lex_mode.as.string.breakpoints)); + size_t index = 3; + + // Now add in the terminator. If the terminator is not already a NULL byte, + // then we'll add it. + if (terminator != '\0') { + 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; + } + + parser->explicit_encoding = NULL; + 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; + xfree(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(const 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; +} + +#ifndef PM_DEBUG_LOGGING +/** + * Debugging logging will print additional information to stdout whenever the + * lexer state changes. + */ +#define PM_DEBUG_LOGGING 0 +#endif + +#if PM_DEBUG_LOGGING +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"); +} + +static 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 + +/******************************************************************************/ +/* Command-line macro helpers */ +/******************************************************************************/ + +/** True if the parser has the given command-line option. */ +#define PM_PARSER_COMMAND_LINE_OPTION(parser, option) ((parser)->command_line & (option)) + +/** True if the -a command line option was given. */ +#define PM_PARSER_COMMAND_LINE_OPTION_A(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_A) + +/** True if the -e command line option was given. */ +#define PM_PARSER_COMMAND_LINE_OPTION_E(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_E) + +/** True if the -l command line option was given. */ +#define PM_PARSER_COMMAND_LINE_OPTION_L(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_L) + +/** True if the -n command line option was given. */ +#define PM_PARSER_COMMAND_LINE_OPTION_N(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_N) + +/** True if the -p command line option was given. */ +#define PM_PARSER_COMMAND_LINE_OPTION_P(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_P) + +/** True if the -x command line option was given. */ +#define PM_PARSER_COMMAND_LINE_OPTION_X(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_X) + +/******************************************************************************/ +/* 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_PARSER_ERR_FORMAT(parser, (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_PARSER_ERR_FORMAT(parser, (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 + * given node and a format string, and add on the content of the node. + */ +#define PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, diag_id) \ + PM_PARSER_ERR_NODE_FORMAT(parser, node, diag_id, (int) ((node)->location.end - (node)->location.start), (const char *) (node)->location.start) + +/** + * 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_PARSER_ERR_FORMAT(parser, (token).start, (token).end, diag_id, __VA_ARGS__) + +/** + * Append an error to the list of errors on the parser using the location of the + * given token and a format string, and add on the content of the token. + */ +#define PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, diag_id) \ + PM_PARSER_ERR_TOKEN_FORMAT(parser, token, diag_id, (int) ((token).end - (token).start), (const char *) (token).start) + +/** + * 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) { + 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); +} + +/** + * Append a warning to the list of warnings on the parser using the location of + * the given node. + */ +static inline void +pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) { + pm_parser_warn(parser, node->location.start, node->location.end, diag_id); +} + +/** + * Append a warning to the list of warnings on the parser using a format string. + */ +#define PM_PARSER_WARN_FORMAT(parser, start, end, diag_id, ...) \ + pm_diagnostic_list_append_format(&parser->warning_list, start, end, diag_id, __VA_ARGS__) + +/** + * Append a warning to the list of warnings on the parser using the location of + * the given token and a format string. + */ +#define PM_PARSER_WARN_TOKEN_FORMAT(parser, token, diag_id, ...) \ + PM_PARSER_WARN_FORMAT(parser, (token).start, (token).end, diag_id, __VA_ARGS__) + +/** + * Append a warning to the list of warnings on the parser using the location of + * the given token and a format string, and add on the content of the token. + */ +#define PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, token, diag_id) \ + PM_PARSER_WARN_TOKEN_FORMAT(parser, token, diag_id, (int) ((token).end - (token).start), (const char *) (token).start) + +/** + * Append a warning to the list of warnings on the parser using the location of + * the given node and a format string. + */ +#define PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, ...) \ + PM_PARSER_WARN_FORMAT(parser, (node)->location.start, (node)->location.end, diag_id, __VA_ARGS__) + +/** + * Add an error for an expected heredoc terminator. This is a special function + * only because it grabs its location off of a lex mode instead of a node or a + * token. + */ +static void +pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) { + PM_PARSER_ERR_FORMAT( + parser, + ident_start, + ident_start + ident_length, + PM_ERR_HEREDOC_TERM, + (int) ident_length, + (const char *) ident_start + ); +} + +/******************************************************************************/ +/* 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 *) xmalloc(sizeof(pm_scope_t)); + if (scope == NULL) return false; + + *scope = (pm_scope_t) { + .previous = parser->current_scope, + .locals = { 0 }, + .parameters = PM_SCOPE_PARAMETERS_NONE, + .implicit_parameters = { 0 }, + .shareable_constant = parser->current_scope == NULL ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant, + .closed = closed + }; + + parser->current_scope = scope; + return true; +} + +/** + * Determine if the current scope is at the top level. This means it is either + * the top-level scope or it is open to the top-level. + */ +static bool +pm_parser_scope_toplevel_p(pm_parser_t *parser) { + pm_scope_t *scope = parser->current_scope; + + do { + if (scope->previous == NULL) return true; + if (scope->closed) return false; + } while ((scope = scope->previous) != NULL); + + assert(false && "unreachable"); + return true; +} + +/** + * Retrieve the scope at the given depth. + */ +static pm_scope_t * +pm_parser_scope_find(pm_parser_t *parser, uint32_t depth) { + pm_scope_t *scope = parser->current_scope; + + while (depth-- > 0) { + assert(scope != NULL); + scope = scope->previous; + } + + return scope; +} + +typedef enum { + PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS, + PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT, + PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL +} pm_scope_forwarding_param_check_result_t; + +static pm_scope_forwarding_param_check_result_t +pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const uint8_t mask) { + pm_scope_t *scope = parser->current_scope; + bool conflict = false; + + while (scope != NULL) { + if (scope->parameters & mask) { + if (scope->closed) { + if (conflict) { + return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT; + } else { + return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS; + } + } + + conflict = true; + } + + if (scope->closed) break; + scope = scope->previous; + } + + return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL; +} + +static void +pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) { + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_AMPERSAND); + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND); + break; + } +} + +static void +pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) { + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR); + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR); + break; + } +} + +static void +pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t *token) { + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_ALL)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + // This shouldn't happen, because ... is not allowed in the + // declaration of blocks. If we get here, we assume we already have + // an error for this. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); + break; + } +} + +static void +pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) { + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR_STAR); + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR); + break; + } +} + +/** + * Get the current state of constant shareability. + */ +static inline pm_shareable_constant_value_t +pm_parser_scope_shareable_constant_get(pm_parser_t *parser) { + return parser->current_scope->shareable_constant; +} + +/** + * Set the current state of constant shareability. We'll set it on all of the + * open scopes so that reads are quick. + */ +static void +pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constant_value_t shareable_constant) { + pm_scope_t *scope = parser->current_scope; + + do { + scope->shareable_constant = shareable_constant; + } while (!scope->closed && (scope = scope->previous) != NULL); +} + +/******************************************************************************/ +/* Local variable-related functions */ +/******************************************************************************/ + +/** + * The point at which the set of locals switches from being a list to a hash. + */ +#define PM_LOCALS_HASH_THRESHOLD 9 + +static void +pm_locals_free(pm_locals_t *locals) { + if (locals->capacity > 0) { + xfree(locals->locals); + } +} + +/** + * Use as simple and fast a hash function as we can that still properly mixes + * the bits. + */ +static uint32_t +pm_locals_hash(pm_constant_id_t name) { + name = ((name >> 16) ^ name) * 0x45d9f3b; + name = ((name >> 16) ^ name) * 0x45d9f3b; + name = (name >> 16) ^ name; + return name; +} + +/** + * Resize the locals list to be twice its current size. If the next capacity is + * above the threshold for switching to a hash, then we'll switch to a hash. + */ +static void +pm_locals_resize(pm_locals_t *locals) { + uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2); + assert(next_capacity > locals->capacity); + + pm_local_t *next_locals = xcalloc(next_capacity, sizeof(pm_local_t)); + if (next_locals == NULL) abort(); + + if (next_capacity < PM_LOCALS_HASH_THRESHOLD) { + if (locals->size > 0) { + memcpy(next_locals, locals->locals, locals->size * sizeof(pm_local_t)); + } + } else { + // If we just switched from a list to a hash, then we need to fill in + // the hash values of all of the locals. + bool hash_needed = (locals->capacity <= PM_LOCALS_HASH_THRESHOLD); + uint32_t mask = next_capacity - 1; + + for (uint32_t index = 0; index < locals->capacity; index++) { + pm_local_t *local = &locals->locals[index]; + + if (local->name != PM_CONSTANT_ID_UNSET) { + if (hash_needed) local->hash = pm_locals_hash(local->name); + + uint32_t hash = local->hash; + while (next_locals[hash & mask].name != PM_CONSTANT_ID_UNSET) hash++; + next_locals[hash & mask] = *local; + } + } + } + + pm_locals_free(locals); + locals->locals = next_locals; + locals->capacity = next_capacity; +} + +/** + * Add a new local to the set of locals. This will automatically rehash the + * locals if the size is greater than 3/4 of the capacity. + * + * @param locals The set of locals to add to. + * @param name The name of the local. + * @param start The source location that represents the start of the local. This + * is used for the location of the warning in case this local is not read. + * @param end The source location that represents the end of the local. This is + * used for the location of the warning in case this local is not read. + * @param reads The initial number of reads for this local. Usually this is set + * to 0, but for some locals (like parameters) we want to initialize it with + * 1 so that we never warn on unused parameters. + * @return True if the local was added, and false if the local already exists. + */ +static bool +pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, const uint8_t *start, const uint8_t *end, uint32_t reads) { + if (locals->size >= (locals->capacity / 4 * 3)) { + pm_locals_resize(locals); + } + + if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) { + for (uint32_t index = 0; index < locals->capacity; index++) { + pm_local_t *local = &locals->locals[index]; + + if (local->name == PM_CONSTANT_ID_UNSET) { + *local = (pm_local_t) { + .name = name, + .location = { .start = start, .end = end }, + .index = locals->size++, + .reads = reads, + .hash = 0 + }; + return true; + } else if (local->name == name) { + return false; + } + } + } else { + uint32_t mask = locals->capacity - 1; + uint32_t hash = pm_locals_hash(name); + uint32_t initial_hash = hash; + + do { + pm_local_t *local = &locals->locals[hash & mask]; + + if (local->name == PM_CONSTANT_ID_UNSET) { + *local = (pm_local_t) { + .name = name, + .location = { .start = start, .end = end }, + .index = locals->size++, + .reads = reads, + .hash = initial_hash + }; + return true; + } else if (local->name == name) { + return false; + } else { + hash++; + } + } while ((hash & mask) != initial_hash); + } + + assert(false && "unreachable"); + return true; +} + +/** + * Finds the index of a local variable in the locals set. If it is not found, + * this returns UINT32_MAX. + */ +static uint32_t +pm_locals_find(pm_locals_t *locals, pm_constant_id_t name) { + if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) { + for (uint32_t index = 0; index < locals->size; index++) { + pm_local_t *local = &locals->locals[index]; + if (local->name == name) return index; + } + } else { + uint32_t mask = locals->capacity - 1; + uint32_t hash = pm_locals_hash(name); + uint32_t initial_hash = hash & mask; + + do { + pm_local_t *local = &locals->locals[hash & mask]; + + if (local->name == PM_CONSTANT_ID_UNSET) { + return UINT32_MAX; + } else if (local->name == name) { + return hash & mask; + } else { + hash++; + } + } while ((hash & mask) != initial_hash); + } + + return UINT32_MAX; +} + +/** + * Called when a variable is read in a certain lexical context. Tracks the read + * by adding to the reads count. + */ +static void +pm_locals_read(pm_locals_t *locals, pm_constant_id_t name) { + uint32_t index = pm_locals_find(locals, name); + assert(index != UINT32_MAX); + + pm_local_t *local = &locals->locals[index]; + assert(local->reads < UINT32_MAX); + + local->reads++; +} + +/** + * Called when a variable read is transformed into a variable write, because a + * write operator is found after the variable name. + */ +static void +pm_locals_unread(pm_locals_t *locals, pm_constant_id_t name) { + uint32_t index = pm_locals_find(locals, name); + assert(index != UINT32_MAX); + + pm_local_t *local = &locals->locals[index]; + assert(local->reads > 0); + + local->reads--; +} + +/** + * Returns the current number of reads for a local variable. + */ +static uint32_t +pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) { + uint32_t index = pm_locals_find(locals, name); + assert(index != UINT32_MAX); + + return locals->locals[index].reads; +} + +/** + * Write out the locals into the given list of constant ids in the correct + * order. This is used to set the list of locals on the nodes in the tree once + * we're sure no additional locals will be added to the set. + * + * This function is also responsible for warning when a local variable has been + * written but not read in certain contexts. + */ +static void +pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) { + pm_constant_id_list_init_capacity(list, locals->size); + + // If we're still below the threshold for switching to a hash, then we only + // need to loop over the locals until we hit the size because the locals are + // stored in a list. + uint32_t capacity = locals->capacity < PM_LOCALS_HASH_THRESHOLD ? locals->size : locals->capacity; + + // We will only warn for unused variables if we're not at the top level, or + // if we're parsing a file outside of eval or -e. + bool warn_unused = !toplevel || (!parser->parsing_eval && !PM_PARSER_COMMAND_LINE_OPTION_E(parser)); + + for (uint32_t index = 0; index < capacity; index++) { + pm_local_t *local = &locals->locals[index]; + + if (local->name != PM_CONSTANT_ID_UNSET) { + pm_constant_id_list_insert(list, (size_t) local->index, local->name); + + if (warn_unused && local->reads == 0 && ((parser->start_line >= 0) || (pm_newline_list_line(&parser->newline_list, local->location.start, parser->start_line) >= 0))) { + pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name); + + if (constant->length >= 1 && *constant->start != '_') { + PM_PARSER_WARN_FORMAT( + parser, + local->location.start, + local->location.end, + PM_WARN_UNUSED_LOCAL_VARIABLE, + (int) constant->length, + (const char *) constant->start + ); + } + } + } + } +} + +/******************************************************************************/ +/* 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, 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_parser_t *parser, 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; + + if (cast->ensure_clause != NULL) { + if (cast->rescue_clause != NULL) { + pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->rescue_clause); + if (vn != NULL) return vn; + } + + if (cast->statements != NULL) { + pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->statements); + if (vn != NULL) return vn; + } + + node = (pm_node_t *) cast->ensure_clause; + } else if (cast->rescue_clause != NULL) { + if (cast->statements == NULL) return NULL; + + pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->statements); + if (vn == NULL) return NULL; + if (void_node == NULL) void_node = vn; + + for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) { + pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) rescue_clause->statements); + if (vn == NULL) { + void_node = NULL; + break; + } + if (void_node == NULL) { + void_node = vn; + } + } + + if (cast->else_clause != NULL) { + node = (pm_node_t *) cast->else_clause; + } else { + return void_node; + } + } else { + node = (pm_node_t *) cast->statements; + } + + break; + } + case PM_ENSURE_NODE: { + pm_ensure_node_t *cast = (pm_ensure_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->subsequent == NULL) { + return NULL; + } + pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->statements); + if (vn == NULL) { + return NULL; + } + if (void_node == NULL) { + void_node = vn; + } + node = cast->subsequent; + break; + } + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + if (cast->statements == NULL || cast->else_clause == NULL) { + return NULL; + } + pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->statements); + if (vn == NULL) { + return NULL; + } + if (void_node == NULL) { + void_node = vn; + } + node = (pm_node_t *) cast->else_clause; + 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; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; + + pm_scope_t *scope = parser->current_scope; + for (uint32_t depth = 0; depth < cast->depth; depth++) scope = scope->previous; + + pm_locals_read(&scope->locals, cast->name); + return NULL; + } + 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(parser, node); + if (void_node != NULL) { + pm_parser_err_node(parser, void_node, PM_ERR_VOID_EXPRESSION); + } +} + +/** + * Warn if the given node is a "void" statement. + */ +static void +pm_void_statement_check(pm_parser_t *parser, const pm_node_t *node) { + const char *type = NULL; + int length = 0; + + switch (PM_NODE_TYPE(node)) { + case PM_BACK_REFERENCE_READ_NODE: + case PM_CLASS_VARIABLE_READ_NODE: + case PM_GLOBAL_VARIABLE_READ_NODE: + case PM_INSTANCE_VARIABLE_READ_NODE: + case PM_LOCAL_VARIABLE_READ_NODE: + case PM_NUMBERED_REFERENCE_READ_NODE: + type = "a variable"; + length = 10; + break; + case PM_CALL_NODE: { + const pm_call_node_t *cast = (const pm_call_node_t *) node; + if (cast->call_operator_loc.start != NULL || cast->message_loc.start == NULL) break; + + const pm_constant_t *message = pm_constant_pool_id_to_constant(&parser->constant_pool, cast->name); + switch (message->length) { + case 1: + switch (message->start[0]) { + case '+': + case '-': + case '*': + case '/': + case '%': + case '|': + case '^': + case '&': + case '>': + case '<': + type = (const char *) message->start; + length = 1; + break; + } + break; + case 2: + switch (message->start[1]) { + case '=': + if (message->start[0] == '<' || message->start[0] == '>' || message->start[0] == '!' || message->start[0] == '=') { + type = (const char *) message->start; + length = 2; + } + break; + case '@': + if (message->start[0] == '+' || message->start[0] == '-') { + type = (const char *) message->start; + length = 2; + } + break; + case '*': + if (message->start[0] == '*') { + type = (const char *) message->start; + length = 2; + } + break; + } + break; + case 3: + if (memcmp(message->start, "<=>", 3) == 0) { + type = "<=>"; + length = 3; + } + break; + } + + break; + } + case PM_CONSTANT_PATH_NODE: + type = "::"; + length = 2; + break; + case PM_CONSTANT_READ_NODE: + type = "a constant"; + length = 10; + break; + case PM_DEFINED_NODE: + type = "defined?"; + length = 8; + break; + case PM_FALSE_NODE: + type = "false"; + length = 5; + break; + case PM_FLOAT_NODE: + case PM_IMAGINARY_NODE: + case PM_INTEGER_NODE: + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + case PM_INTERPOLATED_STRING_NODE: + case PM_RATIONAL_NODE: + case PM_REGULAR_EXPRESSION_NODE: + case PM_SOURCE_ENCODING_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_STRING_NODE: + case PM_SYMBOL_NODE: + type = "a literal"; + length = 9; + break; + case PM_NIL_NODE: + type = "nil"; + length = 3; + break; + case PM_RANGE_NODE: { + const pm_range_node_t *cast = (const pm_range_node_t *) node; + + if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) { + type = "..."; + length = 3; + } else { + type = ".."; + length = 2; + } + + break; + } + case PM_SELF_NODE: + type = "self"; + length = 4; + break; + case PM_TRUE_NODE: + type = "true"; + length = 4; + break; + default: + break; + } + + if (type != NULL) { + PM_PARSER_WARN_NODE_FORMAT(parser, node, PM_WARN_VOID_STATEMENT, length, type); + } +} + +/** + * Warn if any of the statements that are not the last statement in the list are + * a "void" statement. + */ +static void +pm_void_statements_check(pm_parser_t *parser, const pm_statements_node_t *node, bool last_value) { + assert(node->body.size > 0); + const size_t size = node->body.size - (last_value ? 1 : 0); + for (size_t index = 0; index < size; index++) { + pm_void_statement_check(parser, node->body.nodes[index]); + } +} + +/** + * When we're handling the predicate of a conditional, we need to know our + * context in order to determine the kind of warning we should deliver to the + * user. + */ +typedef enum { + PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL, + PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP, + PM_CONDITIONAL_PREDICATE_TYPE_NOT +} pm_conditional_predicate_type_t; + +/** + * Add a warning to the parser if the predicate of a conditional is a literal. + */ +static void +pm_parser_warn_conditional_predicate_literal(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type, pm_diagnostic_id_t diag_id, const char *prefix) { + switch (type) { + case PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL: + PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "condition"); + break; + case PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP: + PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "flip-flop"); + break; + case PM_CONDITIONAL_PREDICATE_TYPE_NOT: + break; + } +} + +/** + * Return true if the value being written within the predicate of a conditional + * is a literal value. + */ +static bool +pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_ARRAY_NODE: { + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true; + + const pm_array_node_t *cast = (const pm_array_node_t *) node; + for (size_t index = 0; index < cast->elements.size; index++) { + if (!pm_conditional_predicate_warn_write_literal_p(cast->elements.nodes[index])) return false; + } + + return true; + } + case PM_HASH_NODE: { + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true; + + const pm_hash_node_t *cast = (const pm_hash_node_t *) node; + for (size_t index = 0; index < cast->elements.size; index++) { + const pm_node_t *element = cast->elements.nodes[index]; + if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return false; + + const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element; + if (!pm_conditional_predicate_warn_write_literal_p(assoc->key) || !pm_conditional_predicate_warn_write_literal_p(assoc->value)) return false; + } + + return true; + } + case PM_FALSE_NODE: + case PM_FLOAT_NODE: + case PM_IMAGINARY_NODE: + case PM_INTEGER_NODE: + case PM_NIL_NODE: + case PM_RATIONAL_NODE: + case PM_REGULAR_EXPRESSION_NODE: + case PM_SOURCE_ENCODING_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_STRING_NODE: + case PM_SYMBOL_NODE: + case PM_TRUE_NODE: + return true; + default: + return false; + } +} + +/** + * Add a warning to the parser if the value that is being written inside of a + * predicate to a conditional is a literal. + */ +static inline void +pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) { + if (pm_conditional_predicate_warn_write_literal_p(node)) { + pm_parser_warn_node(parser, node, parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL); + } +} + +/** + * 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 + * + * We also want to warn the user if they're using a static literal as a + * predicate or writing a static literal as the predicate. + */ +static void +pm_conditional_predicate(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type) { + switch (PM_NODE_TYPE(node)) { + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + break; + } + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + 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(parser, statements->body.nodes[0], type); + } + + break; + } + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + if (cast->statements != NULL) { + pm_statements_node_t *statements = cast->statements; + if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type); + } + break; + } + case PM_RANGE_NODE: { + pm_range_node_t *cast = (pm_range_node_t *) node; + + if (cast->left != NULL) pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP); + if (cast->right != NULL) pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP); + + // 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; + + if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) { + pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "regex "); + } + + 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; + + if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) { + pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "regex "); + } + + break; + case PM_INTEGER_NODE: + if (type == PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP) { + if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) { + pm_parser_warn_node(parser, node, PM_WARN_INTEGER_IN_FLIP_FLOP); + } + } else { + pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, ""); + } + break; + case PM_STRING_NODE: + case PM_SOURCE_FILE_NODE: + case PM_INTERPOLATED_STRING_NODE: + pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "string "); + break; + case PM_SYMBOL_NODE: + case PM_INTERPOLATED_SYMBOL_NODE: + pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "symbol "); + break; + case PM_SOURCE_LINE_NODE: + case PM_SOURCE_ENCODING_NODE: + case PM_FLOAT_NODE: + case PM_RATIONAL_NODE: + case PM_IMAGINARY_NODE: + pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, ""); + break; + case PM_CLASS_VARIABLE_WRITE_NODE: + pm_conditional_predicate_warn_write_literal(parser, ((pm_class_variable_write_node_t *) node)->value); + break; + case PM_CONSTANT_WRITE_NODE: + pm_conditional_predicate_warn_write_literal(parser, ((pm_constant_write_node_t *) node)->value); + break; + case PM_GLOBAL_VARIABLE_WRITE_NODE: + pm_conditional_predicate_warn_write_literal(parser, ((pm_global_variable_write_node_t *) node)->value); + break; + case PM_INSTANCE_VARIABLE_WRITE_NODE: + pm_conditional_predicate_warn_write_literal(parser, ((pm_instance_variable_write_node_t *) node)->value); + break; + case PM_LOCAL_VARIABLE_WRITE_NODE: + pm_conditional_predicate_warn_write_literal(parser, ((pm_local_variable_write_node_t *) node)->value); + break; + case PM_MULTI_WRITE_NODE: + pm_conditional_predicate_warn_write_literal(parser, ((pm_multi_write_node_t *) node)->value); + 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, this happens 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(parser); + */ +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; + + /** The flag indicating whether this arguments list has forwarding argument. */ + bool has_forwarding; +} 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); +} + +/******************************************************************************/ +/* 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(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) { + if (n <= 0) return 0; + + if (parser->encoding_changed) { + size_t width; + + if ((width = parser->encoding->alpha_char(b, n)) != 0) { + return width; + } else if (*b == '_') { + return 1; + } else if (*b >= 0x80) { + return parser->encoding->char_width(b, n); + } else { + return 0; + } + } else if (*b < 0x80) { + return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT ? 1 : 0) || (*b == '_'); + } else { + return pm_encoding_utf_8_char_width(b, n); + } +} + +/** + * 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, ptrdiff_t n) { + if (n <= 0) { + return 0; + } else if (*b < 0x80) { + return (*b == '_') || (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT ? 1 : 0); + } else { + return pm_encoding_utf_8_char_width(b, n); + } +} + +/** + * 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(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) { + if (n <= 0) { + return 0; + } else if (parser->encoding_changed) { + size_t width; + + if ((width = parser->encoding->alnum_char(b, n)) != 0) { + return width; + } else if (*b == '_') { + return 1; + } else if (*b >= 0x80) { + return parser->encoding->char_width(b, n); + } else { + return 0; + } + } else { + return char_is_identifier_utf8(b, n); + } +} + +// 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_BRACKET_LEFT_RIGHT_EQUAL) || + ((token->type == PM_TOKEN_IDENTIFIER) && + (token->end - token->start >= 2) && + (token->end[-1] == '=')) + ); +} + +/** + * Returns true if the given local variable is a keyword. + */ +static bool +pm_local_is_keyword(const char *source, size_t length) { +#define KEYWORD(name) if (memcmp(source, name, length) == 0) return true + + switch (length) { + case 2: + switch (source[0]) { + case 'd': KEYWORD("do"); return false; + case 'i': KEYWORD("if"); KEYWORD("in"); return false; + case 'o': KEYWORD("or"); return false; + default: return false; + } + case 3: + switch (source[0]) { + case 'a': KEYWORD("and"); return false; + case 'd': KEYWORD("def"); return false; + case 'e': KEYWORD("end"); return false; + case 'f': KEYWORD("for"); return false; + case 'n': KEYWORD("nil"); KEYWORD("not"); return false; + default: return false; + } + case 4: + switch (source[0]) { + case 'c': KEYWORD("case"); return false; + case 'e': KEYWORD("else"); return false; + case 'n': KEYWORD("next"); return false; + case 'r': KEYWORD("redo"); return false; + case 's': KEYWORD("self"); return false; + case 't': KEYWORD("then"); KEYWORD("true"); return false; + case 'w': KEYWORD("when"); return false; + default: return false; + } + case 5: + switch (source[0]) { + case 'a': KEYWORD("alias"); return false; + case 'b': KEYWORD("begin"); KEYWORD("break"); return false; + case 'c': KEYWORD("class"); return false; + case 'e': KEYWORD("elsif"); return false; + case 'f': KEYWORD("false"); return false; + case 'r': KEYWORD("retry"); return false; + case 's': KEYWORD("super"); return false; + case 'u': KEYWORD("undef"); KEYWORD("until"); return false; + case 'w': KEYWORD("while"); return false; + case 'y': KEYWORD("yield"); return false; + default: return false; + } + case 6: + switch (source[0]) { + case 'e': KEYWORD("ensure"); return false; + case 'm': KEYWORD("module"); return false; + case 'r': KEYWORD("rescue"); KEYWORD("return"); return false; + case 'u': KEYWORD("unless"); return false; + default: return false; + } + case 8: + KEYWORD("__LINE__"); + KEYWORD("__FILE__"); + return false; + case 12: + KEYWORD("__ENCODING__"); + return false; + default: + return false; + } + +#undef KEYWORD +} + +/******************************************************************************/ +/* Node flag handling functions */ +/******************************************************************************/ + +/** + * Set the given flag on the given node. + */ +static inline void +pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) { + node->flags |= flag; +} + +/** + * Remove the given flag from the given node. + */ +static inline void +pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) { + node->flags &= (pm_node_flags_t) ~flag; +} + +/** + * Set the repeated parameter flag on the given node. + */ +static inline void +pm_node_flag_set_repeated_parameter(pm_node_t *node) { + assert(PM_NODE_TYPE(node) == PM_BLOCK_LOCAL_VARIABLE_NODE || + PM_NODE_TYPE(node) == PM_BLOCK_PARAMETER_NODE || + PM_NODE_TYPE(node) == PM_KEYWORD_REST_PARAMETER_NODE || + PM_NODE_TYPE(node) == PM_OPTIONAL_KEYWORD_PARAMETER_NODE || + PM_NODE_TYPE(node) == PM_OPTIONAL_PARAMETER_NODE || + PM_NODE_TYPE(node) == PM_REQUIRED_KEYWORD_PARAMETER_NODE || + PM_NODE_TYPE(node) == PM_REQUIRED_PARAMETER_NODE || + PM_NODE_TYPE(node) == PM_REST_PARAMETER_NODE); + + pm_node_flag_set(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER); +} + +/******************************************************************************/ +/* Node creation functions */ +/******************************************************************************/ + +/** + * 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(pm_parser_t *parser, const pm_token_t *closing) { + pm_node_flags_t flags = 0; + + if (closing->type == PM_TOKEN_REGEXP_END) { + pm_buffer_t unknown_flags = { 0 }; + + 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: pm_buffer_append_byte(&unknown_flags, *flag); + } + } + + size_t unknown_flags_length = pm_buffer_length(&unknown_flags); + if (unknown_flags_length != 0) { + const char *word = unknown_flags_length >= 2 ? "options" : "option"; + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_REGEXP_UNKNOWN_OPTIONS, word, unknown_flags_length, pm_buffer_value(&unknown_flags)); + } + pm_buffer_free(&unknown_flags); + } + + 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_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline); + +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_node_alloc(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { + void *memory = xcalloc(1, size); + if (memory == NULL) { + fprintf(stderr, "Failed to allocate %d bytes\n", (int) size); + abort(); + } + return memory; +} + +#define PM_NODE_ALLOC(parser, type) (type *) pm_node_alloc(parser, sizeof(type)) +#define PM_NODE_IDENTIFY(parser) (++parser->node_id) + +/** + * 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_NODE_ALLOC(parser, pm_missing_node_t); + + *node = (pm_missing_node_t) {{ + .type = PM_MISSING_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_alias_global_variable_node_t); + + *node = (pm_alias_global_variable_node_t) { + { + .type = PM_ALIAS_GLOBAL_VARIABLE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_alias_method_node_t); + + *node = (pm_alias_method_node_t) { + { + .type = PM_ALIAS_METHOD_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_alternation_pattern_node_t); + + *node = (pm_alternation_pattern_node_t) { + { + .type = PM_ALTERNATION_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_and_node_t); + + *node = (pm_and_node_t) { + { + .type = PM_AND_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_arguments_node_t); + + *node = (pm_arguments_node_t) { + { + .type = PM_ARGUMENTS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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); + + if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) { + if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) { + pm_node_flag_set((pm_node_t *) node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS); + } else { + pm_node_flag_set((pm_node_t *) node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT); + } + } +} + +/** + * 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_NODE_ALLOC(parser, pm_array_node_t); + + *node = (pm_array_node_t) { + { + .type = PM_ARRAY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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; +} + +/** + * 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) || !PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL)) { + pm_node_flag_unset((pm_node_t *)node, PM_NODE_FLAG_STATIC_LITERAL); + } + + if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) { + pm_node_flag_set((pm_node_t *)node, 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_NODE_ALLOC(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + { + .type = PM_ARRAY_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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; + pm_node_t *child; + + PM_NODE_LIST_FOREACH(nodes, index, child) { + 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_NODE_ALLOC(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + { + .type = PM_ARRAY_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + { + .type = PM_ARRAY_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + { + .type = PM_ARRAY_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_assoc_node_t); + const uint8_t *end; + + if (value != NULL && value->location.end > key->location.end) { + end = value->location.end; + } else if (operator->type != PM_TOKEN_NOT_PROVIDED) { + end = operator->end; + } else { + end = key->location.end; + } + + // Hash string keys will be frozen, so we can mark them as frozen here so + // that the compiler picks them up and also when we check for static literal + // on the keys it gets factored in. + if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) { + key->flags |= PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL; + } + + // 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 ( + !PM_NODE_TYPE_P(key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(key, PM_HASH_NODE) && !PM_NODE_TYPE_P(key, PM_RANGE_NODE) && + 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, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_assoc_splat_node_t); + + *node = (pm_assoc_splat_node_t) { + { + .type = PM_ASSOC_SPLAT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_back_reference_read_node_t); + + *node = (pm_back_reference_read_node_t) { + { + .type = PM_BACK_REFERENCE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_begin_node_t); + + *node = (pm_begin_node_t) { + { + .type = PM_BEGIN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_block_argument_node_t); + + *node = (pm_block_argument_node_t) { + { + .type = PM_BLOCK_ARGUMENT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_node_t *parameters, pm_node_t *body, const pm_token_t *closing) { + pm_block_node_t *node = PM_NODE_ALLOC(parser, pm_block_node_t); + + *node = (pm_block_node_t) { + { + .type = PM_BLOCK_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { .start = opening->start, .end = closing->end }, + }, + .locals = *locals, + .parameters = parameters, + .body = body, + .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_NODE_ALLOC(parser, pm_block_parameter_node_t); + + *node = (pm_block_parameter_node_t) { + { + .type = PM_BLOCK_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(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, + .node_id = PM_NODE_IDENTIFY(parser), + .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) { + pm_block_local_variable_node_t *node = PM_NODE_ALLOC(parser, pm_block_local_variable_node_t); + + *node = (pm_block_local_variable_node_t) { + { + .type = PM_BLOCK_LOCAL_VARIABLE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_break_node_t); + + *node = (pm_break_node_t) { + { + .type = PM_BREAK_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = keyword->start, + .end = (arguments == NULL ? keyword->end : arguments->base.location.end) + }, + }, + .arguments = arguments, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) + }; + + return node; +} + +// There are certain flags that we want to use internally but don't want to +// expose because they are not relevant beyond parsing. Therefore we'll define +// them here and not define them in config.yml/a header file. +static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = 0x4; +static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = 0x40; +static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = 0x80; +static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = 0x100; + +/** + * 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_node_flags_t flags) { + pm_call_node_t *node = PM_NODE_ALLOC(parser, pm_call_node_t); + + *node = (pm_call_node_t) { + { + .type = PM_CALL_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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; +} + +/** + * Returns the value that the ignore visibility flag should be set to for the + * given receiver. + */ +static inline pm_node_flags_t +pm_call_node_ignore_visibility_flag(const pm_node_t *receiver) { + return PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY : 0; +} + +/** + * 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_node_flags_t flags = pm_call_node_ignore_visibility_flag(receiver); + if (arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_ARGUMENT_NODE)) { + flags |= PM_CALL_NODE_FLAGS_INDEX; + } + + pm_call_node_t *node = pm_call_node_create(parser, flags); + + 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_node_flags_t flags) { + pm_assert_value_expression(parser, receiver); + pm_assert_value_expression(parser, argument); + + pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver) | flags); + + 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, pm_call_node_ignore_visibility_flag(receiver)); + + 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) { + pm_node_flag_set((pm_node_t *)node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION); + } + + node->name = pm_parser_constant_id_token(parser, message); + return node; +} + +/** + * Allocate and initialize a new synthesized CallNode node from a call expression. + */ +static pm_call_node_t * +pm_call_node_call_synthesized_create(pm_parser_t *parser, pm_node_t *receiver, const char *message, pm_arguments_node_t *arguments) { + pm_call_node_t *node = pm_call_node_create(parser, 0); + node->base.location.start = parser->start; + node->base.location.end = parser->end; + + node->receiver = receiver; + node->call_operator_loc = (pm_location_t) { .start = NULL, .end = NULL }; + node->message_loc = (pm_location_t) { .start = NULL, .end = NULL }; + node->arguments = arguments; + + node->name = pm_parser_constant_id_constant(parser, message, strlen(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, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY); + + 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 synthesized call to a + * method name with the given arguments. + */ +static pm_call_node_t * +pm_call_node_fcall_synthesized_create(pm_parser_t *parser, pm_arguments_node_t *arguments, pm_constant_id_t name) { + pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY); + + node->base.location = PM_LOCATION_NULL_VALUE(parser); + node->arguments = arguments; + + node->name = name; + 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); + if (receiver != NULL) pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT); + + pm_call_node_t *node = pm_call_node_create(parser, receiver == NULL ? 0 : pm_call_node_ignore_visibility_flag(receiver)); + + node->base.location.start = message->start; + if (arguments->closing_loc.start != NULL) { + node->base.location.end = arguments->closing_loc.end; + } else { + assert(receiver != NULL); + 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, pm_call_node_ignore_visibility_flag(receiver)); + + 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) { + pm_node_flag_set((pm_node_t *)node, 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, pm_call_node_ignore_visibility_flag(receiver)); + + 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, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY); + + 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 can be used on the left-hand side of an + * operator assignment. + */ +static inline bool +pm_call_node_writable_p(const pm_parser_t *parser, const pm_call_node_t *node) { + return ( + (node->message_loc.start != NULL) && + (node->message_loc.end[-1] != '!') && + (node->message_loc.end[-1] != '?') && + char_is_identifier_start(parser, node->message_loc.start, parser->end - node->message_loc.start) && + (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 = xmalloc(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_NODE_ALLOC(parser, pm_call_and_write_node_t); + + *node = (pm_call_and_write_node_t) { + { + .type = PM_CALL_AND_WRITE_NODE, + .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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. + xfree(target); + + return node; +} + +/** + * Validate that index expressions do not have keywords or blocks if we are + * parsing as Ruby 3.4+. + */ +static void +pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) { + if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) { + if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) { + pm_node_t *node; + PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) { + if (PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)) { + pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_INDEX_KEYWORDS); + break; + } + } + } + + if (block != NULL) { + pm_parser_err_node(parser, block, PM_ERR_UNEXPECTED_INDEX_BLOCK); + } + } +} + +/** + * 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_NODE_ALLOC(parser, pm_index_and_write_node_t); + + pm_index_arguments_check(parser, target->arguments, target->block); + + assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); + *node = (pm_index_and_write_node_t) { + { + .type = PM_INDEX_AND_WRITE_NODE, + .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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 = (pm_block_argument_node_t *) 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. + xfree(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_NODE_ALLOC(parser, pm_call_operator_write_node_t); + + *node = (pm_call_operator_write_node_t) { + { + .type = PM_CALL_OPERATOR_WRITE_NODE, + .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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, + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .binary_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. + xfree(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_NODE_ALLOC(parser, pm_index_operator_write_node_t); + + pm_index_arguments_check(parser, target->arguments, target->block); + + assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); + *node = (pm_index_operator_write_node_t) { + { + .type = PM_INDEX_OPERATOR_WRITE_NODE, + .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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 = (pm_block_argument_node_t *) target->block, + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .binary_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. + xfree(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_NODE_ALLOC(parser, pm_call_or_write_node_t); + + *node = (pm_call_or_write_node_t) { + { + .type = PM_CALL_OR_WRITE_NODE, + .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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. + xfree(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_NODE_ALLOC(parser, pm_index_or_write_node_t); + + pm_index_arguments_check(parser, target->arguments, target->block); + + assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); + *node = (pm_index_or_write_node_t) { + { + .type = PM_INDEX_OR_WRITE_NODE, + .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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 = (pm_block_argument_node_t *) 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. + xfree(target); + + return node; +} + +/** + * Allocate and initialize a new CallTargetNode node from an existing call + * node. + */ +static pm_call_target_node_t * +pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { + pm_call_target_node_t *node = PM_NODE_ALLOC(parser, pm_call_target_node_t); + + *node = (pm_call_target_node_t) { + { + .type = PM_CALL_TARGET_NODE, + .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), + .location = target->base.location + }, + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .name = target->name, + .message_loc = target->message_loc + }; + + // 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. + xfree(target); + + return node; +} + +/** + * Allocate and initialize a new IndexTargetNode node from an existing call + * node. + */ +static pm_index_target_node_t * +pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { + pm_index_target_node_t *node = PM_NODE_ALLOC(parser, pm_index_target_node_t); + pm_node_flags_t flags = target->base.flags; + + pm_index_arguments_check(parser, target->arguments, target->block); + + assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); + *node = (pm_index_target_node_t) { + { + .type = PM_INDEX_TARGET_NODE, + .flags = flags | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = target->base.location + }, + .receiver = target->receiver, + .opening_loc = target->opening_loc, + .arguments = target->arguments, + .closing_loc = target->closing_loc, + .block = (pm_block_argument_node_t *) target->block, + }; + + // 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. + xfree(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_local_variable_target_node_t *target, const pm_token_t *operator) { + pm_capture_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_capture_pattern_node_t); + + *node = (pm_capture_pattern_node_t) { + { + .type = PM_CAPTURE_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = value->location.start, + .end = target->base.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_NODE_ALLOC(parser, pm_case_node_t); + + *node = (pm_case_node_t) { + { + .type = PM_CASE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = case_keyword->start, + .end = end_keyword->end + }, + }, + .predicate = predicate, + .else_clause = 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 else clause of a CaseNode node. + */ +static void +pm_case_node_else_clause_set(pm_case_node_t *node, pm_else_node_t *else_clause) { + node->else_clause = else_clause; + node->base.location.end = else_clause->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_NODE_ALLOC(parser, pm_case_match_node_t); + + *node = (pm_case_match_node_t) { + { + .type = PM_CASE_MATCH_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = case_keyword->start, + .end = end_keyword->end + }, + }, + .predicate = predicate, + .else_clause = 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 else clause of a CaseMatchNode node. + */ +static void +pm_case_match_node_else_clause_set(pm_case_match_node_t *node, pm_else_node_t *else_clause) { + node->else_clause = else_clause; + node->base.location.end = else_clause->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_NODE_ALLOC(parser, pm_class_node_t); + + *node = (pm_class_node_t) { + { + .type = PM_CLASS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_class_variable_and_write_node_t); + + *node = (pm_class_variable_and_write_node_t) { + { + .type = PM_CLASS_VARIABLE_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_class_variable_operator_write_node_t); + + *node = (pm_class_variable_operator_write_node_t) { + { + .type = PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_class_variable_or_write_node_t); + + *node = (pm_class_variable_or_write_node_t) { + { + .type = PM_CLASS_VARIABLE_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_class_variable_read_node_t); + + *node = (pm_class_variable_read_node_t) { + { + .type = PM_CLASS_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .name = pm_parser_constant_id_token(parser, token) + }; + + return node; +} + +/** + * True if the given node is an implicit array node on a write, as in: + * + * a = *b + * a = 1, 2, 3 + */ +static inline pm_node_flags_t +pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) { + if (PM_NODE_TYPE_P(node, PM_ARRAY_NODE) && ((const pm_array_node_t *) node)->opening_loc.start == NULL) { + return flags; + } + return 0; +} + +/** + * 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_NODE_ALLOC(parser, pm_class_variable_write_node_t); + + *node = (pm_class_variable_write_node_t) { + { + .type = PM_CLASS_VARIABLE_WRITE_NODE, + .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), + .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_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_NODE_ALLOC(parser, pm_constant_path_and_write_node_t); + + *node = (pm_constant_path_and_write_node_t) { + { + .type = PM_CONSTANT_PATH_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_constant_path_operator_write_node_t); + + *node = (pm_constant_path_operator_write_node_t) { + { + .type = PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_constant_path_or_write_node_t); + + *node = (pm_constant_path_or_write_node_t) { + { + .type = PM_CONSTANT_PATH_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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, const pm_token_t *name_token) { + pm_assert_value_expression(parser, parent); + pm_constant_path_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_node_t); + + pm_constant_id_t name = PM_CONSTANT_ID_UNSET; + if (name_token->type == PM_TOKEN_CONSTANT) { + name = pm_parser_constant_id_token(parser, name_token); + } + + *node = (pm_constant_path_node_t) { + { + .type = PM_CONSTANT_PATH_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = parent == NULL ? delimiter->start : parent->location.start, + .end = name_token->end + }, + }, + .parent = parent, + .name = name, + .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), + .name_loc = PM_LOCATION_TOKEN_VALUE(name_token) + }; + + 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_NODE_ALLOC(parser, pm_constant_path_write_node_t); + + *node = (pm_constant_path_write_node_t) { + { + .type = PM_CONSTANT_PATH_WRITE_NODE, + .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_constant_and_write_node_t); + + *node = (pm_constant_and_write_node_t) { + { + .type = PM_CONSTANT_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_constant_operator_write_node_t); + + *node = (pm_constant_operator_write_node_t) { + { + .type = PM_CONSTANT_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_constant_or_write_node_t); + + *node = (pm_constant_or_write_node_t) { + { + .type = PM_CONSTANT_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_constant_read_node_t); + + *node = (pm_constant_read_node_t) { + { + .type = PM_CONSTANT_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_constant_write_node_t); + + *node = (pm_constant_write_node_t) { + { + .type = PM_CONSTANT_WRITE_NODE, + .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), + .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; +} + +/** + * Check if the receiver of a `def` node is allowed. + */ +static void +pm_def_node_receiver_check(pm_parser_t *parser, const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_BEGIN_NODE: { + const pm_begin_node_t *cast = (pm_begin_node_t *) node; + if (cast->statements != NULL) pm_def_node_receiver_check(parser, (pm_node_t *) cast->statements); + break; + } + case PM_PARENTHESES_NODE: { + const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node; + if (cast->body != NULL) pm_def_node_receiver_check(parser, cast->body); + break; + } + case PM_STATEMENTS_NODE: { + const pm_statements_node_t *cast = (const pm_statements_node_t *) node; + pm_def_node_receiver_check(parser, cast->body.nodes[cast->body.size - 1]); + break; + } + case PM_ARRAY_NODE: + case PM_FLOAT_NODE: + case PM_IMAGINARY_NODE: + case PM_INTEGER_NODE: + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + case PM_INTERPOLATED_STRING_NODE: + case PM_INTERPOLATED_SYMBOL_NODE: + case PM_INTERPOLATED_X_STRING_NODE: + case PM_RATIONAL_NODE: + case PM_REGULAR_EXPRESSION_NODE: + case PM_SOURCE_ENCODING_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_STRING_NODE: + case PM_SYMBOL_NODE: + case PM_X_STRING_NODE: + pm_parser_err_node(parser, node, PM_ERR_SINGLETON_FOR_LITERALS); + break; + default: + break; + } +} + +/** + * Allocate and initialize a new DefNode node. + */ +static pm_def_node_t * +pm_def_node_create( + pm_parser_t *parser, + pm_constant_id_t name, + const pm_token_t *name_loc, + 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_NODE_ALLOC(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; + } + + if ((receiver != NULL) && PM_NODE_TYPE_P(receiver, PM_PARENTHESES_NODE)) { + pm_def_node_receiver_check(parser, receiver); + } + + *node = (pm_def_node_t) { + { + .type = PM_DEF_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { .start = def_keyword->start, .end = end }, + }, + .name = name, + .name_loc = PM_LOCATION_TOKEN_VALUE(name_loc), + .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_NODE_ALLOC(parser, pm_defined_node_t); + + *node = (pm_defined_node_t) { + { + .type = PM_DEFINED_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(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, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_embedded_statements_node_t); + + *node = (pm_embedded_statements_node_t) { + { + .type = PM_EMBEDDED_STATEMENTS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_embedded_variable_node_t); + + *node = (pm_embedded_variable_node_t) { + { + .type = PM_EMBEDDED_VARIABLE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_ensure_node_t); + + *node = (pm_ensure_node_t) { + { + .type = PM_ENSURE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_false_node_t); + + *node = (pm_false_node_t) {{ + .type = PM_FALSE_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_find_pattern_node_t); + + pm_node_t *left = nodes->nodes[0]; + assert(PM_NODE_TYPE_P(left, PM_SPLAT_NODE)); + pm_splat_node_t *left_splat_node = (pm_splat_node_t *) left; + + 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]; + assert(PM_NODE_TYPE_P(right, PM_SPLAT_NODE)); + } + +#if PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS + // FindPatternNode#right is typed as SplatNode in this case, so replace the potential MissingNode with a SplatNode. + // The resulting AST will anyway be ignored, but this file still needs to compile. + pm_splat_node_t *right_splat_node = PM_NODE_TYPE_P(right, PM_SPLAT_NODE) ? (pm_splat_node_t *) right : left_splat_node; +#else + pm_node_t *right_splat_node = right; +#endif + *node = (pm_find_pattern_node_t) { + { + .type = PM_FIND_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = left->location.start, + .end = right->location.end, + }, + }, + .constant = NULL, + .left = left_splat_node, + .right = right_splat_node, + .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; +} + +/** + * Parse the value of a double, add appropriate errors if there is an issue, and + * return the value that should be saved on the PM_FLOAT_NODE node. + */ +static double +pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { + ptrdiff_t diff = token->end - token->start; + if (diff <= 0) return 0.0; + + // First, get a buffer of the content. + size_t length = (size_t) diff; + char *buffer = xmalloc(sizeof(char) * (length + 1)); + memcpy((void *) buffer, token->start, length); + + // Next, determine if we need to replace the decimal point because of + // locale-specific options, and then normalize them if we have to. + char decimal_point = *localeconv()->decimal_point; + if (decimal_point != '.') { + for (size_t index = 0; index < length; index++) { + if (buffer[index] == '.') buffer[index] = decimal_point; + } + } + + // Next, handle underscores by removing them from the buffer. + for (size_t index = 0; index < length; index++) { + if (buffer[index] == '_') { + memmove((void *) (buffer + index), (void *) (buffer + index + 1), length - index); + length--; + } + } + + // Null-terminate the buffer so that strtod cannot read off the end. + buffer[length] = '\0'; + + // Now, call strtod to parse the value. Note that CRuby has their own + // version of strtod which avoids locales. We're okay using the locale-aware + // version because we've already validated through the parser that the token + // is in a valid format. + errno = 0; + char *eptr; + double value = strtod(buffer, &eptr); + + // This should never happen, because we've already checked that the token + // is in a valid format. However it's good to be safe. + if ((eptr != buffer + length) || (errno != 0 && errno != ERANGE)) { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, (*token), PM_ERR_FLOAT_PARSE); + xfree((void *) buffer); + return 0.0; + } + + // If errno is set, then it should only be ERANGE. At this point we need to + // check if it's infinity (it should be). + if (errno == ERANGE && PRISM_ISINF(value)) { + int warn_width; + const char *ellipsis; + + if (length > 20) { + warn_width = 20; + ellipsis = "..."; + } else { + warn_width = (int) length; + ellipsis = ""; + } + + pm_diagnostic_list_append_format(&parser->warning_list, token->start, token->end, PM_WARN_FLOAT_OUT_OF_RANGE, warn_width, (const char *) token->start, ellipsis); + value = (value < 0.0) ? -HUGE_VAL : HUGE_VAL; + } + + // Finally we can free the buffer and return the value. + xfree((void *) buffer); + return value; +} + +/** + * 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_NODE_ALLOC(parser, pm_float_node_t); + + *node = (pm_float_node_t) { + { + .type = PM_FLOAT_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .value = pm_double_parse(parser, 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_NODE_ALLOC(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + { + .type = PM_IMAGINARY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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 RationalNode 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_NODE_ALLOC(parser, pm_rational_node_t); + *node = (pm_rational_node_t) { + { + .type = PM_RATIONAL_NODE, + .flags = PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .numerator = { 0 }, + .denominator = { 0 } + }; + + const uint8_t *start = token->start; + const uint8_t *end = token->end - 1; // r + + while (start < end && *start == '0') start++; // 0.1 -> .1 + while (end > start && end[-1] == '0') end--; // 1.0 -> 1. + + size_t length = (size_t) (end - start); + if (length == 1) { + node->denominator.value = 1; + return node; + } + + const uint8_t *point = memchr(start, '.', length); + assert(point && "should have a decimal point"); + + uint8_t *digits = malloc(length); + if (digits == NULL) { + fputs("[pm_float_node_rational_create] Failed to allocate memory", stderr); + abort(); + } + + memcpy(digits, start, (unsigned long) (point - start)); + memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1)); + pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1); + + digits[0] = '1'; + if (end - point > 1) memset(digits + 1, '0', (size_t) (end - point - 1)); + pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + (end - point)); + free(digits); + + pm_integers_reduce(&node->numerator, &node->denominator); + 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_NODE_ALLOC(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + { + .type = PM_IMAGINARY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_for_node_t); + + *node = (pm_for_node_t) { + { + .type = PM_FOR_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_forwarding_arguments_node_t); + + *node = (pm_forwarding_arguments_node_t) {{ + .type = PM_FORWARDING_ARGUMENTS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_forwarding_parameter_node_t); + + *node = (pm_forwarding_parameter_node_t) {{ + .type = PM_FORWARDING_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(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, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_hash_pattern_node_t); + + *node = (pm_hash_pattern_node_t) { + { + .type = PM_HASH_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(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, + .node_id = PM_NODE_IDENTIFY(parser), + .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 + }; + + pm_node_t *element; + PM_NODE_LIST_FOREACH(elements, index, element) { + 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_NODE_ALLOC(parser, pm_global_variable_and_write_node_t); + + *node = (pm_global_variable_and_write_node_t) { + { + .type = PM_GLOBAL_VARIABLE_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_global_variable_operator_write_node_t); + + *node = (pm_global_variable_operator_write_node_t) { + { + .type = PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name = pm_global_variable_write_name(parser, target), + .name_loc = target->location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_global_variable_or_write_node_t); + + *node = (pm_global_variable_or_write_node_t) { + { + .type = PM_GLOBAL_VARIABLE_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_global_variable_read_node_t); + + *node = (pm_global_variable_read_node_t) { + { + .type = PM_GLOBAL_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(name), + }, + .name = pm_parser_constant_id_token(parser, name) + }; + + return node; +} + +/** + * Allocate and initialize a new synthesized GlobalVariableReadNode node. + */ +static pm_global_variable_read_node_t * +pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name) { + pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t); + + *node = (pm_global_variable_read_node_t) { + { + .type = PM_GLOBAL_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_NULL_VALUE(parser) + }, + .name = name + }; + + return node; +} + +/** + * Allocate and initialize 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_NODE_ALLOC(parser, pm_global_variable_write_node_t); + + *node = (pm_global_variable_write_node_t) { + { + .type = PM_GLOBAL_VARIABLE_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .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 and initialize a new synthesized GlobalVariableWriteNode node. + */ +static pm_global_variable_write_node_t * +pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name, pm_node_t *value) { + pm_global_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_write_node_t); + + *node = (pm_global_variable_write_node_t) { + { + .type = PM_GLOBAL_VARIABLE_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_NULL_VALUE(parser) + }, + .name = name, + .name_loc = PM_LOCATION_NULL_VALUE(parser), + .operator_loc = PM_LOCATION_NULL_VALUE(parser), + .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_NODE_ALLOC(parser, pm_hash_node_t); + + *node = (pm_hash_node_t) { + { + .type = PM_HASH_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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); + + bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE); + if (static_literal) { + pm_assoc_node_t *assoc = (pm_assoc_node_t *) element; + static_literal = !PM_NODE_TYPE_P(assoc->key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(assoc->key, PM_HASH_NODE) && !PM_NODE_TYPE_P(assoc->key, PM_RANGE_NODE); + static_literal = static_literal && PM_NODE_FLAG_P(assoc->key, PM_NODE_FLAG_STATIC_LITERAL); + static_literal = static_literal && PM_NODE_FLAG_P(assoc, PM_NODE_FLAG_STATIC_LITERAL); + } + + if (!static_literal) { + pm_node_flag_unset((pm_node_t *)hash, 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 *subsequent, + const pm_token_t *end_keyword +) { + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t); + + const uint8_t *end; + if (end_keyword->type != PM_TOKEN_NOT_PROVIDED) { + end = end_keyword->end; + } else if (subsequent != NULL) { + end = subsequent->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, + .node_id = PM_NODE_IDENTIFY(parser), + .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, + .subsequent = subsequent, + .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(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t); + + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, statements, statement, true); + + *node = (pm_if_node_t) { + { + .type = PM_IF_NODE, + .flags = PM_NODE_FLAG_NEWLINE, + .node_id = PM_NODE_IDENTIFY(parser), + .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, + .subsequent = 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(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + + pm_statements_node_t *if_statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, if_statements, true_expression, true); + + pm_statements_node_t *else_statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, else_statements, false_expression, true); + + 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_NODE_ALLOC(parser, pm_if_node_t); + + *node = (pm_if_node_t) { + { + .type = PM_IF_NODE, + .flags = PM_NODE_FLAG_NEWLINE, + .node_id = PM_NODE_IDENTIFY(parser), + .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, + .subsequent = (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_NODE_ALLOC(parser, pm_implicit_node_t); + + *node = (pm_implicit_node_t) { + { + .type = PM_IMPLICIT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_implicit_rest_node_t); + + *node = (pm_implicit_rest_node_t) { + { + .type = PM_IMPLICIT_REST_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_integer_node_t); + + *node = (pm_integer_node_t) { + { + .type = PM_INTEGER_NODE, + .flags = base | PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .value = { 0 } + }; + + pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL; + switch (base) { + case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break; + case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break; + case PM_INTEGER_BASE_FLAGS_DECIMAL: break; + case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break; + default: assert(false && "unreachable"); break; + } + + pm_integer_parse(&node->value, integer_base, token->start, token->end); + 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_NODE_ALLOC(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + { + .type = PM_IMAGINARY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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 RationalNode 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_NODE_ALLOC(parser, pm_rational_node_t); + *node = (pm_rational_node_t) { + { + .type = PM_RATIONAL_NODE, + .flags = base | PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }, + .numerator = { 0 }, + .denominator = { .value = 1, 0 } + }; + + pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL; + switch (base) { + case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break; + case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break; + case PM_INTEGER_BASE_FLAGS_DECIMAL: break; + case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break; + default: assert(false && "unreachable"); break; + } + + pm_integer_parse(&node->numerator, integer_base, token->start, 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_NODE_ALLOC(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + { + .type = PM_IMAGINARY_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(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, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_instance_variable_and_write_node_t); + + *node = (pm_instance_variable_and_write_node_t) { + { + .type = PM_INSTANCE_VARIABLE_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_instance_variable_operator_write_node_t); + + *node = (pm_instance_variable_operator_write_node_t) { + { + .type = PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .name = target->name, + .name_loc = target->base.location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_instance_variable_or_write_node_t); + + *node = (pm_instance_variable_or_write_node_t) { + { + .type = PM_INSTANCE_VARIABLE_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_instance_variable_read_node_t); + + *node = (pm_instance_variable_read_node_t) { + { + .type = PM_INSTANCE_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_instance_variable_write_node_t); + *node = (pm_instance_variable_write_node_t) { + { + .type = PM_INSTANCE_VARIABLE_WRITE_NODE, + .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), + .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; +} + +/** + * Append a part into a list of string parts. Importantly this handles nested + * interpolated strings by not necessarily removing the marker for static + * literals. + */ +static void +pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) { + switch (PM_NODE_TYPE(part)) { + case PM_STRING_NODE: + pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); + break; + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part; + pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL; + + if (embedded == NULL) { + // If there are no statements or more than one statement, then + // we lose the static literal flag. + pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL); + } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) { + // If the embedded statement is a string, then we can keep the + // static literal flag and mark the string as frozen. + pm_node_flag_set(embedded, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); + } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) { + // If the embedded statement is an interpolated string and it's + // a static literal, then we can keep the static literal flag. + } else { + // Otherwise we lose the static literal flag. + pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: + pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL); + break; + default: + assert(false && "unexpected node type"); + break; + } + + pm_node_list_append(parts, part); +} + +/** + * 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_NODE_ALLOC(parser, pm_interpolated_regular_expression_node_t); + + *node = (pm_interpolated_regular_expression_node_t) { + { + .type = PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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_interpolated_node_append((pm_node_t *) node, &node->parts, part); +} + +static inline void +pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, 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; + pm_node_flag_set((pm_node_t *) node, pm_regular_expression_flags_create(parser, closing)); +} + +/** + * Append a part to an InterpolatedStringNode node. + * + * This has some somewhat complicated semantics, because we need to update + * multiple flags that have somewhat confusing interactions. + * + * PM_NODE_FLAG_STATIC_LITERAL indicates that the node should be treated as a + * single static literal string that can be pushed onto the stack on its own. + * Note that this doesn't necessarily mean that the string will be frozen or + * not; the instructions in CRuby will be either putobject or putstring, + * depending on the combination of `--enable-frozen-string-literal`, + * `# frozen_string_literal: true`, and whether or not there is interpolation. + * + * PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN indicates that the string should be + * explicitly frozen. This will only happen if the string is comprised entirely + * of string parts that are themselves static literals and frozen. + * + * PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE indicates that the string should + * be explicitly marked as mutable. This will happen from + * `--disable-frozen-string-literal` or `# frozen_string_literal: false`. This + * is necessary to indicate that the string should be left up to the runtime, + * which could potentially use a chilled string otherwise. + */ +static inline void +pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_t *part) { +#define CLEAR_FLAGS(node) \ + node->base.flags = (pm_node_flags_t) (node->base.flags & ~(PM_NODE_FLAG_STATIC_LITERAL | PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) + +#define MUTABLE_FLAGS(node) \ + node->base.flags = (pm_node_flags_t) ((node->base.flags | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN); + + if (node->parts.size == 0 && node->opening_loc.start == NULL) { + node->base.location.start = part->location.start; + } + + node->base.location.end = MAX(node->base.location.end, part->location.end); + + switch (PM_NODE_TYPE(part)) { + case PM_STRING_NODE: + part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE); + break; + case PM_INTERPOLATED_STRING_NODE: + if (PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) { + // If the string that we're concatenating is a static literal, + // then we can keep the static literal flag for this string. + } else { + // Otherwise, we lose the static literal flag here and we should + // also clear the mutability flags. + CLEAR_FLAGS(node); + } + break; + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part; + pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL; + + if (embedded == NULL) { + // If we're embedding multiple statements or no statements, then + // the string is not longer a static literal. + CLEAR_FLAGS(node); + } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) { + // If the embedded statement is a string, then we can make that + // string as frozen and static literal, and not touch the static + // literal status of this string. + embedded->flags = (pm_node_flags_t) ((embedded->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE); + + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) { + MUTABLE_FLAGS(node); + } + } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) { + // If the embedded statement is an interpolated string, but that + // string is marked as static literal, then we can keep our + // static literal status for this string. + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) { + MUTABLE_FLAGS(node); + } + } else { + // In all other cases, we lose the static literal flag here and + // become mutable. + CLEAR_FLAGS(node); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: + // Embedded variables clear static literal, which means we also + // should clear the mutability flags. + CLEAR_FLAGS(node); + break; + case PM_X_STRING_NODE: + case PM_INTERPOLATED_X_STRING_NODE: + // If this is an x string, then this is a syntax error. But we want + // to handle it here so that we don't fail the assertion. + CLEAR_FLAGS(node); + break; + default: + assert(false && "unexpected node type"); + break; + } + + pm_node_list_append(&node->parts, part); + +#undef CLEAR_FLAGS +#undef MUTABLE_FLAGS +} + +/** + * 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_NODE_ALLOC(parser, pm_interpolated_string_node_t); + pm_node_flags_t flags = PM_NODE_FLAG_STATIC_LITERAL; + + switch (parser->frozen_string_literal) { + case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED: + flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE; + break; + case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED: + flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN; + break; + } + + *node = (pm_interpolated_string_node_t) { + { + .type = PM_INTERPOLATED_STRING_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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) { + pm_node_t *part; + PM_NODE_LIST_FOREACH(parts, index, part) { + pm_interpolated_string_node_append(node, part); + } + } + + return node; +} + +/** + * 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; +} + +static 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_interpolated_node_append((pm_node_t *) node, &node->parts, part); + node->base.location.end = MAX(node->base.location.end, part->location.end); +} + +static void +pm_interpolated_symbol_node_closing_loc_set(pm_interpolated_symbol_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_NODE_ALLOC(parser, pm_interpolated_symbol_node_t); + + *node = (pm_interpolated_symbol_node_t) { + { + .type = PM_INTERPOLATED_SYMBOL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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) { + pm_node_t *part; + PM_NODE_LIST_FOREACH(parts, index, part) { + pm_interpolated_symbol_node_append(node, part); + } + } + + return node; +} + +/** + * 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_NODE_ALLOC(parser, pm_interpolated_x_string_node_t); + + *node = (pm_interpolated_x_string_node_t) { + { + .type = PM_INTERPOLATED_X_STRING_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_interpolated_node_append((pm_node_t *) node, &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; +} + +/** + * Create a local variable read that is reading the implicit 'it' variable. + */ +static pm_it_local_variable_read_node_t * +pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) { + pm_it_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_it_local_variable_read_node_t); + + *node = (pm_it_local_variable_read_node_t) { + { + .type = PM_IT_LOCAL_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(name) + } + }; + + return node; +} + +/** + * Allocate and initialize a new ItParametersNode node. + */ +static pm_it_parameters_node_t * +pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) { + pm_it_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_it_parameters_node_t); + + *node = (pm_it_parameters_node_t) { + { + .type = PM_IT_PARAMETERS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = opening->start, + .end = closing->end + } + } + }; + + return node; +} + +/** + * 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_NODE_ALLOC(parser, pm_keyword_hash_node_t); + + *node = (pm_keyword_hash_node_t) { + .base = { + .type = PM_KEYWORD_HASH_NODE, + .flags = PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS, + .node_id = PM_NODE_IDENTIFY(parser), + .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) { + // If the element being added is not an AssocNode or does not have a symbol + // key, then we want to turn the SYMBOL_KEYS flag off. + if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_TYPE_P(((pm_assoc_node_t *) element)->key, PM_SYMBOL_NODE)) { + pm_node_flag_unset((pm_node_t *)hash, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS); + } + + 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_NODE_ALLOC(parser, pm_required_keyword_parameter_node_t); + + *node = (pm_required_keyword_parameter_node_t) { + { + .type = PM_REQUIRED_KEYWORD_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_optional_keyword_parameter_node_t); + + *node = (pm_optional_keyword_parameter_node_t) { + { + .type = PM_OPTIONAL_KEYWORD_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_keyword_rest_parameter_node_t); + + *node = (pm_keyword_rest_parameter_node_t) { + { + .type = PM_KEYWORD_REST_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_node_t *parameters, + pm_node_t *body +) { + pm_lambda_node_t *node = PM_NODE_ALLOC(parser, pm_lambda_node_t); + + *node = (pm_lambda_node_t) { + { + .type = PM_LAMBDA_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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 + }; + + 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_IT_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_NODE_ALLOC(parser, pm_local_variable_and_write_node_t); + + *node = (pm_local_variable_and_write_node_t) { + { + .type = PM_LOCAL_VARIABLE_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_local_variable_operator_write_node_t); + + *node = (pm_local_variable_operator_write_node_t) { + { + .type = PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .name = name, + .binary_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_IT_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_NODE_ALLOC(parser, pm_local_variable_or_write_node_t); + + *node = (pm_local_variable_or_write_node_t) { + { + .type = PM_LOCAL_VARIABLE_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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 with constant_id. + */ +static pm_local_variable_read_node_t * +pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_token_t *name, pm_constant_id_t name_id, uint32_t depth, bool missing) { + if (!missing) pm_locals_read(&pm_parser_scope_find(parser, depth)->locals, name_id); + + pm_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_read_node_t); + + *node = (pm_local_variable_read_node_t) { + { + .type = PM_LOCAL_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(name) + }, + .name = name_id, + .depth = depth + }; + + return node; +} + +/** + * Allocate and initialize 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); + return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, false); +} + +/** + * Allocate and initialize a new LocalVariableReadNode node for a missing local + * variable. (This will only happen when there is a syntax error.) + */ +static pm_local_variable_read_node_t * +pm_local_variable_read_node_missing_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); + return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, true); +} + +/** + * 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_NODE_ALLOC(parser, pm_local_variable_write_node_t); + + *node = (pm_local_variable_write_node_t) { + { + .type = PM_LOCAL_VARIABLE_WRITE_NODE, + .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), + .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 `it`. + */ +static inline bool +pm_token_is_it(const uint8_t *start, const uint8_t *end) { + return (end - start == 2) && (start[0] == 'i') && (start[1] == 't'); +} + +/** + * 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(pm_parser_t *parser, const pm_location_t *location, pm_constant_id_t name, uint32_t depth) { + pm_refute_numbered_parameter(parser, location->start, location->end); + pm_local_variable_target_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_target_node_t); + + *node = (pm_local_variable_target_node_t) { + { + .type = PM_LOCAL_VARIABLE_TARGET_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = *location + }, + .name = name, + .depth = depth + }; + + return node; +} + +/** + * 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_NODE_ALLOC(parser, pm_match_predicate_node_t); + + *node = (pm_match_predicate_node_t) { + { + .type = PM_MATCH_PREDICATE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_match_required_node_t); + + *node = (pm_match_required_node_t) { + { + .type = PM_MATCH_REQUIRED_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_match_write_node_t); + + *node = (pm_match_write_node_t) { + { + .type = PM_MATCH_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_module_node_t); + + *node = (pm_module_node_t) { + { + .type = PM_MODULE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_multi_target_node_t); + + *node = (pm_multi_target_node_t) { + { + .type = PM_MULTI_TARGET_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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)) { + 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 (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) { + if (node->rest == NULL) { + node->rest = target; + } else { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST); + 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_NODE_ALLOC(parser, pm_multi_write_node_t); + + *node = (pm_multi_write_node_t) { + { + .type = PM_MULTI_WRITE_NODE, + .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), + .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. + xfree(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_NODE_ALLOC(parser, pm_next_node_t); + + *node = (pm_next_node_t) { + { + .type = PM_NEXT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_nil_node_t); + + *node = (pm_nil_node_t) {{ + .type = PM_NIL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_no_keywords_parameter_node_t); + + *node = (pm_no_keywords_parameter_node_t) { + { + .type = PM_NO_KEYWORDS_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = operator->start, + .end = keyword->end + } + }, + .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) + }; + + return node; +} + +/** + * Allocate and initialize a new NumberedParametersNode node. + */ +static pm_numbered_parameters_node_t * +pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_location_t *location, uint8_t maximum) { + pm_numbered_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_numbered_parameters_node_t); + + *node = (pm_numbered_parameters_node_t) { + { + .type = PM_NUMBERED_PARAMETERS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = *location + }, + .maximum = maximum + }; + + return node; +} + +/** + * The maximum numbered reference value is defined as the maximum value that an + * integer can hold minus 1 bit for CRuby instruction sequence operand tagging. + */ +#define NTH_REF_MAX ((uint32_t) (INT_MAX >> 1)) + +/** + * Parse the decimal number represented by the range of bytes. Returns + * 0 if the number fails to parse or if the number is greater than the maximum + * value representable by a numbered reference. This function assumes that the + * range of bytes has already been validated to contain only decimal digits. + */ +static uint32_t +pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *token) { + const uint8_t *start = token->start + 1; + const uint8_t *end = token->end; + + ptrdiff_t diff = end - start; + assert(diff > 0); +#if PTRDIFF_MAX > SIZE_MAX + assert(diff < (ptrdiff_t) SIZE_MAX); +#endif + size_t length = (size_t) diff; + + char *digits = xcalloc(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')) { + pm_parser_err(parser, start, end, PM_ERR_INVALID_NUMBER_DECIMAL); + value = 0; + } + + xfree(digits); + + if ((errno == ERANGE) || (value > NTH_REF_MAX)) { + PM_PARSER_WARN_FORMAT(parser, start, end, PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start); + value = 0; + } + + return (uint32_t) value; +} + +#undef NTH_REF_MAX + +/** + * Allocate and initialize 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_NODE_ALLOC(parser, pm_numbered_reference_read_node_t); + + *node = (pm_numbered_reference_read_node_t) { + { + .type = PM_NUMBERED_REFERENCE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(name), + }, + .number = pm_numbered_reference_read_node_number(parser, name) + }; + + 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_NODE_ALLOC(parser, pm_optional_parameter_node_t); + + *node = (pm_optional_parameter_node_t) { + { + .type = PM_OPTIONAL_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_or_node_t); + + *node = (pm_or_node_t) { + { + .type = PM_OR_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_parameters_node_t); + + *node = (pm_parameters_node_t) { + { + .type = PM_PARAMETERS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_program_node_t); + + *node = (pm_program_node_t) { + { + .type = PM_PROGRAM_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_node_flags_t flags) { + pm_parentheses_node_t *node = PM_NODE_ALLOC(parser, pm_parentheses_node_t); + + *node = (pm_parentheses_node_t) { + { + .type = PM_PARENTHESES_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_pinned_expression_node_t); + + *node = (pm_pinned_expression_node_t) { + { + .type = PM_PINNED_EXPRESSION_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_pinned_variable_node_t); + + *node = (pm_pinned_variable_node_t) { + { + .type = PM_PINNED_VARIABLE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_post_execution_node_t); + + *node = (pm_post_execution_node_t) { + { + .type = PM_POST_EXECUTION_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_pre_execution_node_t); + + *node = (pm_pre_execution_node_t) { + { + .type = PM_PRE_EXECUTION_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_range_node_t); + pm_node_flags_t flags = 0; + + // Indicate that this node is 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, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_redo_node_t); + + *node = (pm_redo_node_t) {{ + .type = PM_REDO_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_regular_expression_node_t); + + *node = (pm_regular_expression_node_t) { + { + .type = PM_REGULAR_EXPRESSION_NODE, + .flags = pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_required_parameter_node_t); + + *node = (pm_required_parameter_node_t) { + { + .type = PM_REQUIRED_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_rescue_modifier_node_t); + + *node = (pm_rescue_modifier_node_t) { + { + .type = PM_RESCUE_MODIFIER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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 initialize 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_NODE_ALLOC(parser, pm_rescue_node_t); + + *node = (pm_rescue_node_t) { + { + .type = PM_RESCUE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(keyword) + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .reference = NULL, + .statements = NULL, + .subsequent = 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 subsequent of a rescue node, and update the location. + */ +static void +pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subsequent) { + node->subsequent = subsequent; + node->base.location.end = subsequent->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_NODE_ALLOC(parser, pm_rest_parameter_node_t); + + *node = (pm_rest_parameter_node_t) { + { + .type = PM_REST_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_retry_node_t); + + *node = (pm_retry_node_t) {{ + .type = PM_RETRY_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_return_node_t); + + *node = (pm_return_node_t) { + { + .type = PM_RETURN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_self_node_t); + + *node = (pm_self_node_t) {{ + .type = PM_SELF_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate and initialize a new ShareableConstantNode node. + */ +static pm_shareable_constant_node_t * +pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shareable_constant_value_t value) { + pm_shareable_constant_node_t *node = PM_NODE_ALLOC(parser, pm_shareable_constant_node_t); + + *node = (pm_shareable_constant_node_t) { + { + .type = PM_SHAREABLE_CONSTANT_NODE, + .flags = (pm_node_flags_t) value, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_NODE_VALUE(write) + }, + .write = write + }; + + 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_NODE_ALLOC(parser, pm_singleton_class_node_t); + + *node = (pm_singleton_class_node_t) { + { + .type = PM_SINGLETON_CLASS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_source_encoding_node_t); + + *node = (pm_source_encoding_node_t) {{ + .type = PM_SOURCE_ENCODING_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_source_file_node_t); + assert(file_keyword->type == PM_TOKEN_KEYWORD___FILE__); + + pm_node_flags_t flags = 0; + + switch (parser->frozen_string_literal) { + case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED: + flags |= PM_STRING_FLAGS_MUTABLE; + break; + case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED: + flags |= PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN; + break; + } + + *node = (pm_source_file_node_t) { + { + .type = PM_SOURCE_FILE_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(file_keyword), + }, + .filepath = parser->filepath + }; + + 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_NODE_ALLOC(parser, pm_source_line_node_t); + + *node = (pm_source_line_node_t) {{ + .type = PM_SOURCE_LINE_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_splat_node_t); + + *node = (pm_splat_node_t) { + { + .type = PM_SPLAT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(parser, pm_statements_node_t); + + *node = (pm_statements_node_t) { + { + .type = PM_STATEMENTS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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 }; +} + +/** + * Update the location of the statements node based on the statement that is + * being added to the list. + */ +static inline void +pm_statements_node_body_update(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; + } +} + +/** + * Append a new node to the given StatementsNode node's body. + */ +static void +pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline) { + pm_statements_node_body_update(node, statement); + + if (node->body.size > 0) { + const pm_node_t *previous = node->body.nodes[node->body.size - 1]; + + switch (PM_NODE_TYPE(previous)) { + case PM_BREAK_NODE: + case PM_NEXT_NODE: + case PM_REDO_NODE: + case PM_RETRY_NODE: + case PM_RETURN_NODE: + pm_parser_warn_node(parser, statement, PM_WARN_UNREACHABLE_STATEMENT); + break; + default: + break; + } + } + + pm_node_list_append(&node->body, statement); + if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE); +} + +/** + * Prepend a new node to the given StatementsNode node's body. + */ +static void +pm_statements_node_body_prepend(pm_statements_node_t *node, pm_node_t *statement) { + pm_statements_node_body_update(node, statement); + pm_node_list_prepend(&node->body, statement); + pm_node_flag_set(statement, 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_NODE_ALLOC(parser, pm_string_node_t); + pm_node_flags_t flags = 0; + + switch (parser->frozen_string_literal) { + case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED: + flags = PM_STRING_FLAGS_MUTABLE; + break; + case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED: + flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN; + break; + } + + *node = (pm_string_node_t) { + { + .type = PM_STRING_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(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, + .node_id = PM_NODE_IDENTIFY(parser), + .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; +} + +/** + * Read through the contents of a string and check if it consists solely of + * US-ASCII code points. + */ +static bool +pm_ascii_only_p(const pm_string_t *contents) { + const size_t length = pm_string_length(contents); + const uint8_t *source = pm_string_source(contents); + + for (size_t index = 0; index < length; index++) { + if (source[index] & 0x80) return false; + } + + return true; +} + +/** + * Validate that the contents of the given symbol are all valid UTF-8. + */ +static void +parse_symbol_encoding_validate_utf8(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) { + for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) { + size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor); + + if (width == 0) { + pm_parser_err(parser, location->start, location->end, PM_ERR_INVALID_SYMBOL); + break; + } + + cursor += width; + } +} + +/** + * Validate that the contents of the given symbol are all valid in the encoding + * of the parser. + */ +static void +parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) { + const pm_encoding_t *encoding = parser->encoding; + + for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) { + size_t width = encoding->char_width(cursor, end - cursor); + + if (width == 0) { + pm_parser_err(parser, location->start, location->end, PM_ERR_INVALID_SYMBOL); + break; + } + + cursor += width; + } +} + +/** + * Ruby "downgrades" the encoding of Symbols to US-ASCII if the associated + * encoding is ASCII-compatible and the Symbol consists only of US-ASCII code + * points. Otherwise, the encoding may be explicitly set with an escape + * sequence. + * + * If the validate flag is set, then it will check the contents of the symbol + * to ensure that all characters are valid in the encoding. + */ +static inline pm_node_flags_t +parse_symbol_encoding(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents, bool validate) { + if (parser->explicit_encoding != NULL) { + // A Symbol may optionally have its encoding explicitly set. This will + // happen if an escape sequence results in a non-ASCII code point. + if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + if (validate) parse_symbol_encoding_validate_utf8(parser, location, contents); + return PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING; + } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { + return PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING; + } else if (validate) { + parse_symbol_encoding_validate_other(parser, location, contents); + } + } else if (pm_ascii_only_p(contents)) { + // Ruby stipulates that all source files must use an ASCII-compatible + // encoding. Thus, all symbols appearing in source are eligible for + // "downgrading" to US-ASCII. + return PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING; + } else if (validate) { + parse_symbol_encoding_validate_other(parser, location, contents); + } + + return 0; +} + +static pm_node_flags_t +parse_and_validate_regular_expression_encoding_modifier(pm_parser_t *parser, const pm_string_t *source, bool ascii_only, pm_node_flags_t flags, char modifier, const pm_encoding_t *modifier_encoding) { + assert ((modifier == 'n' && modifier_encoding == PM_ENCODING_ASCII_8BIT_ENTRY) || + (modifier == 'u' && modifier_encoding == PM_ENCODING_UTF_8_ENTRY) || + (modifier == 'e' && modifier_encoding == PM_ENCODING_EUC_JP_ENTRY) || + (modifier == 's' && modifier_encoding == PM_ENCODING_WINDOWS_31J_ENTRY)); + + // There's special validation logic used if a string does not contain any character escape sequences. + if (parser->explicit_encoding == NULL) { + // If an ASCII-only string without character escapes is used with an encoding modifier, then resulting Regexp + // has the modifier encoding, unless the ASCII-8BIT modifier is used, in which case the Regexp "downgrades" to + // the US-ASCII encoding. + if (ascii_only) { + return modifier == 'n' ? PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING : flags; + } + + if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { + if (!ascii_only) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name); + } + } else if (parser->encoding != modifier_encoding) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH, modifier, parser->encoding->name); + + if (modifier == 'n' && !ascii_only) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_NON_ESCAPED_MBC, (int) pm_string_length(source), (const char *) pm_string_source(source)); + } + } + + return flags; + } + + // TODO (nirvdrum 21-Feb-2024): To validate regexp sources with character escape sequences we need to know whether hex or Unicode escape sequences were used and Prism doesn't currently provide that data. We handle a subset of unambiguous cases in the meanwhile. + bool mixed_encoding = false; + + if (mixed_encoding) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_ESCAPE, (int) pm_string_length(source), (const char *) pm_string_source(source)); + } else if (modifier != 'n' && parser->explicit_encoding == PM_ENCODING_ASCII_8BIT_ENTRY) { + // TODO (nirvdrum 21-Feb-2024): Validate the content is valid in the modifier encoding. Do this on-demand so we don't pay the cost of computation unnecessarily. + bool valid_string_in_modifier_encoding = true; + + if (!valid_string_in_modifier_encoding) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_ESCAPE, (int) pm_string_length(source), (const char *) pm_string_source(source)); + } + } else if (modifier != 'u' && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + // TODO (nirvdrum 21-Feb-2024): There's currently no way to tell if the source used hex or Unicode character escapes from `explicit_encoding` alone. If the source encoding was already UTF-8, both character escape types would set `explicit_encoding` to UTF-8, but need to be processed differently. Skip for now. + if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING, (int) pm_string_length(source), (const char *) pm_string_source(source)); + } + } + + // We've determined the encoding would naturally be EUC-JP and there is no need to force the encoding to anything else. + return flags; +} + +/** + * Ruby "downgrades" the encoding of Regexps to US-ASCII if the associated encoding is ASCII-compatible and + * the unescaped representation of a Regexp source consists only of US-ASCII code points. This is true even + * when the Regexp is explicitly given an ASCII-8BIT encoding via the (/n) modifier. Otherwise, the encoding + * may be explicitly set with an escape sequence. + */ +static pm_node_flags_t +parse_and_validate_regular_expression_encoding(pm_parser_t *parser, const pm_string_t *source, bool ascii_only, pm_node_flags_t flags) { + // TODO (nirvdrum 22-Feb-2024): CRuby reports a special Regexp-specific error for invalid Unicode ranges. We either need to scan again or modify the "invalid Unicode escape sequence" message we already report. + bool valid_unicode_range = true; + if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && !valid_unicode_range) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_INVALID_UNICODE_RANGE, (int) pm_string_length(source), (const char *) pm_string_source(source)); + return flags; + } + + // US-ASCII strings do not admit multi-byte character literals. However, character escape sequences corresponding + // to multi-byte characters are allowed. + if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY && parser->explicit_encoding == NULL && !ascii_only) { + // CRuby will continue processing even though a SyntaxError has already been detected. It may result in the + // following error message appearing twice. We do the same for compatibility. + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name); + } + + /** + * Start checking modifier flags. We need to process these before considering any explicit encodings that may have + * been set by character literals. The order in which the encoding modifiers is checked does not matter. In the + * event that both an encoding modifier and an explicit encoding would result in the same encoding we do not set + * the corresponding "forced_" flag. Instead, the caller should check the encoding modifier flag and + * determine the encoding that way. + */ + + if (flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 'n', PM_ENCODING_ASCII_8BIT_ENTRY); + } + + if (flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 'u', PM_ENCODING_UTF_8_ENTRY); + } + + if (flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 'e', PM_ENCODING_EUC_JP_ENTRY); + } + + if (flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 's', PM_ENCODING_WINDOWS_31J_ENTRY); + } + + // At this point no encoding modifiers will be present on the regular expression as they would have already + // been processed. Ruby stipulates that all source files must use an ASCII-compatible encoding. Thus, all + // regular expressions without an encoding modifier appearing in source are eligible for "downgrading" to US-ASCII. + if (ascii_only) { + return PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING; + } + + // A Regexp may optionally have its encoding explicitly set via a character escape sequence in the source string + // or by specifying a modifier. + // + // NB: an explicitly set encoding is ignored by Ruby if the Regexp consists of only US ASCII code points. + if (parser->explicit_encoding != NULL) { + if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + return PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING; + } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { + return PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING; + } + } + + return 0; +} + +/** + * 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_node_flags_t flags) { + pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t); + + *node = (pm_symbol_node_t) { + { + .type = PM_SYMBOL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL | flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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, 0); +} + +/** + * 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, parse_symbol_encoding(parser, value, &parser->current_string, false)); + 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); + pm_node_flag_set((pm_node_t *) node, parse_symbol_encoding(parser, &label, &node->unescaped, false)); + + 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; +} + +/** + * Allocate and initialize a new synthesized SymbolNode node. + */ +static pm_symbol_node_t * +pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) { + pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t); + + *node = (pm_symbol_node_t) { + { + .type = PM_SYMBOL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_NULL_VALUE(parser) + }, + .value_loc = PM_LOCATION_NULL_VALUE(parser), + .unescaped = { 0 } + }; + + pm_string_constant_init(&node->unescaped, content, strlen(content)); + 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_NODE_ALLOC(parser, pm_symbol_node_t); + + *new_node = (pm_symbol_node_t) { + { + .type = PM_SYMBOL_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .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 + }; + + pm_token_t content = { .type = PM_TOKEN_IDENTIFIER, .start = node->content_loc.start, .end = node->content_loc.end }; + pm_node_flag_set((pm_node_t *) new_node, parse_symbol_encoding(parser, &content, &node->unescaped, true)); + + // 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. + xfree(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_NODE_ALLOC(parser, pm_string_node_t); + pm_node_flags_t flags = 0; + + switch (parser->frozen_string_literal) { + case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED: + flags = PM_STRING_FLAGS_MUTABLE; + break; + case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED: + flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN; + break; + } + + *new_node = (pm_string_node_t) { + { + .type = PM_STRING_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .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. + xfree(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_NODE_ALLOC(parser, pm_true_node_t); + + *node = (pm_true_node_t) {{ + .type = PM_TRUE_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +/** + * Allocate and initialize a new synthesized TrueNode node. + */ +static pm_true_node_t * +pm_true_node_synthesized_create(pm_parser_t *parser) { + pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t); + + *node = (pm_true_node_t) {{ + .type = PM_TRUE_NODE, + .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { .start = parser->start, .end = parser->end } + }}; + + 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_NODE_ALLOC(parser, pm_undef_node_t); + + *node = (pm_undef_node_t) { + { + .type = PM_UNDEF_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .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(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_unless_node_t *node = PM_NODE_ALLOC(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, + .node_id = PM_NODE_IDENTIFY(parser), + .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, + .else_clause = 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(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_unless_node_t *node = PM_NODE_ALLOC(parser, pm_unless_node_t); + + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, statements, statement, true); + + *node = (pm_unless_node_t) { + { + .type = PM_UNLESS_NODE, + .flags = PM_NODE_FLAG_NEWLINE, + .node_id = PM_NODE_IDENTIFY(parser), + .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, + .else_clause = 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; +} + +/** + * Loop modifiers could potentially modify an expression that contains block + * exits. In this case we need to loop through them and remove them from the + * list of block exits so that they do not later get marked as invalid. + */ +static void +pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statements) { + assert(parser->current_block_exits != NULL); + + // All of the block exits that we want to remove should be within the + // statements, and since we are modifying the statements, we shouldn't have + // to check the end location. + const uint8_t *start = statements->base.location.start; + + for (size_t index = parser->current_block_exits->size; index > 0; index--) { + pm_node_t *block_exit = parser->current_block_exits->nodes[index - 1]; + if (block_exit->location.start < start) break; + + // Implicitly remove from the list by lowering the size. + parser->current_block_exits->size--; + } +} + +/** + * 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 *do_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_NODE_ALLOC(parser, pm_until_node_t); + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + + *node = (pm_until_node_t) { + { + .type = PM_UNTIL_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = keyword->start, + .end = closing->end, + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_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_NODE_ALLOC(parser, pm_until_node_t); + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_loop_modifier_block_exits(parser, statements); + + *node = (pm_until_node_t) { + { + .type = PM_UNTIL_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = statements->base.location.start, + .end = predicate->location.end, + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .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_NODE_ALLOC(parser, pm_when_node_t); + + *node = (pm_when_node_t) { + { + .type = PM_WHEN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = keyword->start, + .end = NULL + } + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .statements = NULL, + .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .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 location of the then keyword of a when node. + */ +static inline void +pm_when_node_then_keyword_loc_set(pm_when_node_t *node, const pm_token_t *then_keyword) { + node->base.location.end = then_keyword->end; + node->then_keyword_loc = PM_LOCATION_TOKEN_VALUE(then_keyword); +} + +/** + * 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 *do_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_NODE_ALLOC(parser, pm_while_node_t); + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + + *node = (pm_while_node_t) { + { + .type = PM_WHILE_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = keyword->start, + .end = closing->end + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_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_NODE_ALLOC(parser, pm_while_node_t); + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_loop_modifier_block_exits(parser, statements); + + *node = (pm_while_node_t) { + { + .type = PM_WHILE_NODE, + .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { + .start = statements->base.location.start, + .end = predicate->location.end + }, + }, + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .predicate = predicate, + .statements = statements + }; + + return node; +} + +/** + * Allocate and initialize a new synthesized while loop. + */ +static pm_while_node_t * +pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_statements_node_t *statements) { + pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t); + + *node = (pm_while_node_t) { + { + .type = PM_WHILE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_NULL_VALUE(parser) + }, + .keyword_loc = PM_LOCATION_NULL_VALUE(parser), + .do_keyword_loc = PM_LOCATION_NULL_VALUE(parser), + .closing_loc = PM_LOCATION_NULL_VALUE(parser), + .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_NODE_ALLOC(parser, pm_x_string_node_t); + + *node = (pm_x_string_node_t) { + { + .type = PM_X_STRING_NODE, + .flags = PM_STRING_FLAGS_FROZEN, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC(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, + .node_id = PM_NODE_IDENTIFY(parser), + .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_NODE_ALLOC +#undef PM_NODE_IDENTIFY + +/** + * 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 (pm_locals_find(&scope->locals, constant_id) != UINT32_MAX) 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, const uint8_t *start, const uint8_t *end, uint32_t reads) { + pm_locals_write(&parser->current_scope->locals, constant_id, start, end, reads); +} + +/** + * 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, uint32_t reads) { + 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, start, end, reads); + return constant_id; +} + +/** + * Add a local variable from a token to the current scope. + */ +static inline pm_constant_id_t +pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token, uint32_t reads) { + return pm_parser_local_add_location(parser, token->start, token->end, reads); +} + +/** + * 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, 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, parser->start, parser->start, 1); + return constant_id; +} + +/** + * Add a local variable from a constant string to the current scope. + */ +static pm_constant_id_t +pm_parser_local_add_constant(pm_parser_t *parser, const char *start, size_t length) { + pm_constant_id_t constant_id = pm_parser_constant_id_constant(parser, start, length); + if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1); + return constant_id; +} + +/** + * Add a parameter name to the current scope and check whether the name of the + * parameter is unique or not. + * + * Returns `true` if this is a duplicate parameter name, otherwise returns + * false. + */ +static bool +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); + + // 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_locals_find(&parser->current_scope->locals, constant_id) != UINT32_MAX) { + // Add an error if the parameter doesn't start with _ and has been seen before + if ((name->start < name->end) && (*name->start != '_')) { + pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_DUPLICATED); + } + return true; + } + return false; +} + +/** + * Pop the current scope off the scope stack. + */ +static void +pm_parser_scope_pop(pm_parser_t *parser) { + pm_scope_t *scope = parser->current_scope; + parser->current_scope = scope->previous; + pm_locals_free(&scope->locals); + pm_node_list_free(&scope->implicit_parameters); + xfree(scope); +} + +/******************************************************************************/ +/* Stack helpers */ +/******************************************************************************/ + +/** + * Pushes a value onto the stack. + */ +static inline void +pm_state_stack_push(pm_state_stack_t *stack, bool value) { + *stack = (*stack << 1) | (value & 1); +} + +/** + * Pops a value off the stack. + */ +static inline void +pm_state_stack_pop(pm_state_stack_t *stack) { + *stack >>= 1; +} + +/** + * Returns the value at the top of the stack. + */ +static inline bool +pm_state_stack_p(const pm_state_stack_t *stack) { + return *stack & 1; +} + +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(const 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(const 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 + * advances 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); +} + +/** + * This is equivalent to the predicate of warn_balanced in CRuby. + */ +static inline bool +ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) { + return !lex_state_p(parser, PM_LEX_STATE_CLASS | PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME | PM_LEX_STATE_ENDFN) && space_seen && !pm_char_is_whitespace(peek(parser)); +} + +/** + * 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) { + const pm_encoding_t *encoding = pm_encoding_find(start, end); + + if (encoding != NULL) { + if (parser->encoding != encoding) { + parser->encoding = encoding; + if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser); + } + + parser->encoding_changed = (encoding != PM_ENCODING_UTF_8_ENTRY); + return true; + } + + 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); + } +} + +typedef enum { + PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE, + PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE, + PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID +} pm_magic_comment_boolean_value_t; + +/** + * Check if this is a magic comment that includes the frozen_string_literal + * pragma. If it does, set that field on the parser. + */ +static pm_magic_comment_boolean_value_t +parser_lex_magic_comment_boolean_value(const uint8_t *value_start, uint32_t value_length) { + if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "true", 4) == 0) { + return PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE; + } else if (value_length == 5 && pm_strncasecmp(value_start, (const uint8_t *) "false", 5) == 0) { + return PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE; + } else { + return PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID; + } +} + +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; + if (*cursor == '"') 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, '-', key_length, parser->encoding_changed, parser->encoding); + + if (dash == NULL) { + pm_string_shared_init(&key, key_start, key_end); + } else { + uint8_t *buffer = xmalloc(key_length); + if (buffer == NULL) break; + + memcpy(buffer, key_start, key_length); + 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, key_length); + } + + // 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); + uint32_t value_length = (uint32_t) (value_end - value_start); + + // 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 && !parser->encoding_locked) { + 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); + } + } + + if (key_length == 11) { + if (pm_strncasecmp(key_source, (const uint8_t *) "warn_indent", 11) == 0) { + switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) { + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID: + PM_PARSER_WARN_TOKEN_FORMAT( + parser, + parser->current, + PM_WARN_INVALID_MAGIC_COMMENT_VALUE, + (int) key_length, + (const char *) key_source, + (int) value_length, + (const char *) value_start + ); + break; + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE: + parser->warn_mismatched_indentation = false; + break; + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE: + parser->warn_mismatched_indentation = true; + break; + } + } + } else if (key_length == 21) { + if (pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) { + // We only want to handle frozen string literal comments if it's + // before any semantic tokens have been seen. + if (semantic_token_seen) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL); + } else { + switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) { + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID: + PM_PARSER_WARN_TOKEN_FORMAT( + parser, + parser->current, + PM_WARN_INVALID_MAGIC_COMMENT_VALUE, + (int) key_length, + (const char *) key_source, + (int) value_length, + (const char *) value_start + ); + break; + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE: + parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED; + break; + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE: + parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED; + break; + } + } + } + } else if (key_length == 24) { + if (pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) { + const uint8_t *cursor = parser->current.start; + while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--; + + if (!((cursor == parser->start) || (cursor[-1] == '\n'))) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE); + } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) { + pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE); + } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) { + pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL); + } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) { + pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING); + } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) { + pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY); + } else { + PM_PARSER_WARN_TOKEN_FORMAT( + parser, + parser->current, + PM_WARN_INVALID_MAGIC_COMMENT_VALUE, + (int) key_length, + (const char *) key_source, + (int) value_length, + (const char *) value_start + ); + } + } + } + + // 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 *) xcalloc(1, sizeof(pm_magic_comment_t))) != 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 = value_length; + 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: + case PM_CONTEXT_DEFINED: + case PM_CONTEXT_MULTI_TARGET: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_RESCUE_MODIFIER: + 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_BEGIN_ENSURE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_SCLASS_ENSURE: + return token->type == PM_TOKEN_KEYWORD_END; + case PM_CONTEXT_LOOP_PREDICATE: + return token->type == PM_TOKEN_KEYWORD_DO || token->type == PM_TOKEN_KEYWORD_THEN; + 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_BEGIN_RESCUE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_SCLASS_RESCUE: + 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_BEGIN_ELSE: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_SCLASS_ELSE: + 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; + case PM_CONTEXT_NONE: + return false; + } + + return false; +} + +/** + * Returns the context that the given token is found to be terminating, or + * returns PM_CONTEXT_NONE. + */ +static pm_context_t +context_recoverable(const 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 context_node->context; + context_node = context_node->prev; + } + + return PM_CONTEXT_NONE; +} + +static bool +context_push(pm_parser_t *parser, pm_context_t context) { + pm_context_node_t *context_node = (pm_context_node_t *) xmalloc(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; + xfree(parser->current_context); + parser->current_context = prev; +} + +static bool +context_p(const 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(const 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_DEF_ENSURE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_DEF_ELSE: + return true; + case PM_CONTEXT_CLASS: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_MODULE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_SCLASS: + case PM_CONTEXT_SCLASS_ENSURE: + case PM_CONTEXT_SCLASS_RESCUE: + case PM_CONTEXT_SCLASS_ELSE: + return false; + default: + context_node = context_node->prev; + } + } + + return false; +} + +/** + * Returns a human readable string for the given context, used in error + * messages. + */ +static const char * +context_human(pm_context_t context) { + switch (context) { + case PM_CONTEXT_NONE: + assert(false && "unreachable"); + return ""; + case PM_CONTEXT_BEGIN: return "begin statement"; + case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block"; + case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block"; + case PM_CONTEXT_CASE_WHEN: return "'when' clause"; + case PM_CONTEXT_CASE_IN: return "'in' clause"; + case PM_CONTEXT_CLASS: return "class definition"; + case PM_CONTEXT_DEF: return "method definition"; + case PM_CONTEXT_DEF_PARAMS: return "method parameters"; + case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value"; + case PM_CONTEXT_DEFINED: return "'defined?' expression"; + case PM_CONTEXT_ELSE: + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_SCLASS_ELSE: return "'else' clause"; + case PM_CONTEXT_ELSIF: return "'elsif' clause"; + case PM_CONTEXT_EMBEXPR: return "embedded expression"; + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause"; + case PM_CONTEXT_FOR: return "for loop"; + case PM_CONTEXT_FOR_INDEX: return "for loop index"; + case PM_CONTEXT_IF: return "if statement"; + case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block"; + case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block"; + case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate"; + case PM_CONTEXT_MAIN: return "top level context"; + case PM_CONTEXT_MODULE: return "module definition"; + case PM_CONTEXT_MULTI_TARGET: return "multiple targets"; + case PM_CONTEXT_PARENS: return "parentheses"; + case PM_CONTEXT_POSTEXE: return "'END' block"; + case PM_CONTEXT_PREDICATE: return "predicate"; + case PM_CONTEXT_PREEXE: return "'BEGIN' block"; + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_RESCUE_MODIFIER: + case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause"; + case PM_CONTEXT_SCLASS: return "singleton class definition"; + case PM_CONTEXT_TERNARY: return "ternary expression"; + case PM_CONTEXT_UNLESS: return "unless statement"; + case PM_CONTEXT_UNTIL: return "until statement"; + case PM_CONTEXT_WHILE: return "while statement"; + } + + assert(false && "unreachable"); + return ""; +} + +/******************************************************************************/ +/* Specific token lexers */ +/******************************************************************************/ + +static inline void +pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *string, size_t length, const uint8_t *invalid) { + if (invalid != NULL) { + pm_diagnostic_id_t diag_id = (invalid == (string + length - 1)) ? PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING : PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER; + pm_parser_err(parser, invalid, invalid + 1, diag_id); + } +} + +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, string, length, 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, string, length, 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, string, length, 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, string, length, 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 ((peek(parser) == 'e') || (peek(parser) == 'E')) { + if ((peek_offset(parser, 1) == '+') || (peek_offset(parser, 1) == '-')) { + parser->current.end += 2; + + if (pm_char_is_decimal_digit(peek(parser))) { + parser->current.end++; + parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end); + } else { + pm_parser_err_current(parser, PM_ERR_INVALID_FLOAT_EXPONENT); + } + } else if (pm_char_is_decimal_digit(peek_offset(parser, 1))) { + parser->current.end++; + parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end); + } else { + return type; + } + + *seen_e = true; + 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 { + match(parser, '_'); + 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 { + match(parser, '_'); + 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 { + match(parser, '_'); + 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 { + match(parser, '_'); + 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); + } + + // At this point we have a completed number, but we want to provide the user + // with a good experience if they put an additional .xxx fractional + // component on the end, so we'll check for that here. + if (peek_offset(parser, 0) == '.' && pm_char_is_decimal_digit(peek_offset(parser, 1))) { + const uint8_t *fraction_start = parser->current.end; + const uint8_t *fraction_end = parser->current.end + 2; + fraction_end += pm_strspn_decimal_digit(fraction_end, parser->end - fraction_end); + pm_parser_err(parser, fraction_start, fraction_end, PM_ERR_INVALID_NUMBER_FRACTION); + } + + 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_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE); + return PM_TOKEN_GLOBAL_VARIABLE; + } + + // True if multiple characters are allowed after the declaration of the + // global variable. Not true when it starts with "$-". + bool allow_multiple = true; + + 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 ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) { + do { + parser->current.end += width; + } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0); + + // $0 isn't allowed to be followed by anything. + pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id); + } + + 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++; + allow_multiple = false; + PRISM_FALLTHROUGH + default: { + size_t width; + + if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) { + do { + parser->current.end += width; + } while (allow_multiple && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0); + } else if (pm_char_is_whitespace(peek(parser))) { + // If we get here, then we have a $ followed by whitespace, + // which is not allowed. + pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE); + } else { + // If we get here, then we have a $ followed by something that + // isn't recognized as a global variable. + pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - parser->current.start), (const char *) parser->current.start); + } + + 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 ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) { + current_end += width; + } + } else { + while ((width = char_is_identifier_utf8(current_end, end - current_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 that 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, parser->end - 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 a 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, parser->end - 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(pm_parser_t *parser, 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]); + } + + // Here we're going to verify that the value is actually a valid Unicode + // codepoint and not a surrogate pair. + if (value >= 0xD800 && value <= 0xDFFF) { + pm_parser_err(parser, string, string + length, PM_ERR_ESCAPE_INVALID_UNICODE); + return 0xFFFD; + } + + 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 &= 0x9f; + 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 flags, const uint8_t *start, const uint8_t *end, uint32_t value) { + // \u escape sequences in string-like structures implicitly change the + // encoding to UTF-8 if they are >= 0x80 or if they are used in a character + // literal. + if (value >= 0x80 || flags & PM_ESCAPE_FLAG_SINGLE) { + if (parser->explicit_encoding != NULL && parser->explicit_encoding != PM_ENCODING_UTF_8_ENTRY) { + PM_PARSER_ERR_FORMAT(parser, start, end, PM_ERR_MIXED_ENCODING, parser->explicit_encoding->name); + } + + parser->explicit_encoding = PM_ENCODING_UTF_8_ENTRY; + } + + if (!pm_buffer_append_unicode_codepoint(buffer, value)) { + 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); + } +} + +/** + * When you're writing a byte to the unescape buffer, if the byte is non-ASCII + * (i.e., the top bit is set) then it locks in the encoding. + */ +static inline void +escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, uint8_t byte) { + if (byte >= 0x80) { + if (parser->explicit_encoding != NULL && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && parser->encoding != PM_ENCODING_UTF_8_ENTRY) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_MIXED_ENCODING, parser->encoding->name); + } + + parser->explicit_encoding = parser->encoding; + } + + pm_buffer_append_byte(buffer, byte); +} + +/** + * 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_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags, uint8_t byte) { + if (flags & PM_ESCAPE_FLAG_REGEXP) { + pm_buffer_append_format(regular_expression_buffer, "\\x%02X", byte); + } + + escape_write_byte_encoded(parser, buffer, byte); +} + +/** + * Write each byte of the given escaped character into the buffer. + */ +static inline void +escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) { + size_t width; + if (parser->encoding_changed) { + width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + } else { + width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end); + } + + if (width == 1) { + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(*parser->current.end++, flags)); + } else if (width > 1) { + // Valid multibyte character. Just ignore escape. + pm_buffer_t *b = (flags & PM_ESCAPE_FLAG_REGEXP) ? regular_expression_buffer : buffer; + pm_buffer_append_bytes(b, parser->current.end, width); + parser->current.end += width; + } else { + // Assume the next character wasn't meant to be part of this escape + // sequence since it is invalid. Add an error and move on. + parser->current.end++; + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + } +} + +/** + * Warn about using a space or a tab character in an escape, as opposed to using + * \\s or \\t. Note that we can quite copy the source because the warning + * message replaces \\c with \\C. + */ +static void +escape_read_warn(pm_parser_t *parser, uint8_t flags, uint8_t flag, const char *type) { +#define FLAG(value) ((value & PM_ESCAPE_FLAG_CONTROL) ? "\\C-" : (value & PM_ESCAPE_FLAG_META) ? "\\M-" : "") + + PM_PARSER_WARN_TOKEN_FORMAT( + parser, + parser->current, + PM_WARN_INVALID_CHARACTER, + FLAG(flags), + FLAG(flag), + type + ); + +#undef FLAG +} + +/** + * Read the value of an escape into the buffer. + */ +static void +escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) { + uint8_t peeked = peek(parser); + switch (peeked) { + case '\\': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\\', flags)); + return; + } + case '\'': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\'', flags)); + return; + } + case 'a': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\a', flags)); + return; + } + case 'b': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\b', flags)); + return; + } + case 'e': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\033', flags)); + return; + } + case 'f': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\f', flags)); + return; + } + case 'n': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\n', flags)); + return; + } + case 'r': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\r', flags)); + return; + } + case 's': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(' ', flags)); + return; + } + case 't': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\t', flags)); + return; + } + case 'v': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\v', flags)); + 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++; + } + } + + value = escape_byte(value, flags); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, 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++; + } + + value = escape_byte(value, flags); + if (flags & PM_ESCAPE_FLAG_REGEXP) { + if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) { + pm_buffer_append_format(regular_expression_buffer, "\\x%02X", value); + } else { + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } + } + + escape_write_byte_encoded(parser, 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 == parser->end) { + const uint8_t *start = parser->current.end - 2; + PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start); + } else if (peek(parser) == '{') { + const uint8_t *unicode_codepoints_start = parser->current.end - 2; + parser->current.end++; + + size_t whitespace; + while (true) { + if ((whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end)) > 0) { + parser->current.end += whitespace; + } else if (peek(parser) == '\\' && peek_offset(parser, 1) == 'n') { + // This is super hacky, but it gets us nicer error + // messages because we can still pass it off to the + // regular expression engine even if we hit an + // unterminated regular expression. + parser->current.end += 2; + } else { + break; + } + } + + 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 + + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // If this is a regular expression, we are going to + // let the regular expression engine handle this + // error instead of us. + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } else { + pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE); + pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_TERM); + } + + return; + } + + parser->current.end += hexadecimal_length; + codepoints_count++; + if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count == 2) { + extra_codepoints_start = unicode_start; + } + + uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length); + escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value); + + parser->current.end += pm_strspn_inline_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 (parser->current.end == parser->end) { + PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_LIST, (int) (parser->current.end - start), start); + } else if (peek(parser) == '}') { + parser->current.end++; + } else { + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // If this is a regular expression, we are going to let + // the regular expression engine handle this error + // instead of us. + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } 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(regular_expression_buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start)); + } + } else { + size_t length = pm_strspn_hexadecimal_digit(parser->current.end, MIN(parser->end - parser->current.end, 4)); + + if (length == 0) { + if (flags & PM_ESCAPE_FLAG_REGEXP) { + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } else { + const uint8_t *start = parser->current.end - 2; + PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start); + } + } else if (length == 4) { + uint32_t value = escape_unicode(parser, parser->current.end, 4); + + if (flags & PM_ESCAPE_FLAG_REGEXP) { + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start)); + } + + escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value); + parser->current.end += 4; + } else { + parser->current.end += length; + + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // If this is a regular expression, we are going to let + // the regular expression engine handle this error + // instead of us. + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } else { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE); + } + } + } + + return; + } + case 'c': { + parser->current.end++; + if (flags & PM_ESCAPE_FLAG_CONTROL) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT); + } + + 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(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags)); + return; + } + case '\\': + parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + + escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL); + return; + case ' ': + parser->current.end++; + escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s"); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL)); + return; + case '\t': + parser->current.end++; + escape_read_warn(parser, flags, 0, "\\t"); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, 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(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL)); + return; + } + } + } + case 'C': { + parser->current.end++; + if (flags & PM_ESCAPE_FLAG_CONTROL) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT); + } + + if (peek(parser) != '-') { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, 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(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags)); + return; + } + case '\\': + parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + + escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL); + return; + case ' ': + parser->current.end++; + escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s"); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL)); + return; + case '\t': + parser->current.end++; + escape_read_warn(parser, flags, 0, "\\t"); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL)); + return; + default: { + if (!char_is_ascii_printable(peeked)) { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_CONTROL); + return; + } + + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL)); + return; + } + } + } + case 'M': { + parser->current.end++; + if (flags & PM_ESCAPE_FLAG_META) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT); + } + + if (peek(parser) != '-') { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, 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); + switch (peeked) { + case '\\': + parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + + escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_META); + return; + case ' ': + parser->current.end++; + escape_read_warn(parser, flags, PM_ESCAPE_FLAG_META, "\\s"); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META)); + return; + case '\t': + parser->current.end++; + escape_read_warn(parser, flags & ((uint8_t) ~PM_ESCAPE_FLAG_CONTROL), PM_ESCAPE_FLAG_META, "\\t"); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META)); + return; + default: + if (!char_is_ascii_printable(peeked)) { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META); + return; + } + + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META)); + return; + } + } + case '\r': { + if (peek_offset(parser, 1) == '\n') { + parser->current.end += 2; + escape_write_byte_encoded(parser, buffer, escape_byte('\n', flags)); + return; + } + PRISM_FALLTHROUGH + } + default: { + if ((flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) && !char_is_ascii_printable(peeked)) { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META); + return; + } + if (parser->current.end < parser->end) { + escape_write_escape_encoded(parser, buffer, regular_expression_buffer, flags); + } else { + pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER); + } + 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, NULL, 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 here. + 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, parser->end - (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; + const uint8_t *end = parser->end; + + size_t width; + if ((width = char_is_identifier_start(parser, parser->current.end, end - parser->current.end)) > 0) { + parser->current.end += width; + + while ((width = char_is_identifier(parser, parser->current.end, end - parser->current.end)) > 0) { + parser->current.end += width; + } + } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) { + pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE; + if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) { + diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3; + } + + size_t width = parser->encoding->char_width(parser->current.end, end - parser->current.end); + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start); + } else { + pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_CLASS_VARIABLE_BARE : PM_ERR_INSTANCE_VARIABLE_BARE; + pm_parser_err_token(parser, &parser->current, diag_id); + } + + // 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 *) xcalloc(1, sizeof(pm_comment_t)); + if (comment == NULL) return NULL; + + *comment = (pm_comment_t) { + .type = type, + .location = { parser->current.start, 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) || // end of file + pm_char_is_whitespace(parser->current.end[4]) || // whitespace + (parser->current.end[4] == '\0') || // NUL or end of script + (parser->current.end[4] == '\004') || // ^D + (parser->current.end[4] == '\032') // ^Z + ) + ) { + 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->location.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->location.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; +} + +/** + * Returns true if the parser has lexed the last token on the current line. +*/ +static bool +parser_end_of_line_p(const pm_parser_t *parser) { + const uint8_t *cursor = parser->current.end; + + while (cursor < parser->end && *cursor != '\n' && *cursor != '#') { + if (!pm_char_is_inline_whitespace(*cursor++)) return false; + } + + return true; +} + +/** + * 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 our 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; + +/** + * In order to properly set a regular expression's encoding and to validate + * the byte sequence for the underlying encoding we must process any escape + * sequences. The unescaped byte sequence will be stored in `buffer` just like + * for other string-like types. However, we also need to store the regular + * expression's source string. That string may be different from what we see + * during lexing because some escape sequences rewrite the source. + * + * This value will only be initialized for regular expressions and only if we + * receive an escape sequence. It will contain the regular expression's source + * string's byte sequence. + */ +typedef struct { + /** The embedded base buffer. */ + pm_token_buffer_t base; + + /** The buffer holding the regexp source. */ + pm_buffer_t regexp_buffer; +} pm_regexp_token_buffer_t; + +/** + * Push the given byte into the token buffer. + */ +static inline void +pm_token_buffer_push_byte(pm_token_buffer_t *token_buffer, uint8_t byte) { + pm_buffer_append_byte(&token_buffer->buffer, byte); +} + +static inline void +pm_regexp_token_buffer_push_byte(pm_regexp_token_buffer_t *token_buffer, uint8_t byte) { + pm_buffer_append_byte(&token_buffer->regexp_buffer, byte); +} + +/** + * Return the width of the character at the end of the current token. + */ +static inline size_t +parser_char_width(const pm_parser_t *parser) { + size_t width; + if (parser->encoding_changed) { + width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + } else { + width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end); + } + + // TODO: If the character is invalid in the given encoding, then we'll just + // push one byte into the buffer. This should actually be an error. + return (width == 0 ? 1 : width); +} + +/** + * Push an escaped character into the token buffer. + */ +static void +pm_token_buffer_push_escaped(pm_token_buffer_t *token_buffer, pm_parser_t *parser) { + size_t width = parser_char_width(parser); + pm_buffer_append_bytes(&token_buffer->buffer, parser->current.end, width); + parser->current.end += width; +} + +static void +pm_regexp_token_buffer_push_escaped(pm_regexp_token_buffer_t *token_buffer, pm_parser_t *parser) { + size_t width = parser_char_width(parser); + pm_buffer_append_bytes(&token_buffer->base.buffer, parser->current.end, width); + pm_buffer_append_bytes(&token_buffer->regexp_buffer, parser->current.end, width); + parser->current.end += width; +} + +static bool +pm_slice_ascii_only_p(const uint8_t *value, size_t length) { + for (size_t index = 0; index < length; index++) { + if (value[index] & 0x80) return false; + } + + return true; +} + +/** + * 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 *) pm_buffer_value(&token_buffer->buffer), pm_buffer_length(&token_buffer->buffer)); +} + +static inline void +pm_regexp_token_buffer_copy(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) { + pm_string_owned_init(&parser->current_string, (uint8_t *) pm_buffer_value(&token_buffer->base.buffer), pm_buffer_length(&token_buffer->base.buffer)); + parser->current_regular_expression_ascii_only = pm_slice_ascii_only_p((const uint8_t *) pm_buffer_value(&token_buffer->regexp_buffer), pm_buffer_length(&token_buffer->regexp_buffer)); + pm_buffer_free(&token_buffer->regexp_buffer); +} + +/** + * 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); + } +} + +static void +pm_regexp_token_buffer_flush(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) { + if (token_buffer->base.cursor == NULL) { + pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end); + parser->current_regular_expression_ascii_only = pm_slice_ascii_only_p(parser->current.start, (size_t) (parser->current.end - parser->current.start)); + } else { + pm_buffer_append_bytes(&token_buffer->base.buffer, token_buffer->base.cursor, (size_t) (parser->current.end - token_buffer->base.cursor)); + pm_buffer_append_bytes(&token_buffer->regexp_buffer, token_buffer->base.cursor, (size_t) (parser->current.end - token_buffer->base.cursor)); + pm_regexp_token_buffer_copy(parser, token_buffer); + } +} + +#define PM_TOKEN_BUFFER_DEFAULT_SIZE 16 + +/** + * 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, PM_TOKEN_BUFFER_DEFAULT_SIZE); + start = parser->current.start; + } else { + start = token_buffer->cursor; + } + + const uint8_t *end = parser->current.end - 1; + assert(end >= start); + pm_buffer_append_bytes(&token_buffer->buffer, start, (size_t) (end - start)); + + token_buffer->cursor = end; +} + +static void +pm_regexp_token_buffer_escape(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) { + const uint8_t *start; + if (token_buffer->base.cursor == NULL) { + pm_buffer_init_capacity(&token_buffer->base.buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE); + pm_buffer_init_capacity(&token_buffer->regexp_buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE); + start = parser->current.start; + } else { + start = token_buffer->base.cursor; + } + + const uint8_t *end = parser->current.end - 1; + pm_buffer_append_bytes(&token_buffer->base.buffer, start, (size_t) (end - start)); + pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, (size_t) (end - start)); + + token_buffer->base.cursor = end; +} + +#undef PM_TOKEN_BUFFER_DEFAULT_SIZE + +/** + * 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; +} + +/** + * Lex past the delimiter of a percent literal. Handle newlines and heredocs + * appropriately. + */ +static uint8_t +pm_lex_percent_delimiter(pm_parser_t *parser) { + size_t eol_length = match_eol(parser); + + if (eol_length) { + if (parser->heredoc_end) { + // If we have already lexed a heredoc, then the newline has already + // been added to the list. In this case we want to just flush the + // heredoc end. + parser_flush_heredoc_end(parser); + } else { + // Otherwise, we'll add the newline to the list of newlines. + pm_newline_list_append(&parser->newline_list, parser->current.end + eol_length - 1); + } + + uint8_t delimiter = *parser->current.end; + + // If our delimiter is \r\n, we want to treat it as if it's \n. + // For example, %\r\nfoo\r\n should be "foo" + if (eol_length == 2) { + delimiter = *(parser->current.end + 1); + } + + parser->current.end += eol_length; + return delimiter; + } + + return *parser->current.end++; +} + +/** + * 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 { + pm_parser_warn(parser, parser->current.end, parser->current.end + 1, PM_WARN_UNEXPECTED_CARRIAGE_RETURN); + 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) { + // If we hit EOF, but the EOF came immediately after a newline, + // set the start of the token to the newline. This way any EOF + // errors will be reported as happening on that line rather than + // a line after. For example "foo(\n" should report an error + // on line 1 even though EOF technically occurs on line 2. + if (parser->current.start > parser->start && (*(parser->current.start - 1) == '\n')) { + parser->current.start -= 1; + } + 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->encoding_locked) { + parser_lex_magic_comment_encoding(parser); + } + } + + lexed_comment = true; + } + PRISM_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; + } + PRISM_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 + // it's 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 ',': + if ((parser->previous.type == PM_TOKEN_COMMA) && (parser->enclosure_nesting > 0)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARRAY_TERM, pm_token_type_human(parser->current.type)); + } + + 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)) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR); + type = PM_TOKEN_USTAR_STAR; + } else if (lex_state_beg_p(parser)) { + type = PM_TOKEN_USTAR_STAR; + } else if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "**", "argument prefix"); + } + + 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; + } else if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "*", "argument prefix"); + } + + 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)) || (peek_offset(parser, 5) == '\0')) + ) { + 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, parser->end - parser->current.end)) == 0) { + parser->current.end = end; + } else { + if (quote == PM_HEREDOC_QUOTE_NONE) { + parser->current.end += width; + + while ((width = char_is_identifier(parser, parser->current.end, parser->end - 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)) { + if (*parser->current.end == '\r' || *parser->current.end == '\n') break; + parser->current.end++; + } + } + + size_t ident_length = (size_t) (parser->current.end - ident_start); + bool ident_error = false; + + if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) { + pm_parser_err(parser, ident_start, ident_start + ident_length, PM_ERR_HEREDOC_IDENTIFIER); + ident_error = true; + } + + parser->explicit_encoding = NULL; + lex_mode_push(parser, (pm_lex_mode_t) { + .mode = PM_LEX_HEREDOC, + .as.heredoc = { + .base = { + .ident_start = ident_start, + .ident_length = ident_length, + .quote = quote, + .indent = indent + }, + .next_start = parser->current.end, + .common_whitespace = NULL, + .line_continuation = false + } + }); + + 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. + if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length); + 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 (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "<<", "here document"); + } + + 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)) { + if ((peek(parser) != ':') || (peek_offset(parser, 1) == '\0')) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND); + } else { + const uint8_t delim = peek_offset(parser, 1); + + if ((delim != '\'') && (delim != '"') && !char_is_identifier(parser, parser->current.end + 1, parser->end - (parser->current.end + 1))) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND); + } + } + + type = PM_TOKEN_UAMPERSAND; + } else if (lex_state_beg_p(parser)) { + type = PM_TOKEN_UAMPERSAND; + } else if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "&", "argument prefix"); + } + + 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); + } + + if ( + lex_state_beg_p(parser) || + (lex_state_spcarg_p(parser, space_seen) ? (pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS), true) : false) + ) { + 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); + } + + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "+", "unary operator"); + } + + 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); + bool is_beg = lex_state_beg_p(parser); + if (!is_beg && spcarg) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS); + } + + if (is_beg || spcarg) { + lex_state_set(parser, PM_LEX_STATE_BEG); + LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS); + } + + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "-", "unary operator"); + } + + 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); + } + + if (parser->enclosure_nesting == 0 && parser_end_of_line_p(parser)) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_DOT_DOT_DOT_EOL); + } + + 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 (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "/", "regexp literal"); + } + + 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_EOF); + 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); + } + + const uint8_t delimiter = pm_lex_percent_delimiter(parser); + lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter)); + 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, pm_lex_percent_delimiter(parser)); + } 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, pm_lex_percent_delimiter(parser)); + } else { + lex_mode_push_list_eof(parser); + } + + LEX(PM_TOKEN_PERCENT_UPPER_I); + } + case 'r': { + parser->current.end++; + + if (parser->current.end < parser->end) { + const uint8_t delimiter = pm_lex_percent_delimiter(parser); + lex_mode_push_regexp(parser, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter)); + } else { + lex_mode_push_regexp(parser, '\0', '\0'); + } + + LEX(PM_TOKEN_REGEXP_BEGIN); + } + case 'q': { + parser->current.end++; + + if (parser->current.end < parser->end) { + const uint8_t delimiter = pm_lex_percent_delimiter(parser); + lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter)); + } else { + lex_mode_push_string_eof(parser); + } + + LEX(PM_TOKEN_STRING_BEGIN); + } + case 'Q': { + parser->current.end++; + + if (parser->current.end < parser->end) { + const uint8_t delimiter = pm_lex_percent_delimiter(parser); + lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter)); + } else { + lex_mode_push_string_eof(parser); + } + + LEX(PM_TOKEN_STRING_BEGIN); + } + case 's': { + parser->current.end++; + + if (parser->current.end < parser->end) { + const uint8_t delimiter = pm_lex_percent_delimiter(parser); + lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter)); + lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM); + } 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, pm_lex_percent_delimiter(parser)); + } 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, pm_lex_percent_delimiter(parser)); + } else { + lex_mode_push_list_eof(parser); + } + + LEX(PM_TOKEN_PERCENT_UPPER_W); + } + case 'x': { + parser->current.end++; + + if (parser->current.end < parser->end) { + const uint8_t delimiter = pm_lex_percent_delimiter(parser); + lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter)); + } 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 + // unparsable. 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; + } + } + + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "%", "string literal"); + } + + 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, parser->end - 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 after adding an appropriate error message. + if (!width) { + if (*parser->current.start >= 0x80) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *parser->current.start); + } else if (*parser->current.start == '\\') { + switch (peek_at(parser, parser->current.start + 1)) { + case ' ': + parser->current.end++; + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped space"); + break; + case '\f': + parser->current.end++; + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped form feed"); + break; + case '\t': + parser->current.end++; + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped horizontal tab"); + break; + case '\v': + parser->current.end++; + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped vertical tab"); + break; + case '\r': + if (peek_at(parser, parser->current.start + 2) != '\n') { + parser->current.end++; + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped carriage return"); + break; + } + PRISM_FALLTHROUGH + default: + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "backslash"); + break; + } + } else if (char_is_ascii_printable(*parser->current.start)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_PRINTABLE_CHARACTER, *parser->current.start); + } else { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_CHARACTER, *parser->current.start); + } + + 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 to 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, true); + + // 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 }; + + while (breakpoint != NULL) { + // 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, true); + 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 a null byte, skip directly past it. + if (*breakpoint == '\0') { + breakpoint = pm_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1), true); + continue; + } + + // 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_byte(&token_buffer, peeked); + parser->current.end++; + break; + case '\r': + parser->current.end++; + if (peek(parser) != '\n') { + pm_token_buffer_push_byte(&token_buffer, '\r'); + break; + } + PRISM_FALLTHROUGH + case '\n': + pm_token_buffer_push_byte(&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_byte(&token_buffer, peeked); + parser->current.end++; + } else if (lex_mode->as.list.interpolation) { + escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE); + } else { + pm_token_buffer_push_byte(&token_buffer, '\\'); + pm_token_buffer_push_escaped(&token_buffer, parser); + } + + break; + } + + token_buffer.cursor = parser->current.end; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true); + 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, true); + 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, true); + 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. + parser->current.end = parser->end; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + 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, false); + pm_regexp_token_buffer_t token_buffer = { 0 }; + + while (breakpoint != NULL) { + uint8_t term = lex_mode->as.regexp.terminator; + bool is_terminator = (*breakpoint == term); + + // If the terminator is newline, we need to consider \r\n _also_ a newline + // For example: `%\nfoo\r\n` + // The string should be "foo", not "foo\r" + if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') { + if (term == '\n') { + is_terminator = true; + } + + // If the terminator is a CR, but we see a CRLF, we need to + // treat the CRLF as a newline, meaning this is _not_ the + // terminator + if (term == '\r') { + is_terminator = false; + } + } + + // If we hit the terminator, we need to determine what kind of + // token to return. + if (is_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, false); + 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_regexp_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + + // Check here if we need to track the newline. + 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; + } + + // Since we've hit the terminator of the regular expression, + // we now need to parse the options. + 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've hit the incrementor, then we need to skip past it + // and find the next breakpoint. + if (*breakpoint && *breakpoint == lex_mode->as.regexp.incrementor) { + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false); + lex_mode->as.regexp.nesting++; + continue; + } + + switch (*breakpoint) { + case '\0': + // If we hit a null byte, skip directly past it. + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false); + break; + case '\r': + if (peek_at(parser, breakpoint + 1) != '\n') { + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false); + break; + } + + breakpoint++; + parser->current.end = breakpoint; + pm_regexp_token_buffer_escape(parser, &token_buffer); + token_buffer.base.cursor = breakpoint; + + PRISM_FALLTHROUGH + case '\n': + // If we've hit a newline, then we need to track that in + // the list of newlines. + 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, false); + break; + } + + parser->current.end = breakpoint + 1; + parser_flush_heredoc_end(parser); + pm_regexp_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + case '\\': { + // 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. + 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; + break; + } + + pm_regexp_token_buffer_escape(parser, &token_buffer); + uint8_t peeked = peek(parser); + + switch (peeked) { + case '\r': + parser->current.end++; + if (peek(parser) != '\n') { + if (lex_mode->as.regexp.terminator != '\r') { + pm_token_buffer_push_byte(&token_buffer.base, '\\'); + } + pm_regexp_token_buffer_push_byte(&token_buffer, '\r'); + pm_token_buffer_push_byte(&token_buffer.base, '\r'); + break; + } + PRISM_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_regexp_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.regexp_buffer, &token_buffer.base.buffer, PM_ESCAPE_FLAG_REGEXP); + break; + default: + if (lex_mode->as.regexp.terminator == peeked) { + // Some characters when they are used as the + // terminator also receive an escape. They are + // enumerated here. + switch (peeked) { + case '$': case ')': case '*': case '+': + case '.': case '>': case '?': case ']': + case '^': case '|': case '}': + pm_token_buffer_push_byte(&token_buffer.base, '\\'); + break; + default: + break; + } + + pm_regexp_token_buffer_push_byte(&token_buffer, peeked); + pm_token_buffer_push_byte(&token_buffer.base, peeked); + parser->current.end++; + break; + } + + if (peeked < 0x80) pm_token_buffer_push_byte(&token_buffer.base, '\\'); + pm_regexp_token_buffer_push_escaped(&token_buffer, parser); + break; + } + + token_buffer.base.cursor = parser->current.end; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false); + break; + } + case '#': { + // If we hit a #, then we will attempt to lex + // interpolation. + 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, false); + break; + } + + if (type == PM_TOKEN_STRING_CONTENT) { + pm_regexp_token_buffer_flush(parser, &token_buffer); + } + + LEX(type); + } + default: + assert(false && "unreachable"); + break; + } + } + + if (parser->current.end > parser->current.start) { + pm_regexp_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. + parser->current.end = parser->end; + pm_regexp_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + 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, true); + + // 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 }; + + 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, true); + continue; + } + + uint8_t term = lex_mode->as.string.terminator; + bool is_terminator = (*breakpoint == term); + + // If the terminator is newline, we need to consider \r\n _also_ a newline + // For example: `%r\nfoo\r\n` + // The string should be /foo/, not /foo\r/ + if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') { + if (term == '\n') { + is_terminator = true; + } + + // If the terminator is a CR, but we see a CRLF, we need to + // treat the CRLF as a newline, meaning this is _not_ the + // terminator + if (term == '\r') { + is_terminator = false; + } + } + + // 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 (is_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, true); + 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); + } + + 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, true); + break; + case '\r': + if (peek_at(parser, breakpoint + 1) != '\n') { + parser->current.end = breakpoint + 1; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true); + break; + } + + // If we hit a \r\n sequence, then we need to treat it + // as a newline. + breakpoint++; + parser->current.end = breakpoint; + pm_token_buffer_escape(parser, &token_buffer); + token_buffer.cursor = breakpoint; + + PRISM_FALLTHROUGH + case '\n': + // 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 (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, true); + break; + } + + parser->current.end = breakpoint + 1; + parser_flush_heredoc_end(parser); + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + 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_byte(&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_byte(&token_buffer, '\\'); + } + pm_token_buffer_push_byte(&token_buffer, '\r'); + break; + } + PRISM_FALLTHROUGH + case '\n': + if (!lex_mode->as.string.interpolation) { + pm_token_buffer_push_byte(&token_buffer, '\\'); + pm_token_buffer_push_byte(&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_byte(&token_buffer, peeked); + parser->current.end++; + } else if (lex_mode->as.string.terminator != '\0' && peeked == lex_mode->as.string.terminator) { + pm_token_buffer_push_byte(&token_buffer, peeked); + parser->current.end++; + } else if (lex_mode->as.string.interpolation) { + escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE); + } else { + pm_token_buffer_push_byte(&token_buffer, '\\'); + pm_token_buffer_push_escaped(&token_buffer, parser); + } + + break; + } + + token_buffer.cursor = parser->current.end; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true); + 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, true); + 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 a string content token. + parser->current.end = parser->end; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + 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; + } + + // 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; + pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base; + + bool line_continuation = lex_mode->as.heredoc.line_continuation; + lex_mode->as.heredoc.line_continuation = false; + + // We'll check if we're at the end of the file. If we are, then we + // will add an error (because we weren't able to find the + // terminator) but still continue parsing so that content after the + // declaration of the heredoc can be parsed. + if (parser->current.end >= parser->end) { + pm_parser_err_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length); + parser->next_start = lex_mode->as.heredoc.next_start; + parser->heredoc_end = parser->current.end; + lex_state_set(parser, PM_LEX_STATE_END); + lex_mode_pop(parser); + LEX(PM_TOKEN_HEREDOC_END); + } + + const uint8_t *ident_start = heredoc_lex_mode->ident_start; + size_t ident_length = heredoc_lex_mode->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; + + if (!line_continuation && (start + ident_length <= parser->end)) { + const uint8_t *newline = next_newline(start, parser->end - start); + const uint8_t *ident_end = newline; + const uint8_t *terminator_end = newline; + + if (newline == NULL) { + terminator_end = parser->end; + ident_end = parser->end; + } else { + terminator_end++; + if (newline[-1] == '\r') { + ident_end--; // Remove \r + } + } + + const uint8_t *terminator_start = ident_end - ident_length; + const uint8_t *cursor = start; + + if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) { + while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) { + cursor++; + } + } + + if ( + (cursor == terminator_start) && + (memcmp(terminator_start, ident_start, ident_length) == 0) + ) { + if (newline != NULL) { + pm_newline_list_append(&parser->newline_list, newline); + } + + parser->current.end = terminator_end; + 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; + } + + lex_state_set(parser, PM_LEX_STATE_END); + lex_mode_pop(parser); + LEX(PM_TOKEN_HEREDOC_END); + } + } + + size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent); + if ( + heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE && + lex_mode->as.heredoc.common_whitespace != NULL && + (*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[] = "\r\n\\#"; + + pm_heredoc_quote_t quote = heredoc_lex_mode->quote; + if (quote == PM_HEREDOC_QUOTE_SINGLE) { + breakpoints[3] = '\0'; + } + + const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true); + pm_token_buffer_t token_buffer = { 0 }; + bool was_line_continuation = 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, true); + break; + case '\r': + parser->current.end = breakpoint + 1; + + if (peek_at(parser, breakpoint + 1) != '\n') { + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true); + break; + } + + // If we hit a \r\n sequence, then we want to replace it + // with a single \n character in the final string. + breakpoint++; + pm_token_buffer_escape(parser, &token_buffer); + token_buffer.cursor = breakpoint; + + PRISM_FALLTHROUGH + 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; + + if (!was_line_continuation && (start + ident_length <= parser->end)) { + // We want to match the terminator starting from the end of the line in case + // there is whitespace in the ident such as <<-' DOC' or <<~' DOC'. + const uint8_t *newline = next_newline(start, parser->end - start); + + if (newline == NULL) { + newline = parser->end; + } else if (newline[-1] == '\r') { + newline--; // Remove \r + } + + // Start of a possible terminator. + const uint8_t *terminator_start = newline - ident_length; + + // Cursor to check for the leading whitespace. We skip the + // leading whitespace if we have a - or ~ heredoc. + const uint8_t *cursor = start; + + if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) { + while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) { + cursor++; + } + } + + if ( + cursor == terminator_start && + (memcmp(terminator_start, ident_start, ident_length) == 0) + ) { + parser->current.end = breakpoint + 1; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + } + + size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.base.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 (lex_mode->as.heredoc.base.indent == PM_HEREDOC_INDENT_TILDE) { + if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') { + *lex_mode->as.heredoc.common_whitespace = whitespace; + } + + parser->current.end = breakpoint + 1; + 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, true); + 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, 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_byte(&token_buffer, '\\'); + pm_token_buffer_push_byte(&token_buffer, '\r'); + break; + } + PRISM_FALLTHROUGH + case '\n': + pm_token_buffer_push_byte(&token_buffer, '\\'); + pm_token_buffer_push_byte(&token_buffer, '\n'); + token_buffer.cursor = parser->current.end + 1; + breakpoint = parser->current.end; + continue; + default: + pm_token_buffer_push_byte(&token_buffer, '\\'); + pm_token_buffer_push_escaped(&token_buffer, parser); + break; + } + } else { + switch (peeked) { + case '\r': + parser->current.end++; + if (peek(parser) != '\n') { + pm_token_buffer_push_byte(&token_buffer, '\r'); + break; + } + PRISM_FALLTHROUGH + case '\n': + // If we are in a tilde here, we should + // break out of the loop and return the + // string content. + if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) { + const uint8_t *end = parser->current.end; + pm_newline_list_append(&parser->newline_list, end); + + // Here we want the buffer to only + // include up to the backslash. + parser->current.end = breakpoint; + pm_token_buffer_flush(parser, &token_buffer); + + // Now we can advance the end of the + // token past the newline. + parser->current.end = end + 1; + lex_mode->as.heredoc.line_continuation = true; + LEX(PM_TOKEN_STRING_CONTENT); + } + + was_line_continuation = true; + token_buffer.cursor = parser->current.end + 1; + breakpoint = parser->current.end; + continue; + default: + escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE); + break; + } + } + + token_buffer.cursor = parser->current.end; + breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true); + 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, true); + break; + } + + if (type == PM_TOKEN_STRING_CONTENT) { + pm_token_buffer_flush(parser, &token_buffer); + } + + LEX(type); + } + default: + assert(false && "unreachable"); + } + + was_line_continuation = 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 a string content token. + parser->current.end = parser->end; + pm_token_buffer_flush(parser, &token_buffer); + LEX(PM_TOKEN_STRING_CONTENT); + } + } + + 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_RESCUE = 4, // rescue + PM_BINDING_POWER_MODIFIER = 6, // if unless until while + 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_MULTI_ASSIGNMENT = 16, // = + PM_BINDING_POWER_ASSIGNMENT = 18, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **= + PM_BINDING_POWER_TERNARY = 20, // ?: + PM_BINDING_POWER_RANGE = 22, // .. ... + PM_BINDING_POWER_LOGICAL_OR = 24, // || + PM_BINDING_POWER_LOGICAL_AND = 26, // && + PM_BINDING_POWER_EQUALITY = 28, // <=> == === != =~ !~ + PM_BINDING_POWER_COMPARISON = 30, // > >= < <= + PM_BINDING_POWER_BITWISE_OR = 32, // | ^ + PM_BINDING_POWER_BITWISE_AND = 34, // & + PM_BINDING_POWER_SHIFT = 36, // << >> + PM_BINDING_POWER_TERM = 38, // + - + PM_BINDING_POWER_FACTOR = 40, // * / % + PM_BINDING_POWER_UMINUS = 42, // -@ + PM_BINDING_POWER_EXPONENT = 44, // ** + PM_BINDING_POWER_UNARY = 46, // ! ~ +@ + PM_BINDING_POWER_INDEX = 48, // [] []= + PM_BINDING_POWER_CALL = 50, // :: . + PM_BINDING_POWER_MAX = 52 +} 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, precedence + 1, true, true } +#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false } + +pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = { + // rescue + [PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = { PM_BINDING_POWER_MODIFIER_RESCUE, PM_BINDING_POWER_COMPOSITION, true, false }, + + // 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), + + // 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] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_BANG_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_EQUAL_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_EQUAL_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY), + [PM_TOKEN_LESS_EQUAL_GREATER] = NON_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] = RIGHT_ASSOCIATIVE_UNARY(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 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); +} + +/** + * Returns true if the current token is any of the nine given types. + */ +static inline bool +match9(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, pm_token_type_t type9) { + 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) || match1(parser, type9); +} + +/** + * 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; +} + +/** + * 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; +} + +/** + * A special expect1 that expects a heredoc terminator and handles popping the + * lex mode accordingly. + */ +static void +expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) { + if (match1(parser, PM_TOKEN_HEREDOC_END)) { + parser_lex(parser); + } else { + pm_parser_err_heredoc_term(parser, ident_start, ident_length); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } +} + +static pm_node_t * +parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth); + +/** + * This is a wrapper of parse_expression, which also checks whether the + * resulting node is a value expression. + */ +static pm_node_t * +parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) { + pm_node_t *node = parse_expression(parser, binding_power, accepts_command_call, accepts_label, diag_id, depth); + 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, bool accepts_command_call, pm_diagnostic_id_t diag_id, uint16_t depth) { + if (accept1(parser, PM_TOKEN_USTAR)) { + pm_token_t operator = parser->previous; + pm_node_t *expression = parse_value_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + return (pm_node_t *) pm_splat_node_create(parser, &operator, expression); + } + + return parse_value_expression(parser, binding_power, accepts_command_call, false, diag_id, depth); +} + +/** + * 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 = xcalloc(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); +} + +/** + * Certain expressions are not targetable, but in order to provide a better + * experience we give a specific error message. In order to maintain as much + * information in the tree as possible, we replace them with local variable + * writes. + */ +static pm_node_t * +parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) { + switch (PM_NODE_TYPE(target)) { + case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break; + case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break; + case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break; + case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break; + case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break; + case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break; + case PM_TRUE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break; + default: break; + } + + pm_constant_id_t name = pm_parser_constant_id_location(parser, target->location.start, target->location.end); + pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0); + + pm_node_destroy(parser, target); + return (pm_node_t *) result; +} + +/** + * When an implicit local variable is written to or targeted, it becomes a + * regular, named local variable. This function removes it from the list of + * implicit parameters when that happens. + */ +static void +parse_target_implicit_parameter(pm_parser_t *parser, pm_node_t *node) { + pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters; + + for (size_t index = 0; index < implicit_parameters->size; index++) { + if (implicit_parameters->nodes[index] == node) { + // If the node is not the last one in the list, we need to shift the + // remaining nodes down to fill the gap. This is extremely unlikely + // to happen. + if (index != implicit_parameters->size - 1) { + memcpy(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *)); + } + + implicit_parameters->size--; + break; + } + } +} + +/** + * Convert the given node into a valid target node. + * + * @param multiple Whether or not this target is part of a larger set of + * targets. If it is, then the &. operator is not allowed. + * @param splat Whether or not this target is a child of a splat target. If it + * is, then fewer patterns are allowed. + */ +static pm_node_t * +parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_parent) { + switch (PM_NODE_TYPE(target)) { + case PM_MISSING_NODE: + return target; + case PM_SOURCE_ENCODING_NODE: + case PM_FALSE_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_NIL_NODE: + case PM_SELF_NODE: + case PM_TRUE_NODE: { + // In these special cases, we have specific error messages and we + // will replace them with local variable writes. + return parse_unwriteable_target(parser, 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: + if (context_def_p(parser)) { + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD); + } + + 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: + if (context_def_p(parser)) { + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD); + } + + 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_FORMAT_CONTENT(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_FORMAT(parser, target->location.start, target->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, target->location.start); + parse_target_implicit_parameter(parser, target); + } + + const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target; + uint32_t name = cast->name; + uint32_t depth = cast->depth; + pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name); + + 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_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + pm_node_t *node = (pm_node_t *) pm_local_variable_target_node_create(parser, &target->location, name, 0); + + parse_target_implicit_parameter(parser, target); + pm_node_destroy(parser, target); + + return node; + } + 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: + if (splat_parent) { + // Multi target is not accepted in all positions. If this is one + // of them, then we need to add an error. + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + + 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, multiple, true); + } + + 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_loc = call->message_loc; + + pm_constant_id_t name = pm_parser_local_add_location(parser, message_loc.start, message_loc.end, 0); + pm_node_destroy(parser, target); + + return (pm_node_t *) pm_local_variable_target_node_create(parser, &message_loc, name, 0); + } + + if (*call->message_loc.start == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) { + if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION); + } + + parse_write_name(parser, &call->name); + return (pm_node_t *) pm_call_target_node_create(parser, 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_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) { + return (pm_node_t *) pm_index_target_node_create(parser, call); + } + } + PRISM_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, bool multiple) { + pm_node_t *result = parse_target(parser, target, multiple, false); + + // 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; +} + +/** + * Potentially wrap a constant write node in a shareable constant node depending + * on the current state. + */ +static pm_node_t * +parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) { + pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser); + + if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) { + return (pm_node_t *) pm_shareable_constant_node_create(parser, write, shareable_constant); + } + + return write; +} + +/** + * 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: + pm_node_destroy(parser, value); + 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: { + pm_node_t *node = (pm_node_t *) pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value); + + if (context_def_p(parser)) { + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD); + } + + return parse_shareable_constant_write(parser, node); + } + case PM_CONSTANT_READ_NODE: { + pm_node_t *node = (pm_node_t *) pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value); + + if (context_def_p(parser)) { + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD); + } + + pm_node_destroy(parser, target); + return parse_shareable_constant_write(parser, node); + } + case PM_BACK_REFERENCE_READ_NODE: + case PM_NUMBERED_REFERENCE_READ_NODE: + PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY); + PRISM_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_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target; + + pm_constant_id_t name = local_read->name; + pm_location_t name_loc = target->location; + + uint32_t depth = local_read->depth; + pm_scope_t *scope = pm_parser_scope_find(parser, depth); + + if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) { + pm_diagnostic_id_t diag_id = (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED; + PM_PARSER_ERR_FORMAT(parser, target->location.start, target->location.end, diag_id, target->location.start); + parse_target_implicit_parameter(parser, target); + } + + pm_locals_unread(&scope->locals, name); + pm_node_destroy(parser, target); + + return (pm_node_t *) pm_local_variable_write_node_create(parser, name, depth, value, &name_loc, operator); + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + pm_node_t *node = (pm_node_t *) pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator); + + parse_target_implicit_parameter(parser, target); + pm_node_destroy(parser, target); + + return node; + } + 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, 0); + 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 (char_is_identifier_start(parser, call->message_loc.start, parser->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); + pm_node_flag_set((pm_node_t *) call, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)); + + 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_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) { + 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); + + // Ensure that the arguments for []= don't contain keywords + pm_index_arguments_check(parser, call->arguments, call->block); + pm_node_flag_set((pm_node_t *) call, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)); + + 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); + } + PRISM_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; + } +} + +/** + * Certain expressions are not writable, but in order to provide a better + * experience we give a specific error message. In order to maintain as much + * information in the tree as possible, we replace them with local variable + * writes. + */ +static pm_node_t * +parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) { + switch (PM_NODE_TYPE(target)) { + case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break; + case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break; + case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break; + case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break; + case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break; + case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break; + case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break; + default: break; + } + + pm_constant_id_t name = pm_parser_local_add_location(parser, target->location.start, target->location.end, 1); + pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals); + + pm_node_destroy(parser, target); + return (pm_node_t *) result; +} + +/** + * 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, uint16_t depth) { + 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, true, false)); + + 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, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + name = parse_target(parser, name, true, true); + } + + 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 (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + context_push(parser, PM_CONTEXT_MULTI_TARGET); + pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); + target = parse_target(parser, target, true, false); + + pm_multi_target_node_targets_append(parser, result, target); + context_pop(parser); + } else if (token_begins_expression_p(parser->current.type)) { + pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); + target = parse_target(parser, target, true, false); + + 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 add an implicit rest node to represent 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); + 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, uint16_t depth) { + pm_node_t *result = parse_targets(parser, first_target, binding_power, depth); + accept1(parser, PM_TOKEN_NEWLINE); + + // 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, uint16_t depth) { + // 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, true, false, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1)); + pm_statements_node_body_append(parser, statements, node, true); + + // 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 consecutive + // 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); + + // If we are at the end of the file, then we need to stop parsing + // the statements entirely at this point. Mark the parser as + // recovering, as we know that EOF closes the top-level context, and + // then break out of the loop. + if (match1(parser, PM_TOKEN_EOF)) { + parser->recovering = true; + break; + } + + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + if (context_terminator(context, &parser->current)) break; + } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) { + // This is an inlined version of accept1 because the error that we + // want to add has varargs. If this happens again, we should + // probably extract a helper function. + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type)); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + } + + context_pop(parser); + bool last_value = true; + switch (context) { + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_DEF_ENSURE: + last_value = false; + break; + default: + break; + } + pm_void_statements_check(parser, statements, last_value); + + return statements; +} + +/** + * Add a node to a set of static literals that holds a set of hash keys. If the + * node is a duplicate, then add an appropriate warning. + */ +static void +pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) { + const pm_node_t *duplicated = pm_static_literals_add(&parser->newline_list, parser->start_line, literals, node, true); + + if (duplicated != NULL) { + pm_buffer_t buffer = { 0 }; + pm_static_literal_inspect(&buffer, &parser->newline_list, parser->start_line, parser->encoding->name, duplicated); + + pm_diagnostic_list_append_format( + &parser->warning_list, + duplicated->location.start, + duplicated->location.end, + PM_WARN_DUPLICATED_HASH_KEY, + (int) pm_buffer_length(&buffer), + pm_buffer_value(&buffer), + pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line).line + ); + + pm_buffer_free(&buffer); + } +} + +/** + * Add a node to a set of static literals that holds a set of hash keys. If the + * node is a duplicate, then add an appropriate warning. + */ +static void +pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) { + pm_node_t *previous; + + if ((previous = pm_static_literals_add(&parser->newline_list, parser->start_line, literals, node, false)) != NULL) { + pm_diagnostic_list_append_format( + &parser->warning_list, + node->location.start, + node->location.end, + PM_WARN_DUPLICATED_WHEN_CLAUSE, + pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line).line, + pm_newline_list_line_column(&parser->newline_list, previous->location.start, parser->start_line).line + ); + } +} + +/** + * Parse all of the elements of a hash. Return true if a double splat was found. + */ +static bool +parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node, uint16_t depth) { + 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 (match1(parser, PM_TOKEN_BRACE_LEFT)) { + // If we're about to parse a nested hash that is being + // pushed into this hash directly with **, then we want the + // inner hash to share the static literals with the outer + // hash. + parser->current_hash_keys = literals; + value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, (uint16_t) (depth + 1)); + } else if (token_begins_expression_p(parser->current.type)) { + value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, (uint16_t) (depth + 1)); + } else { + pm_parser_scope_forwarding_keywords_check(parser, &operator); + } + + 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_hash_key_static_literals_add(parser, literals, key); + + 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, false, false, PM_ERR_HASH_EXPRESSION_AFTER_LABEL, (uint16_t) (depth + 1)); + } 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 = -1; + pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 }; + + if (identifier.end[-1] == '!' || identifier.end[-1] == '?') { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ); + } else { + depth = pm_parser_local_depth(parser, &identifier); + } + + 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, false, true, PM_ERR_HASH_KEY, (uint16_t) (depth + 1)); + + // Hash keys that are strings are automatically frozen. We will + // mark that here. + if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) { + pm_node_flag_set(key, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL); + } + + pm_hash_key_static_literals_add(parser, literals, 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, false, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); + 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, uint16_t depth) { + 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_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; + + pm_static_literals_t hash_keys = { 0 }; + bool contains_keyword_splat = parse_assocs(parser, &hash_keys, (pm_node_t *) hash, (uint16_t) (depth + 1)); + + parse_arguments_append(parser, arguments, argument); + + pm_node_flags_t flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; + if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + pm_node_flag_set((pm_node_t *) arguments->arguments, flags); + + pm_static_literals_free(&hash_keys); + parsed_bare_hash = true; + + 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, false, false, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1)); + } else { + pm_parser_scope_forwarding_block_check(parser, &operator); + } + + 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; + } + + if (match1(parser, PM_TOKEN_COMMA)) { + pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK); + } + + 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)) { + pm_parser_scope_forwarding_positionals_check(parser, &operator); + argument = (pm_node_t *) pm_splat_node_create(parser, &operator, NULL); + if (parsed_bare_hash) { + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT); + } + } else { + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT, (uint16_t) (depth + 1)); + + 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, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + + // If we parse a range, we need to validate that we + // didn't accidentally violate the nonassoc rules of the + // ... operator. + if (PM_NODE_TYPE_P(right, PM_RANGE_NODE)) { + pm_range_node_t *range = (pm_range_node_t *) right; + pm_parser_err(parser, range->operator_loc.start, range->operator_loc.end, PM_ERR_UNEXPECTED_RANGE_OPERATOR); + } + + argument = (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + } else { + pm_parser_scope_forwarding_all_check(parser, &parser->previous); + 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); + pm_node_flag_set((pm_node_t *) arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING); + arguments->has_forwarding = true; + parsed_forwarding_arguments = true; + break; + } + } + } + PRISM_FALLTHROUGH + default: { + if (argument == NULL) { + argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, !parsed_first_argument, true, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1)); + } + + bool contains_keywords = false; + 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); + contains_keywords = true; + + // Create the set of static literals for this hash. + pm_static_literals_t hash_keys = { 0 }; + pm_hash_key_static_literals_add(parser, &hash_keys, argument); + + // Finish parsing the one we are part way through. + pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); + 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, &hash_keys, (pm_node_t *) bare_hash, (uint16_t) (depth + 1)); + } + + pm_static_literals_free(&hash_keys); + parsed_bare_hash = true; + } + + parse_arguments_append(parser, arguments, argument); + + pm_node_flags_t flags = 0; + if (contains_keywords) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; + if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + pm_node_flag_set((pm_node_t *) arguments->arguments, flags); + + 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. + bool accepted_newline = false; + if (terminator != PM_TOKEN_EOF) { + accepted_newline = 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 (accept1(parser, PM_TOKEN_COMMA)) { + // If there was a comma, then we need to check if we also accepted a + // newline. If we did, then this is a syntax error. + if (accepted_newline) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + } + } 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. + 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); + if (pm_parser_parameter_name_check(parser, &name)) { + pm_node_flag_set_repeated_parameter(value); + } + pm_parser_local_add_token(parser, &name, 1); + } + + 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); + if (pm_parser_parameter_name_check(parser, &name)) { + pm_node_flag_set_repeated_parameter(param); + } + pm_parser_local_add_token(parser, &name, 1); + } + + pm_multi_target_node_targets_append(parser, node, param); + } while (accept1(parser, PM_TOKEN_COMMA)); + + accept1(parser, PM_TOKEN_NEWLINE); + 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. + * + * It returns true if it was successful, and false otherwise. + */ +static bool +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 true; + + // 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 true; + } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) { + return true; + } + + if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { + pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR); + return false; + } else if (token->type == PM_TOKEN_UDOT_DOT_DOT && (*current >= PM_PARAMETERS_ORDER_KEYWORDS_REST && *current <= PM_PARAMETERS_ORDER_AFTER_OPTIONAL)) { + pm_parser_err_token(parser, token, *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL ? PM_ERR_PARAMETER_FORWARDING_AFTER_REST : PM_ERR_PARAMETER_ORDER); + return false; + } else 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); + return false; + } + + if (state < *current) *current = state; + return true; +} + +/** + * 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, + bool accepts_blocks_in_defaults, + bool in_block, + uint16_t depth +) { + pm_do_loop_stack_push(parser, false); + + pm_parameters_node_t *params = pm_parameters_node_create(parser); + pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE; + + while (true) { + bool parsing = true; + + 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; + + bool repeated = false; + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + name = parser->previous; + repeated = pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name, 1); + } else { + name = not_provided(parser); + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK; + } + + pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator); + if (repeated) { + pm_node_flag_set_repeated_parameter((pm_node_t *)param); + } + 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); + } + + bool succeeded = update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL; + 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); + if (succeeded) 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); + 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; + bool repeated = pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name, 1); + + if (match1(parser, PM_TOKEN_EQUAL)) { + pm_token_t operator = parser->current; + context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); + parser_lex(parser); + + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name); + uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + + if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); + pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1)); + if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser); + + pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value); + + if (repeated) { + pm_node_flag_set_repeated_parameter((pm_node_t *) param); + } + pm_parameters_node_optionals_append(params, param); + + // If the value of the parameter increased the number of + // reads of that parameter, then we need to warn that we + // have a circular definition. + if ((parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR); + } + + 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) { + parsing = false; + break; + } + } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { + pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name); + if (repeated) { + pm_node_flag_set_repeated_parameter((pm_node_t *)param); + } + pm_parameters_node_requireds_append(params, (pm_node_t *) param); + } else { + pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name); + if (repeated) { + pm_node_flag_set_repeated_parameter((pm_node_t *)param); + } + pm_parameters_node_posts_append(params, (pm_node_t *) param); + } + + break; + } + case PM_TOKEN_LABEL: { + if (!uses_parentheses && !in_block) parser->in_keyword_arg = true; + update_parameter_state(parser, &parser->current, &order); + + context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); + parser_lex(parser); + + pm_token_t name = parser->previous; + pm_token_t local = name; + local.end -= 1; + + if (parser->encoding_changed ? parser->encoding->isupper_char(local.start, local.end - local.start) : pm_encoding_utf_8_isupper_char(local.start, local.end - local.start)) { + pm_parser_err(parser, local.start, local.end, PM_ERR_ARGUMENT_FORMAL_CONSTANT); + } else if (local.end[-1] == '!' || local.end[-1] == '?') { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE); + } + + bool repeated = pm_parser_parameter_name_check(parser, &local); + pm_parser_local_add_token(parser, &local, 1); + + switch (parser->current.type) { + case PM_TOKEN_COMMA: + case PM_TOKEN_PARENTHESIS_RIGHT: + case PM_TOKEN_PIPE: { + context_pop(parser); + + pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + if (repeated) { + pm_node_flag_set_repeated_parameter(param); + } + + pm_parameters_node_keywords_append(params, param); + break; + } + case PM_TOKEN_SEMICOLON: + case PM_TOKEN_NEWLINE: { + context_pop(parser); + + if (uses_parentheses) { + parsing = false; + break; + } + + pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + if (repeated) { + pm_node_flag_set_repeated_parameter(param); + } + + pm_parameters_node_keywords_append(params, param); + break; + } + default: { + pm_node_t *param; + + if (token_begins_expression_p(parser->current.type)) { + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local); + uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + + if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); + pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT_KW, (uint16_t) (depth + 1)); + if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser); + + if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR); + } + + 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); + } + + if (repeated) { + pm_node_flag_set_repeated_parameter(param); + } + + context_pop(parser); + 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) { + parsing = 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; + bool repeated = false; + + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + name = parser->previous; + repeated = pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name, 1); + } else { + name = not_provided(parser); + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS; + } + + pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name); + if (repeated) { + pm_node_flag_set_repeated_parameter(param); + } + + 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: { + pm_parameters_order_t previous_order = order; + 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)) { + if (previous_order <= PM_PARAMETERS_ORDER_KEYWORDS) { + pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW); + } + + param = (pm_node_t *) pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous); + } else { + pm_token_t name; + + bool repeated = false; + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + name = parser->previous; + repeated = pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name, 1); + } else { + name = not_provided(parser); + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS; + } + + param = (pm_node_t *) pm_keyword_rest_parameter_node_create(parser, &operator, &name); + if (repeated) { + pm_node_flag_set_repeated_parameter(param); + } + } + + 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 && order >= PM_PARAMETERS_ORDER_NAMED) { + // 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); + } + } + + parsing = false; + break; + } + + // If we hit some kind of issue while parsing the parameter, this would + // have been set to false. In that case, we need to break out of the + // loop. + if (!parsing) break; + + bool accepted_newline = false; + if (uses_parentheses) { + accepted_newline = accept1(parser, PM_TOKEN_NEWLINE); + } + + if (accept1(parser, PM_TOKEN_COMMA)) { + // If there was a comma, but we also accepted a newline, then this + // is a syntax error. + if (accepted_newline) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + } + } else { + // If there was no comma, then we're done parsing parameters. + break; + } + } + + 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; +} + +/** + * Accepts a parser returns the index of the last newline in the file that was + * ecorded before the current token within the newline list. + */ +static size_t +token_newline_index(const pm_parser_t *parser) { + if (parser->heredoc_end == NULL) { + // This is the common case. In this case we can look at the previously + // recorded newline in the newline list and subtract from the current + // offset. + return parser->newline_list.size - 1; + } else { + // This is unlikely. This is the case that we have already parsed the + // start of a heredoc, so we cannot rely on looking at the previous + // offset of the newline list, and instead must go through the whole + // process of a binary search for the line number. + return (size_t) pm_newline_list_line(&parser->newline_list, parser->current.start, 0); + } +} + +/** + * Accepts a parser, a newline index, and a token and returns the column. The + * important piece of this is that it expands tabs out to the next tab stop. + */ +static int64_t +token_column(const pm_parser_t *parser, size_t newline_index, const pm_token_t *token, bool break_on_non_space) { + const uint8_t *cursor = parser->start + parser->newline_list.offsets[newline_index]; + const uint8_t *end = token->start; + + // Skip over the BOM if it is present. + if ( + newline_index == 0 && + parser->start[0] == 0xef && + parser->start[1] == 0xbb && + parser->start[2] == 0xbf + ) cursor += 3; + + int64_t column = 0; + for (; cursor < end; cursor++) { + switch (*cursor) { + case '\t': + column = ((column / PM_TAB_WHITESPACE_SIZE) + 1) * PM_TAB_WHITESPACE_SIZE; + break; + case ' ': + column++; + break; + default: + column++; + if (break_on_non_space) return -1; + break; + } + } + + return column; +} + +/** + * Accepts a parser, two newline indices, and pointers to two tokens. This + * function warns if the indentation of the two tokens does not match. + */ +static void +parser_warn_indentation_mismatch(pm_parser_t *parser, size_t opening_newline_index, const pm_token_t *opening_token, bool if_after_else, bool allow_indent) { + // If these warnings are disabled (unlikely), then we can just return. + if (!parser->warn_mismatched_indentation) return; + + // If the tokens are on the same line, we do not warn. + size_t closing_newline_index = token_newline_index(parser); + if (opening_newline_index == closing_newline_index) return; + + // If the opening token has anything other than spaces or tabs before it, + // then we do not warn. This is unless we are matching up an `if`/`end` pair + // and the `if` immediately follows an `else` keyword. + int64_t opening_column = token_column(parser, opening_newline_index, opening_token, !if_after_else); + if (!if_after_else && (opening_column == -1)) return; + + // Get a reference to the closing token off the current parser. This assumes + // that the caller has placed this in the correct position. + pm_token_t *closing_token = &parser->current; + + // If the tokens are at the same indentation, we do not warn. + int64_t closing_column = token_column(parser, closing_newline_index, closing_token, true); + if ((closing_column == -1) || (opening_column == closing_column)) return; + + // If the closing column is greater than the opening column and we are + // allowing indentation, then we do not warn. + if (allow_indent && (closing_column > opening_column)) return; + + // Otherwise, add a warning. + PM_PARSER_WARN_FORMAT( + parser, + closing_token->start, + closing_token->end, + PM_WARN_INDENTATION_MISMATCH, + (int) (closing_token->end - closing_token->start), + (const char *) closing_token->start, + (int) (opening_token->end - opening_token->start), + (const char *) opening_token->start, + ((int32_t) opening_newline_index) + parser->start_line + ); +} + +typedef enum { + PM_RESCUES_BEGIN = 1, + PM_RESCUES_BLOCK, + PM_RESCUES_CLASS, + PM_RESCUES_DEF, + PM_RESCUES_LAMBDA, + PM_RESCUES_MODULE, + PM_RESCUES_SCLASS +} pm_rescues_type_t; + +/** + * 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, size_t opening_newline_index, const pm_token_t *opening, pm_begin_node_t *parent_node, pm_rescues_type_t type, uint16_t depth) { + pm_rescue_node_t *current = NULL; + + while (match1(parser, PM_TOKEN_KEYWORD_RESCUE)) { + if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false); + parser_lex(parser); + + 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, false, false, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1)); + reference = parse_target(parser, reference, false, false); + + 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, false, PM_ERR_RESCUE_EXPRESSION, (uint16_t) (depth + 1)); + 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, false, false, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1)); + reference = parse_target(parser, reference, false, false); + + pm_rescue_node_reference_set(rescue, reference); + break; + } + } while (accept1(parser, PM_TOKEN_COMMA)); + } + } + } + + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) { + rescue->then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(&parser->previous); + } + } else { + expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM); + rescue->then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(&parser->previous); + } + + if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + pm_context_t context; + + switch (type) { + case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break; + case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break; + case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break; + case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break; + case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break; + case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break; + case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break; + default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break; + } + + pm_statements_node_t *statements = parse_statements(parser, context, (uint16_t) (depth + 1)); + if (statements != NULL) 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_subsequent_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 subsequent + // clauses. This sets the end location on all rescues once we know it. + if (current != NULL) { + const uint8_t *end_to_set = current->base.location.end; + pm_rescue_node_t *clause = parent_node->rescue_clause; + + while (clause != NULL) { + clause->base.location.end = end_to_set; + clause = clause->subsequent; + } + } + + pm_token_t else_keyword; + if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) { + if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false); + opening_newline_index = token_newline_index(parser); + + else_keyword = parser->current; + opening = &else_keyword; + + parser_lex(parser); + 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); + pm_context_t context; + + switch (type) { + case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break; + case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break; + case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break; + case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break; + case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break; + case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break; + case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break; + default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_ELSE; break; + } + + else_statements = parse_statements(parser, context, (uint16_t) (depth + 1)); + 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 we don't have a `current` rescue node, then this is a dangling + // else, and it's an error. + if (current == NULL) pm_parser_err_node(parser, (pm_node_t *) else_clause, PM_ERR_BEGIN_LONELY_ELSE); + } + + if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) { + if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false); + pm_token_t ensure_keyword = parser->current; + + parser_lex(parser); + 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); + pm_context_t context; + + switch (type) { + case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break; + case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break; + case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break; + case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break; + case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break; + case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break; + case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break; + default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break; + } + + ensure_statements = parse_statements(parser, context, (uint16_t) (depth + 1)); + 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 (match1(parser, PM_TOKEN_KEYWORD_END)) { + if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false); + 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); + } +} + +/** + * Parse a set of rescue clauses with an implicit begin (for example when on a + * class, module, def, etc.). + */ +static pm_begin_node_t * +parse_rescues_implicit_begin(pm_parser_t *parser, size_t opening_newline_index, const pm_token_t *opening, const uint8_t *start, pm_statements_node_t *statements, pm_rescues_type_t type, uint16_t depth) { + pm_token_t begin_keyword = not_provided(parser); + pm_begin_node_t *node = pm_begin_node_create(parser, &begin_keyword, statements); + + parse_rescues(parser, opening_newline_index, opening, node, type, (uint16_t) (depth + 1)); + node->base.location.start = start; + + return 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, + bool accepts_blocks_in_defaults, + uint16_t depth +) { + 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, + accepts_blocks_in_defaults, + true, + (uint16_t) (depth + 1) + ); + } + + 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_NEWLINE); + + if (accept1(parser, PM_TOKEN_SEMICOLON)) { + do { + switch (parser->current.type) { + case PM_TOKEN_CONSTANT: + pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT); + parser_lex(parser); + break; + case PM_TOKEN_INSTANCE_VARIABLE: + pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_IVAR); + parser_lex(parser); + break; + case PM_TOKEN_GLOBAL_VARIABLE: + pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL); + parser_lex(parser); + break; + case PM_TOKEN_CLASS_VARIABLE: + pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CLASS); + parser_lex(parser); + break; + default: + expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE); + break; + } + + bool repeated = pm_parser_parameter_name_check(parser, &parser->previous); + pm_parser_local_add_token(parser, &parser->previous, 1); + + pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous); + if (repeated) pm_node_flag_set_repeated_parameter((pm_node_t *) local); + + pm_block_parameters_node_append_local(block_parameters, local); + } while (accept1(parser, PM_TOKEN_COMMA)); + } + } + + return block_parameters; +} + +/** + * 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->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) return true; + } + + return false; +} + +/** + * These are the names of the various numbered parameters. We have them here so + * that when we insert them into the constant pool we can use a constant string + * and not have to allocate. + */ +static const char * const pm_numbered_parameter_names[] = { + "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9" +}; + +/** + * Return the node that should be used in the parameters field of a block-like + * (block or lambda) node, depending on the kind of parameters that were + * declared in the current scope. + */ +static pm_node_t * +parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_token_t *opening, const pm_token_t *closing) { + pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters; + + // If we have ordinary parameters, then we will return them as the set of + // parameters. + if (parameters != NULL) { + // If we also have implicit parameters, then this is an error. + if (implicit_parameters->size > 0) { + pm_node_t *node = implicit_parameters->nodes[0]; + + if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) { + pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_ORDINARY); + } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) { + pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_ORDINARY); + } else { + assert(false && "unreachable"); + } + } + + return parameters; + } + + // If we don't have any implicit parameters, then the set of parameters is + // NULL. + if (implicit_parameters->size == 0) { + return NULL; + } + + // If we don't have ordinary parameters, then we now must validate our set + // of implicit parameters. We can only have numbered parameters or it, but + // they cannot be mixed. + uint8_t numbered_parameter = 0; + bool it_parameter = false; + + for (size_t index = 0; index < implicit_parameters->size; index++) { + pm_node_t *node = implicit_parameters->nodes[index]; + + if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) { + if (it_parameter) { + pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_IT); + } else if (outer_scope_using_numbered_parameters_p(parser)) { + pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK); + } else if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_INNER) { + pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK); + } else if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { + numbered_parameter = MAX(numbered_parameter, (uint8_t) (node->location.start[1] - '0')); + } else { + assert(false && "unreachable"); + } + } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) { + if (numbered_parameter > 0) { + pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_NUMBERED); + } else { + it_parameter = true; + } + } + } + + if (numbered_parameter > 0) { + // Go through the parent scopes and mark them as being disallowed from + // using numbered parameters because this inner scope is using them. + for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) { + scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_INNER; + } + + const pm_location_t location = { .start = opening->start, .end = closing->end }; + return (pm_node_t *) pm_numbered_parameters_node_create(parser, &location, numbered_parameter); + } + + if (it_parameter) { + return (pm_node_t *) pm_it_parameters_node_create(parser, opening, closing); + } + + return NULL; +} + +/** + * Parse a block. + */ +static pm_block_node_t * +parse_block(pm_parser_t *parser, uint16_t depth) { + 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 *block_parameters = NULL; + + if (accept1(parser, PM_TOKEN_PIPE)) { + pm_token_t block_parameters_opening = parser->previous; + if (match1(parser, PM_TOKEN_PIPE)) { + block_parameters = pm_block_parameters_node_create(parser, NULL, &block_parameters_opening); + parser->command_start = true; + parser_lex(parser); + } else { + block_parameters = parse_block_parameters(parser, true, &block_parameters_opening, false, true, (uint16_t) (depth + 1)); + 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(block_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, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1)); + 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_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1)); + } + } + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END); + } + + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser)); + pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &opening, &parser->previous); + + pm_parser_scope_pop(parser); + pm_accepts_block_stack_pop(parser); + + return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous); +} + +/** + * 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 accepts_command_call, uint16_t depth) { + 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, accepts_block, PM_TOKEN_PARENTHESIS_RIGHT, (uint16_t) (depth + 1)); + + if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_type_human(parser->current.type)); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + + pm_accepts_block_stack_pop(parser); + arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); + } + } else if (accepts_command_call && (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, accepts_block, PM_TOKEN_EOF, (uint16_t) (depth + 1)); + + // 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_TOKEN_FORMAT(parser, parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_type_human(parser->current.type)); + } + + 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, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } + + if (block != NULL) { + if (arguments->block == NULL && !arguments->has_forwarding) { + arguments->block = (pm_node_t *) block; + } else { + pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI); + + if (arguments->block != NULL) { + 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; +} + +/** + * Check that the return is allowed in the current context. If it isn't, add an + * error to the parser. + */ +static void +parse_return(pm_parser_t *parser, pm_node_t *node) { + bool in_sclass = false; + for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) { + switch (context_node->context) { + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_DEFAULT_PARAMS: + case PM_CONTEXT_DEFINED: + case PM_CONTEXT_ELSE: + case PM_CONTEXT_ELSIF: + case PM_CONTEXT_EMBEXPR: + case PM_CONTEXT_FOR_INDEX: + case PM_CONTEXT_FOR: + case PM_CONTEXT_IF: + case PM_CONTEXT_LOOP_PREDICATE: + case PM_CONTEXT_MAIN: + case PM_CONTEXT_MULTI_TARGET: + case PM_CONTEXT_PARENS: + case PM_CONTEXT_POSTEXE: + case PM_CONTEXT_PREDICATE: + case PM_CONTEXT_PREEXE: + case PM_CONTEXT_RESCUE_MODIFIER: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_UNLESS: + case PM_CONTEXT_UNTIL: + case PM_CONTEXT_WHILE: + // Keep iterating up the lists of contexts, because returns can + // see through these. + continue; + case PM_CONTEXT_SCLASS_ELSE: + case PM_CONTEXT_SCLASS_ENSURE: + case PM_CONTEXT_SCLASS_RESCUE: + case PM_CONTEXT_SCLASS: + in_sclass = true; + continue; + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_CLASS: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_MODULE: + // These contexts are invalid for a return. + pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID); + return; + case PM_CONTEXT_BLOCK_BRACES: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_DEF: + case PM_CONTEXT_LAMBDA_BRACES: + case PM_CONTEXT_LAMBDA_DO_END: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_LAMBDA_RESCUE: + // These contexts are valid for a return, and we should not + // continue to loop. + return; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + } + } + if (in_sclass) { + pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID); + } +} + +/** + * Check that the block exit (next, break, redo) is allowed in the current + * context. If it isn't, add an error to the parser. + */ +static void +parse_block_exit(pm_parser_t *parser, pm_node_t *node) { + for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) { + switch (context_node->context) { + case PM_CONTEXT_BLOCK_BRACES: + case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_DEFINED: + case PM_CONTEXT_FOR: + case PM_CONTEXT_LAMBDA_BRACES: + case PM_CONTEXT_LAMBDA_DO_END: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_LOOP_PREDICATE: + case PM_CONTEXT_POSTEXE: + case PM_CONTEXT_UNTIL: + case PM_CONTEXT_WHILE: + // These are the good cases. We're allowed to have a block exit + // in these contexts. + return; + case PM_CONTEXT_DEF: + case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_MAIN: + case PM_CONTEXT_PREEXE: + case PM_CONTEXT_SCLASS: + case PM_CONTEXT_SCLASS_ELSE: + case PM_CONTEXT_SCLASS_ENSURE: + case PM_CONTEXT_SCLASS_RESCUE: + // These are the bad cases. We're not allowed to have a block + // exit in these contexts. + // + // If we get here, then we're about to mark this block exit + // as invalid. However, it could later _become_ valid if we + // find a trailing while/until on the expression. In this + // case instead of adding the error here, we'll add the + // block exit to the list of exits for the expression, and + // the node parsing will handle validating it instead. + assert(parser->current_block_exits != NULL); + pm_node_list_append(parser->current_block_exits, node); + return; + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_CLASS: + case PM_CONTEXT_DEFAULT_PARAMS: + case PM_CONTEXT_ELSE: + case PM_CONTEXT_ELSIF: + case PM_CONTEXT_EMBEXPR: + case PM_CONTEXT_FOR_INDEX: + case PM_CONTEXT_IF: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_MODULE: + case PM_CONTEXT_MULTI_TARGET: + case PM_CONTEXT_PARENS: + case PM_CONTEXT_PREDICATE: + case PM_CONTEXT_RESCUE_MODIFIER: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_UNLESS: + // In these contexts we should continue walking up the list of + // contexts. + break; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + } + } +} + +/** + * When we hit an expression that could contain block exits, we need to stash + * the previous set and create a new one. + */ +static pm_node_list_t * +push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) { + pm_node_list_t *previous_block_exits = parser->current_block_exits; + parser->current_block_exits = current_block_exits; + return previous_block_exits; +} + +/** + * If we did not match a trailing while/until and this was the last chance to do + * so, then all of the block exits in the list are invalid and we need to add an + * error for each of them. + */ +static void +flush_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) { + pm_node_t *block_exit; + PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) { + const char *type; + + switch (PM_NODE_TYPE(block_exit)) { + case PM_BREAK_NODE: type = "break"; break; + case PM_NEXT_NODE: type = "next"; break; + case PM_REDO_NODE: type = "redo"; break; + default: assert(false && "unreachable"); type = ""; break; + } + + PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type); + } + + parser->current_block_exits = previous_block_exits; +} + +/** + * Pop the current level of block exits from the parser, and add errors to the + * parser if any of them are deemed to be invalid. + */ +static void +pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) { + if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) { + // If we matched a trailing while/until, then all of the block exits in + // the contained list are valid. In this case we do not need to do + // anything. + parser->current_block_exits = previous_block_exits; + } else if (previous_block_exits != NULL) { + // If we did not matching a trailing while/until, then all of the block + // exits contained in the list are invalid for this specific context. + // However, they could still become valid in a higher level context if + // there is another list above this one. In this case we'll push all of + // the block exits up to the previous list. + pm_node_list_concat(previous_block_exits, parser->current_block_exits); + parser->current_block_exits = previous_block_exits; + } else { + // If we did not match a trailing while/until and this was the last + // chance to do so, then all of the block exits in the list are invalid + // and we need to add an error for each of them. + flush_block_exits(parser, previous_block_exits); + } +} + +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, uint16_t depth) { + 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, true, false, error_id, (uint16_t) (depth + 1)); + + // 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, size_t opening_newline_index, bool if_after_else, uint16_t depth) { + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + 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, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + 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 (match1(parser, PM_TOKEN_KEYWORD_ELSIF)) { + if (parser_end_of_line_p(parser)) { + PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_KEYWORD_EOL); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); + pm_token_t elsif_keyword = parser->current; + parser_lex(parser); + + pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1)); + pm_accepts_block_stack_push(parser, true); + + pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1)); + 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)->subsequent = elsif; + current = elsif; + } + } + + if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) { + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); + opening_newline_index = token_newline_index(parser); + + 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, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + parser_warn_indentation_mismatch(parser, opening_newline_index, &else_keyword, false, false); + 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)->subsequent = (pm_node_t *) else_node; + break; + case PM_CONTEXT_UNLESS: + ((pm_unless_node_t *) parent)->else_clause = else_node; + break; + default: + assert(false && "unreachable"); + break; + } + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false); + 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)->subsequent; + 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; + } + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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: case PM_IT_LOCAL_VARIABLE_READ_NODE + +// Assert here that the flags are the same so that we can safely switch the type +// of the node without having to move the flags. +PM_STATIC_ASSERT(__LINE__, ((int) PM_STRING_FLAGS_FORCED_UTF8_ENCODING) == ((int) PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING), "Expected the flags to match."); + +/** + * If the encoding was explicitly set through the lexing process, then we need + * to potentially mark the string's flags to indicate how to encode it. + */ +static inline pm_node_flags_t +parse_unescaped_encoding(const pm_parser_t *parser) { + if (parser->explicit_encoding != NULL) { + if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + // If the there's an explicit encoding and it's using a UTF-8 escape + // sequence, then mark the string as UTF-8. + return PM_STRING_FLAGS_FORCED_UTF8_ENCODING; + } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { + // If there's a non-UTF-8 escape sequence being used, then the + // string uses the source encoding, unless the source is marked as + // US-ASCII. In that case the string is forced as ASCII-8BIT in + // order to keep the string valid. + return PM_STRING_FLAGS_FORCED_BINARY_ENCODING; + } + } + return 0; +} + +/** + * 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, uint16_t depth) { + 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); + pm_node_flag_set(node, parse_unescaped_encoding(parser)); + + 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: { + // Ruby disallows seeing encoding around interpolation in strings, + // even though it is known at parse time. + parser->explicit_encoding = NULL; + + 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, (uint16_t) (depth + 1)); + 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; + + // If this set of embedded statements only contains a single + // statement, then Ruby does not consider it as a possible statement + // that could emit a line event. + if (statements != NULL && statements->body.size == 1) { + pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE); + } + + 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: { + // Ruby disallows seeing encoding around interpolation in strings, + // even though it is known at parse time. + parser->explicit_encoding = NULL; + + 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; + } +} + +/** + * When creating a symbol, unary operators that cannot be binary operators + * automatically drop trailing `@` characters. This happens at the parser level, + * such that `~@` is parsed as `~` and `!@` is parsed as `!`. We do that here. + */ +static const uint8_t * +parse_operator_symbol_name(const pm_token_t *name) { + switch (name->type) { + case PM_TOKEN_TILDE: + case PM_TOKEN_BANG: + if (name->end[-1] == '@') return name->end - 1; + PRISM_FALLTHROUGH + default: + return name->end; + } +} + +static pm_node_t * +parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_state_t next_state) { + pm_token_t closing = not_provided(parser); + pm_symbol_node_t *symbol = pm_symbol_node_create(parser, opening, &parser->current, &closing); + + const uint8_t *end = parse_operator_symbol_name(&parser->current); + + if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state); + parser_lex(parser); + + pm_string_shared_init(&symbol->unescaped, parser->previous.start, end); + pm_node_flag_set((pm_node_t *) symbol, PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING); + + return (pm_node_t *) symbol; +} + +/** + * Parse a symbol node. This function will get called immediately after finding + * a symbol opening token. This handles parsing bare symbols and interpolated + * symbols. + */ +static pm_node_t * +parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_state, uint16_t depth) { + const 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_CASE_OPERATOR: + return parse_operator_symbol(parser, &opening, next_state == PM_LEX_STATE_NONE ? PM_LEX_STATE_ENDFN : next_state); + 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; + 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); + pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); + + 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, (uint16_t) (depth + 1)); + + // 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); + } + + pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening); + if (part) pm_interpolated_symbol_node_append(symbol, part); + + while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) { + pm_interpolated_symbol_node_append(symbol, part); + } + } + + if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state); + if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_INTERPOLATED); + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED); + } + + pm_interpolated_symbol_node_closing_loc_set(symbol, &parser->previous); + return (pm_node_t *) symbol; + } + + 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); + + // If we have two string contents in a row, then the content of this + // symbol is split because of heredoc contents. This looks like: + // + // <current, &bounds, &parser->current_string); + pm_interpolated_symbol_node_append(symbol, part); + + if (next_state != PM_LEX_STATE_NONE) { + lex_state_set(parser, next_state); + } + + parser_lex(parser); + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC); + + pm_interpolated_symbol_node_closing_loc_set(symbol, &parser->previous); + return (pm_node_t *) symbol; + } + } 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); + } + + if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_DYNAMIC); + } else { + 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_symbol_encoding(parser, &content, &unescaped, false)); +} + +/** + * 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, uint16_t depth) { + switch (parser->current.type) { + case PM_CASE_OPERATOR: { + const pm_token_t opening = not_provided(parser); + return parse_operator_symbol(parser, &opening, PM_LEX_STATE_NONE); + } + case PM_CASE_KEYWORD: + 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); + pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); + + 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, (uint16_t) (depth + 1)); + } + 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, uint16_t depth) { + switch (parser->current.type) { + case PM_CASE_OPERATOR: { + const pm_token_t opening = not_provided(parser); + return parse_operator_symbol(parser, &opening, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE); + } + 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); + pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); + + 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, (uint16_t) (depth + 1)); + } + 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); + } +} + +/** + * Parse an identifier into either a local variable read. If the local variable + * is not found, it returns NULL instead. + */ +static pm_node_t * +parse_variable(pm_parser_t *parser) { + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &parser->previous); + int depth; + bool is_numbered_param = pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end); + + if (!is_numbered_param && ((depth = pm_parser_local_depth_constant_id(parser, name_id)) != -1)) { + return (pm_node_t *) pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false); + } + + pm_scope_t *current_scope = parser->current_scope; + if (!current_scope->closed && !(current_scope->parameters & PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED)) { + if (is_numbered_param) { + // 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 maximum = (uint8_t) (parser->previous.start[1] - '0'); + for (uint8_t number = 1; number <= maximum; number++) { + pm_parser_local_add_constant(parser, pm_numbered_parameter_names[number - 1], 2); + } + + if (!match1(parser, PM_TOKEN_EQUAL)) { + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_FOUND; + } + + pm_node_t *node = (pm_node_t *) pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false); + pm_node_list_append(¤t_scope->implicit_parameters, node); + + return node; + } else if ((parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) && pm_token_is_it(parser->previous.start, parser->previous.end)) { + pm_node_t *node = (pm_node_t *) pm_it_local_variable_read_node_create(parser, &parser->previous); + pm_node_list_append(¤t_scope->implicit_parameters, node); + + return node; + } + } + + return NULL; +} + +/** + * 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] != '?')) { + pm_node_t *node = parse_variable(parser); + if (node != NULL) return node; + flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL; + } + + pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous); + pm_node_flag_set((pm_node_t *)node, 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: + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_DEF_NAME, pm_token_type_human(parser->current.type)); + 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 + // it 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; + + pm_node_t *node; + PM_NODE_LIST_FOREACH(nodes, read_index, node) { + // 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; +} + +/** + * Return 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, bool accepts_label, uint16_t depth) { + assert(parser->current.type == PM_TOKEN_STRING_BEGIN); + bool concating = false; + + 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. + const pm_lex_mode_t *lex_mode = parser->lex_modes.current; + assert(lex_mode->mode == PM_LEX_STRING); + bool lex_interpolation = lex_mode->as.string.interpolation; + bool label_allowed = lex_mode->as.string.label_allowed && accepts_label; + + pm_token_t opening = parser->current; + parser_lex(parser); + + if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF); + // 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 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; + + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } 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; + pm_token_t content; + + if (match1(parser, PM_TOKEN_EOF)) { + unescaped = PM_STRING_EMPTY; + content = not_provided(parser); + } else { + unescaped = parser->current_string; + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_EXPECT_STRING_CONTENT); + 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_EOF); + node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + + pm_node_list_free(&parts); + } else if (accept1(parser, PM_TOKEN_LABEL_END)) { + node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)); + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } else if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF); + node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); + } else if (accept1(parser, PM_TOKEN_STRING_END)) { + node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + } else { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_type_human(parser->previous.type)); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + 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 (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); + pm_node_flag_set(node, parse_unescaped_encoding(parser)); + + // Kind of odd behavior, but basically if we have an + // unterminated string and it ends in a newline, we back up one + // character so that the error message is on the last line of + // content in the string. + if (!accept1(parser, PM_TOKEN_STRING_END)) { + const uint8_t *location = parser->previous.end; + if (location > parser->start && location[-1] == '\n') location--; + pm_parser_err(parser, location, location, PM_ERR_STRING_LITERAL_EOF); + + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + } else if (accept1(parser, PM_TOKEN_LABEL_END)) { + node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)); + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } 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_flag_set(part, parse_unescaped_encoding(parser)); + 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, (uint16_t) (depth + 1))) != 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); + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } else if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM); + node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current); + } 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); + } + + pm_node_list_free(&parts); + } + } 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, (uint16_t) (depth + 1))) != 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); + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } else if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM); + node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current); + } 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); + } + + pm_node_list_free(&parts); + } + + 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) { + if (!PM_NODE_TYPE_P(current, PM_STRING_NODE) && !PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { + pm_parser_err_node(parser, current, PM_ERR_STRING_CONCATENATION); + } + + 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; +} + +#define PM_PARSE_PATTERN_SINGLE 0 +#define PM_PARSE_PATTERN_TOP 1 +#define PM_PARSE_PATTERN_MULTI 2 + +static pm_node_t * +parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth); + +/** + * Add the newly created local to the list of captures for this pattern matching + * expression. If it is duplicated from a previous local, then we'll need to add + * an error to the parser. + */ +static void +parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_constant_id_t capture, const pm_location_t *location) { + // Skip this capture if it starts with an underscore. + if (*location->start == '_') return; + + if (pm_constant_id_list_includes(captures, capture)) { + pm_parser_err(parser, location->start, location->end, PM_ERR_PATTERN_CAPTURE_DUPLICATE); + } else { + pm_constant_id_list_append(captures, capture); + } +} + +/** + * Accept any number of constants joined by :: delimiters. + */ +static pm_node_t * +parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *node, uint16_t depth) { + // 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); + node = (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, &parser->previous); + } + + // 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, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1)); + 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; + accept1(parser, PM_TOKEN_NEWLINE); + + if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1)); + accept1(parser, PM_TOKEN_NEWLINE); + 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->opening_loc.start == 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->opening_loc.start == 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->opening_loc.start == 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, pm_constant_id_list_t *captures) { + 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_constant_id_t constant_id = pm_parser_constant_id_token(parser, &identifier); + + int depth; + if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { + pm_parser_local_add(parser, constant_id, identifier.start, identifier.end, 0); + } + + parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier)); + name = (pm_node_t *) pm_local_variable_target_node_create( + parser, + &PM_LOCATION_TOKEN_VALUE(&identifier), + constant_id, + (uint32_t) (depth == -1 ? 0 : depth) + ); + } + + // 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, pm_constant_id_list_t *captures) { + 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_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous); + + int depth; + if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { + pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0); + } + + parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); + value = (pm_node_t *) pm_local_variable_target_node_create( + parser, + &PM_LOCATION_TOKEN_VALUE(&parser->previous), + constant_id, + (uint32_t) (depth == -1 ? 0 : depth) + ); + } + + return (pm_node_t *) pm_assoc_splat_node_create(parser, value, &operator); +} + +/** + * Check that the slice of the source given by the bounds parameters constitutes + * a valid local variable name. + */ +static bool +pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + ptrdiff_t length = end - start; + if (length == 0) return false; + + // First ensure that it starts with a valid identifier starting character. + size_t width = char_is_identifier_start(parser, start, end - start); + if (width == 0) return false; + + // Next, ensure that it's not an uppercase character. + if (parser->encoding_changed) { + if (parser->encoding->isupper_char(start, length)) return false; + } else { + if (pm_encoding_utf_8_isupper_char(start, length)) return false; + } + + // Next, iterate through all of the bytes of the string to ensure that they + // are all valid identifier characters. + const uint8_t *cursor = start + width; + while ((width = char_is_identifier(parser, cursor, end - cursor))) cursor += width; + return cursor == end; +} + +/** + * Create an implicit node for the value of a hash pattern that has omitted the + * value. This will use an implicit local variable target. + */ +static pm_node_t * +parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_symbol_node_t *key) { + const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc; + + pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, value_loc->start, value_loc->end); + int depth = -1; + + if (pm_slice_is_valid_local(parser, value_loc->start, value_loc->end)) { + depth = pm_parser_local_depth_constant_id(parser, constant_id); + } else { + pm_parser_err(parser, key->base.location.start, key->base.location.end, PM_ERR_PATTERN_HASH_KEY_LOCALS); + + if ((value_loc->end > value_loc->start) && ((value_loc->end[-1] == '!') || (value_loc->end[-1] == '?'))) { + PM_PARSER_ERR_LOCATION_FORMAT(parser, value_loc, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (value_loc->end - value_loc->start), (const char *) value_loc->start); + } + } + + if (depth == -1) { + pm_parser_local_add(parser, constant_id, value_loc->start, value_loc->end, 0); + } + + parse_pattern_capture(parser, captures, constant_id, value_loc); + pm_local_variable_target_node_t *target = pm_local_variable_target_node_create( + parser, + value_loc, + constant_id, + (uint32_t) (depth == -1 ? 0 : depth) + ); + + return (pm_node_t *) pm_implicit_node_create(parser, (pm_node_t *) target); +} + +/** + * Add a node to the list of keys for a hash pattern, and if it is a duplicate + * then add an error to the parser. + */ +static void +parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) { + if (pm_static_literals_add(&parser->newline_list, parser->start_line, keys, node, true) != NULL) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE); + } +} + +/** + * Parse a hash pattern. + */ +static pm_hash_pattern_node_t * +parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, uint16_t depth) { + pm_node_list_t assocs = { 0 }; + pm_static_literals_t keys = { 0 }; + pm_node_t *rest = NULL; + + switch (PM_NODE_TYPE(first_node)) { + case PM_ASSOC_SPLAT_NODE: + case PM_NO_KEYWORDS_PARAMETER_NODE: + rest = first_node; + break; + case PM_SYMBOL_NODE: { + if (pm_symbol_node_label_p(first_node)) { + parse_pattern_hash_key(parser, &keys, first_node); + pm_node_t *value; + + if (match8(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, PM_TOKEN_EOF)) { + // Otherwise, we will create an implicit local variable + // target for the value. + value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node); + } else { + // Here we have a value for the first assoc in the list, so + // we will parse it now. + value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1)); + } + + pm_token_t operator = not_provided(parser); + pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, first_node, &operator, value); + + pm_node_list_append(&assocs, assoc); + break; + } + } + PRISM_FALLTHROUGH + default: { + // If we get anything else, then this is an error. For this we'll + // create a missing node for the value and create an assoc node for + // the first node in the list. + pm_diagnostic_id_t diag_id = PM_NODE_TYPE_P(first_node, PM_INTERPOLATED_SYMBOL_NODE) ? PM_ERR_PATTERN_HASH_KEY_INTERPOLATED : PM_ERR_PATTERN_HASH_KEY_LABEL; + pm_parser_err_node(parser, first_node, diag_id); + + pm_token_t operator = not_provided(parser); + pm_node_t *value = (pm_node_t *) pm_missing_node_create(parser, first_node->location.start, first_node->location.end); + pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, first_node, &operator, value); + + pm_node_list_append(&assocs, assoc); + 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 (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) { + // Trailing commas are not allowed to follow a rest pattern. + if (rest != NULL) { + pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); + } + + break; + } + + if (match1(parser, PM_TOKEN_USTAR_STAR)) { + pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures); + + if (rest == NULL) { + rest = assoc; + } else { + pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); + pm_node_list_append(&assocs, assoc); + } + } else { + pm_node_t *key; + + if (match1(parser, PM_TOKEN_STRING_BEGIN)) { + key = parse_strings(parser, NULL, true, (uint16_t) (depth + 1)); + + if (PM_NODE_TYPE_P(key, PM_INTERPOLATED_SYMBOL_NODE)) { + pm_parser_err_node(parser, key, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED); + } else if (!pm_symbol_node_label_p(key)) { + pm_parser_err_node(parser, key, PM_ERR_PATTERN_LABEL_AFTER_COMMA); + } + } else { + expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA); + key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); + } + + parse_pattern_hash_key(parser, &keys, key); + 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_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key); + } else { + value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1)); + } + + pm_token_t operator = not_provided(parser); + pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value); + + if (rest != NULL) { + pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); + } + + pm_node_list_append(&assocs, assoc); + } + } + + pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest); + xfree(assocs.nodes); + + pm_static_literals_free(&keys); + return node; +} + +/** + * Parse a pattern expression primitive. + */ +static pm_node_t * +parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_diagnostic_id_t diag_id, uint16_t depth) { + switch (parser->current.type) { + case PM_TOKEN_IDENTIFIER: + case PM_TOKEN_METHOD_NAME: { + parser_lex(parser); + pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous); + + int depth; + if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { + pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0); + } + + parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); + return (pm_node_t *) pm_local_variable_target_node_create( + parser, + &PM_LOCATION_TOKEN_VALUE(&parser->previous), + constant_id, + (uint32_t) (depth == -1 ? 0 : 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, captures, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1)); + + 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_node; + + switch (parser->current.type) { + case PM_TOKEN_LABEL: + parser_lex(parser); + first_node = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); + break; + case PM_TOKEN_USTAR_STAR: + first_node = parse_pattern_keyword_rest(parser, captures); + break; + case PM_TOKEN_STRING_BEGIN: + first_node = parse_expression(parser, PM_BINDING_POWER_MAX, false, true, PM_ERR_PATTERN_HASH_KEY_LABEL, (uint16_t) (depth + 1)); + break; + default: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_type_human(parser->current.type)); + parser_lex(parser); + + first_node = (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + break; + } + } + + node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1)); + + 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, false, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1)); + 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, false, true, diag_id, (uint16_t) (depth + 1)); + + // If we found a label, we need to immediately return to the caller. + if (pm_symbol_node_label_p(node)) return node; + + // 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, false, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1)); + 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 *) parse_variable(parser); + + if (variable == NULL) { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->previous, PM_ERR_NO_LOCAL_VARIABLE); + variable = (pm_node_t *) pm_local_variable_read_node_missing_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, true, false, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN, (uint16_t) (depth + 1)); + 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_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous); + + return parse_pattern_constant_path(parser, captures, (pm_node_t *) node, (uint16_t) (depth + 1)); + } + 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, captures, node, (uint16_t) (depth + 1)); + } + 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_constant_id_list_t *captures, pm_node_t *first_node, pm_diagnostic_id_t diag_id, uint16_t depth) { + pm_node_t *node = first_node; + + while ((node == NULL) || accept1(parser, PM_TOKEN_PIPE)) { + 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, captures, diag_id, (uint16_t) (depth + 1)); + } else { + pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1)); + node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); + } + + break; + } + case PM_TOKEN_PARENTHESIS_LEFT: + case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { + pm_token_t opening = parser->current; + parser_lex(parser); + + pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1)); + accept1(parser, PM_TOKEN_NEWLINE); + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN); + pm_node_t *right = (pm_node_t *) pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0); + + if (node == NULL) { + node = right; + } else { + node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); + } + + 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; + } + } + } + + // 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_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous); + int depth; + + if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { + pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0); + } + + parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); + pm_local_variable_target_node_t *target = pm_local_variable_target_node_create( + parser, + &PM_LOCATION_TOKEN_VALUE(&parser->previous), + constant_id, + (uint32_t) (depth == -1 ? 0 : 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, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) { + 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); + node = (pm_node_t *) parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)); + + if (!(flags & PM_PARSE_PATTERN_TOP)) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); + } + + return node; + } + case PM_TOKEN_USTAR_STAR: { + node = parse_pattern_keyword_rest(parser, captures); + node = (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)); + + if (!(flags & PM_PARSE_PATTERN_TOP)) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); + } + + return node; + } + case PM_TOKEN_STRING_BEGIN: { + // We need special handling for string beginnings because they could + // be dynamic symbols leading to hash patterns. + node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1)); + + if (pm_symbol_node_label_p(node)) { + node = (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)); + + if (!(flags & PM_PARSE_PATTERN_TOP)) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); + } + + return node; + } + + node = parse_pattern_primitives(parser, captures, node, diag_id, (uint16_t) (depth + 1)); + break; + } + case PM_TOKEN_USTAR: { + if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) { + parser_lex(parser); + node = (pm_node_t *) parse_pattern_rest(parser, captures); + leading_rest = true; + break; + } + } + PRISM_FALLTHROUGH + default: + node = parse_pattern_primitives(parser, captures, NULL, diag_id, (uint16_t) (depth + 1)); + 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)) { + return (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)); + } + + if ((flags & PM_PARSE_PATTERN_MULTI) && 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 (match9(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE, PM_TOKEN_EOF,PM_TOKEN_KEYWORD_AND, PM_TOKEN_KEYWORD_OR)) { + node = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); + pm_node_list_append(&nodes, node); + trailing_rest = true; + break; + } + + if (accept1(parser, PM_TOKEN_USTAR)) { + node = (pm_node_t *) parse_pattern_rest(parser, captures); + + // 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, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); + } + + 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 (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) { + node = (pm_node_t *) pm_find_pattern_node_create(parser, &nodes); + + if (nodes.size == 2) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER); + } + } else { + node = (pm_node_t *) pm_array_pattern_node_node_list_create(parser, &nodes); + + if (leading_rest && trailing_rest) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS); + } + } + + xfree(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: { + pm_integer_node_t *cast = (pm_integer_node_t *) node; + cast->base.location.start--; + cast->value.negative = true; + break; + } + case PM_FLOAT_NODE: { + pm_float_node_t *cast = (pm_float_node_t *) node; + cast->base.location.start--; + cast->value = -cast->value; + break; + } + case PM_RATIONAL_NODE: { + pm_rational_node_t *cast = (pm_rational_node_t *) node; + cast->base.location.start--; + cast->numerator.negative = true; + break; + } + case PM_IMAGINARY_NODE: + node->location.start--; + parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric); + break; + default: + assert(false && "unreachable"); + break; + } +} + +/** + * Append an error to the error list on the parser using the given diagnostic + * ID. This function is a specialization that handles formatting the specific + * kind of error that is being appended. + */ +static void +pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { + switch (diag_id) { + case PM_ERR_HASH_KEY: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, diag_id, pm_token_type_human(parser->previous.type)); + break; + } + case PM_ERR_HASH_VALUE: + case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, pm_token_type_human(parser->current.type)); + break; + } + case PM_ERR_UNARY_RECEIVER: { + const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_type_human(parser->current.type)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, diag_id, human, parser->previous.start[0]); + break; + } + case PM_ERR_UNARY_DISALLOWED: + case PM_ERR_EXPECT_ARGUMENT: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, pm_token_type_human(parser->current.type)); + break; + } + default: + pm_parser_err_previous(parser, diag_id); + break; + } +} + +/** + * Ensures that the current retry token is valid in the current context. + */ +static void +parse_retry(pm_parser_t *parser, const pm_node_t *node) { +#define CONTEXT_NONE 0 +#define CONTEXT_THROUGH_ENSURE 1 +#define CONTEXT_THROUGH_ELSE 2 + + pm_context_node_t *context_node = parser->current_context; + int context = CONTEXT_NONE; + + while (context_node != NULL) { + switch (context_node->context) { + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_SCLASS_RESCUE: + case PM_CONTEXT_DEFINED: + case PM_CONTEXT_RESCUE_MODIFIER: + // These are the good cases. We're allowed to have a retry here. + return; + case PM_CONTEXT_CLASS: + case PM_CONTEXT_DEF: + case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_MAIN: + case PM_CONTEXT_MODULE: + case PM_CONTEXT_PREEXE: + case PM_CONTEXT_SCLASS: + // These are the bad cases. We're not allowed to have a retry in + // these contexts. + if (context == CONTEXT_NONE) { + pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE); + } else if (context == CONTEXT_THROUGH_ENSURE) { + pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE); + } else if (context == CONTEXT_THROUGH_ELSE) { + pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE); + } + return; + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_SCLASS_ELSE: + // These are also bad cases, but with a more specific error + // message indicating the else. + context = CONTEXT_THROUGH_ELSE; + break; + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_SCLASS_ENSURE: + // These are also bad cases, but with a more specific error + // message indicating the ensure. + context = CONTEXT_THROUGH_ENSURE; + break; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_BLOCK_BRACES: + case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_DEFAULT_PARAMS: + case PM_CONTEXT_ELSE: + case PM_CONTEXT_ELSIF: + case PM_CONTEXT_EMBEXPR: + case PM_CONTEXT_FOR_INDEX: + case PM_CONTEXT_FOR: + case PM_CONTEXT_IF: + case PM_CONTEXT_LAMBDA_BRACES: + case PM_CONTEXT_LAMBDA_DO_END: + case PM_CONTEXT_LOOP_PREDICATE: + case PM_CONTEXT_MULTI_TARGET: + case PM_CONTEXT_PARENS: + case PM_CONTEXT_POSTEXE: + case PM_CONTEXT_PREDICATE: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_UNLESS: + case PM_CONTEXT_UNTIL: + case PM_CONTEXT_WHILE: + // In these contexts we should continue walking up the list of + // contexts. + break; + } + + context_node = context_node->prev; + } + +#undef CONTEXT_NONE +#undef CONTEXT_ENSURE +#undef CONTEXT_ELSE +} + +/** + * Ensures that the current yield token is valid in the current context. + */ +static void +parse_yield(pm_parser_t *parser, const pm_node_t *node) { + 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_DEFINED: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_DEF_ELSE: + // These are the good cases. We're allowed to have a block exit + // in these contexts. + return; + case PM_CONTEXT_CLASS: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_MAIN: + case PM_CONTEXT_MODULE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_SCLASS: + case PM_CONTEXT_SCLASS_RESCUE: + case PM_CONTEXT_SCLASS_ENSURE: + case PM_CONTEXT_SCLASS_ELSE: + // These are the bad cases. We're not allowed to have a retry in + // these contexts. + pm_parser_err_node(parser, node, PM_ERR_INVALID_YIELD); + return; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BLOCK_BRACES: + case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_DEFAULT_PARAMS: + case PM_CONTEXT_ELSE: + case PM_CONTEXT_ELSIF: + case PM_CONTEXT_EMBEXPR: + case PM_CONTEXT_FOR_INDEX: + case PM_CONTEXT_FOR: + case PM_CONTEXT_IF: + case PM_CONTEXT_LAMBDA_BRACES: + case PM_CONTEXT_LAMBDA_DO_END: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_LOOP_PREDICATE: + case PM_CONTEXT_MULTI_TARGET: + case PM_CONTEXT_PARENS: + case PM_CONTEXT_POSTEXE: + case PM_CONTEXT_PREDICATE: + case PM_CONTEXT_PREEXE: + case PM_CONTEXT_RESCUE_MODIFIER: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_UNLESS: + case PM_CONTEXT_UNTIL: + case PM_CONTEXT_WHILE: + // In these contexts we should continue walking up the list of + // contexts. + break; + } + + context_node = context_node->prev; + } +} + +/** + * This struct is used to pass information between the regular expression parser + * and the error callback. + */ +typedef struct { + /** The parser that we are parsing the regular expression for. */ + pm_parser_t *parser; + + /** The start of the regular expression. */ + const uint8_t *start; + + /** The end of the regular expression. */ + const uint8_t *end; + + /** + * Whether or not the source of the regular expression is shared. This + * impacts the location of error messages, because if it is shared then we + * can use the location directly and if it is not, then we use the bounds of + * the regular expression itself. + */ + bool shared; +} parse_regular_expression_error_data_t; + +/** + * This callback is called when the regular expression parser encounters a + * syntax error. + */ +static void +parse_regular_expression_error(const uint8_t *start, const uint8_t *end, const char *message, void *data) { + parse_regular_expression_error_data_t *callback_data = (parse_regular_expression_error_data_t *) data; + pm_location_t location; + + if (callback_data->shared) { + location = (pm_location_t) { .start = start, .end = end }; + } else { + location = (pm_location_t) { .start = callback_data->start, .end = callback_data->end }; + } + + PM_PARSER_ERR_FORMAT(callback_data->parser, location.start, location.end, PM_ERR_REGEXP_PARSE_ERROR, message); +} + +/** + * Parse the errors for the regular expression and add them to the parser. + */ +static void +parse_regular_expression_errors(pm_parser_t *parser, pm_regular_expression_node_t *node) { + const pm_string_t *unescaped = &node->unescaped; + parse_regular_expression_error_data_t error_data = { + .parser = parser, + .start = node->base.location.start, + .end = node->base.location.end, + .shared = unescaped->type == PM_STRING_SHARED + }; + + pm_regexp_parse(parser, pm_string_source(unescaped), pm_string_length(unescaped), PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED), NULL, NULL, parse_regular_expression_error, &error_data); +} + +/** + * 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, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) { + 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)) { + bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE); + + // Handle the case where we don't have a comma and we have a + // newline followed by a right bracket. + if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) { + break; + } + + // Ensure that we have a comma between elements in the array. + if (array->elements.size > 0) { + if (accept1(parser, PM_TOKEN_COMMA)) { + // If there was a comma but we also accepts a newline, + // then this is a syntax error. + if (accepted_newline) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + } + } else { + // If there was no comma, then we need to add a syntax + // error. + const uint8_t *location = parser->previous.end; + PM_PARSER_ERR_FORMAT(parser, location, location, PM_ERR_ARRAY_SEPARATOR, pm_token_type_human(parser->current.type)); + + parser->previous.start = location; + parser->previous.type = PM_TOKEN_MISSING; + } + } + + // 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)) { + pm_parser_scope_forwarding_positionals_check(parser, &operator); + } else { + expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + } + + 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); + } + + element = (pm_node_t *) pm_keyword_hash_node_create(parser); + pm_static_literals_t hash_keys = { 0 }; + + 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, &hash_keys, element, (uint16_t) (depth + 1)); + } + + pm_static_literals_free(&hash_keys); + parsed_bare_hash = true; + } else { + element = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, true, PM_ERR_ARRAY_EXPRESSION, (uint16_t) (depth + 1)); + + 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_static_literals_t hash_keys = { 0 }; + pm_hash_key_static_literals_add(parser, &hash_keys, element); + + 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_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); + 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, &hash_keys, element, (uint16_t) (depth + 1)); + } + + pm_static_literals_free(&hash_keys); + 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); + + if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARRAY_TERM, pm_token_type_human(parser->current.type)); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + + 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; + pm_node_flags_t flags = 0; + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + parser_lex(parser); + while (true) { + if (accept1(parser, PM_TOKEN_SEMICOLON)) { + flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + } else if (!accept1(parser, PM_TOKEN_NEWLINE)) { + break; + } + } + + // 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); + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return (pm_node_t *) pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, flags); + } + + // 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, true, false, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1)); + 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 = false; + + if (accept1(parser, PM_TOKEN_SEMICOLON)) { + terminator_found = true; + flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + } else if (accept1(parser, PM_TOKEN_NEWLINE)) { + terminator_found = true; + } + + if (terminator_found) { + while (true) { + if (accept1(parser, PM_TOKEN_SEMICOLON)) { + flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + } else if (!accept1(parser, PM_TOKEN_NEWLINE)) { + break; + } + } + } + + // 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); + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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; + + pm_node_t *result; + if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) { + result = parse_targets(parser, (pm_node_t *) multi_target, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + accept1(parser, PM_TOKEN_NEWLINE); + } else { + result = (pm_node_t *) multi_target; + } + + if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) { + // All set, this is explicitly allowed by the parent + // context. + } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) { + // All set, we're inside a for loop and we're parsing + // multiple targets. + } else if (binding_power != PM_BINDING_POWER_STATEMENT) { + // Multi targets are not allowed when it's not a + // statement level. + pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED); + } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) { + // Multi targets must be followed by an equal sign in + // order to be valid (or a right parenthesis if they are + // nested). + pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + + return result; + } + + // 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(parser, statements, statement, true); + + return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous, flags); + } + + // 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); + flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, statements, statement, true); + + // If we didn't find a terminator and we didn't find a right + // parenthesis, then this is a syntax error. + if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type)); + } + + // Parse each statement within the parentheses. + while (true) { + pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, false, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1)); + pm_statements_node_body_append(parser, statements, node, true); + + // 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 if (!match1(parser, PM_TOKEN_EOF)) { + // If we're at the end of the file, then we're going to add + // an error after this for the ) anyway. + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type)); + } + } + + context_pop(parser); + pm_accepts_block_stack_pop(parser); + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + + // When we're parsing multi targets, we allow them to be followed by + // a right parenthesis if they are at the statement level. This is + // only possible if they are the final statement in a parentheses. + // We need to explicitly reject that here. + { + pm_node_t *statement = statements->body.nodes[statements->body.size - 1]; + + if (PM_NODE_TYPE_P(statement, 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, statement); + + statement = (pm_node_t *) multi_target; + statements->body.nodes[statements->body.size - 1] = statement; + } + + if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) { + const uint8_t *offset = statement->location.end; + pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset }; + pm_node_t *value = (pm_node_t *) pm_missing_node_create(parser, offset, offset); + + statement = (pm_node_t *) pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value); + statements->body.nodes[statements->body.size - 1] = statement; + + pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + } + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + pm_void_statements_check(parser, statements, true); + return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous, flags); + } + case PM_TOKEN_BRACE_LEFT: { + // If we were passed a current_hash_keys via the parser, then that + // means we're already parsing a hash and we want to share the set + // of hash keys with this inner hash we're about to parse for the + // sake of warnings. We'll set it to NULL after we grab it to make + // sure subsequent expressions don't use it. Effectively this is a + // way of getting around passing it to every call to + // parse_expression. + pm_static_literals_t *current_hash_keys = parser->current_hash_keys; + parser->current_hash_keys = NULL; + + 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)) { + if (current_hash_keys != NULL) { + parse_assocs(parser, current_hash_keys, (pm_node_t *) node, (uint16_t) (depth + 1)); + } else { + pm_static_literals_t hash_keys = { 0 }; + parse_assocs(parser, &hash_keys, (pm_node_t *) node, (uint16_t) (depth + 1)); + pm_static_literals_free(&hash_keys); + } + + 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); + pm_node_flag_set(node, parse_unescaped_encoding(parser)); + + // 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, false, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1)); + } + + 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) || + (accepts_command_call && (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) && match1(parser, PM_TOKEN_KEYWORD_DO)) || + match1(parser, PM_TOKEN_BRACE_LEFT) + ) { + pm_arguments_t arguments = { 0 }; + parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } + + 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 *node = (pm_node_t *) pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous); + + if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + } + + 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, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + + // Unary .. and ... are special because these are non-associative + // operators that can also be unary operators. In this case we need + // to explicitly reject code that has a .. or ... that follows this + // expression. + if (match2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) { + pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_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, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1)); + } + + 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, accepts_command_call, (uint16_t) (depth + 1))) { + // Since we found arguments, we need to turn off the + // variable call bit in the flags. + pm_node_flag_unset((pm_node_t *)call, 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 ( + (accepts_command_call && (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) && match1(parser, PM_TOKEN_KEYWORD_DO)) || + match1(parser, PM_TOKEN_BRACE_LEFT) + ) { + pm_arguments_t arguments = { 0 }; + parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1)); + pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments); + + if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) { + // If we're about to convert an 'it' implicit local + // variable read into a method call, we need to remove + // it from the list of implicit local variables. + parse_target_implicit_parameter(parser, node); + } else { + // Otherwise, we're about to convert a regular local + // variable read into a method call, in which case we + // need to indicate that this was not a read for the + // purposes of warnings. + assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)); + + if (pm_token_is_numbered_parameter(identifier.start, identifier.end)) { + parse_target_implicit_parameter(parser, node); + } else { + pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; + pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name); + } + } + + 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, (uint16_t) (depth + 1)); + } + + 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. + assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC); + pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base; + + size_t common_whitespace = (size_t) -1; + parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace; + + 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. + expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length); + pm_token_t content = parse_strings_empty_content(parser->previous.start); + + if (lex_mode.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, (uint16_t) (depth + 1))) == 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. + // + // parse_string_part handles its own errors, so there is no need + // for us to add one here. + 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_node_flag_set(part, parse_unescaped_encoding(parser)); + 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 (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) { + assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t)); + cast->base.type = PM_X_STRING_NODE; + } + + if (lex_mode.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; + expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length); + } 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, (uint16_t) (depth + 1))) != NULL) { + pm_node_list_append(&parts, part); + } + } + + // Now that we have all of the parts, create the correct type of + // interpolated node. + if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) { + pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening); + cast->parts = parts; + + expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length); + 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); + pm_node_list_free(&parts); + + expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length); + 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. + if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) { + pm_node_list_t *nodes; + if (lex_mode.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, false, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1)); + pm_node_t *old_name = parse_alias_argument(parser, false, (uint16_t) (depth + 1)); + + 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_NUMBERED_REFERENCE); + } + } 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); + } + } + PRISM_FALLTHROUGH + default: + return (pm_node_t *) pm_alias_method_node_create(parser, &keyword, new_name, old_name); + } + } + case PM_TOKEN_KEYWORD_CASE: { + size_t opening_newline_index = token_newline_index(parser); + parser_lex(parser); + + pm_token_t case_keyword = parser->previous; + pm_node_t *predicate = NULL; + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + 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, true, false, PM_ERR_CASE_EXPRESSION_AFTER_CASE, (uint16_t) (depth + 1)); + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + } + + if (match1(parser, PM_TOKEN_KEYWORD_END)) { + parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false); + parser_lex(parser); + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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); + pm_static_literals_t literals = { 0 }; + + // 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 (match1(parser, PM_TOKEN_KEYWORD_WHEN)) { + parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true); + parser_lex(parser); + + 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, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + + 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, false, false, PM_ERR_CASE_EXPRESSION_AFTER_WHEN, (uint16_t) (depth + 1)); + pm_when_node_conditions_append(when_node, condition); + + // If we found a missing node, then this is a syntax + // error and we should stop looping. + if (PM_NODE_TYPE_P(condition, PM_MISSING_NODE)) break; + + // If this is a string node, then we need to mark it + // as frozen because when clause strings are frozen. + if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) { + pm_node_flag_set(condition, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL); + } else if (PM_NODE_TYPE_P(condition, PM_SOURCE_FILE_NODE)) { + pm_node_flag_set(condition, PM_NODE_FLAG_STATIC_LITERAL); + } + + pm_when_clause_static_literals_add(parser, &literals, condition); + } + } while (accept1(parser, PM_TOKEN_COMMA)); + + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) { + pm_when_node_then_keyword_loc_set(when_node, &parser->previous); + } + } else { + expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER); + pm_when_node_then_keyword_loc_set(when_node, &parser->previous); + } + + 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, (uint16_t) (depth + 1)); + 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); + } + + pm_static_literals_free(&literals); + 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)) { + parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true); + + 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_constant_id_list_t captures = { 0 }; + pm_node_t *pattern = parse_pattern(parser, &captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_IN, (uint16_t) (depth + 1)); + + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + pm_constant_id_list_free(&captures); + + // 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_COMPOSITION, true, false, PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1)); + 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_COMPOSITION, true, false, PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1)); + 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_IN_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, (uint16_t) (depth + 1)); + } + + // 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, (uint16_t) (depth + 1)), &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_else_clause_set((pm_case_node_t *) node, else_node); + } else { + pm_case_match_node_else_clause_set((pm_case_match_node_t *) node, else_node); + } + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false); + 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); + } + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return node; + } + case PM_TOKEN_KEYWORD_BEGIN: { + size_t opening_newline_index = token_newline_index(parser); + parser_lex(parser); + + pm_token_t begin_keyword = parser->previous; + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + pm_statements_node_t *begin_statements = NULL; + + if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN, (uint16_t) (depth + 1)); + 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, opening_newline_index, &begin_keyword, begin_node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1)); + 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); + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return (pm_node_t *) begin_node; + } + case PM_TOKEN_KEYWORD_BEGIN_UPCASE: { + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + 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, (uint16_t) (depth + 1)); + + 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); + } + + flush_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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, (uint16_t) (depth + 1)); + } + } + + switch (keyword.type) { + case PM_TOKEN_KEYWORD_BREAK: { + pm_node_t *node = (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments); + if (!parser->partial_script) parse_block_exit(parser, node); + return node; + } + case PM_TOKEN_KEYWORD_NEXT: { + pm_node_t *node = (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments); + if (!parser->partial_script) parse_block_exit(parser, node); + return node; + } + case PM_TOKEN_KEYWORD_RETURN: { + pm_node_t *node = (pm_node_t *) pm_return_node_create(parser, &keyword, arguments.arguments); + parse_return(parser, node); + return node; + } + 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, accepts_command_call, (uint16_t) (depth + 1)); + + 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, accepts_command_call, (uint16_t) (depth + 1)); + + // It's possible that we've parsed a block argument through our + // call to parse_arguments_list. If we found one, we should mark it + // as invalid and destroy it, as we don't have a place for it on the + // yield node. + if (arguments.block != NULL) { + pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT); + pm_node_destroy(parser, arguments.block); + arguments.block = NULL; + } + + pm_node_t *node = (pm_node_t *) pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc); + if (!parser->parsing_eval && !parser->partial_script) parse_yield(parser, node); + + return node; + } + case PM_TOKEN_KEYWORD_CLASS: { + size_t opening_newline_index = token_newline_index(parser); + parser_lex(parser); + + pm_token_t class_keyword = parser->previous; + pm_do_loop_stack_push(parser, false); + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + if (accept1(parser, PM_TOKEN_LESS_LESS)) { + pm_token_t operator = parser->previous; + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS, (uint16_t) (depth + 1)); + + pm_parser_scope_push(parser, true); + if (!match2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_type_human(parser->current.type)); + } + + pm_node_t *statements = NULL; + if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1)); + 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_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_SCLASS, (uint16_t) (depth + 1)); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false); + } + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM); + + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, false); + + pm_parser_scope_pop(parser); + pm_do_loop_stack_pop(parser); + + flush_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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, false, false, PM_ERR_CLASS_NAME, (uint16_t) (depth + 1)); + 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, true, false, PM_ERR_CLASS_SUPERCLASS, (uint16_t) (depth + 1)); + } else { + inheritance_operator = not_provided(parser); + superclass = NULL; + } + + 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 (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1)); + 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_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_CLASS, (uint16_t) (depth + 1)); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, 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; + pm_locals_order(parser, &parser->current_scope->locals, &locals, false); + + pm_parser_scope_pop(parser); + 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); + } + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + pm_token_t def_keyword = parser->current; + size_t opening_newline_index = token_newline_index(parser); + + pm_node_t *receiver = NULL; + pm_token_t operator = not_provided(parser); + pm_token_t name; + + // 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); + + // This will be false if the method name is not a valid identifier + // but could be followed by an operator. + bool valid_name = true; + + switch (parser->current.type) { + case PM_CASE_OPERATOR: + pm_parser_scope_push(parser, true); + 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); + 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); + + name = parser->previous; + } + + break; + } + case PM_TOKEN_INSTANCE_VARIABLE: + case PM_TOKEN_CLASS_VARIABLE: + case PM_TOKEN_GLOBAL_VARIABLE: + valid_name = false; + PRISM_FALLTHROUGH + case PM_TOKEN_CONSTANT: + 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_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 { + if (!valid_name) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, identifier, PM_ERR_DEF_NAME, pm_token_type_human(identifier.type)); + } + + 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_COMPOSITION, true, false, PM_ERR_DEF_RECEIVER, (uint16_t) (depth + 1)); + + accept1(parser, PM_TOKEN_NEWLINE); + 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, 0); + + // To push `PM_CONTEXT_DEF_PARAMS` again is for the same + // reason as described the above. + pm_parser_scope_push(parser, true); + context_push(parser, PM_CONTEXT_DEF_PARAMS); + name = parse_method_definition_name(parser); + break; + } + default: + pm_parser_scope_push(parser, true); + name = parse_method_definition_name(parser); + break; + } + + 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, true, false, (uint16_t) (depth + 1)); + } + + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->command_start = true; + + context_pop(parser); + if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_type_human(parser->current.type)); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + + 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, true, false, (uint16_t) (depth + 1)); + + context_pop(parser); + break; + } + default: { + lparen = not_provided(parser); + rparen = not_provided(parser); + params = NULL; + + context_pop(parser); + break; + } + } + + 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, binding_power < PM_BINDING_POWER_COMPOSITION, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1)); + + if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); + + pm_token_t rescue_keyword = parser->previous; + pm_node_t *value = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, false, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); + context_pop(parser); + + statement = (pm_node_t *) pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value); + } + + pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false); + 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 (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + } + + if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) { + assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); + statements = (pm_node_t *) parse_rescues_implicit_begin(parser, opening_newline_index, &def_keyword, def_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_DEF, (uint16_t) (depth + 1)); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false); + } + + 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; + pm_locals_order(parser, &parser->current_scope->locals, &locals, false); + pm_parser_scope_pop(parser); + + /** + * If the final character is @. As is the case when defining + * methods to override the unary operators, we should ignore + * the @ in the same way we do for symbols. + */ + pm_constant_id_t name_id = pm_parser_constant_id_location(parser, name.start, parse_operator_symbol_name(&name)); + + flush_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return (pm_node_t *) pm_def_node_create( + parser, + name_id, + &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; + context_push(parser, PM_CONTEXT_DEFINED); + + if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + lparen = parser->previous; + expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1)); + + if (parser->recovering) { + rparen = not_provided(parser); + } else { + accept1(parser, PM_TOKEN_NEWLINE); + 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, false, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1)); + } + + context_pop(parser); + 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, (uint16_t) (depth + 1)); + + 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: { + size_t opening_newline_index = token_newline_index(parser); + parser_lex(parser); + + pm_token_t for_keyword = parser->previous; + pm_node_t *index; + + 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, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + } + + 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, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); + } 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, (uint16_t) (depth + 1)); + } else { + index = parse_target(parser, index, false, false); + } + + context_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, true, false, PM_ERR_FOR_COLLECTION, (uint16_t) (depth + 1)); + 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); + if (!match2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_type_human(parser->current.type)); + } + } + + pm_statements_node_t *statements = NULL; + if (!match1(parser, PM_TOKEN_KEYWORD_END)) { + statements = parse_statements(parser, PM_CONTEXT_FOR, (uint16_t) (depth + 1)); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false); + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM); + + 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: + if (parser_end_of_line_p(parser)) { + PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_KEYWORD_EOL); + } + + size_t opening_newline_index = token_newline_index(parser); + bool if_after_else = parser->previous.type == PM_TOKEN_KEYWORD_ELSE; + parser_lex(parser); + + return parse_conditional(parser, PM_CONTEXT_IF, opening_newline_index, if_after_else, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + + 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, (uint16_t) (depth + 1)); + + 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)) { + pm_token_t lparen = parser->previous; + + if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + receiver = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0); + } else { + arguments.opening_loc = PM_LOCATION_TOKEN_VALUE(&lparen); + receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); + + 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_NOT, true, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); + } + + return (pm_node_t *) pm_call_node_not_create(parser, receiver, &message, &arguments); + } + case PM_TOKEN_KEYWORD_UNLESS: { + size_t opening_newline_index = token_newline_index(parser); + parser_lex(parser); + + return parse_conditional(parser, PM_CONTEXT_UNLESS, opening_newline_index, false, (uint16_t) (depth + 1)); + } + case PM_TOKEN_KEYWORD_MODULE: { + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + size_t opening_newline_index = token_newline_index(parser); + parser_lex(parser); + pm_token_t module_keyword = parser->previous; + + pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_MODULE_NAME, (uint16_t) (depth + 1)); + 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)) { + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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); + constant_path = (pm_node_t *) pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous); + } + + // 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_parser_scope_push(parser, true); + accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE); + pm_node_t *statements = NULL; + + if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + } + + if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) { + assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); + statements = (pm_node_t *) parse_rescues_implicit_begin(parser, opening_newline_index, &module_keyword, module_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_MODULE, (uint16_t) (depth + 1)); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false); + } + + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, false); + + pm_parser_scope_pop(parser); + 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); + } + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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); + + pm_node_t *node = (pm_node_t *) pm_redo_node_create(parser, &parser->previous); + if (!parser->partial_script) parse_block_exit(parser, node); + + return node; + } + case PM_TOKEN_KEYWORD_RETRY: { + parser_lex(parser); + + pm_node_t *node = (pm_node_t *) pm_retry_node_create(parser, &parser->previous); + parse_retry(parser, node); + + return node; + } + 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: { + size_t opening_newline_index = token_newline_index(parser); + + context_push(parser, PM_CONTEXT_LOOP_PREDICATE); + 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, true, false, PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1)); + + pm_do_loop_stack_pop(parser); + context_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); + expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE); + } + + pm_statements_node_t *statements = NULL; + if (!match1(parser, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, PM_CONTEXT_UNTIL, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM); + + return (pm_node_t *) pm_until_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0); + } + case PM_TOKEN_KEYWORD_WHILE: { + size_t opening_newline_index = token_newline_index(parser); + + context_push(parser, PM_CONTEXT_LOOP_PREDICATE); + 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, true, false, PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1)); + + pm_do_loop_stack_pop(parser); + context_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); + expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE); + } + + pm_statements_node_t *statements = NULL; + if (!match1(parser, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, PM_CONTEXT_WHILE, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM); + + return (pm_node_t *) pm_while_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0); + } + case PM_TOKEN_PERCENT_LOWER_I: { + parser_lex(parser); + pm_token_t opening = parser->previous; + pm_array_node_t *array = pm_array_node_create(parser, &opening); + + 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); + } + + pm_token_t closing = parser->current; + if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_LOWER_TERM); + closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM); + } + pm_array_node_close_set(array, &closing); + + return (pm_node_t *) array; + } + case PM_TOKEN_PERCENT_UPPER_I: { + parser_lex(parser); + pm_token_t opening = parser->previous; + pm_array_node_t *array = pm_array_node_create(parser, &opening); + + // 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 symbol 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_symbol_node_t *cast = (pm_symbol_node_t *) current; + pm_token_t bounds = not_provided(parser); + + pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = cast->value_loc.start, .end = cast->value_loc.end }; + pm_node_t *first_string = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped); + pm_node_t *second_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, first_string); + pm_interpolated_symbol_node_append(interpolated, second_string); + + xfree(current); + 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, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + 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); + } + + pm_token_t closing = parser->current; + if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_UPPER_TERM); + closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM); + } + pm_array_node_close_set(array, &closing); + + return (pm_node_t *) array; + } + case PM_TOKEN_PERCENT_LOWER_W: { + parser_lex(parser); + pm_token_t opening = parser->previous; + pm_array_node_t *array = pm_array_node_create(parser, &opening); + + // 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); + } + + pm_token_t closing = parser->current; + if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_LOWER_TERM); + closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM); + } + + pm_array_node_close_set(array, &closing); + return (pm_node_t *) array; + } + case PM_TOKEN_PERCENT_UPPER_W: { + parser_lex(parser); + pm_token_t opening = parser->previous; + pm_array_node_t *array = pm_array_node_create(parser, &opening); + + // 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: { + // Reset the explicit encoding if we hit a separator + // since each element can have its own encoding. + parser->explicit_encoding = NULL; + + 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); + pm_node_flag_set(string, parse_unescaped_encoding(parser)); + 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, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + 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); + } + + pm_token_t closing = parser->current; + if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_UPPER_TERM); + closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM); + } + + pm_array_node_close_set(array, &closing); + 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); + + pm_node_t *node = (pm_node_t *) pm_regular_expression_node_create(parser, &opening, &content, &parser->previous); + pm_node_flag_set(node, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING); + + return node; + } + + pm_interpolated_regular_expression_node_t *interpolated; + + 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; + bool ascii_only = parser->current_regular_expression_ascii_only; + 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)) { + pm_regular_expression_node_t *node = (pm_regular_expression_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + + // If we're not immediately followed by a =~, then we want + // to parse all of the errors at this point. If it is + // followed by a =~, then it will get parsed higher up while + // parsing the named captures as well. + if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) { + parse_regular_expression_errors(parser, node); + } + + pm_node_flag_set((pm_node_t *) node, parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, node->base.flags)); + return (pm_node_t *) node; + } + + // If we get here, then we have interpolation so we'll need to create + // a regular expression node with interpolation. + interpolated = 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); + + if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { + // This is extremely strange, but the first string part of a + // regular expression will always be tagged as binary if we + // are in a US-ASCII file, no matter its contents. + pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING); + } + + pm_interpolated_regular_expression_node_append(interpolated, 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. + interpolated = 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, (uint16_t) (depth + 1))) != NULL) { + pm_interpolated_regular_expression_node_append(interpolated, part); + } + } + + pm_token_t closing = parser->current; + if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_REGEXP_TERM); + closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + } else { + expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM); + } + + pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing); + return (pm_node_t *) interpolated; + } + 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 (match1(parser, PM_TOKEN_STRING_END)) { + pm_node_t *node = (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); + pm_node_flag_set(node, parse_unescaped_encoding(parser)); + parser_lex(parser); + return node; + } + + // 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_node_flag_set(part, parse_unescaped_encoding(parser)); + + 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, (uint16_t) (depth + 1))) != NULL) { + pm_interpolated_xstring_node_append(node, part); + } + } + + pm_token_t closing = parser->current; + if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_XSTRING_TERM); + closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM); + } + pm_interpolated_xstring_node_closing_set(node, &closing); + + 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) { + pm_parser_err_prefix(parser, diag_id); + 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, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1)); + } else { + return parse_target_validate(parser, splat, true); + } + } + case PM_TOKEN_BANG: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } + + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, binding_power < PM_BINDING_POWER_MATCH, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!"); + + pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT); + return (pm_node_t *) node; + } + case PM_TOKEN_TILDE: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~"); + + return (pm_node_t *) node; + } + case PM_TOKEN_UMINUS: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + 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, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + + 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, false, false, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1)); + node = (pm_node_t *) pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0); + 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; + + size_t opening_newline_index = token_newline_index(parser); + 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 *block_parameters; + + switch (parser->current.type) { + case PM_TOKEN_PARENTHESIS_LEFT: { + pm_token_t opening = parser->current; + parser_lex(parser); + + if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + block_parameters = pm_block_parameters_node_create(parser, NULL, &opening); + } else { + block_parameters = parse_block_parameters(parser, false, &opening, true, true, (uint16_t) (depth + 1)); + } + + accept1(parser, PM_TOKEN_NEWLINE); + expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + + pm_block_parameters_node_closing_set(block_parameters, &parser->previous); + break; + } + case PM_CASE_PARAMETER: { + pm_accepts_block_stack_push(parser, false); + pm_token_t opening = not_provided(parser); + block_parameters = parse_block_parameters(parser, false, &opening, true, false, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + break; + } + default: { + block_parameters = 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 (!match1(parser, PM_TOKEN_BRACE_RIGHT)) { + body = (pm_node_t *) parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1)); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false); + 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, (uint16_t) (depth + 1)); + 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_implicit_begin(parser, opening_newline_index, &operator, opening.start, (pm_statements_node_t *) body, PM_RESCUES_LAMBDA, (uint16_t) (depth + 1)); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false); + } + + expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END); + } + + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser)); + pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &operator, &parser->previous); + + 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, parameters, body); + } + case PM_TOKEN_UPLUS: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + 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, accepts_label, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } + default: { + pm_context_t recoverable = context_recoverable(parser, &parser->current); + + if (recoverable != PM_CONTEXT_NONE) { + parser->recovering = true; + + // If the given error is not the generic one, then we'll add it + // here because it will provide more context in addition to the + // recoverable error that we will also add. + if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) { + pm_parser_err_prefix(parser, diag_id); + } + + // If we get here, then we are assuming this token is closing a + // parent context, so we'll indicate that to the user so that + // they know how we behaved. + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_type_human(parser->current.type), context_human(recoverable)); + } else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) { + // We're going to make a special case here, because "cannot + // parse expression" is pretty generic, and we know here that we + // have an unexpected token. + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_type_human(parser->current.type)); + } else { + pm_parser_err_prefix(parser, diag_id); + } + + return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + } + } +} + +/** + * Parse a value that is going to be written to some kind of variable or method + * call. We need to handle this separately because the rescue modifier is + * permitted on the end of the these expressions, which is a deviation from its + * normal binding power. + * + * Note that this will only be called after an operator write, as in &&=, ||=, + * or any of the binary operators that can be written to a variable. + */ +static pm_node_t * +parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id, uint16_t depth) { + pm_node_t *value = parse_value_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, false, diag_id, (uint16_t) (depth + 1)); + + // Contradicting binding powers, the right-hand-side value of the assignment + // allows the `rescue` modifier. + if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); + + pm_token_t rescue = parser->current; + parser_lex(parser); + + pm_node_t *right = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, false, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); + context_pop(parser); + + return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right); + } + + return value; +} + +/** + * When a local variable write node is the value being written in a different + * write, the local variable is considered "used". + */ +static void +parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_BEGIN_NODE: { + const pm_begin_node_t *cast = (const pm_begin_node_t *) node; + if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements); + break; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node; + pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name); + break; + } + case PM_PARENTHESES_NODE: { + const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node; + if (cast->body != NULL) parse_assignment_value_local(parser, cast->body); + break; + } + case PM_STATEMENTS_NODE: { + const pm_statements_node_t *cast = (const pm_statements_node_t *) node; + const pm_node_t *statement; + + PM_NODE_LIST_FOREACH(&cast->body, index, statement) { + parse_assignment_value_local(parser, statement); + } + break; + } + default: + break; + } +} + +/** + * Parse the value (or values, through an implicit array) that is going to be + * written to some kind of variable or method call. We need to handle this + * separately because the rescue modifier is permitted on the end of the these + * expressions, which is a deviation from its normal binding power. + * + * Additionally, if the value is a local variable write node (e.g., a = a = 1), + * the "a" is marked as being used so the parser should not warn on it. + * + * Note that this will only be called after an = operator, as that is the only + * operator that allows multiple values after it. + */ +static pm_node_t * +parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id, uint16_t depth) { + bool permitted = true; + if (previous_binding_power != PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_USTAR)) permitted = false; + + pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, diag_id, (uint16_t) (depth + 1)); + if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE); + + parse_assignment_value_local(parser, value); + bool single_value = true; + + if (previous_binding_power == PM_BINDING_POWER_STATEMENT && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) { + 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, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1)); + + pm_array_node_elements_append(array, element); + if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break; + + parse_assignment_value_local(parser, element); + } + } + + // Contradicting binding powers, the right-hand-side value of the assignment + // allows the `rescue` modifier. + if ((single_value || (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1))) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); + + pm_token_t rescue = parser->current; + parser_lex(parser); + + bool accepts_command_call_inner = false; + + // RHS can accept command call iff the value is a call with arguments + // but without parenthesis. + if (PM_NODE_TYPE_P(value, PM_CALL_NODE)) { + pm_call_node_t *call_node = (pm_call_node_t *) value; + if ((call_node->arguments != NULL) && (call_node->opening_loc.start == NULL)) { + accepts_command_call_inner = true; + } + } + + pm_node_t *right = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, accepts_command_call_inner, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); + context_pop(parser); + + return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right); + } + + return value; +} + +/** + * Ensure 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; + } +} + +/** + * This struct is used to pass information between the regular expression parser + * and the named capture callback. + */ +typedef struct { + /** The parser that is parsing the regular expression. */ + pm_parser_t *parser; + + /** The call node wrapping the regular expression node. */ + pm_call_node_t *call; + + /** The match write node that is being created. */ + pm_match_write_node_t *match; + + /** The list of names that have been parsed. */ + pm_constant_id_list_t names; + + /** + * Whether the content of the regular expression is shared. This impacts + * whether or not we used owned constants or shared constants in the + * constant pool for the names of the captures. + */ + bool shared; +} parse_regular_expression_named_capture_data_t; + +static inline const uint8_t * +pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) { + cursor++; + + if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) { + uint8_t value = escape_hexadecimal_digit(*cursor); + cursor++; + + if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) { + value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(*cursor)); + cursor++; + } + + pm_buffer_append_byte(unescaped, value); + } else { + pm_buffer_append_string(unescaped, "\\x", 2); + } + + return cursor; +} + +static inline const uint8_t * +pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) { + uint8_t value = (uint8_t) (*cursor - '0'); + cursor++; + + if (cursor < end && pm_char_is_octal_digit(*cursor)) { + value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0')); + cursor++; + + if (cursor < end && pm_char_is_octal_digit(*cursor)) { + value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0')); + cursor++; + } + } + + pm_buffer_append_byte(unescaped, value); + return cursor; +} + +static inline const uint8_t * +pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) { + const uint8_t *start = cursor - 1; + cursor++; + + if (cursor >= end) { + pm_buffer_append_string(unescaped, "\\u", 2); + return cursor; + } + + if (*cursor != '{') { + size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4)); + uint32_t value = escape_unicode(parser, cursor, length); + + if (!pm_buffer_append_unicode_codepoint(unescaped, value)) { + pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start)); + } + + return cursor + length; + } + + cursor++; + for (;;) { + while (cursor < end && *cursor == ' ') cursor++; + + if (cursor >= end) break; + if (*cursor == '}') { + cursor++; + break; + } + + size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor); + uint32_t value = escape_unicode(parser, cursor, length); + + (void) pm_buffer_append_unicode_codepoint(unescaped, value); + cursor += length; + } + + return cursor; +} + +static void +pm_named_capture_escape(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *source, const size_t length, const uint8_t *cursor) { + const uint8_t *end = source + length; + pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source)); + + for (;;) { + if (++cursor >= end) { + pm_buffer_append_byte(unescaped, '\\'); + return; + } + + switch (*cursor) { + case 'x': + cursor = pm_named_capture_escape_hex(unescaped, cursor, end); + break; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': + cursor = pm_named_capture_escape_octal(unescaped, cursor, end); + break; + case 'u': + cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end); + break; + default: + pm_buffer_append_byte(unescaped, '\\'); + break; + } + + const uint8_t *next_cursor = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, parser->encoding); + if (next_cursor == NULL) break; + + pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (next_cursor - cursor)); + cursor = next_cursor; + } + + pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (end - cursor)); +} + +/** + * This callback is called when the regular expression parser encounters a named + * capture group. + */ +static void +parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { + parse_regular_expression_named_capture_data_t *callback_data = (parse_regular_expression_named_capture_data_t *) data; + + pm_parser_t *parser = callback_data->parser; + pm_call_node_t *call = callback_data->call; + pm_constant_id_list_t *names = &callback_data->names; + + const uint8_t *source = pm_string_source(capture); + size_t length = pm_string_length(capture); + pm_buffer_t unescaped = { 0 }; + + // First, we need to handle escapes within the name of the capture group. + // This is because regular expressions have three different representations + // in prism. The first is the plain source code. The second is the + // representation that will be sent to the regular expression engine, which + // is the value of the "unescaped" field. This is poorly named, because it + // actually still contains escapes, just a subset of them that the regular + // expression engine knows how to handle. The third representation is fully + // unescaped, which is what we need. + const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding); + if (PRISM_UNLIKELY(cursor != NULL)) { + pm_named_capture_escape(parser, &unescaped, source, length, cursor); + source = (const uint8_t *) pm_buffer_value(&unescaped); + length = pm_buffer_length(&unescaped); + } + + 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 (!pm_slice_is_valid_local(parser, source, source + length)) { + pm_buffer_free(&unescaped); + return; + } + + if (callback_data->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); + } 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 = (pm_location_t) { .start = call->receiver->location.start, .end = call->receiver->location.end }; + + void *memory = xmalloc(length); + if (memory == NULL) abort(); + + memcpy(memory, source, length); + name = pm_parser_constant_id_owned(parser, (uint8_t *) memory, length); + } + + // Add this name to the list of constants if it is valid, not duplicated, + // and not a keyword. + if (name != 0 && !pm_constant_id_list_includes(names, name)) { + pm_constant_id_list_append(names, name); + + int depth; + if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) { + // If the local is not already a local but it is a keyword, then we + // do not want to add a capture for this. + if (pm_local_is_keyword((const char *) source, length)) { + pm_buffer_free(&unescaped); + return; + } + + // If the identifier is not already a local, then we will add it to + // the local table. + pm_parser_local_add(parser, name, location.start, location.end, 0); + } + + // Here we lazily create the MatchWriteNode since we know we're + // about to add a target. + if (callback_data->match == NULL) { + callback_data->match = pm_match_write_node_create(parser, call); + } + + // 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(parser, &location, name, depth == -1 ? 0 : (uint32_t) depth); + pm_node_list_append(&callback_data->match->targets, target); + } + + pm_buffer_free(&unescaped); +} + +/** + * 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, bool extended_mode) { + parse_regular_expression_named_capture_data_t callback_data = { + .parser = parser, + .call = call, + .names = { 0 }, + .shared = content->type == PM_STRING_SHARED + }; + + parse_regular_expression_error_data_t error_data = { + .parser = parser, + .start = call->receiver->location.start, + .end = call->receiver->location.end, + .shared = content->type == PM_STRING_SHARED + }; + + pm_regexp_parse(parser, pm_string_source(content), pm_string_length(content), extended_mode, parse_regular_expression_named_capture, &callback_data, parse_regular_expression_error, &error_data); + pm_constant_id_list_free(&callback_data.names); + + if (callback_data.match != NULL) { + return (pm_node_t *) callback_data.match; + } else { + return (pm_node_t *) call; + } +} + +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, bool accepts_command_call, uint16_t depth) { + 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_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + pm_parser_local_add_location(parser, call_node->message_loc.start, call_node->message_loc.end, 0); + } + } + PRISM_FALLTHROUGH + case PM_CASE_WRITABLE: { + parser_lex(parser); + pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) ? PM_BINDING_POWER_MULTI_ASSIGNMENT + 1 : binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1)); + + if (PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) && previous_binding_power != PM_BINDING_POWER_STATEMENT) { + pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_MULTI_WRITE); + } + + 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, PM_BINDING_POWER_MULTI_ASSIGNMENT + 1, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1)); + return parse_write(parser, (pm_node_t *) multi_target, &token, value); + } + case PM_SOURCE_ENCODING_NODE: + case PM_FALSE_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_NIL_NODE: + case PM_SELF_NODE: + case PM_TRUE_NODE: { + // In these special cases, we have specific error messages + // and we will replace them with local variable writes. + parser_lex(parser); + pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1)); + return parse_unwriteable_write(parser, node, &token, value); + } + default: + // In this case we have an = sign, but we don't know what + // it's for. We need to treat it as an error. We'll mark it + // as an error and skip past it. + parser_lex(parser); + pm_parser_err_token(parser, &token, PM_ERR_EXPRESSION_NOT_WRITABLE); + 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_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY); + PRISM_FALLTHROUGH + case PM_GLOBAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + pm_node_t *write = (pm_node_t *) pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value); + + return parse_shareable_constant_write(parser, write); + } + case PM_CONSTANT_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + pm_node_t *write = (pm_node_t *) pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return parse_shareable_constant_write(parser, write); + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + 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_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0); + + parse_target_implicit_parameter(parser, node); + pm_node_destroy(parser, node); + return result; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { + PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start); + parse_target_implicit_parameter(parser, 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + 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: { + 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_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + 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, 1); + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + 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; + } + + // Move past the token here so that we have already added + // the local variable by this point. + parser_lex(parser); + + // 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_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) { + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + 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(parser, 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); + 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_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY); + PRISM_FALLTHROUGH + case PM_GLOBAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + pm_node_t *write = (pm_node_t *) pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value); + + return parse_shareable_constant_write(parser, write); + } + case PM_CONSTANT_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + pm_node_t *write = (pm_node_t *) pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return parse_shareable_constant_write(parser, write); + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + 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_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0); + + parse_target_implicit_parameter(parser, node); + pm_node_destroy(parser, node); + return result; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { + PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start); + parse_target_implicit_parameter(parser, 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + 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: { + 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_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + 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, 1); + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + 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; + } + + // Move past the token here so that we have already added + // the local variable by this point. + parser_lex(parser); + + // 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_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) { + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + 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(parser, 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); + 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_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY); + PRISM_FALLTHROUGH + case PM_GLOBAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + pm_node_t *write = (pm_node_t *) pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value); + + return parse_shareable_constant_write(parser, write); + } + case PM_CONSTANT_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + pm_node_t *write = (pm_node_t *) pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value); + + pm_node_destroy(parser, node); + return parse_shareable_constant_write(parser, write); + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + 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_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + parser_lex(parser); + + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0); + + parse_target_implicit_parameter(parser, node); + pm_node_destroy(parser, node); + return result; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { + PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start); + parse_target_implicit_parameter(parser, 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + 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_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + 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, 1); + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + 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_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) { + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + 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(parser, 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, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + 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_TOKEN_FORMAT(parser, parser->previous, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_type_human(parser->current.type)); + return node; + } + } + case PM_TOKEN_AMPERSAND_AMPERSAND: + case PM_TOKEN_KEYWORD_AND: { + parser_lex(parser); + + pm_node_t *right = parse_expression(parser, binding_power, parser->previous.type == PM_TOKEN_KEYWORD_AND, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + 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, parser->previous.type == PM_TOKEN_KEYWORD_OR, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + 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, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + + // 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, 0); + 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; + + pm_node_t *part; + PM_NODE_LIST_FOREACH(parts, index, part) { + 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 = xmalloc(total_length); + if (!memory) abort(); + + uint8_t *cursor = memory; + PM_NODE_LIST_FOREACH(parts, index, part) { + pm_string_t *unescaped = &((pm_string_node_t *) part)->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_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)); + 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, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)); + } + + 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_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_token_t operator = parser->previous; + switch (PM_NODE_TYPE(node)) { + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + default: + break; + } + + pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument, 0); + } + case PM_TOKEN_GREATER: + case PM_TOKEN_GREATER_EQUAL: + case PM_TOKEN_LESS: + case PM_TOKEN_LESS_EQUAL: { + if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) { + PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON); + } + + parser_lex(parser); + pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON); + } + 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, false, (uint16_t) (depth + 1)); + return (pm_node_t *) pm_call_node_shorthand_create(parser, node, &operator, &arguments); + } + + switch (PM_NODE_TYPE(node)) { + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + default: + break; + } + + 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_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_type_human(parser->current.type)); + message = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + } + } + + parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } 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, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + } + + 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, true, false, PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1)); + 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, true, false, PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1)); + 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(parser, statements, node, true); + + pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1)); + 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(parser, statements, node, true); + + pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1)); + 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: { + context_push(parser, PM_CONTEXT_TERNARY); + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + pm_token_t qmark = parser->current; + parser_lex(parser); + + pm_node_t *true_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_TERNARY_EXPRESSION_TRUE, (uint16_t) (depth + 1)); + + 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); + + context_pop(parser); + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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, false, false, PM_ERR_TERNARY_EXPRESSION_FALSE, (uint16_t) (depth + 1)); + + context_pop(parser); + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + 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) || + (accepts_command_call && (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, accepts_command_call, (uint16_t) (depth + 1)); + 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. + path = (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, &parser->previous); + } + + // 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, (uint16_t) (depth + 1)); + } + + 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, accepts_command_call, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } + + 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, false, (uint16_t) (depth + 1)); + + return (pm_node_t *) pm_call_node_shorthand_create(parser, node, &delimiter, &arguments); + } + default: { + expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); + return (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, &parser->previous); + } + } + } + case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: { + context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); + parser_lex(parser); + accept1(parser, PM_TOKEN_NEWLINE); + + pm_node_t *value = parse_expression(parser, binding_power, true, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); + context_pop(parser); + + 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, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } + + // 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, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } + + 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_constant_id_list_t captures = { 0 }; + pm_node_t *pattern = parse_pattern(parser, &captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_IN, (uint16_t) (depth + 1)); + + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + pm_constant_id_list_free(&captures); + + 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_constant_id_list_t captures = { 0 }; + pm_node_t *pattern = parse_pattern(parser, &captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET, (uint16_t) (depth + 1)); + + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + pm_constant_id_list_free(&captures); + + return (pm_node_t *) pm_match_required_node_create(parser, node, pattern, &operator); + } + default: + assert(false && "unreachable"); + return NULL; + } +} + +#undef PM_PARSE_PATTERN_SINGLE +#undef PM_PARSE_PATTERN_TOP +#undef PM_PARSE_PATTERN_MULTI + +/** + * Determine if a given call node looks like a "command", which means it has + * arguments but does not have parentheses. + */ +static inline bool +pm_call_node_command_p(const pm_call_node_t *node) { + return ( + (node->opening_loc.start == NULL) && + (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) && + (node->arguments != NULL || node->block != 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, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) { + if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) { + pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP); + return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + } + + pm_node_t *node = parse_expression_prefix(parser, binding_power, accepts_command_call, accepts_label, diag_id, depth); + + 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. + 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_MULTI_WRITE_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) { + return node; + } + break; + case PM_CALL_NODE: + // If we have a call node, then we need to check if it looks like a + // method call without parentheses that contains arguments. If it + // does, then it has different rules for parsing infix operators, + // namely that it only accepts composition (and/or) and modifiers + // (if/unless/etc.). + if ((pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_COMPOSITION) && pm_call_node_command_p((pm_call_node_t *) node)) { + return node; + } + break; + case PM_SYMBOL_NODE: + // If we have a symbol node that is being parsed as a label, then we + // need to immediately return, because there should never be an + // infix operator following this node. + if (pm_symbol_node_label_p(node)) { + 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; + pm_token_type_t current_token_type; + + while ( + current_token_type = parser->current.type, + current_binding_powers = pm_binding_powers[current_token_type], + binding_power <= current_binding_powers.left && + current_binding_powers.binary + ) { + node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call, (uint16_t) (depth + 1)); + + switch (PM_NODE_TYPE(node)) { + case PM_MULTI_WRITE_NODE: + // Multi-write nodes are statements, and cannot be followed by + // operators except modifiers. + if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) { + return node; + } + break; + case PM_CLASS_VARIABLE_WRITE_NODE: + case PM_CONSTANT_PATH_WRITE_NODE: + case PM_CONSTANT_WRITE_NODE: + case PM_GLOBAL_VARIABLE_WRITE_NODE: + case PM_INSTANCE_VARIABLE_WRITE_NODE: + case PM_LOCAL_VARIABLE_WRITE_NODE: + // These expressions are statements, by virtue of the right-hand + // side of their write being an implicit array. + if (PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) { + return node; + } + break; + case PM_CALL_NODE: + // These expressions are also statements, by virtue of the + // right-hand side of the expression (i.e., the last argument to + // the call node) being an implicit array. + if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY) && pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) { + return node; + } + break; + default: + break; + } + + // If the operator is nonassoc and we should not be able to parse the + // upcoming infix operator, break. + if (current_binding_powers.nonassoc) { + // If this is a non-assoc operator and we are about to parse the + // exact same operator, then we need to add an error. + if (match1(parser, current_token_type)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type)); + break; + } + + // If this is an endless range, then we need to reject a couple of + // additional operators because it violates the normal operator + // precedence rules. Those patterns are: + // + // 1.. & 2 + // 1.. * 2 + // + if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) { + if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type)); + break; + } + + if (PM_BINDING_POWER_TERM <= pm_binding_powers[parser->current.type].left) { + break; + } + } else if (current_binding_powers.left <= pm_binding_powers[parser->current.type].left) { + break; + } + } + + if (accepts_command_call) { + // A command-style method call is only accepted on method chains. + // Thus, we check whether the parsed node can continue method chains. + // The method chain can continue if the parsed node is one of the following five kinds: + // (1) index access: foo[1] + // (2) attribute access: foo.bar + // (3) method call with parenthesis: foo.bar(1) + // (4) method call with a block: foo.bar do end + // (5) constant path: foo::Bar + switch (node->type) { + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *)node; + if ( + // (1) foo[1] + !( + cast->call_operator_loc.start == NULL && + cast->message_loc.start != NULL && + cast->message_loc.start[0] == '[' && + cast->message_loc.end[-1] == ']' + ) && + // (2) foo.bar + !( + cast->call_operator_loc.start != NULL && + cast->arguments == NULL && + cast->block == NULL && + cast->opening_loc.start == NULL + ) && + // (3) foo.bar(1) + !( + cast->call_operator_loc.start != NULL && + cast->opening_loc.start != NULL + ) && + // (4) foo.bar do end + !( + cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE) + ) + ) { + accepts_command_call = false; + } + break; + } + // (5) foo::Bar + case PM_CONSTANT_PATH_NODE: + break; + default: + accepts_command_call = false; + break; + } + } + } + + return node; +} + +/** + * ruby -p, ruby -n, ruby -a, and ruby -l options will mutate the AST. We + * perform that mutation here. + */ +static pm_statements_node_t * +wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { + if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) { + if (statements == NULL) { + statements = pm_statements_node_create(parser); + } + + pm_arguments_node_t *arguments = pm_arguments_node_create(parser); + pm_arguments_node_arguments_append( + arguments, + (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)) + ); + + pm_statements_node_body_append(parser, statements, (pm_node_t *) pm_call_node_fcall_synthesized_create( + parser, + arguments, + pm_parser_constant_id_constant(parser, "print", 5) + ), true); + } + + if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) { + if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) { + if (statements == NULL) { + statements = pm_statements_node_create(parser); + } + + pm_arguments_node_t *arguments = pm_arguments_node_create(parser); + pm_arguments_node_arguments_append( + arguments, + (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)) + ); + + pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)); + pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, (pm_node_t *) receiver, "split", arguments); + + pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create( + parser, + pm_parser_constant_id_constant(parser, "$F", 2), + (pm_node_t *) call + ); + + pm_statements_node_body_prepend(statements, (pm_node_t *) write); + } + + pm_arguments_node_t *arguments = pm_arguments_node_create(parser); + pm_arguments_node_arguments_append( + arguments, + (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)) + ); + + if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) { + pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser); + pm_keyword_hash_node_elements_append(keywords, (pm_node_t *) pm_assoc_node_create( + parser, + (pm_node_t *) pm_symbol_node_synthesized_create(parser, "chomp"), + &(pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }, + (pm_node_t *) pm_true_node_synthesized_create(parser) + )); + + pm_arguments_node_arguments_append(arguments, (pm_node_t *) keywords); + pm_node_flag_set((pm_node_t *) arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS); + } + + pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, wrapped_statements, (pm_node_t *) pm_while_node_synthesized_create( + parser, + (pm_node_t *) pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4)), + statements + ), true); + + statements = wrapped_statements; + } + + return statements; +} + +/** + * Parse the top-level program node. + */ +static pm_node_t * +parse_program(pm_parser_t *parser) { + // If the current scope is NULL, then we want to push a new top level scope. + // The current scope could exist in the event that we are parsing an eval + // and the user has passed into scopes that already exist. + if (parser->current_scope == NULL) { + pm_parser_scope_push(parser, true); + } + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + parser_lex(parser); + pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN, 0); + + if (statements != NULL && !parser->parsing_eval) { + // If we have statements, then the top-level statement should be + // explicitly checked as well. We have to do this here because + // everywhere else we check all but the last statement. + assert(statements->body.size > 0); + pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]); + } + + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, true); + pm_parser_scope_pop(parser); + + // At the top level, see if we need to wrap the statements in a program + // node with a while loop based on the options. + if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) { + statements = wrap_statements(parser, statements); + } else { + flush_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + } + + // 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 (statements == NULL) { + statements = pm_statements_node_create(parser); + pm_statements_node_location_set(statements, parser->start, parser->start); + } + + return (pm_node_t *) pm_program_node_create(parser, &locals, statements); +} + +/******************************************************************************/ +/* External functions */ +/******************************************************************************/ + +/** + * A vendored version of strnstr that is used to find a substring within a + * string with a given length. This function is used to search for the Ruby + * engine name within a shebang when the -x option is passed to Ruby. + * + * The only modification that we made here is that we don't do NULL byte checks + * because we know the little parameter will not have a NULL byte and we allow + * the big parameter to have them. + */ +static const char * +pm_strnstr(const char *big, const char *little, size_t big_length) { + size_t little_length = strlen(little); + + for (const char *big_end = big + big_length; big < big_end; big++) { + if (*big == *little && memcmp(big, little, little_length) == 0) return big; + } + + return NULL; +} + +#ifdef _WIN32 +#define pm_parser_warn_shebang_carriage_return(parser, start, length) ((void) 0) +#else +/** + * Potentially warn the user if the shebang that has been found to include + * "ruby" has a carriage return at the end, as that can cause problems on some + * platforms. + */ +static void +pm_parser_warn_shebang_carriage_return(pm_parser_t *parser, const uint8_t *start, size_t length) { + if (length > 2 && start[length - 2] == '\r' && start[length - 1] == '\n') { + pm_parser_warn(parser, start, start + length, PM_WARN_SHEBANG_CARRIAGE_RETURN); + } +} +#endif + +/** + * Process the shebang when initializing the parser. This function assumes that + * the shebang_callback option has already been checked for nullability. + */ +static void +pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const char *engine, size_t length) { + const char *switches = pm_strnstr(engine, " -", length); + if (switches == NULL) return; + + pm_options_t next_options = *options; + options->shebang_callback( + &next_options, + (const uint8_t *) (switches + 1), + length - ((size_t) (switches - engine)) - 1, + options->shebang_callback_data + ); + + size_t encoding_length; + if ((encoding_length = pm_string_length(&next_options.encoding)) > 0) { + const uint8_t *encoding_source = pm_string_source(&next_options.encoding); + parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length); + } + + parser->command_line = next_options.command_line; + parser->frozen_string_literal = next_options.frozen_string_literal; +} + +/** + * 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) { + .node_id = 0, + .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, + .data_loc = { .start = NULL, .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_ENTRY, + .encoding_changed_callback = NULL, + .encoding_comment_start = source, + .lex_callback = NULL, + .filepath = { 0 }, + .constant_pool = { 0 }, + .newline_list = { 0 }, + .integer_base = 0, + .current_string = PM_STRING_EMPTY, + .start_line = 1, + .explicit_encoding = NULL, + .command_line = 0, + .parsing_eval = false, + .partial_script = false, + .command_start = true, + .recovering = false, + .encoding_locked = false, + .encoding_changed = false, + .pattern_matching_newlines = false, + .in_keyword_arg = false, + .current_block_exits = NULL, + .semantic_token_seen = false, + .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET, + .current_regular_expression_ascii_only = false, + .warn_mismatched_indentation = true + }; + + // 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 = 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); + } + + // encoding_locked option + parser->encoding_locked = options->encoding_locked; + + // frozen_string_literal option + parser->frozen_string_literal = options->frozen_string_literal; + + // command_line option + parser->command_line = options->command_line; + + // version option + parser->version = options->version; + + // partial_script + parser->partial_script = options->partial_script; + + // scopes option + parser->parsing_eval = options->scopes_count > 0; + if (parser->parsing_eval) parser->warn_mismatched_indentation = false; + + 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); + + // Scopes given from the outside are not allowed to have numbered + // parameters. + parser->current_scope->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED; + + 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); + + void *allocated = xmalloc(length); + if (allocated == NULL) continue; + + memcpy(allocated, source, length); + pm_parser_local_add_owned(parser, (uint8_t *) 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 (parser->encoding != PM_ENCODING_UTF_8_ENTRY) { + parser->encoding = PM_ENCODING_UTF_8_ENTRY; + if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser); + } + } + + // If the -x command line flag is set, or the first shebang of the file does + // not include "ruby", then we'll search for a shebang that does include + // "ruby" and start parsing from there. + bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser); + + // If the first two bytes of the source are a shebang, then we will do a bit + // of extra processing. + // + // First, we'll indicate that the encoding comment is at the end of the + // shebang. This means that when a shebang is present the encoding comment + // can begin on the second line. + // + // Second, we will check if the shebang includes "ruby". If it does, then we + // we will start parsing from there. We will also potentially warning the + // user if there is a carriage return at the end of the shebang. We will + // also potentially call the shebang callback if this is the main script to + // allow the caller to parse the shebang and find any command-line options. + // If the shebang does not include "ruby" and this is the main script being + // parsed, then we will start searching the file for a shebang that does + // contain "ruby" as if -x were passed on the command line. + const uint8_t *newline = next_newline(parser->start, parser->end - parser->start); + size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->start); + + if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') { + const char *engine; + + if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) { + if (newline != NULL) { + parser->encoding_comment_start = newline + 1; + + if (options == NULL || options->main_script) { + pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1); + } + } + + if (options != NULL && options->main_script && options->shebang_callback != NULL) { + pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start))); + } + + search_shebang = false; + } else if (options->main_script && !parser->parsing_eval) { + search_shebang = true; + } + } + + // Here we're going to find the first shebang that includes "ruby" and start + // parsing from there. + if (search_shebang) { + // If a shebang that includes "ruby" is not found, then we're going to a + // a load error to the list of errors on the parser. + bool found_shebang = false; + + // This is going to point to the start of each line as we check it. + // We'll maintain a moving window looking at each line at they come. + const uint8_t *cursor = parser->start; + + // The newline pointer points to the end of the current line that we're + // considering. If it is NULL, then we're at the end of the file. + const uint8_t *newline = next_newline(cursor, parser->end - cursor); + + while (newline != NULL) { + pm_newline_list_append(&parser->newline_list, newline); + + cursor = newline + 1; + newline = next_newline(cursor, parser->end - cursor); + + size_t length = (size_t) ((newline != NULL ? newline : parser->end) - cursor); + if (length > 2 && cursor[0] == '#' && cursor[1] == '!') { + const char *engine; + if ((engine = pm_strnstr((const char *) cursor, "ruby", length)) != NULL) { + found_shebang = true; + + if (newline != NULL) { + pm_parser_warn_shebang_carriage_return(parser, cursor, length + 1); + parser->encoding_comment_start = newline + 1; + } + + if (options != NULL && options->shebang_callback != NULL) { + pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) cursor))); + } + + break; + } + } + } + + if (found_shebang) { + parser->previous = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor }; + parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor }; + } else { + pm_parser_err(parser, parser->start, parser->start, PM_ERR_SCRIPT_NOT_FOUND); + pm_newline_list_clear(&parser->newline_list); + } + } + + // The encoding comment can start after any amount of inline whitespace, so + // here we'll advance it to the first non-inline-whitespace character so + // that it is ready for future comparisons. + parser->encoding_comment_start += pm_strspn_inline_whitespace(parser->encoding_comment_start, parser->end - parser->encoding_comment_start); +} + +/** + * 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; +} + +/** + * 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; + xfree(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; + xfree(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); + 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_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); +} + +/** + * Read into the stream until the gets callback returns false. If the last read + * line from the stream matches an __END__ marker, then halt and return false, + * otherwise return true. + */ +static bool +pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets) { +#define LINE_SIZE 4096 + char line[LINE_SIZE]; + + while (memset(line, '\n', LINE_SIZE), stream_fgets(line, LINE_SIZE, stream) != NULL) { + size_t length = LINE_SIZE; + while (length > 0 && line[length - 1] == '\n') length--; + + if (length == LINE_SIZE) { + // If we read a line that is the maximum size and it doesn't end + // with a newline, then we'll just append it to the buffer and + // continue reading. + length--; + pm_buffer_append_string(buffer, line, length); + continue; + } + + // Append the line to the buffer. + length--; + pm_buffer_append_string(buffer, line, length); + + // Check if the line matches the __END__ marker. If it does, then stop + // reading and return false. In most circumstances, this means we should + // stop reading from the stream so that the DATA constant can pick it + // up. + switch (length) { + case 7: + if (strncmp(line, "__END__", 7) == 0) return false; + break; + case 8: + if (strncmp(line, "__END__\n", 8) == 0) return false; + break; + case 9: + if (strncmp(line, "__END__\r\n", 9) == 0) return false; + break; + } + } + + return true; +#undef LINE_SIZE +} + +/** + * Determine if there was an unterminated heredoc at the end of the input, which + * would mean the stream isn't finished and we should keep reading. + * + * For the other lex modes we can check if the lex mode has been closed, but for + * heredocs when we hit EOF we close the lex mode and then go back to parse the + * rest of the line after the heredoc declaration so that we get more of the + * syntax tree. + */ +static bool +pm_parse_stream_unterminated_heredoc_p(pm_parser_t *parser) { + pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) parser->error_list.head; + + for (; diagnostic != NULL; diagnostic = (pm_diagnostic_t *) diagnostic->node.next) { + if (diagnostic->diag_id == PM_ERR_HEREDOC_TERM) { + return true; + } + } + + return false; +} + +/** + * Parse a stream of Ruby source and return the tree. + * + * Prism is designed around having the entire source in memory at once, but you + * can stream stdin in to Ruby so we need to support a streaming API. + */ +PRISM_EXPORTED_FUNCTION pm_node_t * +pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const pm_options_t *options) { + pm_buffer_init(buffer); + + bool eof = pm_parse_stream_read(buffer, stream, stream_fgets); + pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options); + pm_node_t *node = pm_parse(parser); + + while (!eof && parser->error_list.size > 0 && (parser->lex_modes.index > 0 || pm_parse_stream_unterminated_heredoc_p(parser))) { + pm_node_destroy(parser, node); + eof = pm_parse_stream_read(buffer, stream, stream_fgets); + + pm_parser_free(parser); + pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options); + node = pm_parse(parser); + } + + return node; +} + +/** + * Parse the source and return true if it parses without errors or warnings. + */ +PRISM_EXPORTED_FUNCTION bool +pm_parse_success_p(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_node_destroy(&parser, node); + + bool result = parser.error_list.size == 0; + pm_parser_free(&parser); + pm_options_free(&options); + + return result; +} + +#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 + +// We optionally support serializing to a binary string. For systems that don't +// want or need this functionality, it can be turned off with the +// PRISM_EXCLUDE_SERIALIZATION define. +#ifndef PRISM_EXCLUDE_SERIALIZATION + +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 AST represented by the source that is read out of the + * given stream into to the given buffer. + */ +PRISM_EXPORTED_FUNCTION void +pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const char *data) { + pm_parser_t parser; + pm_options_t options = { 0 }; + pm_options_read(&options, data); + + pm_buffer_t parser_buffer; + pm_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, stream_fgets, &options); + pm_serialize_header(buffer); + pm_serialize_content(&parser, node, buffer); + pm_buffer_append_byte(buffer, '\0'); + + pm_node_destroy(&parser, node); + pm_buffer_free(&parser_buffer); + 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); +} + +#endif + +/******************************************************************************/ +/* Slice queries for the Ruby API */ +/******************************************************************************/ + +/** The category of slice returned from pm_slice_type. */ +typedef enum { + /** Returned when the given encoding name is invalid. */ + PM_SLICE_TYPE_ERROR = -1, + + /** Returned when no other types apply to the slice. */ + PM_SLICE_TYPE_NONE, + + /** Returned when the slice is a valid local variable name. */ + PM_SLICE_TYPE_LOCAL, + + /** Returned when the slice is a valid constant name. */ + PM_SLICE_TYPE_CONSTANT, + + /** Returned when the slice is a valid method name. */ + PM_SLICE_TYPE_METHOD_NAME +} pm_slice_type_t; + +/** + * Check that the slice is a valid local variable name or constant. + */ +pm_slice_type_t +pm_slice_type(const uint8_t *source, size_t length, const char *encoding_name) { + // first, get the right encoding object + const pm_encoding_t *encoding = pm_encoding_find((const uint8_t *) encoding_name, (const uint8_t *) (encoding_name + strlen(encoding_name))); + if (encoding == NULL) return PM_SLICE_TYPE_ERROR; + + // check that there is at least one character + if (length == 0) return PM_SLICE_TYPE_NONE; + + size_t width; + if ((width = encoding->alpha_char(source, (ptrdiff_t) length)) != 0) { + // valid because alphabetical + } else if (*source == '_') { + // valid because underscore + width = 1; + } else if ((*source >= 0x80) && ((width = encoding->char_width(source, (ptrdiff_t) length)) > 0)) { + // valid because multibyte + } else { + // invalid because no match + return PM_SLICE_TYPE_NONE; + } + + // determine the type of the slice based on the first character + const uint8_t *end = source + length; + pm_slice_type_t result = encoding->isupper_char(source, end - source) ? PM_SLICE_TYPE_CONSTANT : PM_SLICE_TYPE_LOCAL; + + // next, iterate through all of the bytes of the string to ensure that they + // are all valid identifier characters + source += width; + + while (source < end) { + if ((width = encoding->alnum_char(source, end - source)) != 0) { + // valid because alphanumeric + source += width; + } else if (*source == '_') { + // valid because underscore + source++; + } else if ((*source >= 0x80) && ((width = encoding->char_width(source, end - source)) > 0)) { + // valid because multibyte + source += width; + } else { + // invalid because no match + break; + } + } + + // accept a ! or ? at the end of the slice as a method name + if (*source == '!' || *source == '?' || *source == '=') { + source++; + result = PM_SLICE_TYPE_METHOD_NAME; + } + + // valid if we are at the end of the slice + return source == end ? result : PM_SLICE_TYPE_NONE; +} + +/** + * Check that the slice is a valid local variable name. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t +pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name) { + switch (pm_slice_type(source, length, encoding_name)) { + case PM_SLICE_TYPE_ERROR: + return PM_STRING_QUERY_ERROR; + case PM_SLICE_TYPE_NONE: + case PM_SLICE_TYPE_CONSTANT: + case PM_SLICE_TYPE_METHOD_NAME: + return PM_STRING_QUERY_FALSE; + case PM_SLICE_TYPE_LOCAL: + return PM_STRING_QUERY_TRUE; + } + + assert(false && "unreachable"); + return PM_STRING_QUERY_FALSE; +} + +/** + * Check that the slice is a valid constant name. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t +pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name) { + switch (pm_slice_type(source, length, encoding_name)) { + case PM_SLICE_TYPE_ERROR: + return PM_STRING_QUERY_ERROR; + case PM_SLICE_TYPE_NONE: + case PM_SLICE_TYPE_LOCAL: + case PM_SLICE_TYPE_METHOD_NAME: + return PM_STRING_QUERY_FALSE; + case PM_SLICE_TYPE_CONSTANT: + return PM_STRING_QUERY_TRUE; + } + + assert(false && "unreachable"); + return PM_STRING_QUERY_FALSE; +} + +/** + * Check that the slice is a valid method name. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t +pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name) { +#define B(p) ((p) ? PM_STRING_QUERY_TRUE : PM_STRING_QUERY_FALSE) +#define C1(c) (*source == c) +#define C2(s) (memcmp(source, s, 2) == 0) +#define C3(s) (memcmp(source, s, 3) == 0) + + switch (pm_slice_type(source, length, encoding_name)) { + case PM_SLICE_TYPE_ERROR: + return PM_STRING_QUERY_ERROR; + case PM_SLICE_TYPE_NONE: + break; + case PM_SLICE_TYPE_LOCAL: + // numbered parameters are not valid method names + return B((length != 2) || (source[0] != '_') || (source[1] == '0') || !pm_char_is_decimal_digit(source[1])); + case PM_SLICE_TYPE_CONSTANT: + // all constants are valid method names + case PM_SLICE_TYPE_METHOD_NAME: + // all method names are valid method names + return PM_STRING_QUERY_TRUE; + } + + switch (length) { + case 1: + return B(C1('&') || C1('`') || C1('!') || C1('^') || C1('>') || C1('<') || C1('-') || C1('%') || C1('|') || C1('+') || C1('/') || C1('*') || C1('~')); + case 2: + return B(C2("!=") || C2("!~") || C2("[]") || C2("==") || C2("=~") || C2(">=") || C2(">>") || C2("<=") || C2("<<") || C2("**")); + case 3: + return B(C3("===") || C3("<=>") || C3("[]=")); + default: + return PM_STRING_QUERY_FALSE; + } + +#undef B +#undef C1 +#undef C2 +#undef C3 +} diff --git a/vendor/mruby-compiler2/lib/prism/src/regexp.c b/vendor/mruby-compiler2/lib/prism/src/regexp.c new file mode 100644 index 0000000..dcc7476 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/regexp.c @@ -0,0 +1,790 @@ +#include "prism/regexp.h" + +#define PM_REGEXP_PARSE_DEPTH_MAX 4096 + +/** + * This is the parser that is going to handle parsing regular expressions. + */ +typedef struct { + /** The parser that is currently being used. */ + pm_parser_t *parser; + + /** 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; + + /** + * Whether or not the regular expression currently being parsed is in + * extended mode, wherein whitespace is ignored and comments are allowed. + */ + bool extended_mode; + + /** Whether the encoding has changed from the default. */ + bool encoding_changed; + + /** The encoding of the source. */ + const pm_encoding_t *encoding; + + /** The callback to call when a named capture group is found. */ + pm_regexp_name_callback_t name_callback; + + /** The data to pass to the name callback. */ + void *name_data; + + /** The callback to call when a parse error is found. */ + pm_regexp_error_callback_t error_callback; + + /** The data to pass to the error callback. */ + void *error_data; +} pm_regexp_parser_t; + +/** + * Append an error to the parser. + */ +static inline void +pm_regexp_parse_error(pm_regexp_parser_t *parser, const uint8_t *start, const uint8_t *end, const char *message) { + parser->error_callback(start, end, message, parser->error_data); +} + +/** + * This appends a new string to the list of named captures. This function + * assumes the caller has already checked the validity of the name callback. + */ +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); + parser->name_callback(&string, parser->name_data); + 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) { + if (parser->cursor >= parser->end) { + parser->cursor = savepoint; + return true; + } + + 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) { + while (!pm_regexp_char_is_eof(parser)) { + switch (*parser->cursor) { + case '*': + case '+': + case '?': + parser->cursor++; + break; + case '{': + parser->cursor++; + if (!pm_regexp_parse_range_quantifier(parser)) return false; + break; + default: + // In this case there is no quantifier. + return true; + } + } + + 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, uint16_t depth); + +/** + * match-char-set : '[' '^'? (match-range | match-char)* ']' + * ; + */ +static bool +pm_regexp_parse_character_set(pm_regexp_parser_t *parser, uint16_t depth) { + pm_regexp_char_accept(parser, '^'); + + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ']') { + switch (*parser->cursor++) { + case '[': + pm_regexp_parse_lbracket(parser, (uint16_t) (depth + 1)); + 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, uint16_t depth) { + if (depth >= PM_REGEXP_PARSE_DEPTH_MAX) { + pm_regexp_parse_error(parser, parser->start, parser->end, "parse depth limit over"); + return false; + } + + if ((parser->cursor < parser->end) && parser->cursor[0] == ']') { + parser->cursor++; + pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "empty char-class"); + return true; + } + + 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, depth); +} + +// 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, uint16_t depth); + +/** + * 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; +} + +/** + * True if the given key is set in the options. + */ +static uint8_t +pm_regexp_options_state(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); + return options->values[key]; + } + + 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, uint16_t depth) { + const uint8_t *group_start = parser->cursor; + + pm_regexp_options_t options; + pm_regexp_options_init(&options); + + // First, parse any options for the group. + if (pm_regexp_char_accept(parser, '?')) { + if (pm_regexp_char_is_eof(parser)) { + pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern in group"); + return false; + } + + switch (*parser->cursor) { + case '#': { // inline comments + parser->cursor++; + if (pm_regexp_char_is_eof(parser)) { + pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern in group"); + return false; + } + + 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)) { + pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern with unmatched parenthesis"); + 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; + } + + if (parser->cursor - start == 1) { + pm_regexp_parse_error(parser, start, parser->cursor, "group name is empty"); + } + + if (parser->name_callback != NULL) { + 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; + } + + if (parser->name_callback != NULL) { + 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 are at the end of the group of options and there is no + // subexpression, then we are going to be setting the options + // for the parent group. In this case we are safe to return now. + if (*parser->cursor == ')') { + if (pm_regexp_options_state(&options, 'x') == PM_REGEXP_OPTION_STATE_ADDED) { + parser->extended_mode = true; + } + + parser->cursor++; + return true; + } + + // If we hit a -, then we're done parsing options. + if (*parser->cursor != '-') break; + + PRISM_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; + } + + // If we are at the end of the group of options and there is no + // subexpression, then we are going to be setting the options + // for the parent group. In this case we are safe to return now. + if (*parser->cursor == ')') { + switch (pm_regexp_options_state(&options, 'x')) { + case PM_REGEXP_OPTION_STATE_ADDED: + parser->extended_mode = true; + break; + case PM_REGEXP_OPTION_STATE_REMOVED: + parser->extended_mode = false; + break; + } + + parser->cursor++; + return true; + } + + break; + default: + parser->cursor++; + pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "undefined group option"); + break; + } + } + + bool extended_mode = parser->extended_mode; + switch (pm_regexp_options_state(&options, 'x')) { + case PM_REGEXP_OPTION_STATE_ADDED: + parser->extended_mode = true; + break; + case PM_REGEXP_OPTION_STATE_REMOVED: + parser->extended_mode = false; + break; + } + + // Now, parse the expressions within this group. + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')') { + if (!pm_regexp_parse_expression(parser, (uint16_t) (depth + 1))) { + parser->extended_mode = extended_mode; + return false; + } + pm_regexp_char_accept(parser, '|'); + } + + // Finally, make sure we have a closing parenthesis. + parser->extended_mode = extended_mode; + if (pm_regexp_char_expect(parser, ')')) return true; + + pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern with unmatched parenthesis"); + return false; +} + +/** + * 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, uint16_t depth) { + switch (*parser->cursor) { + case '^': + case '$': + parser->cursor++; + return pm_regexp_parse_quantifier(parser); + case '\\': + parser->cursor++; + if (!pm_regexp_char_is_eof(parser)) { + parser->cursor++; + } + return pm_regexp_parse_quantifier(parser); + case '(': + parser->cursor++; + return pm_regexp_parse_group(parser, depth) && pm_regexp_parse_quantifier(parser); + case '[': + parser->cursor++; + return pm_regexp_parse_lbracket(parser, depth) && pm_regexp_parse_quantifier(parser); + case '*': + case '?': + case '+': + parser->cursor++; + pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "target of repeat operator is not specified"); + return true; + case ')': + parser->cursor++; + pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "unmatched close parenthesis"); + return true; + case '#': + if (parser->extended_mode) { + if (!pm_regexp_char_find(parser, '\n')) parser->cursor = parser->end; + return true; + } + PRISM_FALLTHROUGH + default: { + size_t width; + if (!parser->encoding_changed) { + width = pm_encoding_utf_8_char_width(parser->cursor, (ptrdiff_t) (parser->end - parser->cursor)); + } else { + width = parser->encoding->char_width(parser->cursor, (ptrdiff_t) (parser->end - parser->cursor)); + } + + if (width == 0) return false; // TODO: add appropriate error + parser->cursor += width; + + return pm_regexp_parse_quantifier(parser); + } + } +} + +/** + * expression : item+ + * ; + */ +static bool +pm_regexp_parse_expression(pm_regexp_parser_t *parser, uint16_t depth) { + if (depth >= PM_REGEXP_PARSE_DEPTH_MAX) { + pm_regexp_parse_error(parser, parser->start, parser->end, "parse depth limit over"); + return false; + } + + if (!pm_regexp_parse_item(parser, depth)) { + return false; + } + + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')' && *parser->cursor != '|') { + if (!pm_regexp_parse_item(parser, depth)) { + return false; + } + } + + return true; +} + +/** + * pattern : EOF + * | expression EOF + * | expression '|' pattern + * ; + */ +static bool +pm_regexp_parse_pattern(pm_regexp_parser_t *parser) { + do { + if (pm_regexp_char_is_eof(parser)) return true; + if (!pm_regexp_parse_expression(parser, 0)) return false; + } while (pm_regexp_char_accept(parser, '|')); + + return pm_regexp_char_is_eof(parser); +} + +/** + * Parse a regular expression and extract the names of all of the named capture + * groups. + */ +PRISM_EXPORTED_FUNCTION void +pm_regexp_parse(pm_parser_t *parser, const uint8_t *source, size_t size, bool extended_mode, pm_regexp_name_callback_t name_callback, void *name_data, pm_regexp_error_callback_t error_callback, void *error_data) { + pm_regexp_parse_pattern(&(pm_regexp_parser_t) { + .parser = parser, + .start = source, + .cursor = source, + .end = source + size, + .extended_mode = extended_mode, + .encoding_changed = parser->encoding_changed, + .encoding = parser->encoding, + .name_callback = name_callback, + .name_data = name_data, + .error_callback = error_callback, + .error_data = error_data + }); +} diff --git a/vendor/mruby-compiler2/lib/prism/src/serialize.c b/vendor/mruby-compiler2/lib/prism/src/serialize.c new file mode 100644 index 0000000..8bfed8f --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/serialize.c @@ -0,0 +1,2266 @@ +/*----------------------------------------------------------------------------*/ +/* 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" + +// We optionally support serializing to a binary string. For systems that don't +// want or need this functionality, it can be turned off with the +// PRISM_EXCLUDE_SERIALIZATION define. +#ifndef PRISM_EXCLUDE_SERIALIZATION + +#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(const pm_parser_t *parser, const 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; + } +#ifdef PRISM_HAS_MMAP + case PM_STRING_MAPPED: + assert(false && "Cannot serialize mapped strings."); + break; +#endif + } +} + +static void +pm_serialize_integer(const pm_integer_t *integer, pm_buffer_t *buffer) { + pm_buffer_append_byte(buffer, integer->negative ? 1 : 0); + if (integer->values == NULL) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(1)); + pm_buffer_append_varuint(buffer, integer->value); + } else { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(integer->length)); + for (size_t i = 0; i < integer->length; i++) { + pm_buffer_append_varuint(buffer, integer->values[i]); + } + } +} + +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_buffer_append_varuint(buffer, node->node_id); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + } + break; + } + case PM_ARRAY_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + } + break; + } + case PM_ARRAY_PATTERN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_assoc_node_t *)node)->key, buffer); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_back_reference_read_node_t *)node)->name)); + break; + } + case PM_BEGIN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_block_local_variable_node_t *)node)->name)); + break; + } + case PM_BLOCK_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + break; + } + case PM_BLOCK_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + } + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, 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)->binary_operator)); + pm_serialize_location(parser, &((pm_call_operator_write_node_t *)node)->binary_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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, 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_CALL_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_call_target_node_t *)node)->receiver, buffer); + pm_serialize_location(parser, &((pm_call_target_node_t *)node)->call_operator_loc, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_target_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_call_target_node_t *)node)->message_loc, buffer); + break; + } + case PM_CAPTURE_PATTERN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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)->else_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_match_node_t *)node)->else_clause, 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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)->else_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_node_t *)node)->else_clause, 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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)->binary_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)->binary_operator)); + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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)->binary_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)->binary_operator)); + break; + } + case PM_CONSTANT_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_path_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_path_node_t *)node)->delimiter_loc, buffer); + pm_serialize_location(parser, &((pm_constant_path_node_t *)node)->name_loc, buffer); + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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)->binary_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)->binary_operator)); + break; + } + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_path_target_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_path_target_node_t *)node)->delimiter_loc, buffer); + pm_serialize_location(parser, &((pm_constant_path_target_node_t *)node)->name_loc, buffer); + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_FIND_PATTERN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + break; + } + case PM_FLOAT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_double(buffer, ((pm_float_node_t *)node)->value); + break; + } + case PM_FOR_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_FORWARDING_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_FORWARDING_SUPER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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)->binary_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)->binary_operator)); + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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)->subsequent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_if_node_t *)node)->subsequent, 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_imaginary_node_t *)node)->numeric, buffer); + break; + } + case PM_IMPLICIT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_implicit_node_t *)node)->value, buffer); + break; + } + case PM_IMPLICIT_REST_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_IN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, pm_sizet_to_u32(((pm_index_operator_write_node_t *)node)->binary_operator)); + pm_serialize_location(parser, &((pm_index_operator_write_node_t *)node)->binary_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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_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_INDEX_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_index_target_node_t *)node)->receiver, buffer); + pm_serialize_location(parser, &((pm_index_target_node_t *)node)->opening_loc, buffer); + if (((pm_index_target_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_target_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_index_target_node_t *)node)->closing_loc, buffer); + if (((pm_index_target_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_target_node_t *)node)->block, buffer); + } + break; + } + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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)->binary_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)->binary_operator)); + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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_serialize_integer(&((pm_integer_node_t *)node)->value, buffer); + break; + } + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + break; + } + case PM_INTERPOLATED_STRING_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_IT_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_IT_PARAMETERS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_KEYWORD_HASH_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + } + break; + } + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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)->binary_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)->binary_operator)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_operator_write_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + break; + } + case PM_MATCH_PREDICATE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_MODULE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_NO_KEYWORDS_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_PARAMETERS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_byte(buffer, ((pm_numbered_parameters_node_t *)node)->maximum); + break; + } + case PM_NUMBERED_REFERENCE_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + break; + } + case PM_RATIONAL_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_integer(&((pm_rational_node_t *)node)->numerator, buffer); + pm_serialize_integer(&((pm_rational_node_t *)node)->denominator, buffer); + break; + } + case PM_REDO_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_REGULAR_EXPRESSION_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + break; + } + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_required_parameter_node_t *)node)->name)); + break; + } + case PM_RESCUE_MODIFIER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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)->then_keyword_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)->then_keyword_loc, 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)->subsequent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_node_t *)node)->subsequent, buffer); + } + break; + } + case PM_REST_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_RETURN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_SHAREABLE_CONSTANT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_shareable_constant_node_t *)node)->write, buffer); + break; + } + case PM_SINGLETON_CLASS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_SOURCE_FILE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_string(parser, &((pm_source_file_node_t *)node)->filepath, buffer); + break; + } + case PM_SOURCE_LINE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_SPLAT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_UNDEF_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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)->else_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_unless_node_t *)node)->else_clause, 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_until_node_t *)node)->keyword_loc, buffer); + if (((pm_until_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_until_node_t *)node)->do_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); + } + break; + } + case PM_WHEN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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)->then_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_when_node_t *)node)->then_keyword_loc, 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_while_node_t *)node)->keyword_loc, buffer); + if (((pm_while_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_while_node_t *)node)->do_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); + } + break; + } + case PM_X_STRING_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_buffer_append_varuint(buffer, (uint32_t) node->flags); + 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_newline_list(pm_newline_list_t *list, pm_buffer_t *buffer) { + uint32_t size = pm_sizet_to_u32(list->size); + pm_buffer_append_varuint(buffer, size); + + for (uint32_t i = 0; i < size; i++) { + uint32_t offset = pm_sizet_to_u32(list->offsets[i]); + pm_buffer_append_varuint(buffer, offset); + } +} + +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_serialize_location(parser, &comment->location, buffer); +} + +/** + * 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 the type + pm_buffer_append_varuint(buffer, (uint32_t) diagnostic->diag_id); + + // 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_serialize_location(parser, &diagnostic->location, buffer); + + pm_buffer_append_byte(buffer, diagnostic->level); +} + +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(const 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); +} + +static void +pm_serialize_metadata(pm_parser_t *parser, pm_buffer_t *buffer) { + pm_serialize_encoding(parser->encoding, buffer); + pm_buffer_append_varsint(buffer, parser->start_line); + pm_serialize_newline_list(&parser->newline_list, buffer); + 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); +} + +#line 275 "prism/templates/src/serialize.c.erb" +/** + * Serialize the metadata, nodes, and constant pool. + */ +void +pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { + pm_serialize_metadata(parser, 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_metadata(&parser, 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); +} + +#endif diff --git a/vendor/mruby-compiler2/lib/prism/src/static_literals.c b/vendor/mruby-compiler2/lib/prism/src/static_literals.c new file mode 100644 index 0000000..9fa37b9 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/static_literals.c @@ -0,0 +1,617 @@ +#include "prism/static_literals.h" + +/** + * A small struct used for passing around a subset of the information that is + * stored on the parser. We use this to avoid having static literals explicitly + * depend on the parser struct. + */ +typedef struct { + /** The list of newline offsets to use to calculate line numbers. */ + const pm_newline_list_t *newline_list; + + /** The line number that the parser starts on. */ + int32_t start_line; + + /** The name of the encoding that the parser is using. */ + const char *encoding_name; +} pm_static_literals_metadata_t; + +static inline uint32_t +murmur_scramble(uint32_t value) { + value *= 0xcc9e2d51; + value = (value << 15) | (value >> 17); + value *= 0x1b873593; + return value; +} + +/** + * Murmur hash (https://en.wikipedia.org/wiki/MurmurHash) is a non-cryptographic + * general-purpose hash function. It is fast, which is what we care about in + * this case. + */ +static uint32_t +murmur_hash(const uint8_t *key, size_t length) { + uint32_t hash = 0x9747b28c; + uint32_t segment; + + for (size_t index = length >> 2; index; index--) { + memcpy(&segment, key, sizeof(uint32_t)); + key += sizeof(uint32_t); + hash ^= murmur_scramble(segment); + hash = (hash << 13) | (hash >> 19); + hash = hash * 5 + 0xe6546b64; + } + + segment = 0; + for (size_t index = length & 3; index; index--) { + segment <<= 8; + segment |= key[index - 1]; + } + + hash ^= murmur_scramble(segment); + hash ^= (uint32_t) length; + hash ^= hash >> 16; + hash *= 0x85ebca6b; + hash ^= hash >> 13; + hash *= 0xc2b2ae35; + hash ^= hash >> 16; + return hash; +} + +/** + * Hash the value of an integer and return it. + */ +static uint32_t +integer_hash(const pm_integer_t *integer) { + uint32_t hash; + if (integer->values) { + hash = murmur_hash((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length); + } else { + hash = murmur_hash((const uint8_t *) &integer->value, sizeof(uint32_t)); + } + + if (integer->negative) { + hash ^= murmur_scramble((uint32_t) 1); + } + + return hash; +} + +/** + * Return the hash of the given node. It is important that nodes that have + * equivalent static literal values have the same hash. This is because we use + * these hashes to look for duplicates. + */ +static uint32_t +node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_INTEGER_NODE: { + // Integers hash their value. + const pm_integer_node_t *cast = (const pm_integer_node_t *) node; + return integer_hash(&cast->value); + } + case PM_SOURCE_LINE_NODE: { + // Source lines hash their line number. + const pm_line_column_t line_column = pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line); + const int32_t *value = &line_column.line; + return murmur_hash((const uint8_t *) value, sizeof(int32_t)); + } + case PM_FLOAT_NODE: { + // Floats hash their value. + const double *value = &((const pm_float_node_t *) node)->value; + return murmur_hash((const uint8_t *) value, sizeof(double)); + } + case PM_RATIONAL_NODE: { + // Rationals hash their numerator and denominator. + const pm_rational_node_t *cast = (const pm_rational_node_t *) node; + return integer_hash(&cast->numerator) ^ integer_hash(&cast->denominator) ^ murmur_scramble((uint32_t) cast->base.type); + } + case PM_IMAGINARY_NODE: { + // Imaginaries hash their numeric value. Because their numeric value + // is stored as a subnode, we hash that node and then mix in the + // fact that this is an imaginary node. + const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric; + return node_hash(metadata, numeric) ^ murmur_scramble((uint32_t) node->type); + } + case PM_STRING_NODE: { + // Strings hash their value and mix in their flags so that different + // encodings are not considered equal. + const pm_string_t *value = &((const pm_string_node_t *) node)->unescaped; + + pm_node_flags_t flags = node->flags; + flags &= (PM_STRING_FLAGS_FORCED_BINARY_ENCODING | PM_STRING_FLAGS_FORCED_UTF8_ENCODING); + + return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) flags); + } + case PM_SOURCE_FILE_NODE: { + // Source files hash their value and mix in their flags so that + // different encodings are not considered equal. + const pm_string_t *value = &((const pm_source_file_node_t *) node)->filepath; + return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)); + } + case PM_REGULAR_EXPRESSION_NODE: { + // Regular expressions hash their value and mix in their flags so + // that different encodings are not considered equal. + const pm_string_t *value = &((const pm_regular_expression_node_t *) node)->unescaped; + return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node->flags); + } + case PM_SYMBOL_NODE: { + // Symbols hash their value and mix in their flags so that different + // encodings are not considered equal. + const pm_string_t *value = &((const pm_symbol_node_t *) node)->unescaped; + return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node->flags); + } + default: + assert(false && "unreachable"); + return 0; + } +} + +/** + * Insert a node into the node hash. It accepts the hash that should hold the + * new node, the parser that generated the node, the node to insert, and a + * comparison function. The comparison function is used for collision detection, + * and must be able to compare all node types that will be stored in this hash. + */ +static pm_node_t * +pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *metadata, pm_node_t *node, bool replace, int (*compare)(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right)) { + // If we are out of space, we need to resize the hash. This will cause all + // of the nodes to be rehashed and reinserted into the new hash. + if (hash->size * 2 >= hash->capacity) { + // First, allocate space for the new node list. + uint32_t new_capacity = hash->capacity == 0 ? 4 : hash->capacity * 2; + pm_node_t **new_nodes = xcalloc(new_capacity, sizeof(pm_node_t *)); + if (new_nodes == NULL) return NULL; + + // It turns out to be more efficient to mask the hash value than to use + // the modulo operator. Because our capacities are always powers of two, + // we can use a bitwise AND to get the same result as the modulo + // operator. + uint32_t mask = new_capacity - 1; + + // Now, rehash all of the nodes into the new list. + for (uint32_t index = 0; index < hash->capacity; index++) { + pm_node_t *node = hash->nodes[index]; + + if (node != NULL) { + uint32_t index = node_hash(metadata, node) & mask; + new_nodes[index] = node; + } + } + + // Finally, free the old node list and update the hash. + xfree(hash->nodes); + hash->nodes = new_nodes; + hash->capacity = new_capacity; + } + + // Now, insert the node into the hash. + uint32_t mask = hash->capacity - 1; + uint32_t index = node_hash(metadata, node) & mask; + + // We use linear probing to resolve collisions. This means that if the + // current index is occupied, we will move to the next index and try again. + // We are guaranteed that this will eventually find an empty slot because we + // resize the hash when it gets too full. + while (hash->nodes[index] != NULL) { + if (compare(metadata, hash->nodes[index], node) == 0) break; + index = (index + 1) & mask; + } + + // If the current index is occupied, we need to return the node that was + // already in the hash. Otherwise, we can just increment the size and insert + // the new node. + pm_node_t *result = hash->nodes[index]; + + if (result == NULL) { + hash->size++; + hash->nodes[index] = node; + } else if (replace) { + hash->nodes[index] = node; + } + + return result; +} + +/** + * Free the internal memory associated with the given node hash. + */ +static void +pm_node_hash_free(pm_node_hash_t *hash) { + if (hash->capacity > 0) xfree(hash->nodes); +} + +/** + * Compare two values that can be compared with a simple numeric comparison. + */ +#define PM_NUMERIC_COMPARISON(left, right) ((left < right) ? -1 : (left > right) ? 1 : 0) + +/** + * Return the integer value of the given node as an int64_t. + */ +static int64_t +pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_INTEGER_NODE: { + const pm_integer_t *integer = &((const pm_integer_node_t *) node)->value; + if (integer->values) return integer->negative ? INT64_MIN : INT64_MAX; + + int64_t value = (int64_t) integer->value; + return integer->negative ? -value : value; + } + case PM_SOURCE_LINE_NODE: + return (int64_t) pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line).line; + default: + assert(false && "unreachable"); + return 0; + } +} + +/** + * A comparison function for comparing two IntegerNode or SourceLineNode + * instances. + */ +static int +pm_compare_integer_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { + if (PM_NODE_TYPE_P(left, PM_SOURCE_LINE_NODE) || PM_NODE_TYPE_P(right, PM_SOURCE_LINE_NODE)) { + int64_t left_value = pm_int64_value(metadata, left); + int64_t right_value = pm_int64_value(metadata, right); + return PM_NUMERIC_COMPARISON(left_value, right_value); + } + + const pm_integer_t *left_integer = &((const pm_integer_node_t *) left)->value; + const pm_integer_t *right_integer = &((const pm_integer_node_t *) right)->value; + return pm_integer_compare(left_integer, right_integer); +} + +/** + * A comparison function for comparing two FloatNode instances. + */ +static int +pm_compare_float_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { + const double left_value = ((const pm_float_node_t *) left)->value; + const double right_value = ((const pm_float_node_t *) right)->value; + return PM_NUMERIC_COMPARISON(left_value, right_value); +} + +/** + * A comparison function for comparing two nodes that have attached numbers. + */ +static int +pm_compare_number_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { + if (PM_NODE_TYPE(left) != PM_NODE_TYPE(right)) { + return PM_NUMERIC_COMPARISON(PM_NODE_TYPE(left), PM_NODE_TYPE(right)); + } + + switch (PM_NODE_TYPE(left)) { + case PM_IMAGINARY_NODE: + return pm_compare_number_nodes(metadata, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric); + case PM_RATIONAL_NODE: { + const pm_rational_node_t *left_rational = (const pm_rational_node_t *) left; + const pm_rational_node_t *right_rational = (const pm_rational_node_t *) right; + + int result = pm_integer_compare(&left_rational->denominator, &right_rational->denominator); + if (result != 0) return result; + + return pm_integer_compare(&left_rational->numerator, &right_rational->numerator); + } + case PM_INTEGER_NODE: + return pm_compare_integer_nodes(metadata, left, right); + case PM_FLOAT_NODE: + return pm_compare_float_nodes(metadata, left, right); + default: + assert(false && "unreachable"); + return 0; + } +} + +/** + * Return a pointer to the string value of the given node. + */ +static const pm_string_t * +pm_string_value(const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_STRING_NODE: + return &((const pm_string_node_t *) node)->unescaped; + case PM_SOURCE_FILE_NODE: + return &((const pm_source_file_node_t *) node)->filepath; + case PM_SYMBOL_NODE: + return &((const pm_symbol_node_t *) node)->unescaped; + default: + assert(false && "unreachable"); + return NULL; + } +} + +/** + * A comparison function for comparing two nodes that have attached strings. + */ +static int +pm_compare_string_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { + const pm_string_t *left_string = pm_string_value(left); + const pm_string_t *right_string = pm_string_value(right); + return pm_string_compare(left_string, right_string); +} + +/** + * A comparison function for comparing two RegularExpressionNode instances. + */ +static int +pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { + const pm_regular_expression_node_t *left_regexp = (const pm_regular_expression_node_t *) left; + const pm_regular_expression_node_t *right_regexp = (const pm_regular_expression_node_t *) right; + + int result = pm_string_compare(&left_regexp->unescaped, &right_regexp->unescaped); + if (result != 0) return result; + + return PM_NUMERIC_COMPARISON(left_regexp->base.flags, right_regexp->base.flags); +} + +#undef PM_NUMERIC_COMPARISON + +/** + * Add a node to the set of static literals. + */ +pm_node_t * +pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node, bool replace) { + switch (PM_NODE_TYPE(node)) { + case PM_INTEGER_NODE: + case PM_SOURCE_LINE_NODE: + return pm_node_hash_insert( + &literals->integer_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + replace, + pm_compare_integer_nodes + ); + case PM_FLOAT_NODE: + return pm_node_hash_insert( + &literals->float_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + replace, + pm_compare_float_nodes + ); + case PM_RATIONAL_NODE: + case PM_IMAGINARY_NODE: + return pm_node_hash_insert( + &literals->number_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + replace, + pm_compare_number_nodes + ); + case PM_STRING_NODE: + case PM_SOURCE_FILE_NODE: + return pm_node_hash_insert( + &literals->string_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + replace, + pm_compare_string_nodes + ); + case PM_REGULAR_EXPRESSION_NODE: + return pm_node_hash_insert( + &literals->regexp_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + replace, + pm_compare_regular_expression_nodes + ); + case PM_SYMBOL_NODE: + return pm_node_hash_insert( + &literals->symbol_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + replace, + pm_compare_string_nodes + ); + case PM_TRUE_NODE: { + pm_node_t *duplicated = literals->true_node; + if ((duplicated == NULL) || replace) literals->true_node = node; + return duplicated; + } + case PM_FALSE_NODE: { + pm_node_t *duplicated = literals->false_node; + if ((duplicated == NULL) || replace) literals->false_node = node; + return duplicated; + } + case PM_NIL_NODE: { + pm_node_t *duplicated = literals->nil_node; + if ((duplicated == NULL) || replace) literals->nil_node = node; + return duplicated; + } + case PM_SOURCE_ENCODING_NODE: { + pm_node_t *duplicated = literals->source_encoding_node; + if ((duplicated == NULL) || replace) literals->source_encoding_node = node; + return duplicated; + } + default: + return NULL; + } +} + +/** + * Free the internal memory associated with the given static literals set. + */ +void +pm_static_literals_free(pm_static_literals_t *literals) { + pm_node_hash_free(&literals->integer_nodes); + pm_node_hash_free(&literals->float_nodes); + pm_node_hash_free(&literals->number_nodes); + pm_node_hash_free(&literals->string_nodes); + pm_node_hash_free(&literals->regexp_nodes); + pm_node_hash_free(&literals->symbol_nodes); +} + +/** + * A helper to determine if the given node is a static literal that is positive. + * This is used for formatting imaginary nodes. + */ +static bool +pm_static_literal_positive_p(const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_FLOAT_NODE: + return ((const pm_float_node_t *) node)->value > 0; + case PM_INTEGER_NODE: + return !((const pm_integer_node_t *) node)->value.negative; + case PM_RATIONAL_NODE: + return !((const pm_rational_node_t *) node)->numerator.negative; + case PM_IMAGINARY_NODE: + return pm_static_literal_positive_p(((const pm_imaginary_node_t *) node)->numeric); + default: + assert(false && "unreachable"); + return false; + } +} + +/** + * Create a string-based representation of the given static literal. + */ +static inline void +pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_metadata_t *metadata, const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_FALSE_NODE: + pm_buffer_append_string(buffer, "false", 5); + break; + case PM_FLOAT_NODE: { + const double value = ((const pm_float_node_t *) node)->value; + + if (PRISM_ISINF(value)) { + if (*node->location.start == '-') { + pm_buffer_append_byte(buffer, '-'); + } + pm_buffer_append_string(buffer, "Infinity", 8); + } else if (value == 0.0) { + if (*node->location.start == '-') { + pm_buffer_append_byte(buffer, '-'); + } + pm_buffer_append_string(buffer, "0.0", 3); + } else { + pm_buffer_append_format(buffer, "%g", value); + + // %g will not insert a .0 for 1e100 (we'll get back 1e+100). So + // we check for the decimal point and add it in here if it's not + // present. + if (pm_buffer_index(buffer, '.') == SIZE_MAX) { + size_t exponent_index = pm_buffer_index(buffer, 'e'); + size_t index = exponent_index == SIZE_MAX ? pm_buffer_length(buffer) : exponent_index; + pm_buffer_insert(buffer, index, ".0", 2); + } + } + + break; + } + case PM_IMAGINARY_NODE: { + const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric; + pm_buffer_append_string(buffer, "(0", 2); + if (pm_static_literal_positive_p(numeric)) pm_buffer_append_byte(buffer, '+'); + pm_static_literal_inspect_node(buffer, metadata, numeric); + if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) { + pm_buffer_append_byte(buffer, '*'); + } + pm_buffer_append_string(buffer, "i)", 2); + break; + } + case PM_INTEGER_NODE: + pm_integer_string(buffer, &((const pm_integer_node_t *) node)->value); + break; + case PM_NIL_NODE: + pm_buffer_append_string(buffer, "nil", 3); + break; + case PM_RATIONAL_NODE: { + const pm_rational_node_t *rational = (const pm_rational_node_t *) node; + pm_buffer_append_byte(buffer, '('); + pm_integer_string(buffer, &rational->numerator); + pm_buffer_append_byte(buffer, '/'); + pm_integer_string(buffer, &rational->denominator); + pm_buffer_append_byte(buffer, ')'); + break; + } + case PM_REGULAR_EXPRESSION_NODE: { + const pm_string_t *unescaped = &((const pm_regular_expression_node_t *) node)->unescaped; + pm_buffer_append_byte(buffer, '/'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_byte(buffer, '/'); + + if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) pm_buffer_append_string(buffer, "m", 1); + if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) pm_buffer_append_string(buffer, "i", 1); + if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) pm_buffer_append_string(buffer, "x", 1); + if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) pm_buffer_append_string(buffer, "n", 1); + + break; + } + case PM_SOURCE_ENCODING_NODE: + pm_buffer_append_format(buffer, "#", metadata->encoding_name); + break; + case PM_SOURCE_FILE_NODE: { + const pm_string_t *filepath = &((const pm_source_file_node_t *) node)->filepath; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(filepath), pm_string_length(filepath), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_byte(buffer, '"'); + break; + } + case PM_SOURCE_LINE_NODE: + pm_buffer_append_format(buffer, "%d", pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line).line); + break; + case PM_STRING_NODE: { + const pm_string_t *unescaped = &((const pm_string_node_t *) node)->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_byte(buffer, '"'); + break; + } + case PM_SYMBOL_NODE: { + const pm_string_t *unescaped = &((const pm_symbol_node_t *) node)->unescaped; + pm_buffer_append_byte(buffer, ':'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY); + break; + } + case PM_TRUE_NODE: + pm_buffer_append_string(buffer, "true", 4); + break; + default: + assert(false && "unreachable"); + break; + } +} + +/** + * Create a string-based representation of the given static literal. + */ +void +pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node) { + pm_static_literal_inspect_node( + buffer, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = encoding_name + }, + node + ); +} diff --git a/vendor/mruby-compiler2/lib/prism/src/token_type.c b/vendor/mruby-compiler2/lib/prism/src/token_type.c new file mode 100644 index 0000000..ba2d751 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/token_type.c @@ -0,0 +1,701 @@ +/*----------------------------------------------------------------------------*/ +/* 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_name(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: + assert(false && "unreachable"); + return ""; + } + + // Provide a default, because some compilers can't determine that the above + // switch is exhaustive. + assert(false && "unreachable"); + return ""; +} + +/** + * Returns the human name of the given token type. + */ +const char * +pm_token_type_human(pm_token_type_t token_type) { + switch (token_type) { + case PM_TOKEN_EOF: + return "end-of-input"; + case PM_TOKEN_MISSING: + return "missing token"; + case PM_TOKEN_NOT_PROVIDED: + return "not provided token"; + case PM_TOKEN_AMPERSAND: + return "'&'"; + case PM_TOKEN_AMPERSAND_AMPERSAND: + return "'&&'"; + case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL: + return "'&&='"; + case PM_TOKEN_AMPERSAND_DOT: + return "'&.'"; + case PM_TOKEN_AMPERSAND_EQUAL: + return "'&='"; + case PM_TOKEN_BACKTICK: + return "'`'"; + case PM_TOKEN_BACK_REFERENCE: + return "back reference"; + case PM_TOKEN_BANG: + return "'!'"; + case PM_TOKEN_BANG_EQUAL: + return "'!='"; + case PM_TOKEN_BANG_TILDE: + return "'!~'"; + case PM_TOKEN_BRACE_LEFT: + return "'{'"; + case PM_TOKEN_BRACE_RIGHT: + return "'}'"; + case PM_TOKEN_BRACKET_LEFT: + return "'['"; + case PM_TOKEN_BRACKET_LEFT_ARRAY: + return "'['"; + case PM_TOKEN_BRACKET_LEFT_RIGHT: + return "'[]'"; + case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: + return "'[]='"; + case PM_TOKEN_BRACKET_RIGHT: + return "']'"; + case PM_TOKEN_CARET: + return "'^'"; + case PM_TOKEN_CARET_EQUAL: + return "'^='"; + case PM_TOKEN_CHARACTER_LITERAL: + return "character literal"; + case PM_TOKEN_CLASS_VARIABLE: + return "class variable"; + case PM_TOKEN_COLON: + return "':'"; + case PM_TOKEN_COLON_COLON: + return "'::'"; + case PM_TOKEN_COMMA: + return "','"; + case PM_TOKEN_COMMENT: + return "comment"; + case PM_TOKEN_CONSTANT: + return "constant"; + case PM_TOKEN_DOT: + return "'.'"; + case PM_TOKEN_DOT_DOT: + return ".."; + case PM_TOKEN_DOT_DOT_DOT: + return "..."; + case PM_TOKEN_EMBDOC_BEGIN: + return "'=begin'"; + case PM_TOKEN_EMBDOC_END: + return "'=end'"; + case PM_TOKEN_EMBDOC_LINE: + return "embedded documentation line"; + case PM_TOKEN_EMBEXPR_BEGIN: + return "'#{'"; + case PM_TOKEN_EMBEXPR_END: + return "'}'"; + case PM_TOKEN_EMBVAR: + return "'#'"; + case PM_TOKEN_EQUAL: + return "'='"; + case PM_TOKEN_EQUAL_EQUAL: + return "'=='"; + case PM_TOKEN_EQUAL_EQUAL_EQUAL: + return "'==='"; + case PM_TOKEN_EQUAL_GREATER: + return "'=>'"; + case PM_TOKEN_EQUAL_TILDE: + return "'=~'"; + case PM_TOKEN_FLOAT: + return "float"; + case PM_TOKEN_FLOAT_IMAGINARY: + return "imaginary"; + case PM_TOKEN_FLOAT_RATIONAL: + return "rational"; + case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: + return "imaginary"; + case PM_TOKEN_GLOBAL_VARIABLE: + return "global variable"; + case PM_TOKEN_GREATER: + return "'>'"; + case PM_TOKEN_GREATER_EQUAL: + return "'>='"; + case PM_TOKEN_GREATER_GREATER: + return ">>"; + case PM_TOKEN_GREATER_GREATER_EQUAL: + return ">>="; + case PM_TOKEN_HEREDOC_END: + return "heredoc ending"; + case PM_TOKEN_HEREDOC_START: + return "heredoc beginning"; + case PM_TOKEN_IDENTIFIER: + return "local variable or method"; + 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 "imaginary"; + case PM_TOKEN_INTEGER_RATIONAL: + return "rational"; + case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: + return "imaginary"; + case PM_TOKEN_KEYWORD_ALIAS: + return "'alias'"; + case PM_TOKEN_KEYWORD_AND: + return "'and'"; + case PM_TOKEN_KEYWORD_BEGIN: + return "'begin'"; + case PM_TOKEN_KEYWORD_BEGIN_UPCASE: + return "'BEGIN'"; + case PM_TOKEN_KEYWORD_BREAK: + return "'break'"; + case PM_TOKEN_KEYWORD_CASE: + return "'case'"; + case PM_TOKEN_KEYWORD_CLASS: + return "'class'"; + case PM_TOKEN_KEYWORD_DEF: + return "'def'"; + case PM_TOKEN_KEYWORD_DEFINED: + return "'defined?'"; + case PM_TOKEN_KEYWORD_DO: + return "'do'"; + case PM_TOKEN_KEYWORD_DO_LOOP: + return "'do'"; + case PM_TOKEN_KEYWORD_ELSE: + return "'else'"; + case PM_TOKEN_KEYWORD_ELSIF: + return "'elsif'"; + case PM_TOKEN_KEYWORD_END: + return "'end'"; + case PM_TOKEN_KEYWORD_END_UPCASE: + return "'END'"; + case PM_TOKEN_KEYWORD_ENSURE: + return "'ensure'"; + case PM_TOKEN_KEYWORD_FALSE: + return "'false'"; + case PM_TOKEN_KEYWORD_FOR: + return "'for'"; + case PM_TOKEN_KEYWORD_IF: + return "'if'"; + case PM_TOKEN_KEYWORD_IF_MODIFIER: + return "'if'"; + case PM_TOKEN_KEYWORD_IN: + return "'in'"; + case PM_TOKEN_KEYWORD_MODULE: + return "'module'"; + case PM_TOKEN_KEYWORD_NEXT: + return "'next'"; + case PM_TOKEN_KEYWORD_NIL: + return "'nil'"; + case PM_TOKEN_KEYWORD_NOT: + return "'not'"; + case PM_TOKEN_KEYWORD_OR: + return "'or'"; + case PM_TOKEN_KEYWORD_REDO: + return "'redo'"; + case PM_TOKEN_KEYWORD_RESCUE: + return "'rescue'"; + case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: + return "'rescue' modifier"; + case PM_TOKEN_KEYWORD_RETRY: + return "'retry'"; + case PM_TOKEN_KEYWORD_RETURN: + return "'return'"; + case PM_TOKEN_KEYWORD_SELF: + return "'self'"; + case PM_TOKEN_KEYWORD_SUPER: + return "'super'"; + case PM_TOKEN_KEYWORD_THEN: + return "'then'"; + case PM_TOKEN_KEYWORD_TRUE: + return "'true'"; + case PM_TOKEN_KEYWORD_UNDEF: + return "'undef'"; + case PM_TOKEN_KEYWORD_UNLESS: + return "'unless'"; + case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: + return "'unless'"; + case PM_TOKEN_KEYWORD_UNTIL: + return "'until'"; + case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: + return "'until'"; + case PM_TOKEN_KEYWORD_WHEN: + return "'when'"; + case PM_TOKEN_KEYWORD_WHILE: + return "'while'"; + case PM_TOKEN_KEYWORD_WHILE_MODIFIER: + return "'while'"; + case PM_TOKEN_KEYWORD_YIELD: + return "'yield'"; + case PM_TOKEN_KEYWORD___ENCODING__: + return "'__ENCODING__'"; + case PM_TOKEN_KEYWORD___FILE__: + return "'__FILE__'"; + case PM_TOKEN_KEYWORD___LINE__: + return "'__LINE__'"; + case PM_TOKEN_LABEL: + return "label"; + case PM_TOKEN_LABEL_END: + return "label terminator"; + case PM_TOKEN_LAMBDA_BEGIN: + return "'{'"; + case PM_TOKEN_LESS: + return "'<'"; + case PM_TOKEN_LESS_EQUAL: + return "'<='"; + case PM_TOKEN_LESS_EQUAL_GREATER: + return "'<=>'"; + case PM_TOKEN_LESS_LESS: + return "<<"; + case PM_TOKEN_LESS_LESS_EQUAL: + return "<<="; + case PM_TOKEN_METHOD_NAME: + return "method name"; + case PM_TOKEN_MINUS: + return "'-'"; + case PM_TOKEN_MINUS_EQUAL: + return "'-='"; + case PM_TOKEN_MINUS_GREATER: + return "'->'"; + case PM_TOKEN_NEWLINE: + return "newline"; + case PM_TOKEN_NUMBERED_REFERENCE: + return "numbered reference"; + case PM_TOKEN_PARENTHESIS_LEFT: + return "'('"; + case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: + return "'('"; + case PM_TOKEN_PARENTHESIS_RIGHT: + return "')'"; + case PM_TOKEN_PERCENT: + return "'%'"; + case PM_TOKEN_PERCENT_EQUAL: + return "'%='"; + case PM_TOKEN_PERCENT_LOWER_I: + return "'%i'"; + case PM_TOKEN_PERCENT_LOWER_W: + return "'%w'"; + case PM_TOKEN_PERCENT_LOWER_X: + return "'%x'"; + case PM_TOKEN_PERCENT_UPPER_I: + return "'%I'"; + case PM_TOKEN_PERCENT_UPPER_W: + return "'%W'"; + case PM_TOKEN_PIPE: + return "'|'"; + case PM_TOKEN_PIPE_EQUAL: + return "'|='"; + case PM_TOKEN_PIPE_PIPE: + return "'||'"; + case PM_TOKEN_PIPE_PIPE_EQUAL: + return "'||='"; + case PM_TOKEN_PLUS: + return "'+'"; + case PM_TOKEN_PLUS_EQUAL: + return "'+='"; + case PM_TOKEN_QUESTION_MARK: + return "'?'"; + case PM_TOKEN_REGEXP_BEGIN: + return "regular expression beginning"; + case PM_TOKEN_REGEXP_END: + return "regular expression ending"; + case PM_TOKEN_SEMICOLON: + return "';'"; + case PM_TOKEN_SLASH: + return "'/'"; + case PM_TOKEN_SLASH_EQUAL: + return "'/='"; + case PM_TOKEN_STAR: + return "'*'"; + case PM_TOKEN_STAR_EQUAL: + return "'*='"; + case PM_TOKEN_STAR_STAR: + return "'**'"; + case PM_TOKEN_STAR_STAR_EQUAL: + return "'**='"; + case PM_TOKEN_STRING_BEGIN: + return "string literal"; + case PM_TOKEN_STRING_CONTENT: + return "string content"; + case PM_TOKEN_STRING_END: + return "string ending"; + case PM_TOKEN_SYMBOL_BEGIN: + return "symbol literal"; + case PM_TOKEN_TILDE: + return "'~'"; + case PM_TOKEN_UAMPERSAND: + return "'&'"; + case PM_TOKEN_UCOLON_COLON: + return "'::'"; + case PM_TOKEN_UDOT_DOT: + return "'..'"; + case PM_TOKEN_UDOT_DOT_DOT: + return "'...'"; + case PM_TOKEN_UMINUS: + return "'-'"; + case PM_TOKEN_UMINUS_NUM: + return "'-'"; + case PM_TOKEN_UPLUS: + return "'+'"; + case PM_TOKEN_USTAR: + return "*"; + case PM_TOKEN_USTAR_STAR: + return "**"; + case PM_TOKEN_WORDS_SEP: + return "string separator"; + case PM_TOKEN___END__: + return "'__END__'"; + case PM_TOKEN_MAXIMUM: + assert(false && "unreachable"); + return ""; + } + + // Provide a default, because some compilers can't determine that the above + // switch is exhaustive. + assert(false && "unreachable"); + return ""; +} diff --git a/vendor/mruby-compiler2/lib/prism/src/util/pm_buffer.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_buffer.c new file mode 100644 index 0000000..2136a7c --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/util/pm_buffer.c @@ -0,0 +1,357 @@ +#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 *) xmalloc(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(const pm_buffer_t *buffer) { + return buffer->value; +} + +/** + * Return the length of the buffer. + */ +size_t +pm_buffer_length(const pm_buffer_t *buffer) { + return buffer->length; +} + +/** + * Append the given amount of space to the buffer. + */ +static inline bool +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 = xrealloc(buffer->value, buffer->capacity); + if (buffer->value == NULL) return false; + } + + buffer->length = next_length; + return true; +} + +/** + * 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; + if (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; + if (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; + if (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); +} + +/** + * Append a double to the buffer. + */ +void +pm_buffer_append_double(pm_buffer_t *buffer, double value) { + const void *source = &value; + pm_buffer_append(buffer, source, sizeof(double)); +} + +/** + * Append a unicode codepoint to the buffer. + */ +bool +pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t value) { + if (value <= 0x7F) { + pm_buffer_append_byte(buffer, (uint8_t) value); // 0xxxxxxx + return true; + } else if (value <= 0x7FF) { + uint8_t bytes[] = { + (uint8_t) (0xC0 | ((value >> 6) & 0x3F)), // 110xxxxx + (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx + }; + + pm_buffer_append_bytes(buffer, bytes, 2); + return true; + } else if (value <= 0xFFFF) { + uint8_t bytes[] = { + (uint8_t) (0xE0 | ((value >> 12) & 0x3F)), // 1110xxxx + (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx + (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx + }; + + pm_buffer_append_bytes(buffer, bytes, 3); + return true; + } else if (value <= 0x10FFFF) { + uint8_t bytes[] = { + (uint8_t) (0xF0 | ((value >> 18) & 0x3F)), // 11110xxx + (uint8_t) (0x80 | ((value >> 12) & 0x3F)), // 10xxxxxx + (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx + (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx + }; + + pm_buffer_append_bytes(buffer, bytes, 4); + return true; + } else { + return false; + } +} + +/** + * Append a slice of source code to the buffer. + */ +void +pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping) { + for (size_t index = 0; index < length; index++) { + const uint8_t byte = source[index]; + + if ((byte <= 0x06) || (byte >= 0x0E && byte <= 0x1F) || (byte >= 0x7F)) { + if (escaping == PM_BUFFER_ESCAPING_RUBY) { + pm_buffer_append_format(buffer, "\\x%02X", byte); + } else { + pm_buffer_append_format(buffer, "\\u%04X", byte); + } + } else { + switch (byte) { + case '\a': + if (escaping == PM_BUFFER_ESCAPING_RUBY) { + pm_buffer_append_string(buffer, "\\a", 2); + } else { + pm_buffer_append_format(buffer, "\\u%04X", byte); + } + break; + case '\b': + pm_buffer_append_string(buffer, "\\b", 2); + break; + case '\t': + pm_buffer_append_string(buffer, "\\t", 2); + break; + case '\n': + pm_buffer_append_string(buffer, "\\n", 2); + break; + case '\v': + if (escaping == PM_BUFFER_ESCAPING_RUBY) { + pm_buffer_append_string(buffer, "\\v", 2); + } else { + pm_buffer_append_format(buffer, "\\u%04X", byte); + } + break; + case '\f': + pm_buffer_append_string(buffer, "\\f", 2); + break; + case '\r': + pm_buffer_append_string(buffer, "\\r", 2); + break; + case '"': + pm_buffer_append_string(buffer, "\\\"", 2); + break; + case '#': { + if (escaping == PM_BUFFER_ESCAPING_RUBY && index + 1 < length) { + const uint8_t next_byte = source[index + 1]; + if (next_byte == '{' || next_byte == '@' || next_byte == '$') { + pm_buffer_append_byte(buffer, '\\'); + } + } + + pm_buffer_append_byte(buffer, '#'); + break; + } + case '\\': + pm_buffer_append_string(buffer, "\\\\", 2); + break; + default: + pm_buffer_append_byte(buffer, byte); + break; + } + } + } +} + +/** + * Prepend the given string to the buffer. + */ +void +pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length) { + size_t cursor = buffer->length; + if (pm_buffer_append_length(buffer, length)) { + memmove(buffer->value + length, buffer->value, cursor); + memcpy(buffer->value, value, length); + } +} + +/** + * 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); + } +} + +/** + * Clear the buffer by reducing its size to 0. This does not free the allocated + * memory, but it does allow the buffer to be reused. + */ +void +pm_buffer_clear(pm_buffer_t *buffer) { + buffer->length = 0; +} + +/** + * Strip the whitespace from the end of the buffer. + */ +void +pm_buffer_rstrip(pm_buffer_t *buffer) { + while (buffer->length > 0 && pm_char_is_whitespace((uint8_t) buffer->value[buffer->length - 1])) { + buffer->length--; + } +} + +/** + * Checks if the buffer includes the given value. + */ +size_t +pm_buffer_index(const pm_buffer_t *buffer, char value) { + const char *first = memchr(buffer->value, value, buffer->length); + return (first == NULL) ? SIZE_MAX : (size_t) (first - buffer->value); +} + +/** + * Insert the given string into the buffer at the given index. + */ +void +pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length) { + assert(index <= buffer->length); + + if (index == buffer->length) { + pm_buffer_append_string(buffer, value, length); + } else { + pm_buffer_append_zeroes(buffer, length); + memmove(buffer->value + index + length, buffer->value + index, buffer->length - length - index); + memcpy(buffer->value + index, value, length); + } +} + +/** + * Free the memory associated with the buffer. + */ +void +pm_buffer_free(pm_buffer_t *buffer) { + xfree(buffer->value); +} diff --git a/vendor/mruby-compiler2/lib/prism/src/util/pm_char.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_char.c new file mode 100644 index 0000000..a51dc11 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/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, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 4x + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, // 5x + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 6x + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 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 (size > 0 && 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/vendor/mruby-compiler2/lib/prism/src/util/pm_constant_pool.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_constant_pool.c new file mode 100644 index 0000000..38ea01a --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/util/pm_constant_pool.c @@ -0,0 +1,342 @@ +#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; +} + +/** + * Initialize a list of constant ids with a given capacity. + */ +void +pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity) { + if (capacity) { + list->ids = xcalloc(capacity, sizeof(pm_constant_id_t)); + if (list->ids == NULL) abort(); + } else { + list->ids = NULL; + } + + list->size = 0; + list->capacity = capacity; +} + +/** + * 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 *) xrealloc(list->ids, sizeof(pm_constant_id_t) * list->capacity); + if (list->ids == NULL) return false; + } + + list->ids[list->size++] = id; + return true; +} + +/** + * Insert a constant id into a list of constant ids at the specified index. + */ +void +pm_constant_id_list_insert(pm_constant_id_list_t *list, size_t index, pm_constant_id_t id) { + assert(index < list->capacity); + assert(list->ids[index] == PM_CONSTANT_ID_UNSET); + + list->ids[index] = id; + list->size++; +} + +/** + * 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; +} + +/** + * 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) { + xfree(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 = xcalloc(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 != PM_CONSTANT_ID_UNSET) { + 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 != PM_CONSTANT_ID_UNSET) { + 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. + xfree(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 = xcalloc(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 != PM_CONSTANT_ID_UNSET && constant_id <= pool->size); + return &pool->constants[constant_id - 1]; +} + +/** + * Find a constant in a constant pool. Returns the id of the constant, or 0 if + * the constant is not found. + */ +pm_constant_id_t +pm_constant_pool_find(const pm_constant_pool_t *pool, const uint8_t *start, size_t length) { + 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 != PM_CONSTANT_ID_UNSET) { + pm_constant_t *constant = &pool->constants[bucket->id - 1]; + if ((constant->length == length) && memcmp(constant->start, start, length) == 0) { + return bucket->id; + } + + index = (index + 1) & mask; + } + + return PM_CONSTANT_ID_UNSET; +} + +/** + * 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 PM_CONSTANT_ID_UNSET; + } + + 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 != PM_CONSTANT_ID_UNSET) { + // 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. + xfree((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. + xfree((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-existent + // 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 + * PM_CONSTANT_ID_UNSET 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 PM_CONSTANT_ID_UNSET if any + * potential calls to resize fail. + */ +pm_constant_id_t +pm_constant_pool_insert_owned(pm_constant_pool_t *pool, 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 PM_CONSTANT_ID_UNSET 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 != PM_CONSTANT_ID_UNSET && bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) { + pm_constant_t *constant = &pool->constants[bucket->id - 1]; + xfree((void *) constant->start); + } + } + + xfree(pool->buckets); +} diff --git a/vendor/mruby-compiler2/lib/prism/src/util/pm_integer.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_integer.c new file mode 100644 index 0000000..4170ecc --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/util/pm_integer.c @@ -0,0 +1,670 @@ +#include "prism/util/pm_integer.h" + +/** + * Pull out the length and values from the integer, regardless of the form in + * which the length/values are stored. + */ +#define INTEGER_EXTRACT(integer, length_variable, values_variable) \ + if ((integer)->values == NULL) { \ + length_variable = 1; \ + values_variable = &(integer)->value; \ + } else { \ + length_variable = (integer)->length; \ + values_variable = (integer)->values; \ + } + +/** + * Adds two positive pm_integer_t with the given base. + * Return pm_integer_t with values allocated. Not normalized. + */ +static void +big_add(pm_integer_t *destination, pm_integer_t *left, pm_integer_t *right, uint64_t base) { + size_t left_length; + uint32_t *left_values; + INTEGER_EXTRACT(left, left_length, left_values) + + size_t right_length; + uint32_t *right_values; + INTEGER_EXTRACT(right, right_length, right_values) + + size_t length = left_length < right_length ? right_length : left_length; + uint32_t *values = (uint32_t *) xmalloc(sizeof(uint32_t) * (length + 1)); + if (values == NULL) return; + + uint64_t carry = 0; + for (size_t index = 0; index < length; index++) { + uint64_t sum = carry + (index < left_length ? left_values[index] : 0) + (index < right_length ? right_values[index] : 0); + values[index] = (uint32_t) (sum % base); + carry = sum / base; + } + + if (carry > 0) { + values[length] = (uint32_t) carry; + length++; + } + + *destination = (pm_integer_t) { length, values, 0, false }; +} + +/** + * Internal use for karatsuba_multiply. Calculates `a - b - c` with the given + * base. Assume a, b, c, a - b - c all to be positive. + * Return pm_integer_t with values allocated. Not normalized. + */ +static void +big_sub2(pm_integer_t *destination, pm_integer_t *a, pm_integer_t *b, pm_integer_t *c, uint64_t base) { + size_t a_length; + uint32_t *a_values; + INTEGER_EXTRACT(a, a_length, a_values) + + size_t b_length; + uint32_t *b_values; + INTEGER_EXTRACT(b, b_length, b_values) + + size_t c_length; + uint32_t *c_values; + INTEGER_EXTRACT(c, c_length, c_values) + + uint32_t *values = (uint32_t*) xmalloc(sizeof(uint32_t) * a_length); + int64_t carry = 0; + + for (size_t index = 0; index < a_length; index++) { + int64_t sub = ( + carry + + a_values[index] - + (index < b_length ? b_values[index] : 0) - + (index < c_length ? c_values[index] : 0) + ); + + if (sub >= 0) { + values[index] = (uint32_t) sub; + carry = 0; + } else { + sub += 2 * (int64_t) base; + values[index] = (uint32_t) ((uint64_t) sub % base); + carry = sub / (int64_t) base - 2; + } + } + + while (a_length > 1 && values[a_length - 1] == 0) a_length--; + *destination = (pm_integer_t) { a_length, values, 0, false }; +} + +/** + * Multiply two positive integers with the given base using karatsuba algorithm. + * Return pm_integer_t with values allocated. Not normalized. + */ +static void +karatsuba_multiply(pm_integer_t *destination, pm_integer_t *left, pm_integer_t *right, uint64_t base) { + size_t left_length; + uint32_t *left_values; + INTEGER_EXTRACT(left, left_length, left_values) + + size_t right_length; + uint32_t *right_values; + INTEGER_EXTRACT(right, right_length, right_values) + + if (left_length > right_length) { + size_t temporary_length = left_length; + left_length = right_length; + right_length = temporary_length; + + uint32_t *temporary_values = left_values; + left_values = right_values; + right_values = temporary_values; + } + + if (left_length <= 10) { + size_t length = left_length + right_length; + uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); + if (values == NULL) return; + + for (size_t left_index = 0; left_index < left_length; left_index++) { + uint32_t carry = 0; + for (size_t right_index = 0; right_index < right_length; right_index++) { + uint64_t product = (uint64_t) left_values[left_index] * right_values[right_index] + values[left_index + right_index] + carry; + values[left_index + right_index] = (uint32_t) (product % base); + carry = (uint32_t) (product / base); + } + values[left_index + right_length] = carry; + } + + while (length > 1 && values[length - 1] == 0) length--; + *destination = (pm_integer_t) { length, values, 0, false }; + return; + } + + if (left_length * 2 <= right_length) { + uint32_t *values = (uint32_t *) xcalloc(left_length + right_length, sizeof(uint32_t)); + + for (size_t start_offset = 0; start_offset < right_length; start_offset += left_length) { + size_t end_offset = start_offset + left_length; + if (end_offset > right_length) end_offset = right_length; + + pm_integer_t sliced_left = { + .length = left_length, + .values = left_values, + .value = 0, + .negative = false + }; + + pm_integer_t sliced_right = { + .length = end_offset - start_offset, + .values = right_values + start_offset, + .value = 0, + .negative = false + }; + + pm_integer_t product; + karatsuba_multiply(&product, &sliced_left, &sliced_right, base); + + uint32_t carry = 0; + for (size_t index = 0; index < product.length; index++) { + uint64_t sum = (uint64_t) values[start_offset + index] + product.values[index] + carry; + values[start_offset + index] = (uint32_t) (sum % base); + carry = (uint32_t) (sum / base); + } + + if (carry > 0) values[start_offset + product.length] += carry; + pm_integer_free(&product); + } + + *destination = (pm_integer_t) { left_length + right_length, values, 0, false }; + return; + } + + size_t half = left_length / 2; + pm_integer_t x0 = { half, left_values, 0, false }; + pm_integer_t x1 = { left_length - half, left_values + half, 0, false }; + pm_integer_t y0 = { half, right_values, 0, false }; + pm_integer_t y1 = { right_length - half, right_values + half, 0, false }; + + pm_integer_t z0 = { 0 }; + karatsuba_multiply(&z0, &x0, &y0, base); + + pm_integer_t z2 = { 0 }; + karatsuba_multiply(&z2, &x1, &y1, base); + + // For simplicity to avoid considering negative values, + // use `z1 = (x0 + x1) * (y0 + y1) - z0 - z2` instead of original karatsuba algorithm. + pm_integer_t x01 = { 0 }; + big_add(&x01, &x0, &x1, base); + + pm_integer_t y01 = { 0 }; + big_add(&y01, &y0, &y1, base); + + pm_integer_t xy = { 0 }; + karatsuba_multiply(&xy, &x01, &y01, base); + + pm_integer_t z1; + big_sub2(&z1, &xy, &z0, &z2, base); + + size_t length = left_length + right_length; + uint32_t *values = (uint32_t*) xcalloc(length, sizeof(uint32_t)); + + assert(z0.values != NULL); + memcpy(values, z0.values, sizeof(uint32_t) * z0.length); + + assert(z2.values != NULL); + memcpy(values + 2 * half, z2.values, sizeof(uint32_t) * z2.length); + + uint32_t carry = 0; + for(size_t index = 0; index < z1.length; index++) { + uint64_t sum = (uint64_t) carry + values[index + half] + z1.values[index]; + values[index + half] = (uint32_t) (sum % base); + carry = (uint32_t) (sum / base); + } + + for(size_t index = half + z1.length; carry > 0; index++) { + uint64_t sum = (uint64_t) carry + values[index]; + values[index] = (uint32_t) (sum % base); + carry = (uint32_t) (sum / base); + } + + while (length > 1 && values[length - 1] == 0) length--; + pm_integer_free(&z0); + pm_integer_free(&z1); + pm_integer_free(&z2); + pm_integer_free(&x01); + pm_integer_free(&y01); + pm_integer_free(&xy); + + *destination = (pm_integer_t) { length, values, 0, false }; +} + +/** + * The values of a hexadecimal digit, where the index is the ASCII character. + * Note that there's an odd exception here where _ is mapped to 0. This is + * because it's possible for us to end up trying to parse a number that has + * already had an error attached to it, and we want to provide _something_ to + * the user. + */ +static const int8_t pm_integer_parse_digit_values[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1x + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2x + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 3x + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4x + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, // 5x + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6x + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7x + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8x + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9x + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ax + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Bx + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Cx + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Dx + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ex + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Fx +}; + +/** + * Return the value of a hexadecimal digit in a uint8_t. + */ +static uint8_t +pm_integer_parse_digit(const uint8_t character) { + int8_t value = pm_integer_parse_digit_values[character]; + assert(value != -1 && "invalid digit"); + + return (uint8_t) value; +} + +/** + * Create a pm_integer_t from uint64_t with the given base. It is assumed that + * the memory for the pm_integer_t pointer has been zeroed. + */ +static void +pm_integer_from_uint64(pm_integer_t *integer, uint64_t value, uint64_t base) { + if (value < base) { + integer->value = (uint32_t) value; + return; + } + + size_t length = 0; + uint64_t length_value = value; + while (length_value > 0) { + length++; + length_value /= base; + } + + uint32_t *values = (uint32_t *) xmalloc(sizeof(uint32_t) * length); + if (values == NULL) return; + + for (size_t value_index = 0; value_index < length; value_index++) { + values[value_index] = (uint32_t) (value % base); + value /= base; + } + + integer->length = length; + integer->values = values; +} + +/** + * Normalize pm_integer_t. + * Heading zero values will be removed. If the integer fits into uint32_t, + * values is set to NULL, length is set to 0, and value field will be used. + */ +static void +pm_integer_normalize(pm_integer_t *integer) { + if (integer->values == NULL) { + return; + } + + while (integer->length > 1 && integer->values[integer->length - 1] == 0) { + integer->length--; + } + + if (integer->length > 1) { + return; + } + + uint32_t value = integer->values[0]; + bool negative = integer->negative && value != 0; + + pm_integer_free(integer); + *integer = (pm_integer_t) { .values = NULL, .value = value, .length = 0, .negative = negative }; +} + +/** + * Convert base of the integer. + * In practice, it converts 10**9 to 1<<32 or 1<<32 to 10**9. + */ +static void +pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, uint64_t base_from, uint64_t base_to) { + size_t source_length; + const uint32_t *source_values; + INTEGER_EXTRACT(source, source_length, source_values) + + size_t bigints_length = (source_length + 1) / 2; + assert(bigints_length > 0); + + pm_integer_t *bigints = (pm_integer_t *) xcalloc(bigints_length, sizeof(pm_integer_t)); + if (bigints == NULL) return; + + for (size_t index = 0; index < source_length; index += 2) { + uint64_t value = source_values[index] + base_from * (index + 1 < source_length ? source_values[index + 1] : 0); + pm_integer_from_uint64(&bigints[index / 2], value, base_to); + } + + pm_integer_t base = { 0 }; + pm_integer_from_uint64(&base, base_from, base_to); + + while (bigints_length > 1) { + pm_integer_t next_base; + karatsuba_multiply(&next_base, &base, &base, base_to); + + pm_integer_free(&base); + base = next_base; + + size_t next_length = (bigints_length + 1) / 2; + pm_integer_t *next_bigints = (pm_integer_t *) xcalloc(next_length, sizeof(pm_integer_t)); + + for (size_t bigints_index = 0; bigints_index < bigints_length; bigints_index += 2) { + if (bigints_index + 1 == bigints_length) { + next_bigints[bigints_index / 2] = bigints[bigints_index]; + } else { + pm_integer_t multiplied = { 0 }; + karatsuba_multiply(&multiplied, &base, &bigints[bigints_index + 1], base_to); + + big_add(&next_bigints[bigints_index / 2], &bigints[bigints_index], &multiplied, base_to); + pm_integer_free(&bigints[bigints_index]); + pm_integer_free(&bigints[bigints_index + 1]); + pm_integer_free(&multiplied); + } + } + + xfree(bigints); + bigints = next_bigints; + bigints_length = next_length; + } + + *destination = bigints[0]; + destination->negative = source->negative; + pm_integer_normalize(destination); + + xfree(bigints); + pm_integer_free(&base); +} + +#undef INTEGER_EXTRACT + +/** + * Convert digits to integer with the given power-of-two base. + */ +static void +pm_integer_parse_powof2(pm_integer_t *integer, uint32_t base, const uint8_t *digits, size_t digits_length) { + size_t bit = 1; + while (base > (uint32_t) (1 << bit)) bit++; + + size_t length = (digits_length * bit + 31) / 32; + uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); + + for (size_t digit_index = 0; digit_index < digits_length; digit_index++) { + size_t bit_position = bit * (digits_length - digit_index - 1); + uint32_t value = digits[digit_index]; + + size_t index = bit_position / 32; + size_t shift = bit_position % 32; + + values[index] |= value << shift; + if (32 - shift < bit) values[index + 1] |= value >> (32 - shift); + } + + while (length > 1 && values[length - 1] == 0) length--; + *integer = (pm_integer_t) { .length = length, .values = values, .value = 0, .negative = false }; + pm_integer_normalize(integer); +} + +/** + * Convert decimal digits to pm_integer_t. + */ +static void +pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t digits_length) { + const size_t batch = 9; + size_t length = (digits_length + batch - 1) / batch; + + uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); + uint32_t value = 0; + + for (size_t digits_index = 0; digits_index < digits_length; digits_index++) { + value = value * 10 + digits[digits_index]; + + size_t reverse_index = digits_length - digits_index - 1; + if (reverse_index % batch == 0) { + values[reverse_index / batch] = value; + value = 0; + } + } + + // Convert base from 10**9 to 1<<32. + pm_integer_convert_base(integer, &((pm_integer_t) { .length = length, .values = values, .value = 0, .negative = false }), 1000000000, ((uint64_t) 1 << 32)); + xfree(values); +} + +/** + * Parse a large integer from a string that does not fit into uint32_t. + */ +static void +pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t *start, const uint8_t *end) { + // Allocate an array to store digits. + uint8_t *digits = xmalloc(sizeof(uint8_t) * (size_t) (end - start)); + size_t digits_length = 0; + + for (; start < end; start++) { + if (*start == '_') continue; + digits[digits_length++] = pm_integer_parse_digit(*start); + } + + // Construct pm_integer_t from the digits. + if (multiplier == 10) { + pm_integer_parse_decimal(integer, digits, digits_length); + } else { + pm_integer_parse_powof2(integer, multiplier, digits, digits_length); + } + + xfree(digits); +} + +/** + * Parse an integer from a string. This assumes that the format of the integer + * has already been validated, as internal validation checks are not performed + * here. + */ +void +pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end) { + // Ignore unary +. Unary - is parsed differently and will not end up here. + // Instead, it will modify the parsed integer later. + if (*start == '+') start++; + + // Determine the multiplier from the base, and skip past any prefixes. + uint32_t multiplier = 10; + switch (base) { + case PM_INTEGER_BASE_DEFAULT: + while (*start == '0') start++; // 01 -> 1 + break; + case PM_INTEGER_BASE_BINARY: + start += 2; // 0b + multiplier = 2; + break; + case PM_INTEGER_BASE_OCTAL: + start++; // 0 + if (*start == '_' || *start == 'o' || *start == 'O') start++; // o + multiplier = 8; + break; + case PM_INTEGER_BASE_DECIMAL: + if (*start == '0' && (end - start) > 1) start += 2; // 0d + break; + case PM_INTEGER_BASE_HEXADECIMAL: + start += 2; // 0x + multiplier = 16; + break; + case PM_INTEGER_BASE_UNKNOWN: + if (*start == '0' && (end - start) > 1) { + switch (start[1]) { + case '_': start += 2; multiplier = 8; break; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': start++; multiplier = 8; break; + case 'b': case 'B': start += 2; multiplier = 2; break; + case 'o': case 'O': start += 2; multiplier = 8; break; + case 'd': case 'D': start += 2; break; + case 'x': case 'X': start += 2; multiplier = 16; break; + default: assert(false && "unreachable"); break; + } + } + break; + } + + // It's possible that we've consumed everything at this point if there is an + // invalid integer. If this is the case, we'll just return 0. + if (start >= end) return; + + const uint8_t *cursor = start; + uint64_t value = (uint64_t) pm_integer_parse_digit(*cursor++); + + for (; cursor < end; cursor++) { + if (*cursor == '_') continue; + value = value * multiplier + (uint64_t) pm_integer_parse_digit(*cursor); + + if (value > UINT32_MAX) { + // If the integer is too large to fit into a single uint32_t, then + // we'll parse it as a big integer. + pm_integer_parse_big(integer, multiplier, start, end); + return; + } + } + + integer->value = (uint32_t) value; +} + +/** + * Compare two integers. This function returns -1 if the left integer is less + * than the right integer, 0 if they are equal, and 1 if the left integer is + * greater than the right integer. + */ +int +pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right) { + if (left->negative != right->negative) return left->negative ? -1 : 1; + int negative = left->negative ? -1 : 1; + + if (left->values == NULL && right->values == NULL) { + if (left->value < right->value) return -1 * negative; + if (left->value > right->value) return 1 * negative; + return 0; + } + + if (left->values == NULL || left->length < right->length) return -1 * negative; + if (right->values == NULL || left->length > right->length) return 1 * negative; + + for (size_t index = 0; index < left->length; index++) { + size_t value_index = left->length - index - 1; + uint32_t left_value = left->values[value_index]; + uint32_t right_value = right->values[value_index]; + + if (left_value < right_value) return -1 * negative; + if (left_value > right_value) return 1 * negative; + } + + return 0; +} + +/** + * Reduce a ratio of integers to its simplest form. + */ +void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator) { + // If either the numerator or denominator do not fit into a 32-bit integer, + // then this function is a no-op. In the future, we may consider reducing + // even the larger numbers, but for now we're going to keep it simple. + if ( + // If the numerator doesn't fit into a 32-bit integer, return early. + numerator->length != 0 || + // If the denominator doesn't fit into a 32-bit integer, return early. + denominator->length != 0 || + // If the numerator is 0, then return early. + numerator->value == 0 || + // If the denominator is 1, then return early. + denominator->value == 1 + ) return; + + // Find the greatest common divisor of the numerator and denominator. + uint32_t divisor = numerator->value; + uint32_t remainder = denominator->value; + + while (remainder != 0) { + uint32_t temporary = remainder; + remainder = divisor % remainder; + divisor = temporary; + } + + // Divide the numerator and denominator by the greatest common divisor. + numerator->value /= divisor; + denominator->value /= divisor; +} + +/** + * Convert an integer to a decimal string. + */ +PRISM_EXPORTED_FUNCTION void +pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { + if (integer->negative) { + pm_buffer_append_byte(buffer, '-'); + } + + // If the integer fits into a single uint32_t, then we can just append the + // value directly to the buffer. + if (integer->values == NULL) { + pm_buffer_append_format(buffer, "%" PRIu32, integer->value); + return; + } + + // If the integer is two uint32_t values, then we can | them together and + // append the result to the buffer. + if (integer->length == 2) { + const uint64_t value = ((uint64_t) integer->values[0]) | ((uint64_t) integer->values[1] << 32); + pm_buffer_append_format(buffer, "%" PRIu64, value); + return; + } + + // Otherwise, first we'll convert the base from 1<<32 to 10**9. + pm_integer_t converted = { 0 }; + pm_integer_convert_base(&converted, integer, (uint64_t) 1 << 32, 1000000000); + + if (converted.values == NULL) { + pm_buffer_append_format(buffer, "%" PRIu32, converted.value); + pm_integer_free(&converted); + return; + } + + // Allocate a buffer that we'll copy the decimal digits into. + size_t digits_length = converted.length * 9; + char *digits = xcalloc(digits_length, sizeof(char)); + if (digits == NULL) return; + + // Pack bigdecimal to digits. + for (size_t value_index = 0; value_index < converted.length; value_index++) { + uint32_t value = converted.values[value_index]; + + for (size_t digit_index = 0; digit_index < 9; digit_index++) { + digits[digits_length - 9 * value_index - digit_index - 1] = (char) ('0' + value % 10); + value /= 10; + } + } + + size_t start_offset = 0; + while (start_offset < digits_length - 1 && digits[start_offset] == '0') start_offset++; + + // Finally, append the string to the buffer and free the digits. + pm_buffer_append_string(buffer, digits + start_offset, digits_length - start_offset); + xfree(digits); + pm_integer_free(&converted); +} + +/** + * Free the internal memory of an integer. This memory will only be allocated if + * the integer exceeds the size of a single uint32_t. + */ +PRISM_EXPORTED_FUNCTION void +pm_integer_free(pm_integer_t *integer) { + if (integer->values) { + xfree(integer->values); + } +} diff --git a/vendor/mruby-compiler2/lib/prism/src/util/pm_list.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_list.c new file mode 100644 index 0000000..ad2294c --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/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; + xfree(node); + node = next; + } + + list->size = 0; +} diff --git a/vendor/mruby-compiler2/lib/prism/src/util/pm_memchr.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_memchr.c new file mode 100644 index 0000000..7ea20ac --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/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, const 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/vendor/mruby-compiler2/lib/prism/src/util/pm_newline_list.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_newline_list.c new file mode 100644 index 0000000..8331618 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/util/pm_newline_list.c @@ -0,0 +1,125 @@ +#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 *) xcalloc(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; +} + +/** + * Clear out the newlines that have been appended to the list. + */ +void +pm_newline_list_clear(pm_newline_list_t *list) { + list->size = 1; +} + +/** + * 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 *) xcalloc(list->capacity, sizeof(size_t)); + if (list->offsets == NULL) return false; + + memcpy(list->offsets, original_offsets, list->size * sizeof(size_t)); + xfree(original_offsets); + } + + 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; +} + +/** + * Returns the line of the given offset. If the offset is not in the list, the + * line of the closest offset less than the given offset is returned. + */ +int32_t +pm_newline_list_line(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line) { + 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 ((int32_t) mid) + start_line; + } + + if (list->offsets[mid] < offset) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return ((int32_t) left) + start_line - 1; +} + +/** + * 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, int32_t start_line) { + 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) { ((int32_t) mid) + start_line, 0 }); + } + + if (list->offsets[mid] < offset) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return ((pm_line_column_t) { + .line = ((int32_t) left) + start_line - 1, + .column = (uint32_t) (offset - list->offsets[left - 1]) + }); +} + +/** + * Free the internal memory allocated for the newline list. + */ +void +pm_newline_list_free(pm_newline_list_t *list) { + xfree(list->offsets); +} diff --git a/vendor/mruby-compiler2/lib/prism/src/util/pm_string.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_string.c new file mode 100644 index 0000000..75422fb --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/util/pm_string.c @@ -0,0 +1,383 @@ +#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 + }; +} + +#ifdef _WIN32 +/** + * Represents a file handle on Windows, where the path will need to be freed + * when the file is closed. + */ +typedef struct { + /** The path to the file, which will become allocated memory. */ + WCHAR *path; + + /** The handle to the file, which will start as uninitialized memory. */ + HANDLE file; +} pm_string_file_handle_t; + +/** + * Open the file indicated by the filepath parameter for reading on Windows. + * Perform any kind of normalization that needs to happen on the filepath. + */ +static pm_string_init_result_t +pm_string_file_handle_open(pm_string_file_handle_t *handle, const char *filepath) { + int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0); + if (length == 0) return PM_STRING_INIT_ERROR_GENERIC; + + handle->path = xmalloc(sizeof(WCHAR) * ((size_t) length)); + if ((handle->path == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, handle->path, length) == 0)) { + xfree(handle->path); + return PM_STRING_INIT_ERROR_GENERIC; + } + + handle->file = CreateFileW(handle->path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + if (handle->file == INVALID_HANDLE_VALUE) { + pm_string_init_result_t result = PM_STRING_INIT_ERROR_GENERIC; + + if (GetLastError() == ERROR_ACCESS_DENIED) { + DWORD attributes = GetFileAttributesW(handle->path); + if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = PM_STRING_INIT_ERROR_DIRECTORY; + } + } + + xfree(handle->path); + return result; + } + + return PM_STRING_INIT_SUCCESS; +} + +/** + * Close the file handle and free the path. + */ +static void +pm_string_file_handle_close(pm_string_file_handle_t *handle) { + xfree(handle->path); + CloseHandle(handle->file); +} +#endif + +/** + * 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`. + */ +PRISM_EXPORTED_FUNCTION pm_string_init_result_t +pm_string_mapped_init(pm_string_t *string, const char *filepath) { +#ifdef _WIN32 + // Open the file for reading. + pm_string_file_handle_t handle; + pm_string_init_result_t result = pm_string_file_handle_open(&handle, filepath); + if (result != PM_STRING_INIT_SUCCESS) return result; + + // Get the file size. + DWORD file_size = GetFileSize(handle.file, NULL); + if (file_size == INVALID_FILE_SIZE) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // 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) { + pm_string_file_handle_close(&handle); + const uint8_t source[] = ""; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + return PM_STRING_INIT_SUCCESS; + } + + // Create a mapping of the file. + HANDLE mapping = CreateFileMapping(handle.file, NULL, PAGE_READONLY, 0, 0, NULL); + if (mapping == NULL) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Map the file into memory. + uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + CloseHandle(mapping); + pm_string_file_handle_close(&handle); + + if (source == NULL) { + return PM_STRING_INIT_ERROR_GENERIC; + } + + *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = (size_t) file_size }; + return PM_STRING_INIT_SUCCESS; +#elif defined(_POSIX_MAPPED_FILES) + // Open the file for reading + int fd = open(filepath, O_RDONLY); + if (fd == -1) { + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Stat the file to get the file size + struct stat sb; + if (fstat(fd, &sb) == -1) { + close(fd); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Ensure it is a file and not a directory + if (S_ISDIR(sb.st_mode)) { + close(fd); + return PM_STRING_INIT_ERROR_DIRECTORY; + } + + // 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 PM_STRING_INIT_SUCCESS; + } + + source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (source == MAP_FAILED) { + close(fd); + return PM_STRING_INIT_ERROR_GENERIC; + } + + close(fd); + *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = size }; + return PM_STRING_INIT_SUCCESS; +#else + return pm_string_file_init(string, filepath); +#endif +} + +/** + * 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. + */ +PRISM_EXPORTED_FUNCTION pm_string_init_result_t +pm_string_file_init(pm_string_t *string, const char *filepath) { +#ifdef _WIN32 + // Open the file for reading. + pm_string_file_handle_t handle; + pm_string_init_result_t result = pm_string_file_handle_open(&handle, filepath); + if (result != PM_STRING_INIT_SUCCESS) return result; + + // Get the file size. + DWORD file_size = GetFileSize(handle.file, NULL); + if (file_size == INVALID_FILE_SIZE) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // 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) { + pm_string_file_handle_close(&handle); + const uint8_t source[] = ""; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + return PM_STRING_INIT_SUCCESS; + } + + // Create a buffer to read the file into. + uint8_t *source = xmalloc(file_size); + if (source == NULL) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Read the contents of the file + DWORD bytes_read; + if (!ReadFile(handle.file, source, file_size, &bytes_read, NULL)) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Check the number of bytes read + if (bytes_read != file_size) { + xfree(source); + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + pm_string_file_handle_close(&handle); + *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = (size_t) file_size }; + return PM_STRING_INIT_SUCCESS; +#elif defined(PRISM_HAS_FILESYSTEM) + // Open the file for reading + int fd = open(filepath, O_RDONLY); + if (fd == -1) { + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Stat the file to get the file size + struct stat sb; + if (fstat(fd, &sb) == -1) { + close(fd); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Ensure it is a file and not a directory + if (S_ISDIR(sb.st_mode)) { + close(fd); + return PM_STRING_INIT_ERROR_DIRECTORY; + } + + // Check the size to see if it's empty + size_t size = (size_t) sb.st_size; + if (size == 0) { + close(fd); + const uint8_t source[] = ""; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + return PM_STRING_INIT_SUCCESS; + } + + size_t length = (size_t) size; + uint8_t *source = xmalloc(length); + if (source == NULL) { + close(fd); + return PM_STRING_INIT_ERROR_GENERIC; + } + + long bytes_read = (long) read(fd, source, length); + close(fd); + + if (bytes_read == -1) { + xfree(source); + return PM_STRING_INIT_ERROR_GENERIC; + } + + *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = length }; + return PM_STRING_INIT_SUCCESS; +#else + (void) string; + (void) filepath; + perror("pm_string_file_init is not implemented for this platform"); + return PM_STRING_INIT_ERROR_GENERIC; +#endif +} + +/** + * 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 = xmalloc(length); + if (!memory) return; + + pm_string_owned_init(string, memory, length); + memcpy((void *) string->source, source, length); +} + +/** + * Compare the underlying lengths and bytes of two strings. Returns 0 if the + * strings are equal, a negative number if the left string is less than the + * right string, and a positive number if the left string is greater than the + * right string. + */ +int +pm_string_compare(const pm_string_t *left, const pm_string_t *right) { + size_t left_length = pm_string_length(left); + size_t right_length = pm_string_length(right); + + if (left_length < right_length) { + return -1; + } else if (left_length > right_length) { + return 1; + } + + return memcmp(pm_string_source(left), pm_string_source(right), left_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) { + xfree(memory); +#ifdef PRISM_HAS_MMAP + } else if (string->type == PM_STRING_MAPPED && string->length) { +#if defined(_WIN32) + UnmapViewOfFile(memory); +#elif defined(_POSIX_MAPPED_FILES) + munmap(memory, string->length); +#endif +#endif /* PRISM_HAS_MMAP */ + } +} diff --git a/vendor/mruby-compiler2/lib/prism/src/util/pm_strncasecmp.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_strncasecmp.c new file mode 100644 index 0000000..3f58421 --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/util/pm_strncasecmp.c @@ -0,0 +1,36 @@ +#include "prism/util/pm_strncasecmp.h" + +/** + * A locale-insensitive version of `tolower(3)` + */ +static inline int +pm_tolower(int c) +{ + if ('A' <= c && c <= 'Z') { + return c | 0x20; + } + return c; +} + +/** + * 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 = pm_tolower(string1[offset]) - pm_tolower(string2[offset])) != 0) return difference; + offset++; + } + + return difference; +} diff --git a/vendor/mruby-compiler2/lib/prism/src/util/pm_strpbrk.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_strpbrk.c new file mode 100644 index 0000000..916a4cc --- /dev/null +++ b/vendor/mruby-compiler2/lib/prism/src/util/pm_strpbrk.c @@ -0,0 +1,206 @@ +#include "prism/util/pm_strpbrk.h" + +/** + * Add an invalid multibyte character error to the parser. + */ +static inline void +pm_strpbrk_invalid_multibyte_character(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + pm_diagnostic_list_append_format(&parser->error_list, start, end, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *start); +} + +/** + * Set the explicit encoding for the parser to the current encoding. + */ +static inline void +pm_strpbrk_explicit_encoding_set(pm_parser_t *parser, const uint8_t *source, size_t width) { + if (parser->explicit_encoding != NULL) { + if (parser->explicit_encoding == parser->encoding) { + // Okay, we already locked to this encoding. + } else if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + // Not okay, we already found a Unicode escape sequence and this + // conflicts. + pm_diagnostic_list_append_format(&parser->error_list, source, source + width, PM_ERR_MIXED_ENCODING, parser->encoding->name); + } else { + // Should not be anything else. + assert(false && "unreachable"); + } + } + + parser->explicit_encoding = parser->encoding; +} + +/** + * This is the default path. + */ +static inline const uint8_t * +pm_strpbrk_utf8(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) { + size_t index = 0; + + while (index < maximum) { + if (strchr((const char *) charset, source[index]) != NULL) { + return source + index; + } + + if (source[index] < 0x80) { + index++; + } else { + size_t width = pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index)); + + if (width > 0) { + index += width; + } else if (!validate) { + index++; + } else { + // At this point we know we have an invalid multibyte character. + // We'll walk forward as far as we can until we find the next + // valid character so that we don't spam the user with a ton of + // the same kind of error. + const size_t start = index; + + do { + index++; + } while (index < maximum && pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); + + pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index); + } + } + } + + return NULL; +} + +/** + * This is the path when the encoding is ASCII-8BIT. + */ +static inline const uint8_t * +pm_strpbrk_ascii_8bit(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) { + size_t index = 0; + + while (index < maximum) { + if (strchr((const char *) charset, source[index]) != NULL) { + return source + index; + } + + if (validate && source[index] >= 0x80) pm_strpbrk_explicit_encoding_set(parser, source, 1); + index++; + } + + return NULL; +} + +/** + * 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, bool validate) { + size_t index = 0; + const pm_encoding_t *encoding = parser->encoding; + + while (index < maximum) { + if (strchr((const char *) charset, source[index]) != NULL) { + return source + index; + } + + if (source[index] < 0x80) { + index++; + } else { + size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + if (validate) pm_strpbrk_explicit_encoding_set(parser, source, width); + + if (width > 0) { + index += width; + } else if (!validate) { + index++; + } else { + // At this point we know we have an invalid multibyte character. + // We'll walk forward as far as we can until we find the next + // valid character so that we don't spam the user with a ton of + // the same kind of error. + const size_t start = index; + + do { + index++; + } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); + + pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index); + } + } + } + + return NULL; +} + +/** + * This is the fast path that does not care about the encoding because we know + * the encoding only supports single-byte characters. + */ +static inline const uint8_t * +pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) { + size_t index = 0; + const pm_encoding_t *encoding = parser->encoding; + + while (index < maximum) { + if (strchr((const char *) charset, source[index]) != NULL) { + return source + index; + } + + if (source[index] < 0x80 || !validate) { + index++; + } else { + size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + pm_strpbrk_explicit_encoding_set(parser, source, width); + + if (width > 0) { + index += width; + } else { + // At this point we know we have an invalid multibyte character. + // We'll walk forward as far as we can until we find the next + // valid character so that we don't spam the user with a ton of + // the same kind of error. + const size_t start = index; + + do { + index++; + } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); + + pm_strpbrk_invalid_multibyte_character(parser, source + start, source + 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, bool validate) { + if (length <= 0) { + return NULL; + } else if (!parser->encoding_changed) { + return pm_strpbrk_utf8(parser, source, charset, (size_t) length, validate); + } else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) { + return pm_strpbrk_ascii_8bit(parser, source, charset, (size_t) length, validate); + } else if (parser->encoding->multibyte) { + return pm_strpbrk_multi_byte(parser, source, charset, (size_t) length, validate); + } else { + return pm_strpbrk_single_byte(parser, source, charset, (size_t) length, validate); + } +} diff --git a/vendor/mruby-compiler2/mrbgem.rake b/vendor/mruby-compiler2/mrbgem.rake new file mode 100644 index 0000000..0919b96 --- /dev/null +++ b/vendor/mruby-compiler2/mrbgem.rake @@ -0,0 +1,66 @@ +MRuby::Gem::Specification.new('mruby-compiler2') do |spec| + spec.license = 'MIT' + spec.author = 'HASUMI Hitoshi' + spec.summary = 'mruby compiler using a universal parser' + + lib_dir = "#{dir}/lib" + cc.include_paths << "#{dir}/include" + + prism_dir = "#{lib_dir}/prism" + ruby_dir = "#{lib_dir}/ruby" + + cc.defines.flatten! + + cc.defines << "PRISM_XALLOCATOR" + if cc.defines.include?("PICORB_VM_MRUBY") + cc.defines << "MRC_TARGET_MRUBY" + elsif cc.defines.include?("PICORB_VM_MRUBYC") + cc.defines << "MRC_TARGET_MRUBYC" + end + + if cc.defines.include?("PICORB_INT32") + cc.defines << "MRC_INT64" + end + if cc.defines.any? { _1.match? /\A(PICORUBY|MRB)_DEBUG(=|\z)/ } + cc.defines << "MRC_DEBUG" + cc.defines << "MRC_DUMP_PRETTY" + else + cc.defines << "PRISM_BUILD_MINIMAL" + end + + prism_templates_dir = "#{lib_dir}/prism/templates" + cc.include_paths << "#{prism_dir}/include" + + next if %w(clean deep_clean).include?(Rake.application.top_level_tasks.first) + + directory prism_dir do + FileUtils.cd dir do + sh "git submodule update --init" + end + end + + task :prism_templates => prism_dir do + FileUtils.cd prism_dir do + sh "templates/template.rb" + end + end + + %w(node prettyprint serialize token_type).each do |name| + dst = "#{prism_dir}/src/#{name}.c" + # file task does not work when dst does not exist. why? + Rake::Task[:prism_templates].invoke unless File.exist?(dst) + file dst => ["#{prism_templates_dir}/src/#{name}.c.erb", "#{prism_templates_dir}/template.rb"] do |t| + Rake::Task[:prism_templates].invoke + end + end + + Dir.glob("#{prism_dir}/src/**/*.c").map do |src| + obj = objfile(src.pathmap("#{build_dir}/lib/%n")) + objs << obj + file obj => [src] do |f| + cc.run f.name, f.prerequisites.first + end + end + +end + diff --git a/vendor/mruby-compiler2/src/ccontext.c b/vendor/mruby-compiler2/src/ccontext.c new file mode 100644 index 0000000..f18b294 --- /dev/null +++ b/vendor/mruby-compiler2/src/ccontext.c @@ -0,0 +1,59 @@ +#include +#include "../include/mrc_ccontext.h" +#include "../include/mrc_parser_util.h" + +MRC_API mrc_ccontext * +mrc_ccontext_new(mrb_state *mrb) +{ + mrc_ccontext temp_c = {0}; + temp_c.mrb = mrb; + mrc_ccontext *c = (mrc_ccontext *)mrc_calloc((&temp_c), 1, sizeof(mrc_ccontext)); + c->p = (mrc_parser_state *)mrc_calloc((&temp_c), 1, sizeof(mrc_parser_state)); + c->mrb = temp_c.mrb; + return c; +} + + +MRC_API void +mrc_ccontext_cleanup_local_variables(mrc_ccontext *cc) +{ + cc->keep_lv = FALSE; + + if (cc->options && cc->options->scopes) { + for (int i = 0; i < cc->options->scopes[0].locals_count; i++) { + mrc_free(cc, (void *)cc->options->scopes[0].locals[i].source); + } + mrc_free(cc, cc->options); + } +} + +MRC_API const char * +mrc_ccontext_filename(mrc_ccontext *c, const char *s) +{ + if (s) { + size_t len = strlen(s); + char *p = (char*)mrc_malloc(c, len + 1); + + if (p == NULL) return NULL; + memcpy(p, s, len + 1); + if (c->filename) { + mrc_free(c, c->filename); + } + c->filename = p; + } + return c->filename; +} + +MRC_API void +mrc_ccontext_free(mrc_ccontext *c) +{ + if (c->filename_table) mrc_free(c, c->filename_table); + if (c->filename) mrc_free(c, c->filename); + pm_parser_free(c->p); + mrc_diagnostic_list_free(c); + if (c->p->lex_callback) { + mrc_free(c, c->p->lex_callback); + } + mrc_free(c, c->p); + mrc_free(c, c); +} diff --git a/vendor/mruby-compiler2/src/cdump.c b/vendor/mruby-compiler2/src/cdump.c new file mode 100644 index 0000000..51446df --- /dev/null +++ b/vendor/mruby-compiler2/src/cdump.c @@ -0,0 +1,583 @@ +/* +** cdump.c - mruby binary dumper (in C) +** +** See Copyright Notice in mruby.h +*/ + +#include +#include +#include "../include/mrc_ccontext.h" +#include "../include/mrc_irep.h" +#include "../include/mrc_dump.h" +#include "../include/mrc_debug.h" +#include "../include/mrc_irep_pool_type.h" + +#ifndef MRC_NO_STDIO + +#ifndef MRC_NO_FLOAT +//#include <../include/endian.h> +#define MRC_FLOAT_FMT "%.17g" +#endif + +#define ISALPHA(c) ((((unsigned)(c) | 0x20) - 'a') < 26) +#define ISDIGIT(c) (((unsigned)(c) - '0') < 10) +#define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c)) + +typedef struct mrc_string { + char *ptr; + size_t len; + size_t capa; +} mrc_string; + +static mrc_string* +mrc_str_new_capa(mrc_ccontext *c, size_t capa) +{ + mrc_string *s = (mrc_string *)mrc_malloc(c, sizeof(mrc_string)); + if (s) { + s->ptr = (char *)mrc_calloc(c, 1, capa); + if (s->ptr) { + s->len = 0; + s->capa = capa; + return s; + } + mrc_free(c, s); + } + return NULL; +} + +static mrc_string* +mrc_str_new(mrc_ccontext *c, const char *ptr, size_t len) +{ + mrc_string *s = mrc_str_new_capa(c, len+1); + if (s) { + memcpy(s->ptr, ptr, len); + s->len = len; + s->ptr[len] = '\0'; + } + return s; +} + +static mrc_string* +mrc_str_new_cstr(mrc_ccontext *c, const char *cstr) +{ + return mrc_str_new(c, cstr, strlen(cstr)); +} + +static void +mrc_str_cat_lit(mrc_ccontext *c, mrc_string *s, const char *lit) +{ + size_t len = strlen(lit); + if (s->len+len+1 > s->capa) { + s->capa = s->len+len+1; + s->ptr = (char *)mrc_realloc(c, s->ptr, s->capa); + } + memcpy(s->ptr+s->len, lit, len); + s->len += len; + s->ptr[s->len] = '\0'; +} + +static void +mrc_str_cat_cstr(mrc_ccontext *c, mrc_string *s, const char *cstr) +{ + if (!cstr) return; + mrc_str_cat_lit(c, s, cstr); +} + +static void +mrc_str_cat_str(mrc_ccontext *c, mrc_string *s, mrc_string *s2) +{ + if (s->len+s2->len+1 > s->capa) { + s->capa = s->len+s2->len+1; + s->ptr = (char *)mrc_realloc(c, s->ptr, s->capa); + } + memcpy(s->ptr+s->len, s2->ptr, s2->len); + s->len += s2->len; + s->ptr[s->len] = '\0'; +} + +static void +mrc_str_free(mrc_ccontext *c, mrc_string *s) +{ + mrc_free(c, s->ptr); + mrc_free(c, s); +} + +static mrc_string* +mrc_str_escape(mrc_ccontext *c, mrc_string *s) +{ + mrc_string *s2 = mrc_str_new_capa(c, s->len*2+1); + mrc_str_cat_lit(c, s2, "\""); + if (s2) { + for (size_t i=0; ilen; i++) { + char ch[2] = {s->ptr[i], '\0'}; + if (ch[0] == '"' || ch[0] == '\\') { + mrc_str_cat_lit(c, s2, "\\"); + } + mrc_str_cat_lit(c, s2, ch); + } + } + mrc_str_cat_lit(c, s2, "\""); + return s2; +} + +#define MRC_STRING_PTR(s) ((s)->ptr) +#define MRC_STRING_LEN(s) ((s)->len) + +static int +cdump_pool(mrc_ccontext *c, const mrc_pool_value *p, FILE *fp) +{ + if (p->tt & IREP_TT_NFLAG) { /* number */ + switch (p->tt) { +#ifdef MRC_64BIT + case IREP_TT_INT64: + if (p->u.i64 < INT32_MIN || INT32_MAX < p->u.i64) { + fprintf(fp, "{IREP_TT_INT64, {.i64=%" PRId64 "}},\n", p->u.i64); + } + else { + fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", (int32_t)p->u.i64); + } + break; +#endif + case IREP_TT_INT32: + fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", p->u.i32); + break; + case IREP_TT_FLOAT: +#ifndef MRC_NO_FLOAT + fprintf(fp, "{IREP_TT_FLOAT, {.f=" MRC_FLOAT_FMT "}},\n", p->u.f); +#endif + break; + case IREP_TT_BIGINT: + { + const char *s = p->u.str; + int len = s[0]+2; + fputs("{IREP_TT_BIGINT, {\"", fp); + for (int i=0; itt>>2; + const char *s = p->u.str; + fprintf(fp, "{IREP_TT_STR|(%d<<2), {\"", len); + for (i=0; i= 2 && name[len-1] == '=' && sym_name_word_p(name, len-1); +} + +static mrc_bool +sym_name_with_question_mark_p(const char *name, mrc_int len) +{ + return len >= 2 && name[len-1] == '?' && sym_name_word_p(name, len-1); +} + +static mrc_bool +sym_name_with_bang_p(const char *name, mrc_int len) +{ + return len >= 2 && name[len-1] == '!' && sym_name_word_p(name, len-1); +} + +static mrc_bool +sym_name_ivar_p(const char *name, mrc_int len) +{ + return len >= 2 && name[0] == '@' && sym_name_word_p(name+1, len-1); +} + +static mrc_bool +sym_name_cvar_p(const char *name, mrc_int len) +{ + return len >= 3 && name[0] == '@' && sym_name_ivar_p(name+1, len-1); +} + +#define OPERATOR_SYMBOL(sym_name, name) {name, sym_name, sizeof(sym_name)-1} +struct operator_symbol { + const char *name; + const char *sym_name; + uint16_t sym_name_len; +}; +static const struct operator_symbol operator_table[] = { + OPERATOR_SYMBOL("!", "not"), + OPERATOR_SYMBOL("%", "mod"), + OPERATOR_SYMBOL("&", "and"), + OPERATOR_SYMBOL("*", "mul"), + OPERATOR_SYMBOL("+", "add"), + OPERATOR_SYMBOL("-", "sub"), + OPERATOR_SYMBOL("/", "div"), + OPERATOR_SYMBOL("<", "lt"), + OPERATOR_SYMBOL(">", "gt"), + OPERATOR_SYMBOL("^", "xor"), + OPERATOR_SYMBOL("`", "tick"), + OPERATOR_SYMBOL("|", "or"), + OPERATOR_SYMBOL("~", "neg"), + OPERATOR_SYMBOL("!=", "neq"), + OPERATOR_SYMBOL("!~", "nmatch"), + OPERATOR_SYMBOL("&&", "andand"), + OPERATOR_SYMBOL("**", "pow"), + OPERATOR_SYMBOL("+@", "plus"), + OPERATOR_SYMBOL("-@", "minus"), + OPERATOR_SYMBOL("<<", "lshift"), + OPERATOR_SYMBOL("<=", "le"), + OPERATOR_SYMBOL("==", "eq"), + OPERATOR_SYMBOL("=~", "match"), + OPERATOR_SYMBOL(">=", "ge"), + OPERATOR_SYMBOL(">>", "rshift"), + OPERATOR_SYMBOL("[]", "aref"), + OPERATOR_SYMBOL("||", "oror"), + OPERATOR_SYMBOL("<=>", "cmp"), + OPERATOR_SYMBOL("===", "eqq"), + OPERATOR_SYMBOL("[]=", "aset"), +}; + +static const char* +sym_operator_name(const char *sym_name, mrc_int len) +{ + mrc_sym table_size = sizeof(operator_table)/sizeof(struct operator_symbol); + if (operator_table[table_size-1].sym_name_len < len) return NULL; + + for (mrc_sym start = 0; table_size != 0; table_size/=2) { + mrc_sym idx = start+table_size/2; + const struct operator_symbol *op_sym = &operator_table[idx]; + int cmp = (int)len-(int)op_sym->sym_name_len; + if (cmp == 0) { + cmp = memcmp(sym_name, op_sym->sym_name, len); + if (cmp == 0) return op_sym->name; + } + if (0 < cmp) { + start = ++idx; + table_size--; + } + } + return NULL; +} + +static mrc_string* +sym_var_name_str(mrc_ccontext *c, const char *initname, const char *key, int n) +{ + char buf[32]; + mrc_string *s = mrc_str_new_cstr(c, initname); + mrc_str_cat_lit(c, s, "_"); + mrc_str_cat_cstr(c, s, key); + mrc_str_cat_lit(c, s, "_"); + snprintf(buf, sizeof(buf), "%d", n); + mrc_str_cat_cstr(c, s, buf); + return s; +} + +static int +cdump_sym(mrc_ccontext *c, mrc_sym sym, const char *var_name, int idx, mrc_string *init_syms_code, FILE *fp) +{ + if (sym == 0) { + fputs("0,", fp); + return MRC_DUMP_OK; + } + + const pm_constant_t *constant = pm_constant_pool_id_to_constant(&c->p->constant_pool, sym); + mrc_string *name_obj = mrc_str_new(c, (const char *)constant->start, constant->length); + const char *name = MRC_STRING_PTR(name_obj); + const char *op_name; + mrc_int len = constant->length; + + if (*name == '\0') { + mrc_str_free(c, name_obj); + return MRC_DUMP_INVALID_ARGUMENT; + } + if (sym_name_word_p(name, len)) { + fprintf(fp, "MRB_SYM(%s)", name); + } + else if (sym_name_with_equal_p(name, len)) { + fprintf(fp, "MRB_SYM_E(%.*s)", (int)(len-1), name); + } + else if (sym_name_with_question_mark_p(name, len)) { + fprintf(fp, "MRB_SYM_Q(%.*s)", (int)(len-1), name); + } + else if (sym_name_with_bang_p(name, len)) { + fprintf(fp, "MRB_SYM_B(%.*s)", (int)(len-1), name); + } + else if (sym_name_ivar_p(name, len)) { + fprintf(fp, "MRB_IVSYM(%s)", name+1); + } + else if (sym_name_cvar_p(name, len)) { + fprintf(fp, "MRB_CVSYM(%s)", name+2); + } + else if ((op_name = sym_operator_name(name, len))) { + fprintf(fp, "MRB_OPSYM(%s)", op_name); + } + else { + char buf[32]; + mrc_string *name_obj = mrc_str_new(c, name, len); + mrc_str_cat_lit(c, init_syms_code, " "); + mrc_str_cat_cstr(c, init_syms_code, var_name); + snprintf(buf, sizeof(buf), "[%d] = ", idx); + mrc_str_cat_cstr(c, init_syms_code, buf); + mrc_str_cat_lit(c, init_syms_code, "mrb_intern_lit(mrb, "); + mrc_string *escaped = mrc_str_escape(c, name_obj); + mrc_str_free(c, name_obj); + mrc_str_cat_str(c, init_syms_code, escaped); + mrc_str_free(c, escaped); + mrc_str_cat_lit(c, init_syms_code, ");\n"); + fputs("0", fp); + } + fputs(", ", fp); + mrc_str_free(c, name_obj); + return MRC_DUMP_OK; +} + +static int +cdump_syms(mrc_ccontext *c, const char *name, const char *key, int n, int syms_len, const mrc_sym *syms, mrc_string *init_syms_code, FILE *fp) +{ + int ai = mrc_gc_arena_save(c); + mrc_int code_len = MRC_STRING_LEN(init_syms_code); + mrc_string *var_name = sym_var_name_str(c, name, key, n); + + fprintf(fp, "mrb_DEFINE_SYMS_VAR(%s, %d, (", MRC_STRING_PTR(var_name), syms_len); + for (int i=0; iflen != 1) { + return 0; + } + return 1; +} + +//Adds debug information to c-structs and +//adds filenames in init_syms_code block +static int +cdump_debug(mrc_ccontext *c, const char *name, int n, mrc_irep_debug_info *info, + mrc_string *init_syms_code, FILE *fp) +{ + int ai = mrc_gc_arena_save(c); + char buffer[256]; + const char *line_type = "mrb_debug_line_ary"; + + if (!simple_debug_info(info)) + return MRC_DUMP_INVALID_IREP; + + int len = info->files[0]->line_entry_count; + + const pm_constant_t *fn_constant = pm_constant_pool_id_to_constant(&c->p->constant_pool, info->files[0]->filename_sym); + const char *filename = (const char *)fn_constant->start; + snprintf(buffer, sizeof(buffer), " %s_debug_file_%d.filename_sym = mrb_intern_lit(mrb,", name, n); + mrc_str_cat_cstr(c, init_syms_code, buffer); + mrc_string *filename_str = mrc_str_new_cstr(c, filename); + mrc_string *escaped = mrc_str_escape(c, filename_str); + mrc_str_free(c, filename_str); + mrc_str_cat_str(c, init_syms_code, escaped); + mrc_str_free(c, escaped); + mrc_str_cat_cstr(c, init_syms_code, ");\n"); + + switch (info->files[0]->line_type) { + case mrc_debug_line_ary: + fprintf(fp, "static uint16_t %s_debug_lines_%d[%d] = {", name, n, len); + for (int i=0; ifiles[0]->lines.ary[i]); + } + fputs("};\n", fp); + break; + + case mrc_debug_line_flat_map: + line_type = "mrb_debug_line_flat_map"; + fprintf(fp, "static struct mrb_irep_debug_info_line %s_debug_lines_%d[%d] = {", name, n, len); + for (int i=0; ifiles[0]->lines.flat_map[i]; + fprintf(fp, "\t{.start_pos=0x%04x,.line=%d},\n", fmap->start_pos, fmap->line); + } + fputs("};\n", fp); + break; + + case mrc_debug_line_packed_map: + line_type = "mrb_debug_line_packed_map"; + fprintf(fp, "static const char %s_debug_lines_%d[] = \"", name, n); + const uint8_t *pmap = info->files[0]->lines.packed_map; + for (int i=0; ifiles[0]->start_pos, + info->files[0]->filename_sym, + info->files[0]->line_entry_count, + line_type, + name, n); + fprintf(fp, "static mrb_irep_debug_info_file *%s_debug_file_%d_ = &%s_debug_file_%d;\n", name, n, name, n); + + fprintf(fp, "static mrb_irep_debug_info %s_debug_%d = {\n", name, n); + fprintf(fp, "%d, %d, &%s_debug_file_%d_};\n", info->pc_count, info->flen, name, n); + + mrc_gc_arena_restore(c, ai); + return MRC_DUMP_OK; +} + +static int +cdump_irep_struct(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, FILE *fp, const char *name, int n, mrc_string *init_syms_code, int *mp) +{ + int i, len; + int max = *mp; + int debug_available = 0; + + /* dump reps */ + if (0 < irep->rlen) { + for (i=0,len=irep->rlen; ireps[i], flags, fp, name, max+i, init_syms_code, mp) != MRC_DUMP_OK) + return MRC_DUMP_INVALID_ARGUMENT; + } + fprintf(fp, "static const mrb_irep *%s_reps_%d[%d] = {\n", name, n, len); + for (i=0,len=irep->rlen; iplen) { + len=irep->plen; + fprintf(fp, "static const mrb_irep_pool %s_pool_%d[%d] = {\n", name, n, len); + for (i=0; ipool[i], fp) != MRC_DUMP_OK) + return MRC_DUMP_INVALID_ARGUMENT; + } + fputs("};\n", fp); + } + /* dump syms */ + if (0 < irep->slen) { + cdump_syms(c, name, "syms", n, irep->slen, irep->syms, init_syms_code, fp); + } + /* dump iseq */ + len=irep->ilen+sizeof(struct mrc_irep_catch_handler)*irep->clen; + fprintf(fp, "static const mrb_code %s_iseq_%d[%d] = {", name, n, len); + for (i=0; iiseq[i]); + } + fputs("};\n", fp); + /* dump lv */ + if (irep->lv) { + cdump_syms(c, name, "lv", n, irep->nlocals-1, irep->lv, init_syms_code, fp); + } + /* dump debug */ + if (flags & MRC_DUMP_DEBUG_INFO) { + if (cdump_debug(c, name, n, irep->debug_info, init_syms_code, fp) == MRC_DUMP_OK) { + debug_available = 1; + } + } + + + /* dump irep */ + fprintf(fp, "static const mrb_irep %s_irep_%d = {\n", name, n); + fprintf(fp, " %d,%d,%d,\n", irep->nlocals, irep->nregs, irep->clen); + fprintf(fp, " MRB_IREP_STATIC,%s_iseq_%d,\n", name, n); + if (0 < irep->plen) { + fprintf(fp, " %s_pool_%d,", name, n); + } + else { + fputs( " NULL,", fp); + } + if (0 < irep->slen) { + fprintf(fp, "%s_syms_%d,", name, n); + } + else { + fputs( "NULL,", fp); + } + if (0 < irep->rlen) { + fprintf(fp, "%s_reps_%d,\n", name, n); + } + else { + fputs( "NULL,\n", fp); + } + if (irep->lv) { + fprintf(fp, " %s_lv_%d,\n", name, n); + } + else { + fputs( " NULL,\t\t\t\t\t/* lv */\n", fp); + } + if (debug_available) { + fprintf(fp, " &%s_debug_%d,\n", name, n); + } + else { + fputs(" NULL,\t\t\t\t\t/* debug_info */\n", fp); + } + fprintf(fp, " %d,%d,%d,%d,0\n};\n", irep->ilen, irep->plen, irep->slen, irep->rlen); + + return MRC_DUMP_OK; +} + +int +mrc_dump_irep_cstruct(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, FILE *fp, const char *initname) +{ + if (fp == NULL || initname == NULL || initname[0] == '\0') { + return MRC_DUMP_INVALID_ARGUMENT; + } + if (fprintf(fp, "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n") < 0) { + return MRC_DUMP_WRITE_FAULT; + } + fputs("#define mrb_BRACED(...) {__VA_ARGS__}\n", fp); + fputs("#define mrb_DEFINE_SYMS_VAR(name, len, syms, qualifier) \\\n", fp); + fputs(" static qualifier mrb_sym name[len] = mrb_BRACED syms\n", fp); + fputs("\n", fp); + mrc_string *init_syms_code = mrc_str_new_capa(c, 1); + int max = 1; + int n = cdump_irep_struct(c, irep, flags, fp, initname, 0, init_syms_code, &max); + if (n != MRC_DUMP_OK) return n; + fprintf(fp, + "%s\n" + "const struct RProc %s[] = {{\n", + (flags & MRC_DUMP_STATIC) ? "static" + : "#ifdef __cplusplus\n" + "extern\n" + "#endif", + initname); + fprintf(fp, "NULL,NULL,MRB_TT_PROC,MRB_GC_RED,MRB_OBJ_IS_FROZEN,0,{&%s_irep_0},NULL,{NULL},\n}};\n", initname); + fputs("static void\n", fp); + fprintf(fp, "%s_init_syms(mrb_state *mrb)\n", initname); + fputs("{\n", fp); + fputs(MRC_STRING_PTR(init_syms_code), fp); + fputs("}\n", fp); + mrc_str_free(c, init_syms_code); + return MRC_DUMP_OK; +} +#endif /* MRC_NO_STDIO */ diff --git a/vendor/mruby-compiler2/src/codedump.c b/vendor/mruby-compiler2/src/codedump.c new file mode 100644 index 0000000..974919c --- /dev/null +++ b/vendor/mruby-compiler2/src/codedump.c @@ -0,0 +1,678 @@ +#include +#include "../include/mrc_ccontext.h" +#include "../include/mrc_irep.h" +#include "../include/mrc_dump.h" +#include "../include/mrc_opcode.h" +#include "../include/mrc_parser_util.h" +#include "../include/mrc_pool.h" +#include "../include/mrc_debug.h" +#include "../include/mrc_irep_pool_type.h" +#include + +const char * +mrc_sym_dump(mrc_ccontext *c, mrc_sym sym) +{ + mrc_int lenp; + const char *name = mrc_sym_name_len(c, sym, &lenp); + if (!name) { + return NULL; + } + if (strlen(name) == (size_t)lenp) { + return name; + } + else { + char *buf = (char*)mrc_pool_alloc(c->pool, lenp+1); + memcpy(buf, name, lenp); + buf[lenp] = '\0'; + return buf; + } +} + +static inline const struct mrc_irep_catch_handler * +mrc_irep_catch_handler_table(const mrc_irep *irep) +{ + if (irep->clen > 0) { + return (const struct mrc_irep_catch_handler*)(irep->iseq + irep->ilen); + } + else { + return (const struct mrc_irep_catch_handler*)NULL; + } +} + +#ifndef MRC_NO_STDIO +static void +print_r(mrc_ccontext *c, const mrc_irep *irep, size_t n, FILE *out) +{ + if (n == 0) return; + if (n >= irep->nlocals) return; + if (!irep->lv[n-1]) return; + fprintf(out, " R%d:%s", (int)n, mrc_sym_dump(c, irep->lv[n-1])); +} + +static void +print_lv_a(mrc_ccontext *c, const mrc_irep *irep, uint16_t a, FILE *out) +{ + if (!irep->lv || a >= irep->nlocals || a == 0) { + fprintf(out, "\n"); + return; + } + fprintf(out, "\t;"); + print_r(c, irep, a, out); + fprintf(out, "\n"); +} + +static void +print_lv_ab(mrc_ccontext *c, const mrc_irep *irep, uint16_t a, uint16_t b, FILE *out) +{ + if (!irep->lv || (a >= irep->nlocals && b >= irep->nlocals) || a+b == 0) { + fprintf(out, "\n"); + return; + } + fprintf(out, "\t;"); + if (a > 0) print_r(c, irep, a, out); + if (b > 0) print_r(c, irep, b, out); + fprintf(out, "\n"); +} + +static void +print_header(mrc_ccontext *c, const mrc_irep *irep, ptrdiff_t i, FILE *out) +{ + int32_t line; + + mrc_assert(i <= UINT32_MAX); + line = mrc_debug_get_line(c, irep, (uint32_t)i); + if (line < 0) { + fprintf(out, " "); + } + else { + fprintf(out, "%5d ", line); + } + + fprintf(out, "%03d ", (int)i); +} + +static void +print_args(uint16_t i, FILE *out) +{ + mrc_assert(i <= 255); + uint8_t n = i&0xf; + uint8_t nk = (i>>4)&0xf; + + if (n == 15) { + fprintf(out, "n=*"); + } + else { + fprintf(out, "n=%d", n); + } + if (nk > 0) { + fprintf(out, "|"); + if (nk == 15) { + fprintf(out, "nk=*"); + } + else { + fprintf(out, "nk=%d", nk); + } + } + fprintf(out, "\n"); +} + +#define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn + +static void +codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) +{ +// int ai; + const mrc_code *pc, *pcend; + mrc_code ins; + const char *file = NULL, *next_file; + + if (!irep) return; + fprintf(out, "irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d ilen=%d\n", (void*)irep, + irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen, (int)irep->ilen); + + if (irep->lv) { + int head = FALSE; + + for (int i = 1; i < irep->nlocals; i++) { + char const *s = mrc_sym_dump(c, irep->lv[i - 1]); + if (s) { + if (!head) { + head = TRUE; + fprintf(out, "local variable names:\n"); + } + fprintf(out, " R%d:%s\n", i, s); + } + } + } + + if (irep->clen > 0) { + const struct mrc_irep_catch_handler *e = mrc_irep_catch_handler_table(irep); + + for (int i = irep->clen; i > 0; i--,e++) { + uint32_t begin = mrc_irep_catch_handler_unpack(e->begin); + uint32_t end = mrc_irep_catch_handler_unpack(e->end); + uint32_t target = mrc_irep_catch_handler_unpack(e->target); + char buf[20]; + const char *type; + + switch (e->type) { + case MRC_CATCH_RESCUE: + type = "rescue"; + break; + case MRC_CATCH_ENSURE: + type = "ensure"; + break; + default: + buf[0] = '\0'; + snprintf(buf, sizeof(buf), "0x%02x ", (int)e->type); + type = buf; + break; + } + fprintf(out, "catch type: %-8s begin: %04" PRIu32 " end: %04" PRIu32 " target: %04" PRIu32 "\n", type, begin, end, target); + } + } + + pc = irep->iseq; + pcend = pc + irep->ilen; + while (pc < pcend) { + ptrdiff_t i; + uint32_t a; + uint16_t b; + uint16_t cc; + + int ai = mrc_gc_arena_save(c); + + i = pc - irep->iseq; + next_file = mrc_debug_get_filename(c, irep, (uint32_t)i); + if (next_file && file != next_file) { + fprintf(out, "file: %s\n", next_file); + file = next_file; + } + print_header(c, irep, i, out); + ins = READ_B(); + switch (ins) { + CASE(OP_NOP, Z): + fprintf(out, "NOP\n"); + break; + CASE(OP_MOVE, BB): + fprintf(out, "MOVE\t\tR%d\tR%d\t", a, b); + print_lv_ab(c, irep, a, b, out); + break; + + CASE(OP_LOADL, BB): + switch (irep->pool[b].tt) { +#ifndef MRC_NO_FLOAT + case IREP_TT_FLOAT: + fprintf(out, "LOADL\t\tR%d\tL[%d]\t; %f", a, b, (double)irep->pool[b].u.f); + break; +#endif + case IREP_TT_INT32: + fprintf(out, "LOADL\t\tR%d\tL[%d]\t; %" PRId32, a, b, irep->pool[b].u.i32); + break; +#ifdef MRC_64BIT + case IREP_TT_INT64: + fprintf(out, "LOADL\t\tR%d\tL[%d]\t; %" PRId64, a, b, irep->pool[b].u.i64); + break; +#endif + default: + fprintf(out, "LOADL\t\tR%d\tL[%d]\t", a, b); + break; + } + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADI, BB): + fprintf(out, "LOADI\t\tR%d\t%d\t", a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADINEG, BB): + fprintf(out, "LOADINEG\tR%d\t-%d\t", a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADI16, BS): + fprintf(out, "LOADI16\tR%d\t%d\t", a, (int)(int16_t)b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADI32, BSS): + fprintf(out, "LOADI32\tR%d\t%d\t", a, (int32_t)(((uint32_t)b<<16)+cc)); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADI__1, B): + fprintf(out, "LOADI__1\tR%d\t(-1)\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADI_0, B): goto L_LOADI; + CASE(OP_LOADI_1, B): goto L_LOADI; + CASE(OP_LOADI_2, B): goto L_LOADI; + CASE(OP_LOADI_3, B): goto L_LOADI; + CASE(OP_LOADI_4, B): goto L_LOADI; + CASE(OP_LOADI_5, B): goto L_LOADI; + CASE(OP_LOADI_6, B): goto L_LOADI; + CASE(OP_LOADI_7, B): + L_LOADI: + b = ins-(int)OP_LOADI_0; + fprintf(out, "LOADI_%d\tR%d\t(%d)\t", b, a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADSYM, BB): + fprintf(out, "LOADSYM\tR%d\t:%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADNIL, B): + fprintf(out, "LOADNIL\tR%d\t(nil)\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADSELF, B): + fprintf(out, "LOADSELF\tR%d\t(R0)\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADT, B): + fprintf(out, "LOADT\t\tR%d\t(true)\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LOADF, B): + fprintf(out, "LOADF\t\tR%d\t(false)\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_GETGV, BB): + fprintf(out, "GETGV\t\tR%d\t%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SETGV, BB): + fprintf(out, "SETGV\t\t%s\tR%d\t", mrc_sym_dump(c, irep->syms[b]), a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_GETSV, BB): + fprintf(out, "GETSV\t\tR%d\t%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SETSV, BB): + fprintf(out, "SETSV\t\t%s\tR%d\t", mrc_sym_dump(c, irep->syms[b]), a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_GETCONST, BB): + fprintf(out, "GETCONST\tR%d\t%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SETCONST, BB): + fprintf(out, "SETCONST\t%s\tR%d\t", mrc_sym_dump(c, irep->syms[b]), a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_GETMCNST, BB): + fprintf(out, "GETMCNST\tR%d\tR%d::%s\t", a, a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SETMCNST, BB): + fprintf(out, "SETMCNST\tR%d::%s\tR%d\t", a+1, mrc_sym_dump(c, irep->syms[b]), a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_GETIV, BB): + fprintf(out, "GETIV\t\tR%d\t%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SETIV, BB): + fprintf(out, "SETIV\t\t%s\tR%d\t", mrc_sym_dump(c, irep->syms[b]), a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_GETUPVAR, BBB): + fprintf(out, "GETUPVAR\tR%d\t%d\t%d\t", a, b, cc); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SETUPVAR, BBB): + fprintf(out, "SETUPVAR\tR%d\t%d\t%d\t", a, b, cc); + print_lv_a(c, irep, a, out); + break; + CASE(OP_GETCV, BB): + fprintf(out, "GETCV\t\tR%d\t%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SETCV, BB): + fprintf(out, "SETCV\t\t%s\tR%d\t", mrc_sym_dump(c, irep->syms[b]), a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_GETIDX, B): + fprintf(out, "GETIDX\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_SETIDX, B): + fprintf(out, "SETIDX\tR%d\tR%d\tR%d\n", a, a+1, a+2); + break; + CASE(OP_JMP, S): + i = pc - irep->iseq; + fprintf(out, "JMP\t\t%03d\n", (int)i+(int16_t)a); + break; + CASE(OP_JMPUW, S): + i = pc - irep->iseq; + fprintf(out, "JMPUW\t\t%03d\n", (int)i+(int16_t)a); + break; + CASE(OP_JMPIF, BS): + i = pc - irep->iseq; + fprintf(out, "JMPIF\t\tR%d\t%03d\t", a, (int)i+(int16_t)b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_JMPNOT, BS): + i = pc - irep->iseq; + fprintf(out, "JMPNOT\tR%d\t%03d\t", a, (int)i+(int16_t)b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_JMPNIL, BS): + i = pc - irep->iseq; + fprintf(out, "JMPNIL\tR%d\t%03d\t", a, (int)i+(int16_t)b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SSEND, BBB): + fprintf(out, "SSEND\t\tR%d\t:%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_args(cc, out); + break; + CASE(OP_SSENDB, BBB): + fprintf(out, "SSENDB\tR%d\t:%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_args(cc, out); + break; + CASE(OP_SEND, BBB): + fprintf(out, "SEND\t\tR%d\t:%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_args(cc, out); + break; + CASE(OP_SENDB, BBB): + fprintf(out, "SENDB\t\tR%d\t:%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_args(cc, out); + break; + CASE(OP_CALL, Z): + fprintf(out, "CALL\n"); + break; + CASE(OP_SUPER, BB): + fprintf(out, "SUPER\t\tR%d\t", a); + print_args(b, out); + break; + CASE(OP_ARGARY, BS): + fprintf(out, "ARGARY\tR%d\t%d:%d:%d:%d (%d)\t", a, + (b>>11)&0x3f, + (b>>10)&0x1, + (b>>5)&0x1f, + (b>>4)&0x1, + (b>>0)&0xf); + print_lv_a(c, irep, a, out); + break; + CASE(OP_ENTER, W): + fprintf(out, "ENTER\t\t%d:%d:%d:%d:%d:%d:%d (0x%x)\n", + MRC_ASPEC_REQ(a), + MRC_ASPEC_OPT(a), + MRC_ASPEC_REST(a), + MRC_ASPEC_POST(a), + MRC_ASPEC_KEY(a), + MRC_ASPEC_KDICT(a), + MRC_ASPEC_BLOCK(a), a); + break; + CASE(OP_KEY_P, BB): + fprintf(out, "KEY_P\t\tR%d\t:%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_KEYEND, Z): + fprintf(out, "KEYEND\n"); + break; + CASE(OP_KARG, BB): + fprintf(out, "KARG\t\tR%d\t:%s\t", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_RETURN, B): + fprintf(out, "RETURN\tR%d\t\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_RETURN_BLK, B): + fprintf(out, "RETURN_BLK\tR%d\t\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_BREAK, B): + fprintf(out, "BREAK\t\tR%d\t\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_BLKPUSH, BS): + fprintf(out, "BLKPUSH\tR%d\t%d:%d:%d:%d (%d)\t", a, + (b>>11)&0x3f, + (b>>10)&0x1, + (b>>5)&0x1f, + (b>>4)&0x1, + (b>>0)&0xf); + print_lv_a(c, irep, a, out); + break; + CASE(OP_LAMBDA, BB): + fprintf(out, "LAMBDA\tR%d\tI[%d]\n", a, b); + break; + CASE(OP_BLOCK, BB): + fprintf(out, "BLOCK\t\tR%d\tI[%d]\n", a, b); + break; + CASE(OP_METHOD, BB): + fprintf(out, "METHOD\tR%d\tI[%d]\n", a, b); + break; + CASE(OP_RANGE_INC, B): + fprintf(out, "RANGE_INC\tR%d\n", a); + break; + CASE(OP_RANGE_EXC, B): + fprintf(out, "RANGE_EXC\tR%d\n", a); + break; + CASE(OP_DEF, BB): + fprintf(out, "DEF\t\tR%d\t:%s\n", a, mrc_sym_dump(c, irep->syms[b])); + break; + CASE(OP_UNDEF, B): + fprintf(out, "UNDEF\t\t:%s\n", mrc_sym_dump(c, irep->syms[a])); + break; + CASE(OP_ALIAS, BB): + fprintf(out, "ALIAS\t\t:%s\t%s\n", mrc_sym_dump(c, irep->syms[a]), mrc_sym_dump(c, irep->syms[b])); + break; + CASE(OP_ADD, B): + fprintf(out, "ADD\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_ADDI, BB): + fprintf(out, "ADDI\t\tR%d\t%d\t", a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SUB, B): + fprintf(out, "SUB\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_SUBI, BB): + fprintf(out, "SUBI\t\tR%d\t%d\t", a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_MUL, B): + fprintf(out, "MUL\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_DIV, B): + fprintf(out, "DIV\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_LT, B): + fprintf(out, "LT\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_LE, B): + fprintf(out, "LE\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_GT, B): + fprintf(out, "GT\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_GE, B): + fprintf(out, "GE\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_EQ, B): + fprintf(out, "EQ\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_ARRAY, BB): + fprintf(out, "ARRAY\t\tR%d\tR%d\t%d", a, a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_ARRAY2, BBB): + fprintf(out, "ARRAY\t\tR%d\tR%d\t%d", a, b, cc); + print_lv_ab(c, irep, a, b, out); + break; + CASE(OP_ARYCAT, B): + fprintf(out, "ARYCAT\tR%d\tR%d\t", a, a+1); + print_lv_a(c, irep, a, out); + break; + CASE(OP_ARYPUSH, BB): + fprintf(out, "ARYPUSH\tR%d\t%d\t", a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_ARYSPLAT, B): + fprintf(out, "ARYSPLAT\tR%d\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_AREF, BBB): + fprintf(out, "AREF\t\tR%d\tR%d\t%d", a, b, cc); + print_lv_ab(c, irep, a, b, out); + break; + CASE(OP_ASET, BBB): + fprintf(out, "ASET\t\tR%d\tR%d\t%d", a, b, cc); + print_lv_ab(c, irep, a, b, out); + break; + CASE(OP_APOST, BBB): + fprintf(out, "APOST\t\tR%d\t%d\t%d", a, b, cc); + print_lv_a(c, irep, a, out); + break; + CASE(OP_INTERN, B): + fprintf(out, "INTERN\tR%d\t\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SYMBOL, BB): + mrc_assert((irep->pool[b].tt&IREP_TT_NFLAG)==0); + fprintf(out, "SYMBOL\tR%d\tL[%d]\t; %s", a, b, irep->pool[b].u.str); + print_lv_a(c, irep, a, out); + break; + CASE(OP_STRING, BB): + mrc_assert((irep->pool[b].tt&IREP_TT_NFLAG)==0); + fprintf(out, "STRING\tR%d\tL[%d]\t; %s", a, b, irep->pool[b].u.str); + print_lv_a(c, irep, a, out); + break; + CASE(OP_STRCAT, B): + fprintf(out, "STRCAT\tR%d\tR%d\t", a, a+1); + print_lv_a(c, irep, a, out); + break; + CASE(OP_HASH, BB): + fprintf(out, "HASH\t\tR%d\t%d\t", a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_HASHADD, BB): + fprintf(out, "HASHADD\tR%d\t%d\t", a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_HASHCAT, B): + fprintf(out, "HASHCAT\tR%d\tR%d\t", a, a+1); + print_lv_a(c, irep, a, out); + break; + + CASE(OP_OCLASS, B): + fprintf(out, "OCLASS\tR%d\t\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_CLASS, BB): + fprintf(out, "CLASS\t\tR%d\t:%s", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_MODULE, BB): + fprintf(out, "MODULE\tR%d\t:%s", a, mrc_sym_dump(c, irep->syms[b])); + print_lv_a(c, irep, a, out); + break; + CASE(OP_EXEC, BB): + fprintf(out, "EXEC\t\tR%d\tI[%d]", a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SCLASS, B): + fprintf(out, "SCLASS\tR%d\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_TCLASS, B): + fprintf(out, "TCLASS\tR%d\t\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_ERR, B): + if ((irep->pool[a].tt & IREP_TT_NFLAG) == 0) { + fprintf(out, "ERR\t\t%s\n", irep->pool[a].u.str); + } + else { + fprintf(out, "ERR\tL[%d]\n", a); + } + break; + CASE(OP_EXCEPT, B): + fprintf(out, "EXCEPT\tR%d\t\t", a); + print_lv_a(c, irep, a, out); + break; + CASE(OP_RESCUE, BB): + fprintf(out, "RESCUE\tR%d\tR%d", a, b); + print_lv_ab(c, irep, a, b, out); + break; + CASE(OP_RAISEIF, B): + fprintf(out, "RAISEIF\tR%d\t\t", a); + print_lv_a(c, irep, a, out); + break; + + CASE(OP_DEBUG, BBB): + fprintf(out, "DEBUG\t\t%d\t%d\t%d\n", a, b, cc); + break; + + CASE(OP_STOP, Z): + fprintf(out, "STOP\n"); + break; + + CASE(OP_EXT1, Z): + fprintf(out, "EXT1\n"); + print_header(c, irep, pc-irep->iseq, out); + ins = READ_B(); + switch (ins) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); goto L_OP_ ## i; +#include "mrc_ops.h" +#undef OPCODE + } + break; + CASE(OP_EXT2, Z): + fprintf(out, "EXT2\n"); + print_header(c, irep, pc-irep->iseq, out); + ins = READ_B(); + switch (ins) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); goto L_OP_ ## i; +#include "mrc_ops.h" +#undef OPCODE + } + break; + CASE(OP_EXT3, Z): + fprintf(out, "EXT3\n"); + print_header(c, irep, pc-irep->iseq, out); + ins = READ_B(); + switch (ins) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); goto L_OP_ ## i; +#include "mrc_ops.h" +#undef OPCODE + } + break; + + default: + fprintf(out, "unknown_op (0x%x)\n", ins); + break; + } + mrc_gc_arena_restore(c, ai); + } + fprintf(out, "\n"); +} + +static void +codedump_recur(mrc_ccontext *c, const mrc_irep *irep, FILE *out) +{ + codedump(c, irep, out); + if (irep->reps) { + for (int i=0; irlen; i++) { + codedump_recur(c, irep->reps[i], out); + } + } +} + +void +mrc_codedump_all_file(mrc_ccontext *c, mrc_irep *irep, FILE *out) +{ + c->pool = mrc_pool_open(c); + codedump_recur(c, irep, out); + fflush(out); + mrc_pool_close(c->pool); +} + +#endif // MRC_NO_STDIO + +void +mrc_codedump_all(mrc_ccontext *c, mrc_irep *irep) +{ +#ifndef MRC_NO_STDIO + mrc_codedump_all_file(c, irep, stdout); +#endif +} diff --git a/vendor/mruby-compiler2/src/codegen.c b/vendor/mruby-compiler2/src/codegen.c new file mode 100644 index 0000000..54e4aa2 --- /dev/null +++ b/vendor/mruby-compiler2/src/codegen.c @@ -0,0 +1,1776 @@ + +#include +#include "../include/mrc_irep.h" +#include "../include/mrc_ccontext.h" +#include "../include/mrc_parser_util.h" +#include "../include/mrc_throw.h" +#include "../include/mrc_opcode.h" +#include "../include/mrc_presym.h" +#include "../include/mrc_pool.h" +#include "../include/mrc_dump.h" +#include "../include/mrc_debug.h" +#include "../include/mrc_irep_pool_type.h" + +#if defined(PICORB_VM_MRUBY) +#include "../include/mrc_proc.h" +#endif + +#ifdef MRBC_REQUIRE_32BIT_ALIGNMENT +#include +#define printf(...) console_printf(__VA_ARGS__) +#else +#define printf(n) ((void)0) +#endif + +#if defined(MRC_INT64) +# define MRC_INT_BIT 64 +# define MRC_INT_MIN INT64_MIN +# define MRC_INT_MAX INT64_MAX +# define MRC_PRIo PRIo64 +# define MRC_PRId PRId64 +# define MRC_PRIx PRIx64 +#else + typedef int32_t mrc_int; + typedef uint32_t mrc_uint; +# define MRC_INT_BIT 32 +# define MRC_INT_MIN INT32_MIN +# define MRC_INT_MAX INT32_MAX +# define MRC_PRIo PRIo32 +# define MRC_PRId PRId32 +# define MRC_PRIx PRIx32 +#endif + +/** + * Function requires n arguments. + * + * @param n + * The number of required arguments. + */ +#define MRC_ARGS_REQ(n) ((mrc_aspec)((n)&0x1f) << 18) + +/** + * Function takes n optional arguments + * + * @param n + * The number of optional arguments. + */ +#define MRC_ARGS_OPT(n) ((mrc_aspec)((n)&0x1f) << 13) + +/** + * Function takes n1 mandatory arguments and n2 optional arguments + * + * @param n1 + * The number of required arguments. + * @param n2 + * The number of optional arguments. + */ +#define MRC_ARGS_ARG(n1,n2) (MRC_ARGS_REQ(n1)|MRC_ARGS_OPT(n2)) + +/** rest argument */ +#define MRC_ARGS_REST() ((mrc_aspec)(1 << 12)) + +/** required arguments after rest */ +#define MRC_ARGS_POST(n) ((mrc_aspec)((n)&0x1f) << 7) + +/** keyword arguments (n of keys, kdict) */ +#define MRC_ARGS_KEY(n1,n2) ((mrc_aspec)((((n1)&0x1f) << 2) | ((n2)?(1<<1):0))) + +/** + * Function takes a block argument + */ +#define MRC_ARGS_BLOCK() ((mrc_aspec)1) + +/** + * Function accepts any number of arguments + */ +#define MRC_ARGS_ANY() MRC_ARGS_REST() + +/** + * Function accepts no arguments + */ +#define MRC_ARGS_NONE() ((mrc_aspec)0) + + +#define MRC_INT_OVERFLOW_MASK ((mrc_uint)1 << (MRC_INT_BIT - 1)) + +static inline mrc_bool +mrc_int_add_overflow(mrc_int a, mrc_int b, mrc_int *c) +{ + mrc_uint x = (mrc_uint)a; + mrc_uint y = (mrc_uint)b; + mrc_uint z = (mrc_uint)(x + y); + *c = (mrc_int)z; + return !!(((x ^ z) & (y ^ z)) & MRC_INT_OVERFLOW_MASK); +} + +static inline mrc_bool +mrc_int_sub_overflow(mrc_int a, mrc_int b, mrc_int *c) +{ + mrc_uint x = (mrc_uint)a; + mrc_uint y = (mrc_uint)b; + mrc_uint z = (mrc_uint)(x - y); + *c = (mrc_int)z; + return !!(((x ^ z) & (~y ^ z)) & MRC_INT_OVERFLOW_MASK); +} + +static inline mrc_bool +mrc_int_mul_overflow(mrc_int a, mrc_int b, mrc_int *c) +{ +#ifdef MRC_INT32 + int64_t n = (int64_t)a * b; + *c = (mrc_int)n; + return n > MRC_INT_MAX || n < MRC_INT_MIN; +#else /* MRC_INT64 */ + if (a > 0 && b > 0 && a > MRC_INT_MAX / b) return TRUE; + if (a < 0 && b > 0 && a < MRC_INT_MIN / b) return TRUE; + if (a > 0 && b < 0 && b < MRC_INT_MIN / a) return TRUE; + if (a < 0 && b < 0 && (a <= MRC_INT_MIN || b <= MRC_INT_MIN || -a > MRC_INT_MAX / -b)) + return TRUE; + *c = a * b; + return FALSE; +#endif +} + +static mrc_int +mrc_div_int(mrc_int x, mrc_int y) +{ + mrc_int div = x / y; + + if ((x ^ y) < 0 && x != div * y) { + div -= 1; + } + return div; +} + +#define NUMERIC_SHIFT_WIDTH_MAX (MRC_INT_BIT-1) + +static mrc_bool +mrc_num_shift(mrc_int val, mrc_int width, mrc_int *num) +{ + if (width < 0) { /* rshift */ + if (width == MRC_INT_MIN || -width >= NUMERIC_SHIFT_WIDTH_MAX) { + if (val < 0) { + *num = -1; + } + else { + *num = 0; + } + } + else { + *num = val >> -width; + } + } + else if (val > 0) { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val > (MRC_INT_MAX >> width))) { + return FALSE; + } + *num = val << width; + } + else { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val < (MRC_INT_MIN >> width))) { + return FALSE; + } + if (width == NUMERIC_SHIFT_WIDTH_MAX) + *num = MRC_INT_MIN; + else + *num = val * ((mrc_int)1 << width); + } + return TRUE; +} + +#ifdef MRC_ENDIAN_BIG +# define MRC_ENDIAN_LOHI(a,b) a b +#else +# define MRC_ENDIAN_LOHI(a,b) b a +#endif +#ifndef MRC_CODEGEN_LEVEL_MAX +#define MRC_CODEGEN_LEVEL_MAX 256 +#endif + +enum looptype { + LOOP_NORMAL, + LOOP_BLOCK, + LOOP_FOR, + LOOP_BEGIN, + LOOP_RESCUE, +}; + +struct loopinfo { + enum looptype type; + uint32_t pc0; /* `next` destination */ + uint32_t pc1; /* `redo` destination */ + uint32_t pc2; /* `break` destination */ + int reg; /* destination register */ + struct loopinfo *prev; +}; + +typedef struct scope { + mrc_pool *mpool; // -> *page + + struct scope *prev; + + mrc_constant_id_list *lv; + + uint16_t sp; + uint32_t pc; + uint32_t lastpc; + uint32_t lastlabel; + uint16_t ainfo:15; + mrc_bool mscope:1; + + struct loopinfo *loop; + //mrc_sym filename_sym; + const char *filename; + uint16_t lineno; + + mrc_code *iseq; + uint16_t *lines; + uint32_t icapa; + + mrc_irep *irep; + mrc_pool_value *pool; + mrc_sym *syms; + mrc_irep **reps; + struct mrc_irep_catch_handler *catch_table; + uint32_t pcapa, scapa, rcapa; + + uint16_t nlocals; + uint16_t nregs; + int ai; + + int debug_start_pos; + uint16_t filename_index; + mrc_ccontext* c; + + int rlev; /* recursion levels */ +} mrc_codegen_scope; + +static void codegen(mrc_codegen_scope *s, mrc_node *tree, int val); + +static void +codegen_error(mrc_codegen_scope *s, const char *message) +{ + if (!s) return; + s->c->capture_errors = TRUE; + + mrc_diagnostic_list_append(s->c, 0, message, MRC_GENERATOR_ERROR); + +#ifndef MRC_NO_STDIO + if (s->filename && s->lineno) { + const char *filename = (const char *)s->filename; + fprintf(stderr, "%s:%d: %s\n", filename, s->lineno, message); + } + else { + fprintf(stderr, "%s\n", message); + } + +#endif + while (s->prev) { + mrc_codegen_scope *tmp = s->prev; + if (s->irep) { + mrc_free(s->c, s->iseq); + for (int i=0; iirep->plen; i++) { + mrc_pool_value *pv = &s->pool[i]; + if ((pv->tt & 0x3) == IREP_TT_STR || pv->tt == IREP_TT_BIGINT) { + mrc_free(s->c, (void*)pv->u.str); + } + } + mrc_free(s->c, s->pool); + mrc_free(s->c, s->syms); + mrc_free(s->c, s->catch_table); + if (s->reps) { + /* copied from mrc_irep_free() in state.c */ + //for (int i=0; iirep->rlen; i++) { + // if (s->reps[i]) + // mrc_irep_decref(s->mrb, (mrc_irep*)s->reps[i]); + //} + mrc_free(s->c, s->reps); + } + mrc_free(s->c, s->lines); + } + mrc_pool_close(s->mpool); + s = tmp; + } + MRC_THROW(s->c->jmp); +} + +static void* +codegen_palloc(mrc_codegen_scope *s, size_t len) +{ + void *p = mrc_pool_alloc(s->mpool, len); + + if (!p) codegen_error(s, "pool memory allocation"); + return p; +} + +static void* +codegen_realloc(mrc_codegen_scope *s, void *p, size_t oldlen, size_t newlen) +{ + p = mrc_pool_realloc(s->mpool, p, oldlen, newlen); + + if (!p && 0 < newlen) codegen_error(s, "pool memory reallocation"); + return p; +} + +static void +check_no_ext_ops(mrc_codegen_scope *s, uint16_t a, uint16_t b) +{ + if (s->c->no_ext_ops && (a | b) > 0xff) { + codegen_error(s, "need OP_EXTs instruction (currently OP_EXTs are prohibited)"); + } +} + +static int +new_label(mrc_codegen_scope *s) +{ + return s->lastlabel = s->pc; +} + +static void +emit_B(mrc_codegen_scope *s, uint32_t pc, uint8_t i) +{ + if (pc >= s->icapa) { + if (pc == UINT32_MAX) { + codegen_error(s, "too big code block"); + } + if (pc >= UINT32_MAX / 2) { + pc = UINT32_MAX; + } + else { + s->icapa *= 2; + } + s->iseq = (mrc_code*)mrc_realloc(s->c, s->iseq, sizeof(mrc_code)*s->icapa); + if (s->lines) { + s->lines = (uint16_t*)mrc_realloc(s->c, s->lines, sizeof(uint16_t)*s->icapa); + } + } + if (s->lines) { + if (s->lineno > 0 || pc == 0) + s->lines[pc] = s->lineno; + else + s->lines[pc] = s->lines[pc-1]; + } + s->iseq[pc] = i; +} + +static void +emit_S(mrc_codegen_scope *s, int pc, uint16_t i) +{ + uint8_t hi = i>>8; + uint8_t lo = i&0xff; + + emit_B(s, pc, hi); + emit_B(s, pc+1, lo); +} + +static void +gen_B(mrc_codegen_scope *s, uint8_t i) +{ + emit_B(s, s->pc, i); + s->pc++; +} + +static void +gen_S(mrc_codegen_scope *s, uint16_t i) +{ + emit_S(s, s->pc, i); + s->pc += 2; +} + +static void +genop_0(mrc_codegen_scope *s, mrc_code i) +{ + s->lastpc = s->pc; + gen_B(s, i); +} + +static void +genop_1(mrc_codegen_scope *s, mrc_code i, uint16_t a) +{ + s->lastpc = s->pc; + check_no_ext_ops(s, a, 0); + if (a > 0xff) { + gen_B(s, OP_EXT1); + gen_B(s, i); + gen_S(s, a); + } + else { + gen_B(s, i); + gen_B(s, (uint8_t)a); + } +} + +static void +genop_2(mrc_codegen_scope *s, mrc_code i, uint16_t a, uint16_t b) +{ + s->lastpc = s->pc; + check_no_ext_ops(s, a, b); + if (a > 0xff && b > 0xff) { + gen_B(s, OP_EXT3); + gen_B(s, i); + gen_S(s, a); + gen_S(s, b); + } + else if (b > 0xff) { + gen_B(s, OP_EXT2); + gen_B(s, i); + gen_B(s, (uint8_t)a); + gen_S(s, b); + } + else if (a > 0xff) { + gen_B(s, OP_EXT1); + gen_B(s, i); + gen_S(s, a); + gen_B(s, (uint8_t)b); + } + else { + gen_B(s, i); + gen_B(s, (uint8_t)a); + gen_B(s, (uint8_t)b); + } +} + +static void +genop_3(mrc_codegen_scope *s, mrc_code i, uint16_t a, uint16_t b, uint16_t c) +{ + genop_2(s, i, a, b); + gen_B(s, (uint8_t)c); +} + +static void +genop_2S(mrc_codegen_scope *s, mrc_code i, uint16_t a, uint16_t b) +{ + genop_1(s, i, a); + gen_S(s, b); +} + +static void +genop_2SS(mrc_codegen_scope *s, mrc_code i, uint16_t a, uint32_t b) +{ + genop_1(s, i, a); + gen_S(s, b>>16); + gen_S(s, b&0xffff); +} + +static void +genop_W(mrc_codegen_scope *s, mrc_code i, uint32_t a) +{ + uint8_t a1 = (a>>16) & 0xff; + uint8_t a2 = (a>>8) & 0xff; + uint8_t a3 = a & 0xff; + + s->lastpc = s->pc; + gen_B(s, i); + gen_B(s, a1); + gen_B(s, a2); + gen_B(s, a3); +} + +#define NOVAL 0 +#define VAL 1 + +#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0) +static void +push_n_(mrc_codegen_scope *s, int n) +{ + if (s->sp+n >= 0xffff) { + codegen_error(s, "too complex expression"); + } + s->sp+=n; + nregs_update; +} + +static void +pop_n_(mrc_codegen_scope *s, int n) +{ + if ((int)s->sp-n < 0) { + codegen_error(s, "stack pointer underflow"); + } + s->sp-=n; +} + +#define push() push_n_(s,1) +#define push_n(n) push_n_(s,n) +#define pop() pop_n_(s,1) +#define pop_n(n) pop_n_(s,n) +#define cursp() (s->sp) + +static mrc_irep* +mrc_add_irep(mrc_ccontext *c) +{ + static const mrc_irep mrc_irep_zero = { 0 }; + mrc_irep *irep = (mrc_irep *)mrc_malloc(c, sizeof(mrc_irep)); + *irep = mrc_irep_zero; + irep->refcnt = 1; + return irep; +} + +static void +scope_add_irep(mrc_codegen_scope *s) +{ + mrc_irep *irep; + mrc_codegen_scope *prev = s->prev; + if (prev->irep == NULL) { + irep = mrc_add_irep(s->c); + prev->irep = s->irep = irep; + return; + } + else { + if (prev->irep->rlen == UINT16_MAX) { + codegen_error(s, "too many nested blocks/methods"); + } + s->irep = irep = mrc_add_irep(s->c); + if (prev->irep->rlen == prev->rcapa) { + prev->rcapa *= 2; + prev->reps = (mrc_irep **)mrc_realloc(s->c, prev->reps, sizeof(mrc_irep *)*prev->rcapa); + } + prev->reps[prev->irep->rlen++] = irep; + } +} + +static mrc_codegen_scope * +scope_new(mrc_ccontext *c, mrc_codegen_scope *prev, mrc_constant_id_list *nlv) +{ + static const mrc_codegen_scope codegen_scope_zero = { 0 }; + mrc_pool *pool = mrc_pool_open(c); + mrc_codegen_scope *s = (mrc_codegen_scope *)mrc_pool_alloc(pool, sizeof(mrc_codegen_scope)); + if (!s) { + if (prev) + codegen_error(prev, "unexpected scope"); + return NULL; + } + *s = codegen_scope_zero; + if (prev) { + s->c = prev->c; + } else { + s->c = c; + } + s->mpool = pool; + if (!prev) return s; + s->prev = prev; + s->ainfo = 0; + s->mscope = 0; + + scope_add_irep(s); + + s->rcapa = 8; + s->reps = (mrc_irep **)mrc_malloc(c, sizeof(mrc_irep *)*s->rcapa); + s->icapa = 1024; + s->iseq = (mrc_code *)mrc_malloc(c, sizeof(mrc_code)*s->icapa); + s->pcapa = 32; + s->pool = (mrc_pool_value *)mrc_malloc(c, sizeof(mrc_pool_value)*s->pcapa); + s->scapa = 256; + s->syms = (mrc_sym *)mrc_malloc(c, sizeof(mrc_sym)*s->scapa); + assert(nlv != NULL); // `if (!prev) return s;` prevents this from being NULL + s->lv = nlv; + + s->sp += nlv->size + 1; // add self + s->nlocals = s->nregs = s->sp; + if (nlv) { + mrc_sym *lv; + size_t size = sizeof(mrc_sym) * nlv->size; + if (0 < size) { + s->irep->lv = lv = (mrc_sym *)mrc_malloc(c, sizeof(mrc_sym) * (s->nlocals - 1)); + memcpy(lv, nlv->ids, size); + } + else { + s->irep->lv = lv = NULL; + } + mrc_assert(nlv->size < UINT16_MAX); + } + + int ai = mrc_gc_arena_save(c); + s->ai = ai; + s->filename = prev->filename; + if (s->filename) { + s->lines = (uint16_t *)mrc_malloc(c, sizeof(uint16_t)*s->icapa); + } + s->lineno = prev->lineno; + + /* degug info */ + s->debug_start_pos = 0; + if (s->filename) { + mrc_debug_info_alloc(c, s->irep); + } + else { + s->irep->debug_info = NULL; + } + s->c = prev->c; + s->filename_index = prev->filename_index; + s->rlev = prev->rlev + 1; + return s; +} + +static mrc_bool +no_optimize(mrc_codegen_scope *s) +{ + if (s && s->c && s->c->no_optimize) + return TRUE; + return FALSE; +} + +struct mrc_insn_data +mrc_decode_insn(const mrc_code *pc) +{ + struct mrc_insn_data data = { 0 }; + if (pc == 0) return data; + data.addr = pc; + mrc_code insn = READ_B(); + uint16_t a = 0; + uint16_t b = 0; + uint16_t cc = 0; + + switch (insn) { +#define FETCH_Z() /* empty */ +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x (); break; +#include "mrc_ops.h" +#undef OPCODE + } + switch (insn) { + case OP_EXT1: + insn = READ_B(); + switch (insn) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); break; +#include "mrc_ops.h" +#undef OPCODE + } + break; + case OP_EXT2: + insn = READ_B(); + switch (insn) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); break; +#include "mrc_ops.h" +#undef OPCODE + } + break; + case OP_EXT3: + insn = READ_B(); + switch (insn) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); break; +#include "mrc_ops.h" +#undef OPCODE + } + break; + default: + break; + } + data.insn = insn; + data.a = a; + data.b = b; + data.cc = cc; + return data; +} + +#undef OPCODE +#define Z 1 +#define S 3 +#define W 4 +#define OPCODE(_,x) x, +/* instruction sizes */ +static uint8_t mrc_insn_size[] = { +#define B 2 +#define BB 3 +#define BBB 4 +#define BS 4 +#define BSS 6 +#include "mrc_ops.h" +#undef B +#undef BB +#undef BBB +#undef BS +#undef BSS +}; +/* EXT1 instruction sizes */ +static uint8_t mrc_insn_size1[] = { +#define B 3 +#define BB 4 +#define BBB 5 +#define BS 5 +#define BSS 7 +#include "mrc_ops.h" +#undef B +#undef BS +#undef BSS +}; +/* EXT2 instruction sizes */ +static uint8_t mrc_insn_size2[] = { +#define B 2 +#define BS 4 +#define BSS 6 +#include "mrc_ops.h" +#undef B +#undef BB +#undef BBB +#undef BS +#undef BSS +}; +/* EXT3 instruction sizes */ +#define B 3 +#define BB 5 +#define BBB 6 +#define BS 5 +#define BSS 7 +static uint8_t mrc_insn_size3[] = { +#include "mrc_ops.h" +}; +#undef B +#undef BB +#undef BBB +#undef BS +#undef BSS +#undef OPCODE + +static const mrc_code* +mrc_prev_pc(mrc_codegen_scope *s, const mrc_code *pc) +{ + const mrc_code *prev_pc = NULL; + const mrc_code *i = s->iseq; + + while (iiseq[(s)->pc]) +#define addr_pc(s, addr) (uint32_t)((addr) - s->iseq) +#define rewind_pc(s) s->pc = s->lastpc + +static struct mrc_insn_data +mrc_last_insn(mrc_codegen_scope *s) +{ + if (s->pc == 0) { + struct mrc_insn_data data = { OP_NOP, 0, 0, 0, NULL }; + return data; + } + return mrc_decode_insn(&s->iseq[s->lastpc]); +} + +static mrc_bool +get_int_operand(mrc_codegen_scope *s, struct mrc_insn_data *data, mrc_int *n) +{ + switch (data->insn) { + case OP_LOADI__1: + *n = -1; + return TRUE; + + case OP_LOADINEG: + *n = -data->b; + return TRUE; + + case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3: + case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7: + *n = data->insn - OP_LOADI_0; + return TRUE; + + case OP_LOADI: + case OP_LOADI16: + *n = (int16_t)data->b; + return TRUE; + + case OP_LOADI32: + *n = (int32_t)((uint32_t)data->b<<16)+data->cc; + return TRUE; + + case OP_LOADL: + { + mrc_pool_value *pv = &s->pool[data->b]; + + if (pv->tt == IREP_TT_INT32) { + *n = (mrc_int)pv->u.i32; + } +#ifdef MRC_INT64 + else if (pv->tt == IREP_TT_INT64) { + *n = (mrc_int)pv->u.i64; + } +#endif + else { + return FALSE; + } + } + return TRUE; + + default: + return FALSE; + } +} + +static mrc_bool +no_peephole(mrc_codegen_scope *s) +{ + return no_optimize(s) || s->lastlabel == s->pc || s->pc == 0 || s->pc == s->lastpc; +} + +#define JMPLINK_START UINT32_MAX + +static void +gen_jmpdst(mrc_codegen_scope *s, uint32_t pc) +{ + + if (pc == JMPLINK_START) { + pc = 0; + } + uint32_t pos2 = s->pc+2; + int32_t off = pc - pos2; + + if (off > INT16_MAX || INT16_MIN > off) { + codegen_error(s, "too big jump offset"); + } + gen_S(s, (uint16_t)off); +} + +static uint32_t +genjmp(mrc_codegen_scope *s, mrc_code i, uint32_t pc) +{ + uint32_t pos; + + genop_0(s, i); + pos = s->pc; + gen_jmpdst(s, pc); + return pos; +} + +#define genjmp_0(s,i) genjmp(s,i,JMPLINK_START) + +static uint32_t +genjmp2(mrc_codegen_scope *s, mrc_code i, uint16_t a, uint32_t pc, int val) +{ + uint32_t pos; + + if (!no_peephole(s) && !val) { + struct mrc_insn_data data = mrc_last_insn(s); + + switch (data.insn) { + case OP_MOVE: + if (data.a == a && data.a > s->nlocals) { + rewind_pc(s); + a = data.b; + } + break; + case OP_LOADNIL: + case OP_LOADF: + if (data.a == a || data.a > s->nlocals) { + s->pc = addr_pc(s, data.addr); + if (i == OP_JMPNOT || (i == OP_JMPNIL && data.insn == OP_LOADNIL)) { + return genjmp(s, OP_JMP, pc); + } + else { /* OP_JMPIF */ + return JMPLINK_START; + } + } + break; + case OP_LOADT: case OP_LOADI: case OP_LOADINEG: case OP_LOADI__1: + case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3: + case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7: + if (data.a == a || data.a > s->nlocals) { + s->pc = addr_pc(s, data.addr); + if (i == OP_JMPIF) { + return genjmp(s, OP_JMP, pc); + } + else { /* OP_JMPNOT and OP_JMPNIL */ + return JMPLINK_START; + } + } + break; + } + } + + if (a > 0xff) { + check_no_ext_ops(s, a, 0); + gen_B(s, OP_EXT1); + genop_0(s, i); + gen_S(s, a); + } + else { + genop_0(s, i); + gen_B(s, (uint8_t)a); + } + pos = s->pc; + gen_jmpdst(s, pc); + return pos; +} + +#define genjmp2_0(s,i,a,val) genjmp2(s,i,a,JMPLINK_START,val) + +static int new_lit_int(mrc_codegen_scope *s, mrc_int num); + +static void +gen_int(mrc_codegen_scope *s, uint16_t dst, mrc_int i) +{ + if (i < 0) { + if (i == -1) genop_1(s, OP_LOADI__1, dst); + else if (i >= -0xff) genop_2(s, OP_LOADINEG, dst, (uint16_t)-i); + else if (i >= INT16_MIN) genop_2S(s, OP_LOADI16, dst, (uint16_t)i); + else if (i >= INT32_MIN) genop_2SS(s, OP_LOADI32, dst, (uint32_t)i); + else goto int_lit; + } + else if (i < 8) genop_1(s, OP_LOADI_0 + (uint8_t)i, dst); + else if (i <= 0xff) genop_2(s, OP_LOADI, dst, (uint16_t)i); + else if (i <= INT16_MAX) genop_2S(s, OP_LOADI16, dst, (uint16_t)i); + else if (i <= INT32_MAX) genop_2SS(s, OP_LOADI32, dst, (uint32_t)i); + else { + int_lit: + genop_2(s, OP_LOADL, dst, new_lit_int(s, i)); + } +} + +static void +gen_move(mrc_codegen_scope *s, uint16_t dst, uint16_t src, int nopeep) +{ + if (nopeep || no_peephole(s)) goto normal; + else if (dst == src) return; + else { + struct mrc_insn_data data = mrc_last_insn(s); + + switch (data.insn) { + case OP_MOVE: + if (dst == src) return; /* remove useless MOVE */ + if (data.a == src) { + if (data.b == dst) /* skip swapping MOVE */ + return; + if (data.a < s->nlocals) goto normal; + rewind_pc(s); + s->lastpc = addr_pc(s, mrc_prev_pc(s, data.addr)); + gen_move(s, dst, data.b, FALSE); + return; + } + if (dst == data.a) { /* skip overwritten move */ + rewind_pc(s); + s->lastpc = addr_pc(s, mrc_prev_pc(s, data.addr)); + gen_move(s, dst, src, FALSE); + return; + } + goto normal; + case OP_LOADNIL: case OP_LOADSELF: case OP_LOADT: case OP_LOADF: + case OP_LOADI__1: + case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3: + case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7: + if (data.a != src || data.a < s->nlocals) goto normal; + rewind_pc(s); + genop_1(s, data.insn, dst); + return; + case OP_HASH: + if (data.b != 0) goto normal; + /* fall through */ + case OP_LOADI: case OP_LOADINEG: + case OP_LOADL: case OP_LOADSYM: + case OP_GETGV: case OP_GETSV: case OP_GETIV: case OP_GETCV: + case OP_GETCONST: case OP_STRING: + case OP_LAMBDA: case OP_BLOCK: case OP_METHOD: case OP_BLKPUSH: + if (data.a != src || data.a < s->nlocals) goto normal; + rewind_pc(s); + genop_2(s, data.insn, dst, data.b); + return; + case OP_LOADI16: + if (data.a != src || data.a < s->nlocals) goto normal; + rewind_pc(s); + genop_2S(s, data.insn, dst, data.b); + return; + case OP_LOADI32: + if (data.a != src || data.a < s->nlocals) goto normal; + else { + uint32_t i = (uint32_t)data.b<<16|data.cc; + rewind_pc(s); + genop_2SS(s, data.insn, dst, i); + } + return; + case OP_ARRAY: + if (data.a != src || data.a < s->nlocals || data.a < dst) goto normal; + rewind_pc(s); + if (data.b == 0 || dst == data.a) + genop_2(s, OP_ARRAY, dst, 0); + else + genop_3(s, OP_ARRAY2, dst, data.a, data.b); + return; + case OP_ARRAY2: + if (data.a != src || data.a < s->nlocals || data.a < dst) goto normal; + rewind_pc(s); + genop_3(s, OP_ARRAY2, dst, data.b, data.cc); + return; + case OP_AREF: + case OP_GETUPVAR: + if (data.a != src || data.a < s->nlocals) goto normal; + rewind_pc(s); + genop_3(s, data.insn, dst, data.b, data.cc); + return; + case OP_ADDI: case OP_SUBI: + if (addr_pc(s, data.addr) == s->lastlabel || data.a != src || data.a < s->nlocals) goto normal; + else { + struct mrc_insn_data data0 = mrc_decode_insn(mrc_prev_pc(s, data.addr)); + if (data0.insn != OP_MOVE || data0.a != data.a || data0.b != dst) goto normal; + s->pc = addr_pc(s, data0.addr); + if (addr_pc(s, data0.addr) != s->lastlabel) { + /* constant folding */ + data0 = mrc_decode_insn(mrc_prev_pc(s, data0.addr)); + mrc_int n; + if (data0.a == dst && get_int_operand(s, &data0, &n)) { + if ((data.insn == OP_ADDI && !mrc_int_add_overflow(n, data.b, &n)) || + (data.insn == OP_SUBI && !mrc_int_sub_overflow(n, data.b, &n))) { + s->pc = addr_pc(s, data0.addr); + gen_int(s, dst, n); + return; + } + } + } + } + genop_2(s, data.insn, dst, data.b); + return; + default: + break; + } + } + normal: + genop_2(s, OP_MOVE, dst, src); + return; +} + +static int +lv_idx(mrc_codegen_scope *s, mrc_sym id) +{ + if (!s->lv) return 0; + for (size_t n = 0; n < s->lv->size; n++) { + if (s->lv->ids[n] == id) return n + 1; + } + return 0; +} + + +#define MRC_PROC_CFUNC_FL 128 +#define MRC_PROC_CFUNC_P(p) (((p)->flags & MRC_PROC_CFUNC_FL) != 0) +#define MRC_PROC_SCOPE 2048 +#define MRC_PROC_SCOPE_P(p) (((p)->flags & MRC_PROC_SCOPE) != 0) + +static int +search_upvar(mrc_codegen_scope *s, mrc_sym id, int *idx) +{ + int lv = 0; + mrc_codegen_scope *up = s->prev; + + while (up) { + *idx = lv_idx(up, id); + if (*idx > 0) { + return lv; + } + lv++; + up = up->prev; + } + +#if defined(PICORB_VM_MRUBY) + const struct RProc *u; + + if (lv < 1) lv = 1; + u = s->c->upper; + pm_constant_t *constant = pm_constant_pool_id_to_constant(&s->c->p->constant_pool, id); + mrc_sym intern = mrb_intern(s->c->mrb, (const char *)constant->start, constant->length); + if (0 < intern) { + while (u && !MRC_PROC_CFUNC_P(u)) { + const struct mrc_irep *ir = u->body.irep; + uint_fast16_t n = ir->nlocals; + int i; + const mrc_sym *v = ir->lv; + if (v) { + for (i=1; n > 1; n--, v++, i++) { + if (*v == intern) { + *idx = i; + return lv - 1; + } + } + } + if (MRC_PROC_SCOPE_P(u)) break; + u = u->upper; + lv++; + } + } +#endif + + if (id == MRC_OPSYM_2(and)) { + codegen_error(s, "No anonymous block parameter"); + } + else if (id == MRC_OPSYM_2(mul)) { + codegen_error(s, "No anonymous rest parameter"); + } + else if (id == MRC_OPSYM_2(pow)) { + codegen_error(s, "No anonymous keyword rest parameter"); + } + else { + codegen_error(s, "Can't find local variables"); + } + return -1; /* not reached */ +} + +static void +gen_getupvar(mrc_codegen_scope *s, uint16_t dst, mrc_sym id, int depth) +{ + int idx; + int lv = search_upvar(s, id, &idx); + + mrc_assert(lv == depth-1); + + if (!no_peephole(s)) { + struct mrc_insn_data data = mrc_last_insn(s); + if (data.insn == OP_SETUPVAR && data.a == dst && data.b == idx && data.cc == lv) { + /* skip GETUPVAR right after SETUPVAR */ + return; + } + } + genop_3(s, OP_GETUPVAR, dst, idx, lv); +} + +static void +gen_setupvar(mrc_codegen_scope *s, uint16_t dst, mrc_sym id, int depth) +{ + int idx; + int lv = search_upvar(s, id, &idx); + + mrc_assert(lv == depth-1); + + if (!no_peephole(s)) { + struct mrc_insn_data data = mrc_last_insn(s); + if (data.insn == OP_MOVE && data.a == dst) { + dst = data.b; + rewind_pc(s); + } + } + genop_3(s, OP_SETUPVAR, dst, idx, lv); +} + +static void +gen_return(mrc_codegen_scope *s, uint8_t op, uint16_t src) +{ + if (no_peephole(s)) { + genop_1(s, op, src); + } + else { + struct mrc_insn_data data = mrc_last_insn(s); + + if (data.insn == OP_MOVE && src == data.a) { + rewind_pc(s); + genop_1(s, op, data.b); + } + else if (data.insn != OP_RETURN) { + genop_1(s, op, src); + } + } +} + +static void* +simple_realloc(mrc_ccontext *c, void *p, size_t len) +{ + if (len == 0) return p; + return mrc_realloc(c, p, len); +} + +static const char* +mrc_parser_get_filename(mrc_ccontext *c, uint16_t idx) { + if (idx >= c->filename_table_length) return 0; + else { + return c->filename_table[idx].filename; + } +} + +static void +scope_finish(mrc_codegen_scope *s) +{ + mrc_irep *irep = s->irep; + + if (0xff < s->nlocals) { + codegen_error(s, "too many local variables"); + } + irep->flags = 0; + if (s->iseq) { + size_t catchsize = sizeof(struct mrc_irep_catch_handler) * irep->clen; + irep->iseq = (const mrc_code *)mrc_realloc(s->c, s->iseq, sizeof(mrc_code)*s->pc + catchsize); + irep->ilen = s->pc; + if (0 < irep->clen) { + memcpy((void *)(irep->iseq + irep->ilen), s->catch_table, catchsize); + } + } + else { + irep->clen = 0; + } + mrc_free(s->c, s->catch_table); + s->catch_table = NULL; + irep->pool = (const mrc_pool_value *)simple_realloc(s->c, s->pool, sizeof(mrc_pool_value)*irep->plen); + irep->syms = (const mrc_sym *)simple_realloc(s->c, s->syms, sizeof(mrc_sym)*irep->slen); + irep->reps = (const mrc_irep **)simple_realloc(s->c, s->reps, sizeof(mrc_irep *)*irep->rlen); + if (s->filename) { + const char *filename = mrc_parser_get_filename(s->c, s->filename_index); + mrc_debug_info_append_file(s->c, s->irep->debug_info, + filename, s->lines, s->debug_start_pos, s->pc); + } + mrc_free(s->c, s->lines); + irep->nlocals = s->nlocals; + irep->nregs = s->nregs; + + mrc_gc_arena_restore(s->c, s->ai); + mrc_pool_close(s->mpool); +} + +static mrc_pool_value* +lit_pool_extend(mrc_codegen_scope *s) +{ + if (s->irep->plen == s->pcapa) { + s->pcapa *= 2; + s->pool = (mrc_pool_value*)mrc_realloc(s->c, s->pool, sizeof(mrc_pool_value)*s->pcapa); + } + + return &s->pool[s->irep->plen++]; +} + +#ifndef MRC_NO_FLOAT +static int +new_lit_float(mrc_codegen_scope *s, mrc_float num) +{ + int i; + mrc_pool_value *pv; + + for (i=0; iirep->plen; i++) { + mrc_float f; + pv = &s->pool[i]; + if (pv->tt != IREP_TT_FLOAT) continue; + f = pv->u.f; + if (f == num && !signbit(f) == !signbit(num)) return i; + } + + pv = lit_pool_extend(s); + + pv->tt = IREP_TT_FLOAT; + pv->u.f = num; + + return i; +} +#endif + +static int +new_sym(mrc_codegen_scope *s, mrc_sym sym) +{ + int i, len; + + mrc_assert(s->irep); + + len = s->irep->slen; + for (i=0; isyms[i] == sym) return i; + } + if (s->irep->slen >= s->scapa) { + s->scapa *= 2; + if (s->scapa > 0xffff) { + codegen_error(s, "too many symbols"); + } + s->syms = (mrc_sym*)mrc_realloc(s->c, s->syms, sizeof(mrc_sym)*s->scapa); + } + s->syms[s->irep->slen] = sym; + return s->irep->slen++; +} + +static void +gen_addsub(mrc_codegen_scope *s, uint8_t op, uint16_t dst) +{ + if (no_peephole(s)) { + normal: + genop_1(s, op, dst); + return; + } + else { + struct mrc_insn_data data = mrc_last_insn(s); + mrc_int n; + + if (!get_int_operand(s, &data, &n)) { + /* not integer immediate */ + goto normal; + } + struct mrc_insn_data data0 = mrc_decode_insn(mrc_prev_pc(s, data.addr)); + mrc_int n0; + if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data0, &n0)) { + /* OP_ADDI/OP_SUBI takes upto 8bits */ + if (n > INT8_MAX || n < INT8_MIN) goto normal; + rewind_pc(s); + if (n == 0) return; + if (n > 0) { + if (op == OP_ADD) genop_2(s, OP_ADDI, dst, (uint16_t)n); + else genop_2(s, OP_SUBI, dst, (uint16_t)n); + } + else { /* n < 0 */ + n = -n; + if (op == OP_ADD) genop_2(s, OP_SUBI, dst, (uint16_t)n); + else genop_2(s, OP_ADDI, dst, (uint16_t)n); + } + return; + } + if (op == OP_ADD) { + if (mrc_int_add_overflow(n0, n, &n)) goto normal; + } + else { /* OP_SUB */ + if (mrc_int_sub_overflow(n0, n, &n)) goto normal; + } + s->pc = addr_pc(s, data0.addr); + gen_int(s, dst, n); + } +} + +static void +gen_muldiv(mrc_codegen_scope *s, uint8_t op, uint16_t dst) +{ + if (no_peephole(s)) { + normal: + genop_1(s, op, dst); + return; + } + else { + struct mrc_insn_data data = mrc_last_insn(s); + mrc_int n, n0; + if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data, &n)) { + /* not integer immediate */ + goto normal; + } + struct mrc_insn_data data0 = mrc_decode_insn(mrc_prev_pc(s, data.addr)); + if (!get_int_operand(s, &data0, &n0)) { + goto normal; + } + if (op == OP_MUL) { + if (mrc_int_mul_overflow(n0, n, &n)) goto normal; + } + else { /* OP_DIV */ + if (n == 0) goto normal; + if (n0 == MRC_INT_MIN && n == -1) goto normal; + n = mrc_div_int(n0, n); + } + s->pc = addr_pc(s, data0.addr); + gen_int(s, dst, n); + } +} + +static mrc_bool +gen_uniop(mrc_codegen_scope *s, mrc_sym sym, uint16_t dst) +{ + if (no_peephole(s)) return FALSE; + struct mrc_insn_data data = mrc_last_insn(s); + mrc_int n; + + if (!get_int_operand(s, &data, &n)) return FALSE; + if (sym == MRC_OPSYM_2(add)) { + /* unary plus does nothing */ + } + else if (sym == MRC_OPSYM_2(sub)) { + if (n == MRC_INT_MIN) return FALSE; + n = -n; + } + else if (sym == MRC_OPSYM_2(neg)) { + n = ~n; + } + else { + return FALSE; + } + s->pc = addr_pc(s, data.addr); + gen_int(s, dst, n); + return TRUE; +} + +static mrc_bool +gen_binop(mrc_codegen_scope *s, mrc_sym op, uint16_t dst) +{ + if (no_peephole(s)) return FALSE; + else if (op == MRC_OPSYM_2(aref)) { + genop_1(s, OP_GETIDX, dst); + return TRUE; + } + else { + struct mrc_insn_data data = mrc_last_insn(s); + mrc_int n, n0; + if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data, &n)) { + /* not integer immediate */ + return FALSE; + } + struct mrc_insn_data data0 = mrc_decode_insn(mrc_prev_pc(s, data.addr)); + if (!get_int_operand(s, &data0, &n0)) { + return FALSE; + } + if (op == MRC_OPSYM_2(lshift)) { + if (!mrc_num_shift(n0, n, &n)) return FALSE; + } + else if (op == MRC_OPSYM_2(rshift)) { + if (n == MRC_INT_MIN) return FALSE; + if (!mrc_num_shift(n0, -n, &n)) return FALSE; + } + else if (op == MRC_OPSYM_2(mod) && n != 0) { + if (n0 == MRC_INT_MIN && n == -1) { + n = 0; + } + else { + mrc_int n1 = n0 % n; + if ((n0 < 0) != (n < 0) && n1 != 0) { + n1 += n; + } + n = n1; + } + } + else if (op == MRC_OPSYM_2(and)) { + n = n0 & n; + } + else if (op == MRC_OPSYM_2(or)) { + n = n0 | n; + } + else if (op == MRC_OPSYM_2(xor)) { + n = n0 ^ n; + } + else { + return FALSE; + } + s->pc = addr_pc(s, data0.addr); + gen_int(s, dst, n); + return TRUE; + } +} + +#define JMPLINK_START UINT32_MAX + +static uint32_t +dispatch(mrc_codegen_scope *s, uint32_t pos0) +{ + int32_t pos1; + int32_t offset; + int16_t newpos; + + if (pos0 == JMPLINK_START) return 0; + + pos1 = pos0 + 2; + offset = s->pc - pos1; + if (offset > INT16_MAX) { + codegen_error(s, "too big jmp offset"); + } + s->lastlabel = s->pc; + newpos = (int16_t)PEEK_S(s->iseq+pos0); + emit_S(s, pos0, (uint16_t)offset); + if (newpos == 0) return 0; + return pos1+newpos; +} + +static void +dispatch_linked(mrc_codegen_scope *s, uint32_t pos) +{ + if (pos==JMPLINK_START) return; + for (;;) { + pos = dispatch(s, pos); + if (pos==0) break; + } +} + +static int +new_litbint(mrc_codegen_scope *s, const char *p, int base, mrc_bool neg) +{ + int i; + size_t plen; + mrc_pool_value *pv; + + plen = strlen(p); + if (plen > 255) { + codegen_error(s, "integer too big"); + } + for (i=0; iirep->plen; i++) { + size_t len; + pv = &s->pool[i]; + if (pv->tt != IREP_TT_BIGINT) continue; + len = pv->u.str[0]; + if (len == plen && pv->u.str[1] == base && memcmp(pv->u.str+2, p, len) == 0) + return i; + } + + pv = lit_pool_extend(s); + + char *buf; + pv->tt = IREP_TT_BIGINT; + buf = (char*)mrc_realloc(s->c, NULL, plen+3); + buf[0] = (char)plen; + if (neg) buf[1] = -base; + else buf[1] = base; + memcpy(buf+2, p, plen); + buf[plen+2] = '\0'; + pv->u.str = buf; + + return i; +} + +static int +new_lit_str(mrc_codegen_scope *s, const char *str, mrc_int len) +{ + int i; + mrc_pool_value *pv; + + for (i=0; iirep->plen; i++) { + pv = &s->pool[i]; + if (pv->tt & IREP_TT_NFLAG) continue; + mrc_int plen = pv->tt>>2; + if (len != plen) continue; + if (memcmp(pv->u.str, str, plen) == 0) + return i; + } + + pv = lit_pool_extend(s); + + //if (mrb_ro_data_p(str)) { + // pv->tt = (uint32_t)(len<<2) | IREP_TT_SSTR; + // pv->u.str = str; + //} + //else { + char *p; + pv->tt = (uint32_t)(len<<2) | IREP_TT_STR; + p = (char*)mrc_realloc(s->c, NULL, len+1); + memcpy(p, str, len); + p[len] = '\0'; + pv->u.str = p; + //} + + return i; +} + +static int +new_lit_cstr(mrc_codegen_scope *s, const char *str) +{ + return new_lit_str(s, str, (mrc_int)strlen(str)); +} + +static int +new_lit_int(mrc_codegen_scope *s, mrc_int num) +{ + int i; + mrc_pool_value *pv; + + for (i=0; iirep->plen; i++) { + pv = &s->pool[i]; + if (pv->tt == IREP_TT_INT32) { + if (num == pv->u.i32) return i; + } +#ifdef MRC_64BIT + else if (pv->tt == IREP_TT_INT64) { + if (num == pv->u.i64) return i; + } + continue; +#endif + } + + pv = lit_pool_extend(s); + +#ifdef MRC_INT64 + pv->tt = IREP_TT_INT64; + pv->u.i64 = num; +#else + pv->tt = IREP_TT_INT32; + pv->u.i32 = num; +#endif + + return i; +} + +static int +catch_handler_new(mrc_codegen_scope *s) +{ + size_t newsize = sizeof(struct mrc_irep_catch_handler) * (s->irep->clen + 1); + s->catch_table = (struct mrc_irep_catch_handler*)mrc_realloc(s->c, (void*)s->catch_table, newsize); + return s->irep->clen++; +} + +static void +catch_handler_set(mrc_codegen_scope *s, int ent, enum mrc_catch_type type, uint32_t begin, uint32_t end, uint32_t target) +{ + struct mrc_irep_catch_handler *e; + mrc_assert(ent >= 0 && ent < s->irep->clen); + e = &s->catch_table[ent]; + mrc_uint8_to_bin(type, &e->type); + mrc_irep_catch_handler_pack(begin, e->begin); + mrc_irep_catch_handler_pack(end, e->end); + mrc_irep_catch_handler_pack(target, e->target); +} + +static void +raise_error(mrc_codegen_scope *s, const char *msg) +{ + int idx = new_lit_cstr(s, msg); + + genop_1(s, OP_ERR, idx); +} + +static struct loopinfo* +loop_push(mrc_codegen_scope *s, enum looptype t) +{ + struct loopinfo *p = (struct loopinfo*)codegen_palloc(s, sizeof(struct loopinfo)); + + p->type = t; + p->pc0 = p->pc1 = p->pc2 = JMPLINK_START; + p->prev = s->loop; + p->reg = cursp(); + s->loop = p; + + return p; +} + +// Implementation in codegen_prism.inc +static void gen_retval(mrc_codegen_scope *s, mrc_node *tree); + +static void +loop_break(mrc_codegen_scope *s, mrc_node *tree) +{ + if (!s->loop) { + codegen(s, tree, NOVAL); + raise_error(s, "unexpected break"); + } + else { + struct loopinfo *loop; + + loop = s->loop; + if (tree) { + if (loop->reg < 0) { + codegen(s, tree, NOVAL); + } + else { + gen_retval(s, tree); + } + } + while (loop) { + if (loop->type == LOOP_BEGIN) { + loop = loop->prev; + } + else if (loop->type == LOOP_RESCUE) { + loop = loop->prev; + } + else{ + break; + } + } + if (!loop) { + raise_error(s, "unexpected break"); + return; + } + + if (loop->type == LOOP_NORMAL) { + int tmp; + + if (loop->reg >= 0) { + if (tree) { + gen_move(s, loop->reg, cursp(), 0); + } + else { + genop_1(s, OP_LOADNIL, loop->reg); + } + } + tmp = genjmp(s, OP_JMPUW, loop->pc2); + loop->pc2 = tmp; + } + else { + if (!tree) { + genop_1(s, OP_LOADNIL, cursp()); + } + gen_return(s, OP_BREAK, cursp()); + } + } +} + +static void +loop_pop(mrc_codegen_scope *s, int val) +{ + if (val) { + genop_1(s, OP_LOADNIL, cursp()); + } + dispatch_linked(s, s->loop->pc2); + s->loop = s->loop->prev; + if (val) push(); +} + +static void +gen_blkmove(mrc_codegen_scope *s, uint16_t ainfo, int lv) +{ + int m1 = (ainfo>>7)&0x3f; + int r = (ainfo>>6)&0x1; + int m2 = (ainfo>>1)&0x1f; + int kd = (ainfo)&0x1; + int off = m1+r+m2+kd+1; + if (lv == 0) { + gen_move(s, cursp(), off, 0); + } + else { + genop_3(s, OP_GETUPVAR, cursp(), off, lv); + } + push(); +} + +static void +gen_setxv(mrc_codegen_scope *s, uint8_t op, uint16_t dst, mrc_sym sym, int val) +{ + int idx = new_sym(s, sym); + if (!val && !no_peephole(s)) { + struct mrc_insn_data data = mrc_last_insn(s); + if (data.insn == OP_MOVE && data.a == dst) { + dst = data.b; + rewind_pc(s); + } + } + genop_2(s, op, dst, idx); +} + +static mrc_irep * +generate_code(mrc_ccontext *c, mrc_node *node, int val) +{ + mrc_codegen_scope *scope = scope_new(c, NULL, NULL); + struct mrc_jmpbuf *prev_jmp = c->jmp; + struct mrc_jmpbuf jmpbuf; + + c->jmp = &jmpbuf; + + scope->c = c; + scope->filename_index = 0; + scope->filename = (const char *)c->filename_table[0].filename; + + MRC_TRY(c->jmp) { + codegen(scope, node, val); + // TODO: mrc_ccontext has an upper Proc if MRC_TARGET_MRUBY + //proc->c = NULL; + //if (mrb->c->cibase && mrb->c->cibase->proc == proc->upper) { + // proc->upper = NULL; + //} + //mrc_irep_free(c, scope->irep); + mrc_irep *irep = scope->irep; + mrc_pool_close(scope->mpool); + c->jmp = prev_jmp; + return irep; + } + MRC_CATCH(c->jmp) { + // TODO? + //mrc_irep_free(c, scope->irep); + mrc_pool_close(scope->mpool); + c->jmp = prev_jmp; + return NULL; + } + MRC_END_EXC(c->jmp); +} + +MRC_API mrc_irep * +mrc_generate_code(mrc_ccontext *c, mrc_node *node) +{ + return generate_code(c, node, VAL); +} + +#define CALL_MAXARGS 15 +#define GEN_LIT_ARY_MAX 64 +#define GEN_VAL_STACK_MAX 99 + +/*-------------------------------------------------------------------------- + * Parser dependent code + *------------------------------------------------------------------------*/ + +#include "codegen_prism.inc" diff --git a/vendor/mruby-compiler2/src/codegen_prism.inc b/vendor/mruby-compiler2/src/codegen_prism.inc new file mode 100644 index 0000000..094e56f --- /dev/null +++ b/vendor/mruby-compiler2/src/codegen_prism.inc @@ -0,0 +1,2957 @@ +#define nint(node) PM_NODE_TYPE(node) + +#define CAST3(name, from, to) \ + pm_##name##_node_t *to = (pm_##name##_node_t *)from +#define CAST(name) CAST3(name,tree,cast) + +static void gen_massignment(mrc_codegen_scope *s, mrc_node *tree, int rhs, int val); + +static mrc_sym +nsym(mrc_parser_state *p, const uint8_t *start, size_t length) +{ + mrc_sym sym = pm_constant_pool_insert_constant(&p->constant_pool, start, length); + return sym; +} + +static int32_t +node_lineno(mrc_ccontext *c, mrc_node *node) +{ + pm_location_t *loc = &((pm_node_t *)node)->location; + return pm_newline_list_line(&c->p->newline_list, loc->start, 1); +} + +static mrc_bool +true_always(mrc_node *tree) +{ + switch (nint(tree)) { + case PM_TRUE_NODE: + case PM_INTEGER_NODE: + case PM_STRING_NODE: + case PM_SYMBOL_NODE: + return TRUE; + default: + return FALSE; + } +} + +static mrc_bool +false_always(mrc_node *tree) +{ + switch (nint(tree)) { + case PM_FALSE_NODE: + case PM_NIL_NODE: + return TRUE; + default: + return FALSE; + } +} + +static void +gen_retval(mrc_codegen_scope *s, mrc_node *tree) +{ + CAST(arguments); + if (cast->arguments.size == 1 ) { + if (nint(cast->arguments.nodes[0]) == PM_SPLAT_NODE) { + codegen(s, ((pm_splat_node_t *)cast->arguments.nodes[0])->expression, VAL); + pop(); + genop_1(s, OP_ARYSPLAT, cursp()); + } + else { + codegen(s, cast->arguments.nodes[0], VAL); + pop(); + } + } + else { + codegen(s, tree, VAL); + pop(); + } +} + +static void +gen_assignment_lvar(mrc_codegen_scope *s, int sp, mrc_sym name, int depth, int val) +{ + if (depth == 0) { + int idx = lv_idx(s, name); + if (idx != sp) { + gen_move(s, idx, sp, val); + } + } + else { + gen_setupvar(s, sp, name, depth); + } +} + +static int +gen_values(mrc_codegen_scope *s, mrc_node *tree, int val, int limit) +{ + CAST(arguments); + mrc_node *t; + + int n = 0; + int first = 1; + int slimit = GEN_VAL_STACK_MAX; + + if (limit == 0) limit = GEN_LIT_ARY_MAX; + if (cursp() >= slimit) slimit = INT16_MAX; + + if (!val) { + for (size_t i = 0; i < cast->arguments.size; i++) { + t = (mrc_node *)cast->arguments.nodes[i]; + codegen(s, t, NOVAL); + n++; + } + return n; + } + + for (size_t i = 0; i < cast->arguments.size; i++) { + t = (mrc_node *)cast->arguments.nodes[i]; + if (nint(t) == PM_KEYWORD_HASH_NODE) break; + int is_splat = nint(t) == PM_SPLAT_NODE; + + if (is_splat || cursp() >= slimit) { /* flush stack */ + pop_n(n); + if (first) { + if (n == 0) { + genop_1(s, OP_LOADNIL, cursp()); + } + else { + genop_2(s, OP_ARRAY, cursp(), n); + } + push(); + first = 0; + limit = GEN_LIT_ARY_MAX; + } + else if (n > 0) { + pop(); + genop_2(s, OP_ARYPUSH, cursp(), n); + push(); + } + n = 0; + } + if (is_splat) { + CAST3(array, ((pm_splat_node_t *)t)->expression, a); + codegen(s, (mrc_node *)a, val); + pop(); pop(); + genop_1(s, OP_ARYCAT, cursp()); + push(); + } + else { + codegen(s, t, val); + n++; + } + } + if (!first) { + pop(); + if (n > 0) { + pop_n(n); + genop_2(s, OP_ARYPUSH, cursp(), n); + } + return -1; /* variable length */ + } + else if (n > limit) { + pop_n(n); + genop_2(s, OP_ARRAY, cursp(), n); + return -1; + } + return n; +} + +static void +gen_assignment(mrc_codegen_scope *s, mrc_node *tree, mrc_node *rhs, int sp, int val) +{ + int idx; + + switch (nint(tree)) { + case PM_LOCAL_VARIABLE_WRITE_NODE: + case PM_LOCAL_VARIABLE_TARGET_NODE: + case PM_INSTANCE_VARIABLE_WRITE_NODE: + case PM_INSTANCE_VARIABLE_TARGET_NODE: + case PM_CONSTANT_WRITE_NODE: + case PM_CONSTANT_TARGET_NODE: + case PM_GLOBAL_VARIABLE_WRITE_NODE: + case PM_GLOBAL_VARIABLE_TARGET_NODE: + case PM_CLASS_VARIABLE_WRITE_NODE: + case PM_CLASS_VARIABLE_TARGET_NODE: + case PM_MULTI_TARGET_NODE: + case PM_REQUIRED_PARAMETER_NODE: + case PM_INDEX_TARGET_NODE: + case PM_CALL_TARGET_NODE: + { + if (rhs) { + codegen(s, rhs, VAL); + pop(); + sp = cursp(); + } + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: + break; + default: + { + codegen_error(s, "Not implemented (#1)"); + break; + } + } + + switch (nint(tree)) { + case PM_LOCAL_VARIABLE_WRITE_NODE: + case PM_LOCAL_VARIABLE_TARGET_NODE: + case PM_REQUIRED_PARAMETER_NODE: + { + CAST(local_variable_write); + gen_assignment_lvar(s, sp, cast->name, cast->depth, val); + break; + } + case PM_INSTANCE_VARIABLE_WRITE_NODE: + case PM_INSTANCE_VARIABLE_TARGET_NODE: + { + CAST(instance_variable_write); + gen_setxv(s, OP_SETIV, sp, cast->name, val); + break; + } + case PM_CONSTANT_WRITE_NODE: + case PM_CONSTANT_TARGET_NODE: + { + CAST(constant_write); + gen_setxv(s, OP_SETCONST, sp, cast->name, val); + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: + case PM_CONSTANT_PATH_TARGET_NODE: + { + CAST(constant_path_write); + if (sp) { + gen_move(s, cursp(), sp, 0); + } + sp = cursp(); + push(); + if (cast->target->parent) { + codegen(s, cast->target->parent, VAL); + idx = new_sym(s, cast->target->name); + } + else { /* NODE_COLON3 */ + genop_1(s, OP_OCLASS, cursp()); + push(); + idx = new_sym(s, cast->target->name); + } + if (rhs) { + codegen(s, rhs, VAL); pop(); + gen_move(s, sp, cursp(), 0); + } + pop_n(2); + genop_2(s, OP_SETMCNST, sp, idx); + break; + } + case PM_GLOBAL_VARIABLE_WRITE_NODE: + case PM_GLOBAL_VARIABLE_TARGET_NODE: + { + CAST(global_variable_read); + gen_setxv(s, OP_SETGV, sp, cast->name, val); + break; + } + case PM_CLASS_VARIABLE_WRITE_NODE: + case PM_CLASS_VARIABLE_TARGET_NODE: + { + CAST(class_variable_read); + gen_setxv(s, OP_SETCV, sp, cast->name, val); + break; + } + case PM_MULTI_TARGET_NODE: + { + gen_massignment(s, tree, sp, val); + break; + } + case PM_INDEX_TARGET_NODE: + { + CAST(index_target); + codegen(s, cast->receiver, VAL); + int n = gen_values(s, (mrc_node *)cast->arguments, VAL, 14); + genop_2(s, OP_MOVE, cursp(), cursp() - n * 2 + 1); + if (n == 1) { + pop_n(2); + genop_1(s, OP_SETIDX, cursp()); + } + else { + pop_n(n+1); + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(aset)), n+1); + } + break; + } + case PM_CALL_TARGET_NODE: + { + CAST(call_target); + codegen(s, cast->receiver, VAL); + genop_2(s, OP_MOVE, cursp(), sp); + pop(); + genop_3(s, OP_SEND, cursp(), new_sym(s, cast->name), 1); + break; + } + default: + { + codegen_error(s, "Not implemented (#2)"); + break; + } + } + if (val) push(); +} + +static int +scope_body(mrc_codegen_scope *s, mrc_node *tree, int val) +{ + mrc_constant_id_list *nlv; + mrc_node *statements; + switch (nint(tree)) { + case PM_PROGRAM_NODE: + { + CAST3(program, tree, program); + nlv = &program->locals; + statements = (mrc_node *)program->statements; + break; + } + case PM_CLASS_NODE: + { + CAST3(class, tree, class); + nlv = &class->locals; + statements = class->body; + break; + } + case PM_SINGLETON_CLASS_NODE: + { + CAST3(singleton_class, tree, sclass); + nlv = &sclass->locals; + statements = sclass->body; + break; + } + case PM_MODULE_NODE: + { + CAST3(module, tree, module); + nlv = &module->locals; + statements = module->body; + break; + } + default: + { + codegen_error(s, "Not implemented (#3)"); + statements = NULL; + nlv = NULL; + break; + } + } + mrc_codegen_scope *scope = scope_new(s->c, s, nlv); + + codegen(scope, statements, VAL); + + // For PICOIRB + s->c->scope_sp = scope->sp - 1; + + gen_return(scope, OP_RETURN, scope->sp-1); + if (!s->iseq) { + genop_0(scope, OP_STOP); + } + scope_finish(scope); + if (!s->irep) { + /* should not happen */ + return 0; + } + return s->irep->rlen - 1; +} + + +static int +gen_hash(mrc_codegen_scope *s, mrc_node *tree, int val, int limit) +{ + struct pm_node_list elements; + if (nint(tree) == PM_HASH_NODE) { + CAST(hash); + elements = cast->elements; + } + else { + CAST(keyword_hash); + elements = cast->elements; + } + + int slimit = GEN_VAL_STACK_MAX; + if (cursp() >= GEN_LIT_ARY_MAX) slimit = INT16_MAX; + int len = 0; + mrc_bool update = FALSE; + mrc_bool first = TRUE; + + //while (tree) { + for (size_t i = 0; i < elements.size; i++) { + if (nint(elements.nodes[i]) == PM_ASSOC_SPLAT_NODE) { + CAST3(assoc_splat, elements.nodes[i], assocsplat); + if (val && first) { + genop_2(s, OP_HASH, cursp(), 0); + push(); + update = TRUE; + } + else if (val && len > 0) { + pop_n(len*2); + if (!update) { + genop_2(s, OP_HASH, cursp(), len); + } + else { + pop(); + genop_2(s, OP_HASHADD, cursp(), len); + } + push(); + } + codegen(s, assocsplat->value, val); + if (val && (len > 0 || update)) { + pop(); pop(); + genop_1(s, OP_HASHCAT, cursp()); + push(); + } + update = TRUE; + len = 0; + } + else { + CAST3(assoc, elements.nodes[i], assoc); + codegen(s, assoc->key, val); + codegen(s, assoc->value, val); + len++; + } + if (val && cursp() >= slimit) { + pop_n(len*2); + if (!update) { + genop_2(s, OP_HASH, cursp(), len); + } + else { + pop(); + genop_2(s, OP_HASHADD, cursp(), len); + } + push(); + update = TRUE; + len = 0; + } + first = FALSE; + } + if (val && len > limit) { + pop_n(len*2); + genop_2(s, OP_HASH, cursp(), len); + push(); + return -1; + } + if (update) { + if (val && len > 0) { + pop_n(len*2+1); + genop_2(s, OP_HASHADD, cursp(), len); + push(); + } + return -1; /* variable length */ + } + return len; +} + +static void +gen_call(mrc_codegen_scope *s, mrc_node *tree, int val, int safe) +{ + CAST(call); + const mrc_sym sym = cast->name; + int skip = 0, n = 0, nk = 0, noop = no_optimize(s), noself = 0, blk = 0, sp_save = cursp(); + + if (cast->receiver == NULL) { + noself = noop = 1; + push(); + } + else { + codegen(s, cast->receiver, VAL); /* receiver */ + } + if (safe) { + int recv = cursp()-1; + gen_move(s, cursp(), recv, 1); + skip = genjmp2_0(s, OP_JMPNIL, cursp(), val); + } + CAST3(arguments, cast->arguments, arguments); + if (arguments) { + if (0 < arguments->arguments.size) { /* positional arguments */ + n = gen_values(s, (mrc_node *)arguments, VAL, 14); + if (n < 0) { /* variable length */ + noop = 1; /* not operator */ + n = 15; + push(); + } + } + for (size_t i = 0; i < arguments->arguments.size; i++) { + mrc_node *t = (mrc_node *)arguments->arguments.nodes[i]; + if (nint(t) == PM_KEYWORD_HASH_NODE) { /* keyword arguments */ + noop = 1; + nk = gen_hash(s, t, VAL, 14); + if (nk < 0) nk = 15; + } + } + } + if (cast->block) { + codegen(s, cast->block, VAL); + pop(); + noop = 1; + blk = 1; + } + if (cast->arguments && cast->arguments->base.flags &PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING) { + blk = 1; + n = 0xFF; + } + push();pop(); + s->sp = sp_save; + + if (!noop && sym == MRC_OPSYM_2(add) && n == 1) { + gen_addsub(s, OP_ADD, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(sub) && n == 1) { + gen_addsub(s, OP_SUB, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(mul) && n == 1) { + gen_muldiv(s, OP_MUL, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(div) && n == 1) { + gen_muldiv(s, OP_DIV, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(lt) && n == 1) { + genop_1(s, OP_LT, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(le) && n == 1) { + genop_1(s, OP_LE, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(gt) && n == 1) { + genop_1(s, OP_GT, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(ge) && n == 1) { + genop_1(s, OP_GE, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(eq) && n == 1) { + genop_1(s, OP_EQ, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(aset) && n == 2) { + genop_1(s, OP_SETIDX, cursp()); + } + else if (!noop && n == 0 && gen_uniop(s, sym, cursp())) { + /* constant folding succeeded */ + } + else if (!noop && n == 1 && gen_binop(s, sym, cursp())) { + /* constant folding succeeded */ + } + else if (noself) { + genop_3(s, blk ? OP_SSENDB : OP_SSEND, cursp(), new_sym(s, sym), n|(nk<<4)); + } + else { + genop_3(s, blk ? OP_SENDB : OP_SEND, cursp(), new_sym(s, sym), n|(nk<<4)); + } + if (safe) { + dispatch(s, skip); + } + if (val) { + push(); + } +} + +static void +gen_massignment(mrc_codegen_scope *s, mrc_node *tree, int rhs, int val) +{ + CAST(multi_write); + int n = cast->lefts.size, post = cast->rights.size; + + if (0 < n) { /* pre */ + for (int i = 0; i < n; i++) { + int sp = cursp(); + genop_3(s, OP_AREF, sp, rhs, i); + push(); + gen_assignment(s, cast->lefts.nodes[i], NULL, sp, NOVAL); + pop(); + } + } + if (cast->rest || 0 < post) { + gen_move(s, cursp(), rhs, val); + push_n(post+1); + pop_n(post+1); + genop_3(s, OP_APOST, cursp(), n, post); + if (cast->rest) { /* rest */ + gen_assignment(s, ((pm_splat_node_t *)cast->rest)->expression, NULL, cursp(), NOVAL); + } + for (int i = 0; i < post; i++) { + gen_assignment(s, cast->rights.nodes[i], NULL, cursp()+i+1, NOVAL); + } + if (val) { + gen_move(s, cursp(), rhs, 0); + } + } +} + +static void +for_body(mrc_codegen_scope *s, mrc_node *tree) +{ + mrc_codegen_scope *prev = s; + int idx; + struct loopinfo *lp; + mrc_node *n2; + + CAST(for); + + /* generate receiver */ + codegen(s, (mrc_node *)cast->collection, VAL); + /* generate loop-block */ + s = scope_new(s->c, s, s->lv); + // ^^^^^ Different from original mruby + // drawing the upper scope's lvars + + push(); /* push for a block parameter */ + + /* generate loop variable */ + n2 = cast->index; + genop_W(s, OP_ENTER, 0x40000); + if (nint(n2) == PM_MULTI_TARGET_NODE) { + gen_massignment(s, n2, 1, VAL); + } + else { + gen_assignment(s, n2, NULL, 1, NOVAL); + } + /* construct loop */ + lp = loop_push(s, LOOP_FOR); + lp->pc1 = new_label(s); + + /* loop body */ + codegen(s, (mrc_node *)cast->statements, VAL); + pop(); + gen_return(s, OP_RETURN, cursp()); + loop_pop(s, NOVAL); + scope_finish(s); + s = prev; + genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1); + push();pop(); /* space for a block */ + pop(); + idx = new_sym(s, MRC_SYM_1(each)); + genop_3(s, OP_SENDB, cursp(), idx, 0); +} + +static void +mrc_constant_id_list_init_capacity(mrc_codegen_scope *s, pm_constant_id_list_t *list, size_t capacity) +{ + list->ids = (pm_constant_id_t *)codegen_palloc(s, capacity * sizeof(pm_constant_id_t)); + if (list->ids == NULL) codegen_error(s, "memory allocation error"); + list->size = 0; + list->capacity = capacity; +} + +static mrc_bool +mrc_constant_id_list_append(mrc_codegen_scope *s, pm_constant_id_list_t *list, pm_constant_id_t id) +{ + if (list->size >= list->capacity) { + size_t oldlen = sizeof(pm_constant_id_t) * list->capacity; + list->capacity = list->capacity == 0 ? 8 : list->capacity * 2; + size_t newlen = sizeof(pm_constant_id_t) * list->capacity; + list->ids = (pm_constant_id_t *)codegen_realloc(s, list->ids, oldlen, newlen); + if (list->ids == NULL) return FALSE; + } + list->ids[list->size++] = id; + return TRUE; +} + +static int +lambda_body(mrc_codegen_scope *s, mrc_node *tree, mrc_node *body, pm_constant_id_list_t *locals, int blk) +{ + mrc_codegen_scope *parent = s; + pm_parameters_node_t *parameters = NULL; + int na = 0; + if (tree) { + switch nint(tree) { + case PM_DEF_NODE: + parameters = ((pm_def_node_t *)tree)->parameters; + break; + case PM_BLOCK_PARAMETERS_NODE: + parameters = ((pm_block_parameters_node_t *)tree)->parameters; + break; + case PM_NUMBERED_PARAMETERS_NODE: + parameters = NULL; + na = ((pm_numbered_parameters_node_t *)tree)->maximum; + break; + default: + codegen_error(s, "should not happen"); + } + } + else { + parameters = NULL; + } + + + size_t i, ma, mma, oa, ra, pa, ppa, ka, kd, ba, forwarding;; + forwarding = 0; + int block_reg = 0; + pm_constant_id_list_t *lv = (pm_constant_id_list_t *)codegen_palloc(s, sizeof(pm_constant_id_list_t)); + pm_constant_id_t null_mark = pm_constant_pool_insert_constant(&s->c->p->constant_pool, NULL, 0); + + // Create lv regs from Prism's locals + if (parameters == NULL) { + // TODO: FIXME this doesn't work with numbered parameters + // TODO: `it` + mrc_constant_id_list_init_capacity(s, lv, 2); + mrc_constant_id_list_append(s, lv, null_mark); + for (i = 0; i < locals->size; i++) { + /* numbered parameters */ + mrc_constant_id_list_append(s, lv, locals->ids[i]); + } + ma = mma = oa = ra = pa = ppa = ka = kd = ba = 0; + } + else { + int nregs; + /* mandatory arguments */ + ma = parameters->requireds.size; + mma = 0; + for (i = 0; i < ma; i++) { + if (nint(parameters->requireds.nodes[i]) == PM_MULTI_TARGET_NODE) { + CAST3(multi_target, parameters->requireds.nodes[i], m); + mma += m->lefts.size; + } + } + oa = parameters->optionals.size; + ra = parameters->rest ? 1 : 0; + pa = parameters->posts.size; + ppa = 0; + for (i = 0; i < pa; i++) { + if (nint(parameters->posts.nodes[i]) == PM_MULTI_TARGET_NODE) { + CAST3(multi_target, parameters->posts.nodes[i], m); + ppa += m->lefts.size; + } + } + ka = parameters->keywords.size; + kd = parameters->keyword_rest ? 1 : 0; + ba = parameters->block ? 1 : 0; + nregs = ma + mma + oa + ra + pa + ppa + ka + kd + ba; + mrc_constant_id_list_init_capacity(s, lv, nregs); + // mandatory + for (i = 0; i < ma; i++) { + if (nint(parameters->requireds.nodes[i]) == PM_MULTI_TARGET_NODE) { + mrc_constant_id_list_append(s, lv, null_mark); + } else { + mrc_constant_id_list_append(s, lv, ((pm_required_parameter_node_t *)parameters->requireds.nodes[i])->name); + } + } + // optional + for (i = 0; i < oa; i++) { + mrc_constant_id_list_append(s, lv, ((pm_optional_parameter_node_t *)parameters->optionals.nodes[i])->name); + } + // rest + if (ra) { + if (((pm_rest_parameter_node_t *)parameters->rest)->name) { + mrc_constant_id_list_append(s, lv, ((pm_rest_parameter_node_t *)parameters->rest)->name); + } else { + pm_constant_id_t astr = MRC_OPSYM_2(mul); + mrc_constant_id_list_append(s, lv, astr); + } + } + // post + for (i = 0; i < pa; i++) { + if (nint(parameters->posts.nodes[i]) == PM_MULTI_TARGET_NODE) { + mrc_constant_id_list_append(s, lv, null_mark); + } else { + mrc_constant_id_list_append(s, lv, ((pm_required_parameter_node_t *)parameters->posts.nodes[i])->name); + } + } + // keywords and block + if (ka || kd || ba) { + // keyword rest + mrc_bool write_dastr = false; + if (ka || kd) { + write_dastr = true; + } + if (kd) { + switch (nint(parameters->keyword_rest)) { + case PM_KEYWORD_REST_PARAMETER_NODE: { + if (((pm_keyword_rest_parameter_node_t *)parameters->keyword_rest)->name) { + mrc_constant_id_list_append(s, lv, ((pm_keyword_rest_parameter_node_t *)parameters->keyword_rest)->name); + write_dastr = false; + } + break; + } + case PM_FORWARDING_PARAMETER_NODE: { + forwarding = 1; + write_dastr = false; + pm_constant_id_t astr = MRC_OPSYM_2(mul); + mrc_constant_id_list_append(s, lv, astr); + pm_constant_id_t dastr = MRC_OPSYM_2(pow); + mrc_constant_id_list_append(s, lv, dastr); + mrc_constant_id_list_append(s, lv, null_mark); + pm_constant_id_t and = MRC_OPSYM_2(and); + mrc_constant_id_list_append(s, lv, and); + block_reg = lv->size; + break; + } + default: + codegen_error(s, "Unknown node"); + } + } + if (write_dastr) { + pm_constant_id_t dastr = MRC_OPSYM_2(pow); + mrc_constant_id_list_append(s, lv, dastr); + } + } + if (forwarding == 0) { + mrc_constant_id_list_append(s, lv, null_mark); + } + // block + if (ba) { + if (((pm_block_parameter_node_t *)parameters->block)->name) { + mrc_constant_id_list_append(s, lv, ((pm_block_parameter_node_t *)parameters->block)->name); + } + else { + pm_constant_id_t and = MRC_OPSYM_2(and); + mrc_constant_id_list_append(s, lv, and); + } + block_reg = lv->size; + } + // keywords + for (i = 0; i < ka; i++) { + if (nint(parameters->keywords.nodes[i]) == PM_REQUIRED_KEYWORD_PARAMETER_NODE) { + mrc_constant_id_list_append(s, lv, ((pm_required_keyword_parameter_node_t *)parameters->keywords.nodes[i])->name); + } + else { + mrc_constant_id_list_append(s, lv, ((pm_optional_keyword_parameter_node_t *)parameters->keywords.nodes[i])->name); + } + } + for (i = 0; i < ma; i++) { + if (nint(parameters->requireds.nodes[i]) == PM_MULTI_TARGET_NODE) { + CAST3(multi_target, parameters->requireds.nodes[i], m); + for (size_t j = 0; j < m->lefts.size; j++) { + mrc_constant_id_list_append(s, lv, ((pm_required_parameter_node_t *)m->lefts.nodes[j])->name); + } + } + } + for (i = 0; i < pa; i++) { + if (nint(parameters->posts.nodes[i]) == PM_MULTI_TARGET_NODE) { + CAST3(multi_target, parameters->posts.nodes[i], m); + for (size_t j = 0; j < m->lefts.size; j++) { + mrc_constant_id_list_append(s, lv, ((pm_required_parameter_node_t *)m->lefts.nodes[j])->name); + } + } + } + } + if (locals) { + for (i = 0; i < locals->size; i++) { + if (!pm_constant_id_list_includes(lv, locals->ids[i])) { + mrc_constant_id_list_append(s, lv, locals->ids[i]); + } + } + // free Prism's locals + pm_constant_id_list_free(locals); + locals->ids = NULL; + locals->size = locals->capacity = 0; + } + + s = scope_new(s->c, s, lv); + + s->mscope = !blk; + + if (blk) { + struct loopinfo *lp = loop_push(s, LOOP_BLOCK); + lp->pc0 = new_label(s); + } + if (parameters == NULL) { /* empty parameter OR numbered parameters */ + genop_W(s, OP_ENTER, MRC_ARGS_REQ(na)); + s->ainfo = (na & 0x3f) << 7; + } + else { + mrc_aspec a; + uint32_t pos; + mrc_node **margs, **pargs; + + /* mandatory arguments */ + margs = parameters->requireds.nodes; + /* mandatory arguments after rest argument */ + pargs = parameters->posts.nodes; + + if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) { + codegen_error(s, "too many formal arguments"); + } + /* (23bits = 5:5:1:5:5:1:1) */ + ra = ra|forwarding; + ba = ba|forwarding; + a = MRC_ARGS_REQ(ma) + | MRC_ARGS_OPT(oa) + | (ra? MRC_ARGS_REST() : 0) + | MRC_ARGS_POST(pa) + | MRC_ARGS_KEY(ka, kd) + | (ba? MRC_ARGS_BLOCK() : 0); + genop_W(s, OP_ENTER, a); + /* (12bits = 5:1:5:1) */ + s->ainfo = (((ma+oa) & 0x3f) << 7) + | ((ra & 0x1) << 6) + | ((pa & 0x1f) << 1) + | ((ka | kd) ? 1 : 0); + /* generate jump table for optional arguments initializer */ + pos = new_label(s); + for (i=0; i 0) { + genjmp_0(s, OP_JMP); + } + for (i = 0; i < oa; i++) { + CAST3(optional_parameter, parameters->optionals.nodes[i], opt); + int idx; + mrc_sym id = opt->name; + + dispatch(s, pos+i*3+1); + codegen(s, opt->value, VAL); + pop(); + idx = lv_idx(s, id); + if (idx > 0) { + gen_move(s, idx, cursp(), 0); + } + else { + // TODO + //gen_getupvar(s, cursp(), id, ); + codegen_error(s, "getupvar happens in optional argument"); + } + } + if (oa > 0) { + dispatch(s, pos+i*3+1); + } + + /* keyword arguments */ + if (ka) { + for (i = 0; i < ka; i++) { + int jmpif_key_p, jmp_def_set = -1; + CAST3(required_keyword_parameter, parameters->keywords.nodes[i], kwd); + mrc_sym kwd_sym = kwd->name; + + if (nint((mrc_node *)kwd) == PM_OPTIONAL_KEYWORD_PARAMETER_NODE) { + int idx; + genop_2(s, OP_KEY_P, lv_idx(s, kwd_sym), new_sym(s, kwd_sym)); + jmpif_key_p = genjmp2_0(s, OP_JMPIF, lv_idx(s, kwd_sym), NOVAL); + codegen(s, ((pm_optional_keyword_parameter_node_t *)kwd)->value, VAL); + pop(); + idx = lv_idx(s, kwd_sym); + if (idx > 0) { + gen_move(s, idx, cursp(), 0); + } + else { + // TODO + // gen_getupvar(s, cursp(), kwd_sym); + codegen_error(s, "getupvar happens in keyword argument"); + } + jmp_def_set = genjmp_0(s, OP_JMP); + dispatch(s, jmpif_key_p); + } + genop_2(s, OP_KARG, lv_idx(s, kwd_sym), new_sym(s, kwd_sym)); + if (jmp_def_set != -1) { + dispatch(s, jmp_def_set); + } + } + if (!kd) { + genop_0(s, OP_KEYEND); + } + } + + /* block argument */ + if (block_reg) { + gen_move(s, block_reg, block_reg-1, 0); + } + + /* argument destructuring */ + if (margs) { + pos = 1; + for (i = 0; i < ma; i++) { + if (nint(margs[i]) == PM_MULTI_TARGET_NODE) { + gen_massignment(s, margs[i], pos, NOVAL); + // Enabling the following three lines would generate VM code equivalent to mruby-compiler, + // but it would not work in mruby/c. + // It appears to work correctly even when commented out, so it is left commented out + uint16_t n = ((pm_multi_target_node_t *)margs[i])->lefts.size; + gen_move(s, cursp(), pos, 0); + genop_3(s, OP_APOST, cursp(), n, 0); + } + pos++; + } + } + if (pargs) { + pos = ma+oa+ra+1; + for (i = 0; i < pa; i++) { + if (nint(pargs[i]) == PM_MULTI_TARGET_NODE) { + gen_massignment(s, pargs[i], pos, NOVAL); + // Enabling the following three lines would generate VM code equivalent to mruby-compiler, + // but it would not work in mruby/c. + // It appears to work correctly even when commented out, so it is left commented out + uint16_t n = ((pm_multi_target_node_t *)pargs[i])->lefts.size; + gen_move(s, cursp(), pos, 0); + genop_3(s, OP_APOST, cursp(), n, 0); + } + pos++; + } + } + } + + codegen(s, body, VAL); + pop(); + if (s->pc > 0) { + gen_return(s, OP_RETURN, cursp()); + } + if (blk) { + loop_pop(s, NOVAL); + } + scope_finish(s); + return parent->irep->rlen - 1; +} + +static void +gen_lvar(mrc_codegen_scope *s, mrc_sym name, int depth) +{ + if (depth == 0) { + gen_move(s, cursp(), lv_idx(s, name), 1); + } + else { + gen_getupvar(s, cursp(), name, depth); + } + push(); +} + +static void +gen_binary_operator(mrc_codegen_scope *s, mrc_sym binary_operator) +{ + if (binary_operator == MRC_OPSYM_2(add)) { + gen_addsub(s, OP_ADD, cursp()); + } + else if (binary_operator == MRC_OPSYM_2(sub)) { + gen_addsub(s, OP_SUB, cursp()); + } + else if (binary_operator == MRC_OPSYM_2(mul)) { + genop_1(s, OP_MUL, cursp()); + } + else if (binary_operator == MRC_OPSYM_2(div)) { + genop_1(s, OP_DIV, cursp()); + } + else { + int idx = new_sym(s, binary_operator); + genop_3(s, OP_SEND, cursp(), idx, 1); + } +} + +static void +regex_set_flags(pm_node_flags_t flags, char *p2, char *p3) +{ + int p2_len = 0; + if (flags&PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE) p2[p2_len++] = 'i'; + if (flags&PM_REGULAR_EXPRESSION_FLAGS_EXTENDED) p2[p2_len++] = 'x'; + if (flags&PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE) p2[p2_len++] = 'm'; + // mruby does not support once-only subexpression + // if (flags|PM_REGULAR_EXPRESSION_FLAGS_ONCE) p2[p2_len++] = 'o'; + if (flags&PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) p3[0] = 'e'; + else if (flags&PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) p3[0] = 'n'; + else if (flags&PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) p3[0] = 's'; + else if (flags&PM_REGULAR_EXPRESSION_FLAGS_UTF_8) p3[0] = 'u'; + // TODO??? + // /** internal bytes forced the encoding to UTF-8 */ + // PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING + // /** internal bytes forced the encoding to binary */ + // PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING + // /** internal bytes forced the encoding to US-ASCII */ + // PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING +} + +static void +gen_begin(mrc_codegen_scope *s, mrc_node *tree, int val) +{ + CAST(begin); + if (val && !cast->statements) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + if (cast->statements) { + size_t last_index = cast->statements->body.size; + for (uint32_t i = 0; i < last_index; i++) { + codegen(s, (mrc_node *)cast->statements->body.nodes[i], (i+1 < last_index) ? NOVAL : val); + } + } +} + +static void +gen_rescue(mrc_codegen_scope *s, mrc_node *tree, uint32_t *pos1, int *exc, uint32_t *extend, int val) +{ + CAST3(rescue, tree, rescue); + if (nint((mrc_node *)rescue) != PM_RESCUE_NODE) { + codegen_error(s, "should not happen"); + } + size_t i; + uint32_t pos2, tmp; + + dispatch(s, *pos1); + pos2 = JMPLINK_START; + + /* handle classes */ + if (rescue->exceptions.size == 0) { + genop_2(s, OP_GETCONST, cursp(), new_sym(s, MRC_SYM_1(StandardError))); + push(); + pop(); + genop_2(s, OP_RESCUE, *exc, cursp()); + tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, val); + pos2 = tmp; + } + else { + for (i = 0; i < rescue->exceptions.size; i++) { + if (nint((mrc_node *)rescue->exceptions.nodes[i]) == PM_SPLAT_NODE) { + codegen(s, (mrc_node *)rescue->exceptions.nodes[i], VAL); + gen_move(s, cursp(), *exc, 0); + push_n(2); pop_n(2); /* space for one arg and a block */ + pop(); + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_SYM_1(__case_eqq)), 1); + } + else { + codegen(s, (mrc_node *)rescue->exceptions.nodes[i], VAL); + pop(); + genop_2(s, OP_RESCUE, *exc, cursp()); + } + tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, val); + pos2 = tmp; + } + } + *pos1 = genjmp_0(s, OP_JMP); + dispatch_linked(s, pos2); + + pop(); + /* exc_var: `=> e` */ + if (rescue->reference) { + gen_assignment(s, rescue->reference, NULL, *exc, NOVAL); + } + /* handle body */ + codegen(s, (mrc_node *)rescue->statements, val); + if (val) pop(); + tmp = genjmp(s, OP_JMP, *extend); + *extend = tmp; + push(); + /* rest of rescue(s) */ + if (rescue->subsequent) { + gen_rescue(s, (mrc_node *)rescue->subsequent, pos1, exc, extend, val); + } +} + +static void +gen_ensure(mrc_codegen_scope *s, mrc_node *tree, uint32_t catch_entry, uint32_t begin) +{ + CAST3(ensure, tree, ensure); + int ensure_end, ensure_target; + int idx; + push(); + ensure_end = ensure_target = s->pc; + push(); + idx = cursp(); + genop_1(s, OP_EXCEPT, idx); + push(); + codegen(s, (mrc_node *)ensure->statements, NOVAL); + pop(); + genop_1(s, OP_RAISEIF, idx); + pop(); + catch_handler_set(s, catch_entry, MRC_CATCH_ENSURE, begin, ensure_end, ensure_target); +} + +static void +codegen(mrc_codegen_scope *s, mrc_node *tree, int val) +{ + int rlev = s->rlev; + + if (!tree) { + if (val) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + return; + } + + s->rlev++; + if (s->rlev > MRC_CODEGEN_LEVEL_MAX) { + codegen_error(s, "too complex expression"); + } + uint32_t token_pos = (uint32_t)(tree->location.start - s->c->p->start); + + if (s->filename_index+1 < s->c->filename_table_length) { + if (s->c->filename_table[s->filename_index+1].start <= token_pos) { + s->filename = (const char *)s->c->filename_table[s->filename_index++].filename; + + mrc_debug_info_append_file(s->c, s->irep->debug_info, + s->filename, s->lines, s->debug_start_pos, s->pc); + s->debug_start_pos = s->pc; + } + } + + int nt = nint(tree); + + s->lineno = node_lineno(s->c, tree); + + switch (nt) { + case PM_PROGRAM_NODE: { + scope_body(s, tree, val); + break; + } + case PM_STATEMENTS_NODE: + { + CAST(statements); + size_t last_index = cast->body.size; + if (last_index == 0) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + break; + } + for (uint32_t i = 0; i < last_index; i++) { + codegen(s, (mrc_node *)cast->body.nodes[i], (i+1 < last_index) ? NOVAL : val); + } + break; + } + case PM_INSTANCE_VARIABLE_READ_NODE: + { + CAST(instance_variable_read); + int sym = new_sym(s, cast->name); + + genop_2(s, OP_GETIV, cursp(), sym); + if (val) push(); + break; + } + case PM_LOCAL_VARIABLE_READ_NODE: + { + if (val) { + CAST(local_variable_read); + gen_lvar(s, cast->name, cast->depth); + } + break; + } + case PM_GLOBAL_VARIABLE_READ_NODE: + { + CAST(global_variable_read); + int sym = new_sym(s, cast->name); + genop_2(s, OP_GETGV, cursp(), sym); + if (val) push(); + break; + } + case PM_CLASS_VARIABLE_READ_NODE: + { + CAST(class_variable_read); + int sym = new_sym(s, cast->name); + genop_2(s, OP_GETCV, cursp(), sym); + if (val) push(); + break; + } + case PM_CONSTANT_READ_NODE: + { + CAST(constant_read); + int sym = new_sym(s, cast->name); + genop_2(s, OP_GETCONST, cursp(), sym); + if (val) push(); + break; + } +#define case_WRITE_NODE(NODE_TYPE, CAST_TYPE) \ + case NODE_TYPE##_WRITE_NODE: \ + { \ + CAST_TYPE##_write_node_t *cast = (CAST_TYPE##_write_node_t *)tree; \ + gen_assignment(s, tree, (mrc_node *)cast->value, 0, val); \ + break; \ + } \ + case NODE_TYPE##_TARGET_NODE: \ + { \ + gen_assignment(s, tree, NULL, 0, val); \ + break; \ + } + case_WRITE_NODE(PM_INSTANCE_VARIABLE, pm_instance_variable) + case_WRITE_NODE(PM_LOCAL_VARIABLE, pm_local_variable) + case_WRITE_NODE(PM_CONSTANT, pm_constant) + case_WRITE_NODE(PM_GLOBAL_VARIABLE, pm_global_variable) + case_WRITE_NODE(PM_CLASS_VARIABLE, pm_global_variable) + case_WRITE_NODE(PM_CONSTANT_PATH, pm_constant_path) + case PM_MULTI_WRITE_NODE: + { + CAST(multi_write); + size_t len = 0, n = 0, post = 0; + CAST3(array, cast->value, t); + int rhs = cursp(); + + if (!val && nint((mrc_node *)t) == PM_ARRAY_NODE && !(t->base.flags & PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT) ) { + /* fixed rhs */ + len = t->elements.size; + for (size_t i = 0; i < len; i++) { + codegen(s, t->elements.nodes[i], VAL); + } + if (0 < cast->lefts.size) { + n = 0; + for (size_t i = 0; i < cast->lefts.size; i++) { + if (i < len) { + gen_assignment(s, cast->lefts.nodes[i], NULL, rhs+n, NOVAL); + n++; + } + else { + genop_1(s, OP_LOADNIL, rhs+n); + gen_assignment(s, cast->lefts.nodes[i], NULL, rhs+n, NOVAL); + } + } + } + post = cast->rights.size; + if (cast->rest) { + int rn; + if (len < post + n) { + rn = 0; + } + else { + rn = len - post - n; + } + if (cursp() == rhs+n) { + genop_2(s, OP_ARRAY, cursp(), rn); + } + else { + genop_3(s, OP_ARRAY2, cursp(), rhs+n, rn); + } + gen_assignment(s, ((pm_splat_node_t *)cast->rest)->expression, NULL, cursp(), NOVAL); + n += rn; + } + if (0 < post) { + for (size_t i = 0; i < post; i++) { + if (n < len) { + gen_assignment(s, cast->rights.nodes[i], NULL, rhs+n, NOVAL); + } + else { + genop_1(s, OP_LOADNIL, cursp()); + gen_assignment(s, cast->rights.nodes[i], NULL, cursp(), NOVAL); + n++; + } + } + } + pop_n(len); + } + else { + /* variable rhs */ + codegen(s, cast->value, VAL); + gen_massignment(s, tree, rhs, val); + if (!val) { + pop(); + } + } + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: + { + codegen_error(s, "constant re-assignment"); + break; + } + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: + case PM_CONSTANT_OPERATOR_WRITE_NODE: + { + mrc_sym name = -1, binary_operator = -1; + mrc_node *value = NULL; + int op_set = -1, op_get = -1, depth = -1; +#define CAST_OP_WRITE(type) \ + CAST(type); \ + name = cast->name; \ + value = cast->value; \ + binary_operator = cast->binary_operator + switch (nt) { + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: + { + CAST_OP_WRITE(local_variable_operator_write); + depth = cast->depth; + break; + } + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: + { + CAST_OP_WRITE(global_variable_operator_write); + op_set = OP_SETGV; op_get = OP_GETGV; + break; + } + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: + { + CAST_OP_WRITE(class_variable_operator_write); + op_set = OP_SETCV; op_get = OP_GETCV; + break; + } + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: + { + CAST_OP_WRITE(instance_variable_operator_write); + op_set = OP_SETIV; op_get = OP_GETIV; + break; + } + case PM_CONSTANT_OPERATOR_WRITE_NODE: + { + CAST_OP_WRITE(constant_operator_write); + op_set = OP_SETCONST; op_get = OP_GETCONST; + break; + } + default: codegen_error(s, "Not implemented (#5)"); + } + switch (nt) { + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: + gen_lvar(s, name, depth); + break; + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: + case PM_CONSTANT_OPERATOR_WRITE_NODE: + genop_2(s, op_get, cursp(), new_sym(s, name)); + push(); + break; + default: codegen_error(s, "Not implemented (#6)"); + } + codegen(s, (mrc_node *)value, VAL); + push(); pop(); + pop(); pop(); + + gen_binary_operator(s, binary_operator); + switch (nt) { + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: + gen_assignment_lvar(s, cursp(), name, depth, val); + break; + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: + case PM_CONSTANT_OPERATOR_WRITE_NODE: + gen_setxv(s, op_set, cursp(), name, val); + break; + default: codegen_error(s, "Not implemented (#7)"); + } + if (val) push(); + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: + case PM_CALL_OR_WRITE_NODE: + case PM_CALL_AND_WRITE_NODE: + { +#define CAST_CALL_WRITE(type) \ + CAST(type); \ + receiver = (mrc_node *)cast->receiver; \ + value = (mrc_node *)cast->value; \ + read_name = cast->read_name; \ + write_name = cast->write_name; + mrc_node *receiver = NULL, *value = NULL; + mrc_sym read_name = -1, write_name = -1, binary_operator = -1, op_jmp = -1; + uint32_t pos = -1; + switch (nt) { + case PM_CALL_OPERATOR_WRITE_NODE: + { + CAST_CALL_WRITE(call_operator_write); + binary_operator = cast->binary_operator; + break; + } + case PM_CALL_OR_WRITE_NODE: + { + CAST_CALL_WRITE(call_or_write); + op_jmp = OP_JMPIF; + break; + } + case PM_CALL_AND_WRITE_NODE: + { + CAST_CALL_WRITE(call_and_write); + op_jmp = OP_JMPNOT; + break; + } + default: codegen_error(s, "Not implemented (call_operator|or|and_write)"); + } + int base; + int idx, vsp = -1; + if (val) { + vsp = cursp(); + push(); + } + codegen(s, receiver, VAL); + idx = new_sym(s, read_name); + base = cursp()-1; + /* copy receiver and arguments */ + gen_move(s, cursp(), base, 1); + push_n(2); pop_n(2); /* space for receiver, arguments and a block */ + genop_3(s, OP_SEND, cursp(), idx, 0); + + if (-1 != (int32_t)binary_operator) { + push(); + codegen(s, value, VAL); + push(); pop(); + pop(); pop(); + gen_binary_operator(s, binary_operator); + } + else { /* OR or AND */ + if (0 <= vsp) { + gen_move(s, vsp, cursp(), 0); + } + pos = genjmp2_0(s, op_jmp, cursp(), val); + codegen(s, value, VAL); + pop(); + } + if (0 <= vsp) { + gen_move(s, vsp, cursp(), 0); + } + pop(); + idx = new_sym(s, write_name); + genop_3(s, OP_SEND, cursp(), idx, 1); + if (0 < pos) { dispatch(s, pos); } + break; + } + case PM_INDEX_OPERATOR_WRITE_NODE: + case PM_INDEX_OR_WRITE_NODE: + case PM_INDEX_AND_WRITE_NODE: + { +#define CAST_INDEX_WRITE(type) \ + CAST(type); \ + receiver = (mrc_node *)cast->receiver; \ + value = (mrc_node *)cast->value; \ + arguments = (mrc_node *)cast->arguments; + mrc_node *receiver, *value, *arguments; + mrc_sym binary_operator = -1; + mrc_sym op_jmp = -1; + switch (nt) { + case PM_INDEX_OPERATOR_WRITE_NODE: + { + CAST_INDEX_WRITE(index_operator_write); + binary_operator = cast->binary_operator; + break; + } + case PM_INDEX_OR_WRITE_NODE: + { + CAST_INDEX_WRITE(index_or_write); + op_jmp = OP_JMPIF; + break; + } + case PM_INDEX_AND_WRITE_NODE: + { + CAST_INDEX_WRITE(index_and_write); + op_jmp = OP_JMPNOT; + break; + } + default: + { + codegen_error(s, "Not implemented (index_operator|or|and_write)"); + return; + } + } + int base, nargs = 0; + int idx, callargs = -1, vsp = -1; + int32_t pos = -1; + if (val) { + vsp = cursp(); + push(); + } + codegen(s, (mrc_node *)receiver, VAL); + idx = new_sym(s, MRC_OPSYM_2(aref)); + base = cursp()-1; + nargs = gen_values(s, (mrc_node *)arguments, VAL, 13); + if (nargs >= 0) { + callargs = nargs; + } + else { /* varargs */ + push(); + nargs = 1; + callargs = CALL_MAXARGS; + } + /* copy receiver and arguments */ + gen_move(s, cursp(), base, 1); + for (int i = 0; i < nargs; i++) { + gen_move(s, cursp()+i+1, base+i+1, 1); + } + push_n(nargs + 2); pop_n(nargs + 2); /* space for receiver, arguments and a block */ + genop_3(s, OP_SEND, cursp(), idx, callargs); + if (-1 != (int32_t)binary_operator) { + push(); + codegen(s, value, VAL); + push(); pop(); + pop(); pop(); + gen_binary_operator(s, binary_operator); + } + else { /* OR or AND */ + if (0 <= vsp) { + gen_move(s, vsp, cursp(), 0); + } + pos = genjmp2_0(s, op_jmp, cursp(), val); + codegen(s, value, VAL); + pop(); + dispatch(s, pos); + } + if (val && vsp >= 0) { + gen_move(s, vsp, cursp(), 0); + } + if (callargs == CALL_MAXARGS) { + pop(); + genop_2(s, OP_ARYPUSH, cursp(), 1); + } + else { + pop_n(callargs); + callargs++; + } + pop(); + idx = new_sym(s, MRC_OPSYM_2(aset)); + genop_3(s, OP_SEND, cursp(), idx, callargs); + if (0 <= pos) { dispatch(s, pos); } + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: + case PM_CLASS_VARIABLE_OR_WRITE_NODE: + case PM_CLASS_VARIABLE_AND_WRITE_NODE: + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: + case PM_CONSTANT_OR_WRITE_NODE: + case PM_CONSTANT_AND_WRITE_NODE: + { + mrc_sym name = 0; + mrc_node *value = NULL; + int op_set = -1, op_get = -1, depth = -1; + int op_jmp = OP_JMPNOT; +#define CAST_OR_WRITE(type) \ + CAST(type); \ + name = cast->name; \ + value = cast->value + switch (nt) { + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: op_jmp = OP_JMPIF; /* fall through */ + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: + { + CAST_OR_WRITE(local_variable_or_write); + depth = cast->depth; + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: op_jmp = OP_JMPIF; /* fall through */ + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: + { + CAST_OR_WRITE(instance_variable_or_write); + op_set = OP_SETIV; op_get = OP_GETIV; + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: op_jmp = OP_JMPIF; /* fall through */ + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: + { + CAST_OR_WRITE(global_variable_or_write); + op_set = OP_SETGV; op_get = OP_GETGV; + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: op_jmp = OP_JMPIF; /* fall through */ + case PM_CLASS_VARIABLE_AND_WRITE_NODE: + { + CAST_OR_WRITE(class_variable_or_write); + op_set = OP_SETCV; op_get = OP_GETCV; + break; + } + case PM_CONSTANT_OR_WRITE_NODE: op_jmp = OP_JMPIF; /* fall through */ + case PM_CONSTANT_AND_WRITE_NODE: + { + CAST_OR_WRITE(constant_or_write); + op_set = OP_SETCONST; op_get = OP_GETCONST; + break; + } + default: codegen_error(s, "Not implemented (or_write_node)"); + } + switch (nt) { + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: + gen_lvar(s, name, depth); + break; + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: + case PM_CLASS_VARIABLE_AND_WRITE_NODE: + case PM_CONSTANT_AND_WRITE_NODE: + genop_2(s, op_get, cursp(), new_sym(s, name)); + push(); + break; + case PM_CLASS_VARIABLE_OR_WRITE_NODE: + case PM_CONSTANT_OR_WRITE_NODE: + { + int catch_entry, begin, end; + int noexc, exc; + struct loopinfo *lp; + + lp = loop_push(s, LOOP_BEGIN); + lp->pc0 = new_label(s); + catch_entry = catch_handler_new(s); + begin = s->pc; + exc = cursp(); + genop_2(s, op_get, cursp(), new_sym(s, name)); + push(); + end = s->pc; + noexc = genjmp_0(s, OP_JMP); + lp->type = LOOP_RESCUE; + catch_handler_set(s, catch_entry, MRC_CATCH_RESCUE, begin, end, s->pc); + genop_1(s, OP_EXCEPT, exc); + genop_1(s, OP_LOADF, exc); + dispatch(s, noexc); + loop_pop(s, NOVAL); + break; + } + default: codegen_error(s, "Not implemented (or_write_node)"); + } + uint32_t pos; + pop(); + pos = genjmp2_0(s, op_jmp, cursp(), val); + codegen(s, value, VAL); + pop(); + switch (nt) { + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: + gen_assignment_lvar(s, cursp(), name, depth, val); + break; + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: + case PM_CLASS_VARIABLE_OR_WRITE_NODE: + case PM_CLASS_VARIABLE_AND_WRITE_NODE: + case PM_CONSTANT_OR_WRITE_NODE: + case PM_CONSTANT_AND_WRITE_NODE: + gen_setxv(s, op_set, cursp(), name, val); + push(); + break; + default: codegen_error(s, "Not implemented (or_write)"); + } + dispatch(s, pos); + break; + } + case PM_INTEGER_NODE: + { + if (val) { + CAST(integer); + if (cast->value.length == 0) { + if (!cast->value.negative) { + gen_int(s, cursp(), (mrc_int)cast->value.value); + } + else { + gen_int(s, cursp(), (mrc_int)cast->value.value * -1); + } + } + else { + if (cast->value.length == 2) { +#ifdef MRC_INT64 + mrc_uint value = ((mrc_uint)cast->value.values[0])|((mrc_uint)cast->value.values[1] << 32); + if (!cast->value.negative && MRC_INT_MAX < value) goto overflow; + if (cast->value.negative) { + if (value < MRC_INT_MIN) goto overflow; + value *= -1; + } + gen_int(s, cursp(), value); +#else + goto overflow; +#endif + } + else { + overflow: + { + pm_buffer_t buf = {0}; + pm_integer_string(&buf, &cast->value); + buf.value[buf.length] = '\0'; + if (cast->value.negative) { + memmove(buf.value, buf.value+1, buf.length); + buf.length--; + } + int off = new_litbint(s, buf.value, 10, cast->value.negative); + genop_2(s, OP_LOADL, cursp(), off); + pm_buffer_free(&buf); + } + } + } + push(); + } + break; + } +#ifndef MRC_NO_FLOAT + case PM_FLOAT_NODE: + { + if (val) { + CAST(float); + int off = new_lit_float(s, (mrc_float)cast->value); + genop_2(s, OP_LOADL, cursp(), off); + push(); + } + break; + } +#endif + case PM_CALL_NODE: + { + CAST(call); + gen_call(s, tree, val, (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) ? 1 : 0); + break; + } + case PM_ARRAY_NODE: + case PM_ARGUMENTS_NODE: /* TODO: Confirm when multi args in return */ + { + int n; + n = gen_values(s, tree, val, 0); + if (val) { + if (n >= 0) { + pop_n(n); + genop_2(s, OP_ARRAY, cursp(), n); + } + push(); + } + break; + } + case PM_SYMBOL_NODE: + { + if (val) { + CAST(symbol); + int sym = new_sym(s, nsym(s->c->p, cast->unescaped.source, cast->unescaped.length)); + + genop_2(s, OP_LOADSYM, cursp(), sym); + push(); + } + break; + } + case PM_KEYWORD_HASH_NODE: + case PM_HASH_NODE: + { + int nk = gen_hash(s, tree, val, GEN_LIT_ARY_MAX); + if (val && nk >= 0) { + pop_n(nk*2); + genop_2(s, OP_HASH, cursp(), nk); + push(); + } + break; + } + case PM_SPLAT_NODE: + { + CAST(splat); + codegen(s, (mrc_node *)cast->expression, val); + break; + } + case PM_STRING_NODE: + { + if (val) { + CAST(string); + char *p = (char *)cast->unescaped.source; + mrc_int len = cast->unescaped.length; + int off = new_lit_str(s, p, len); + + genop_2(s, OP_STRING, cursp(), off); + push(); + } + break; + } + case PM_X_STRING_NODE: + { + CAST(x_string); + char *p = (char *)cast->unescaped.source; + mrc_int len = cast->unescaped.length; + int off = new_lit_str(s, p, len); + int sym = new_sym(s, MRC_OPSYM_2(tick)); + + genop_1(s, OP_LOADSELF, cursp()); + push(); + genop_2(s, OP_STRING, cursp(), off); + push(); push(); + pop_n(3); + genop_3(s, OP_SEND, cursp(), sym, 1); + if (val) push(); + break; + } + case PM_REGULAR_EXPRESSION_NODE: + { + if (val) { + CAST(regular_expression); + char *p1 = (char *)cast->unescaped.source; + char p2[4] = {0, 0, 0, 0}; + char p3[2] = {0, 0}; + regex_set_flags(cast->base.flags, p2, p3); + int sym = new_sym(s, MRC_SYM_1(Regexp)); + int off = new_lit_str(s, p1, cast->unescaped.length); + int argc = 1; + + genop_1(s, OP_OCLASS, cursp()); + genop_2(s, OP_GETMCNST, cursp(), sym); + push(); + genop_2(s, OP_STRING, cursp(), off); + push(); + if (p2[0] || p3[0]) { + if (p2[0]) { /* opt */ + off = new_lit_cstr(s, p2); + genop_2(s, OP_STRING, cursp(), off); + } + else { + genop_1(s, OP_LOADNIL, cursp()); + } + push(); + argc++; + if (p3[0]) { /* enc */ + off = new_lit_str(s, p3, 1); + genop_2(s, OP_STRING, cursp(), off); + push(); + argc++; + } + } + push(); /* space for a block */ + pop_n(argc+2); + sym = new_sym(s, MRC_SYM_1(compile)); + genop_3(s, OP_SEND, cursp(), sym, argc); + push(); + } + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + { + CAST(interpolated_regular_expression); + if (val) { + int sym = new_sym(s, MRC_SYM_1(Regexp)); + int argc = 1; + + genop_1(s, OP_OCLASS, cursp()); + genop_2(s, OP_GETMCNST, cursp(), sym); + push(); + + mrc_bool str_begin = FALSE; + if (nint(cast->parts.nodes[0]) != PM_STRING_NODE) { + genop_2(s, OP_STRING, cursp(), new_lit_cstr(s, "")); + push(); + str_begin = TRUE; + } + for (size_t i = 0; i < cast->parts.size; i++) { + codegen(s, cast->parts.nodes[i], VAL); + pop(); + if (str_begin || 0 < i) { + pop(); + genop_1(s, OP_STRCAT, cursp()); + } + push(); + } + + char p2[4] = {0, 0, 0, 0}; + char p3[2] = {0, 0}; + regex_set_flags(cast->base.flags, p2, p3); + if (p2[0]) { /* opt */ + genop_2(s, OP_STRING, cursp(), new_lit_cstr(s, p2)); + push(); + argc++; + } + if (p3[0]) { /* enc */ + genop_2(s, OP_STRING, cursp(), new_lit_cstr(s, p3)); + push(); + argc++; + } + push(); /* space for a block */ + pop_n(argc+2); + sym = new_sym(s, MRC_SYM_1(compile)); + genop_3(s, OP_SEND, cursp(), sym, argc); + push(); + } + else { + for (size_t i = 0; i < cast->parts.size; i++) { + if (nint(cast->parts.nodes[i]) != PM_STRING_NODE) { + codegen(s, cast->parts.nodes[i], NOVAL); + } + } + } + break; + } + case PM_BACK_REFERENCE_READ_NODE: + { + if (val) { + int sym = new_sym(s, MRC_SYM_2(back_ref)); + genop_2(s, OP_GETGV, cursp(), sym); + push(); + } + break; + } + case PM_NUMBERED_REFERENCE_READ_NODE: + { + if (val) { + CAST(numbered_reference_read); + char buf[100]; + buf[0] = '$'; + sprintf(buf+1, "%d", cast->number); + int sym = new_sym(s, nsym(s->c->p, (const uint8_t *)buf, strlen(buf))); + genop_2(s, OP_GETGV, cursp(), sym); + push(); + } + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: + { + CAST(embedded_statements); + codegen(s, (mrc_node *)cast->statements, val); + break; + } + case PM_INTERPOLATED_STRING_NODE: + case PM_INTERPOLATED_SYMBOL_NODE: + { + size_t i; + mrc_node **nodes; + uint32_t size; + if (nt == PM_INTERPOLATED_SYMBOL_NODE) { + CAST(interpolated_symbol); + nodes = (mrc_node **)cast->parts.nodes; + size = cast->parts.size; + } + else { + CAST(interpolated_string); + nodes = (mrc_node **)cast->parts.nodes; + size = cast->parts.size; + } + mrc_bool str_begin = FALSE; + if (val) { + if (nint(nodes[0]) != PM_STRING_NODE) { + genop_2(s, OP_STRING, cursp(), new_lit_cstr(s, "")); + push(); + str_begin = TRUE; + } + for (i = 0; i < size; i++) { + codegen(s, nodes[i], VAL); + pop(); + if (str_begin || 0 < i) { + pop(); + genop_1(s, OP_STRCAT, cursp()); + } + push(); + } + } + else { + /* example: + * def my_method + * "Hey, #{something} happens!" + * return 1 + * end + * # The return value of the interpolated string will not be used. + * # (This is a case when val is FALSE) + * # So we ignore `'Hey, '` and `' happens!'`. + * # However, we need to evaluate `something` as it may have side effects. + */ + for (i = 0; i < size; i++) { + if (nint(nodes[i]) != PM_STRING_NODE) { + codegen(s, nodes[i], NOVAL); + pop(); + } + } + } + if (nt == PM_INTERPOLATED_SYMBOL_NODE) { + if (val) { + pop(); + genop_1(s, OP_INTERN, cursp()); + push(); + } + } + break; + } + case PM_INTERPOLATED_X_STRING_NODE: + { + size_t i; + CAST(interpolated_x_string); + int sym = new_sym(s, MRC_SYM_1(Kernel)); + + genop_1(s, OP_LOADSELF, cursp()); + push(); + codegen(s, (mrc_node *)cast->parts.nodes[0], VAL); + for (i = 1; i < cast->parts.size; i++) { + codegen(s, (mrc_node *)cast->parts.nodes[i], VAL); + pop(); pop(); + genop_1(s, OP_STRCAT, cursp()); + push(); + } + push(); + pop_n(3); + sym = new_sym(s, MRC_OPSYM_2(tick)); + genop_3(s, OP_SEND, cursp(), sym, 1); + if (val) push(); + break; + } + case PM_SINGLETON_CLASS_NODE: + { + CAST(singleton_class); + int idx; + codegen(s, cast->expression, VAL); + pop(); + genop_1(s, OP_SCLASS, cursp()); + if (cast->body == NULL) { + genop_1(s, OP_LOADNIL, cursp()); + } + else { + idx = scope_body(s, tree, val); + genop_2(s, OP_EXEC, cursp(), idx); + } + if (val) { + push(); + } + break; + } + case PM_DEF_NODE: + { + CAST(def); + int sym = new_sym(s, cast->name); + int idx = lambda_body(s, (mrc_node *)cast, cast->body, &cast->locals, 0); + + if (cast->receiver == NULL) { + genop_1(s, OP_TCLASS, cursp()); + push(); + } + else { + codegen(s, cast->receiver, VAL); + pop(); + genop_1(s, OP_SCLASS, cursp()); + push(); + } + genop_2(s, OP_METHOD, cursp(), idx); + push(); pop(); + pop(); + genop_2(s, OP_DEF, cursp(), sym); + if (val) push(); + break; + } + case PM_LAMBDA_NODE: + { + if (val) { + CAST(lambda); + mrc_node *parameters = NULL; + if ((pm_block_parameters_node_t *)cast->parameters) { + parameters = (mrc_node *)cast->parameters; + } + int idx = lambda_body(s, parameters, cast->body, &cast->locals, 1); + genop_2(s, OP_LAMBDA, cursp(), idx); + push(); + } + break; + } + case PM_BLOCK_NODE: + { + if (val) { + CAST(block); + mrc_node *parameters = NULL; + if ((pm_block_parameters_node_t *)cast->parameters) { + parameters = (mrc_node *)cast->parameters; + } + int idx = lambda_body(s, parameters, cast->body, &cast->locals, 1); + genop_2(s, OP_BLOCK, cursp(), idx); + push(); + } + break; + } + case PM_IF_NODE: + case PM_UNLESS_NODE: + { + mrc_node *predicate, *subsequent, *statements; + if (nt == PM_IF_NODE) { + CAST(if); + predicate = (mrc_node *)cast->predicate; + subsequent = (mrc_node *)cast->subsequent; + statements = (mrc_node *)cast->statements; + } + else { /* unless */ + CAST(unless); + predicate = (mrc_node *)cast->predicate; + subsequent = (mrc_node *)cast->statements; /* opposite */ + statements = (mrc_node *)cast->else_clause; /* opposite */ + } + uint32_t pos1, pos2; + mrc_bool nil_p = FALSE; + + if (!predicate) { + codegen(s, subsequent, val); + goto exit; + } + if (true_always(predicate)) { + codegen(s, statements, val); + goto exit; + } + if (false_always(predicate)) { + codegen(s, subsequent, val); + goto exit; + } + if (nint(predicate) == PM_CALL_NODE) { + pm_call_node_t *n = (pm_call_node_t *)predicate; + mrc_sym mid = n->name; + mrc_sym sym_nil_p = MRC_SYM_2(nil_p); + if (mid == sym_nil_p && n->arguments == NULL) { + nil_p = TRUE; + codegen(s, (mrc_node *)n->receiver, VAL); + } + } + if (!nil_p) { + codegen(s, predicate, VAL); + } + pop(); + if (val || statements) { + if (nil_p) { + pos2 = genjmp2_0(s, OP_JMPNIL, cursp(), val); + pos1 = genjmp_0(s, OP_JMP); + dispatch(s, pos2); + } + else { + pos1 = genjmp2_0(s, OP_JMPNOT, cursp(), val); + } + codegen(s, statements, val); + if (val) pop(); + if (subsequent || val) { + pos2 = genjmp_0(s, OP_JMP); + dispatch(s, pos1); + codegen(s, subsequent, val); + dispatch(s, pos2); + } + else { + dispatch(s, pos1); + } + } + else { /* empty then-part */ + if (subsequent) { + if (nil_p) { + pos1 = genjmp2_0(s, OP_JMPNIL, cursp(), val); + } + else { + pos1 = genjmp2_0(s, OP_JMPIF, cursp(), val); + } + codegen(s, subsequent, val); + dispatch(s, pos1); + } + else if (val && !nil_p) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + } + break; + } + case PM_ELSE_NODE: + { + CAST(else); + codegen(s, (mrc_node *)cast->statements, val); + break; + } + case PM_AND_NODE: + { + CAST(and); + uint32_t pos; + if (true_always(cast->left)) { + codegen(s, cast->right, val); + goto exit; + } + if (false_always(cast->left)) { + codegen(s, cast->left, val); + goto exit; + } + codegen(s, cast->left, VAL); + pop(); + pos = genjmp2_0(s, OP_JMPNOT, cursp(), val); + codegen(s, cast->right, val); + dispatch(s, pos); + break; + } + case PM_OR_NODE: + { + CAST(or); + uint32_t pos; + if (true_always(cast->left)) { + codegen(s, cast->left, val); + goto exit; + } + if (false_always(cast->left)) { + codegen(s, cast->right, val); + goto exit; + } + codegen(s, cast->left, VAL); + pop(); + pos = genjmp2_0(s, OP_JMPIF, cursp(), val); + codegen(s, cast->right, val); + dispatch(s, pos); + break; + } + case PM_PARENTHESES_NODE: + { + CAST(parentheses); + codegen(s, cast->body, val); + break; + } + case PM_WHILE_NODE: + case PM_UNTIL_NODE: + { + CAST(while); /* Compatible with until? */ + if (true_always(cast->predicate)) { + if (nt == PM_UNTIL_NODE) { + if (val) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + goto exit; + } + } + else if (false_always(cast->predicate)) { + if (nt == PM_WHILE_NODE) { + if (val) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + goto exit; + } + } + + uint32_t pos = JMPLINK_START; + struct loopinfo *lp = loop_push(s, LOOP_NORMAL); + + if (!val) lp->reg = -1; + lp->pc0 = new_label(s); + codegen(s, cast->predicate, VAL); + pop(); + if (nt == PM_WHILE_NODE) { + pos = genjmp2_0(s, OP_JMPNOT, cursp(), NOVAL); + } + else { + pos = genjmp2_0(s, OP_JMPIF, cursp(), NOVAL); + } + lp->pc1 = new_label(s); + codegen(s, (mrc_node *)cast->statements, NOVAL); + genjmp(s, OP_JMP, lp->pc0); + dispatch(s, pos); + loop_pop(s, val); + break; + } + case PM_FOR_NODE: + { + for_body(s, tree); + if (val) push(); + break; + } + case PM_CASE_NODE: + { + CAST(case); + int head = 0; + uint32_t pos1, pos2, pos3, tmp; + + pos3 = JMPLINK_START; + if (cast->predicate) { + head = cursp(); + codegen(s, (mrc_node *)cast->predicate, VAL); + } + for (size_t i = 0; i < cast->conditions.size; i++) { + pm_when_node_t *when = (pm_when_node_t *)cast->conditions.nodes[i]; + pos1 = pos2 = JMPLINK_START; + for (size_t j = 0; j < when->conditions.size; j++) { + mrc_node *cond = when->conditions.nodes[j]; + mrc_bool splat = FALSE; + if (nint(cond) == PM_SPLAT_NODE) { + splat = TRUE; + codegen(s, (mrc_node *)((pm_splat_node_t *)cond)->expression, VAL); + } + else { + codegen(s, cond, VAL); + } + if (head) { + gen_move(s, cursp(), head, 0); + push(); push(); pop(); pop(); pop(); + if (splat) { + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_SYM_1(__case_eqq)), 1); + } + else { + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(eqq)), 1); + } + } + else { + pop(); + } + tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, !head); + pos2 = tmp; + } + pos1 = genjmp_0(s, OP_JMP); + dispatch_linked(s, pos2); + codegen(s, (mrc_node *)when->statements, val); + if (val) pop(); + tmp = genjmp(s, OP_JMP, pos3); + pos3 = tmp; + dispatch(s, pos1); + } + if (cast->else_clause) { + codegen(s, (mrc_node *)cast->else_clause, val); + if (val) pop(); + tmp = genjmp(s, OP_JMP, pos3); + pos3 = tmp; + } + if (val) { + uint32_t pos = cursp(); + genop_1(s, OP_LOADNIL, pos); + if (pos3 != JMPLINK_START) dispatch_linked(s, pos3); + if (head) pop(); + if (cursp() != pos) { + gen_move(s, cursp(), pos, 0); + } + push(); + } + else { + if (pos3 != JMPLINK_START) dispatch_linked(s, pos3); + if (head) pop(); + } + break; + } + case PM_SELF_NODE: + { + if (val) { + genop_1(s, OP_LOADSELF, cursp()); + push(); + } + break; + } + case PM_TRUE_NODE: + { + if (val) { + genop_1(s, OP_LOADT, cursp()); + push(); + } + break; + } + case PM_FALSE_NODE: + { + if (val) { + genop_1(s, OP_LOADF, cursp()); + push(); + } + break; + } + case PM_NIL_NODE: + { + if (val) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + break; + } + case PM_CONSTANT_PATH_NODE: + { + CAST(constant_path); + int sym = new_sym(s, cast->name); + if (cast->parent) { + codegen(s, cast->parent, VAL); + pop(); + } + else { /* NODE_COLON3 */ + genop_1(s, OP_OCLASS, cursp()); + } + genop_2(s, OP_GETMCNST, cursp(), sym); + if (val) push(); + break; + } + case PM_CLASS_NODE: + { + int idx; + CAST(class); + mrc_node *cpath = (mrc_node *)cast->constant_path; + switch (nint(cpath)) { + case PM_CONSTANT_READ_NODE: + { + genop_1(s, OP_LOADNIL, cursp()); + push(); + break; + } + case PM_CONSTANT_PATH_NODE: + { + mrc_node *parent = ((pm_constant_path_node_t *)cpath)->parent; + codegen(s, parent, VAL); + break; + } + default: + codegen_error(s, "Invalid constant path node"); + } + + if (cast->superclass) { + codegen(s, cast->superclass, VAL); + } + else { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + pop(); pop(); + idx = new_sym(s, cast->name); + genop_2(s, OP_CLASS, cursp(), idx); + if (!cast->body) { + genop_1(s, OP_LOADNIL, cursp()); + } + else { + idx = scope_body(s, tree, val); + genop_2(s, OP_EXEC, cursp(), idx); + } + if (val) { + push(); + } + break; + } + case PM_MODULE_NODE: + { + int idx; + CAST(module); + switch nint(cast->constant_path) { + case PM_CONSTANT_PATH_NODE: + { + CAST3(constant_path, cast->constant_path, cpath); + // TODO: they should be wrong + if (cpath->parent == (mrc_node *)1) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + else if (cpath->parent == (mrc_node *)0) { + genop_1(s, OP_OCLASS, cursp()); + push(); + } + else { + codegen(s, cpath->parent, VAL); + } + break; + } + case PM_CONSTANT_READ_NODE: + // CAST3(constant_read, cast->constant_path, read); + genop_1(s, OP_LOADNIL, cursp()); + push(); + break; + default: + codegen_error(s, "Invalid constant path node"); + } + pop(); + idx = new_sym(s, cast->name); + genop_2(s, OP_MODULE, cursp(), idx); + if (!cast->body) { + genop_1(s, OP_LOADNIL, cursp()); + } + else { + idx = scope_body(s, tree, val); + genop_2(s, OP_EXEC, cursp(), idx); + } + if (val) { + push(); + } + break; + } + case PM_ALIAS_METHOD_NODE: + { + CAST(alias_method); + CAST3(symbol, cast->new_name, new_name); + CAST3(symbol, cast->old_name, old_name); + int a = new_sym(s, nsym(s->c->p, new_name->unescaped.source, new_name->unescaped.length)); + int b = new_sym(s, nsym(s->c->p, old_name->unescaped.source, old_name->unescaped.length)); + genop_2(s, OP_ALIAS, a, b); + if (val) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + break; + } + case PM_UNDEF_NODE: + { + CAST(undef); + for (size_t i = 0; i < cast->names.size; i++) { + CAST3(symbol, cast->names.nodes[i], name); + int symbol = new_sym(s, nsym(s->c->p, name->unescaped.source, name->unescaped.length)); + genop_1(s, OP_UNDEF, symbol); + } + if (val) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + break; + } + case PM_SUPER_NODE: + { + CAST(super); + mrc_codegen_scope *s2 = s; + int lv = 0; + int n = 0, nk = 0, st = 0; + + push(); + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + CAST3(arguments, cast->arguments, arguments); + if (arguments) { + st = n = gen_values(s, (mrc_node *)arguments, VAL, 14); + if (n < 0) { + st = 1; n = 15; + push(); + } + /* keyword arguments */ + for (size_t i = 0; i < arguments->arguments.size; i++) { + mrc_node *t = (mrc_node *)arguments->arguments.nodes[i]; + if (nint(t) == PM_KEYWORD_HASH_NODE) { + nk = gen_hash(s, t, VAL, 14); + if (nk < 0) {st++; nk = 15;} + else st += nk*2; + n |= nk<<4; + } + } + /* block argument */ + if (cast->block) { + codegen(s, (mrc_node *)cast->block, VAL); + } + else if (s2) gen_blkmove(s, s2->ainfo, lv); + else { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + } + else { /* `super()` parentheses without argument */ + if (s2) gen_blkmove(s, s2->ainfo, lv); + else { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + } + st++; + pop_n(st+1); + genop_2(s, OP_SUPER, cursp(), n); + if (val) push(); + break; + } + case PM_FORWARDING_SUPER_NODE: + { + CAST(forwarding_super); + mrc_codegen_scope *s2 = s; + int lv = 0; + uint16_t ainfo = 0; + int n = CALL_MAXARGS; + int sp = cursp(); + + push(); /* room for receiver */ + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + if (s2 && s2->ainfo > 0) { + ainfo = s2->ainfo; + } + if (ainfo > 0) { + genop_2S(s, OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)); + push(); push(); push(); /* ARGARY pushes 3 values at most */ + pop(); pop(); pop(); + /* keyword arguments */ + if (ainfo & 0x1) { + n |= CALL_MAXARGS<<4; + push(); + } + /* block argument */ + if (cast->block) { + push(); + codegen(s, (mrc_node *)cast->block, VAL); + } + } + else { + /* block argument */ + if (cast->block) { + codegen(s, (mrc_node *)cast->block, VAL); + } + else { + gen_blkmove(s, 0, lv); + } + n = 0; + } + s->sp = sp; + genop_2(s, OP_SUPER, cursp(), n); + if (val) push(); + break; + } + case PM_RETURN_NODE: + { + CAST(return); + if (cast->arguments) { + gen_retval(s, (mrc_node *)cast->arguments); + } + else { + genop_1(s, OP_LOADNIL, cursp()); + } + if (s->loop) { + gen_return(s, OP_RETURN_BLK, cursp()); + } + else { + gen_return(s, OP_RETURN, cursp()); + } + if (val) push(); + break; + } + case PM_YIELD_NODE: + { + CAST(yield); + mrc_codegen_scope *s2 = s; + int lv = 0, ainfo = -1; + int n = 0, sendv = 0; + + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + if (s2) { + ainfo = (int)s2->ainfo; + } + if (ainfo < 0) codegen_error(s, "invalid yield (SyntaxError)"); + push(); + if (cast->arguments) { + n = gen_values(s, (mrc_node *)cast->arguments, VAL, 14); + if (n < 0) { + n = sendv = 1; + push(); + } + } + push(); pop(); /* space for a block */ + pop_n(n+1); + genop_2S(s, OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)); + if (sendv) n = CALL_MAXARGS; + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_SYM_1(call)), n); + if (val) push(); + break; + } + case PM_BREAK_NODE: + { + CAST(break); + loop_break(s, (mrc_node *)cast->arguments); + if (val) push(); + break; + } + case PM_NEXT_NODE: + { + CAST(next); + if (!s->loop) { + raise_error(s, "unexpected next"); + } + else if (s->loop->type == LOOP_NORMAL) { + codegen(s, (mrc_node *)cast->arguments, NOVAL); + genjmp(s, OP_JMPUW, s->loop->pc0); + } + else { + if ((mrc_node *)cast->arguments) { + codegen(s, (mrc_node *)cast->arguments, VAL); + pop(); + } + else { + genop_1(s, OP_LOADNIL, cursp()); + } + gen_return(s, OP_RETURN, cursp()); + } + if (val) push(); + break; + } + case PM_REDO_NODE: + { + if (!s->loop || s->loop->type == LOOP_BEGIN || s->loop->type == LOOP_RESCUE) { + raise_error(s, "unexpected redo"); + } + else { + genjmp(s, OP_JMPUW, s->loop->pc1); + } + if (val) push(); + break; + } + case PM_RETRY_NODE: + { + const char *msg = "unexpected retry"; + const struct loopinfo *lp = s->loop; + while (lp && lp->type != LOOP_RESCUE) { + lp = lp->prev; + } + if (!lp) { + raise_error(s, msg); + } + else { + genjmp(s, OP_JMPUW, lp->pc0); + } + if (val) push(); + break; + } + case PM_BEGIN_NODE: + { + CAST(begin); + int noexc; + uint32_t exend, pos1; + struct loopinfo *lp; + int catch_entry, begin, end; + + /* for ensure */ + int ensure_catch_entry = -1, ensure_begin = 0; + if (cast->ensure_clause && cast->ensure_clause->statements) { + ensure_catch_entry = catch_handler_new(s); + ensure_begin = s->pc; + } + + lp = loop_push(s, LOOP_BEGIN); + lp->pc0 = new_label(s); + catch_entry = catch_handler_new(s); + begin = s->pc; + /* begin */ + gen_begin(s, (mrc_node *)cast, VAL); + pop(); + lp->type = LOOP_RESCUE; + end = s->pc; + noexc = genjmp_0(s, OP_JMP); + catch_handler_set(s, catch_entry, MRC_CATCH_RESCUE, begin, end, s->pc); + exend = JMPLINK_START; + pos1 = JMPLINK_START; + if (cast->rescue_clause) { + int exc = cursp(); + genop_1(s, OP_EXCEPT, exc); + push(); + /* rescue */ + gen_rescue(s, (mrc_node *)cast->rescue_clause, &pos1, &exc, &exend, val); + if (pos1 != JMPLINK_START) { + dispatch(s, pos1); + genop_1(s, OP_RAISEIF, exc); + } + } + pop(); + dispatch(s, noexc); + if (cast->else_clause) { + codegen(s, (mrc_node *)cast->else_clause, val); + } + else if (val) { + push(); + } + dispatch_linked(s, exend); + loop_pop(s, NOVAL); + + /* ensure */ + if (cast->ensure_clause && cast->ensure_clause->statements) { + gen_ensure(s, (mrc_node *)cast->ensure_clause, ensure_catch_entry, ensure_begin); + } + else { + /* empty ensure ignored */ + } + break; + } + case PM_RESCUE_MODIFIER_NODE: + { + CAST(rescue_modifier); + int catch_entry, begin_pos, end_pos; + struct loopinfo *lp; + + lp = loop_push(s, LOOP_BEGIN); + lp->pc0 = new_label(s); + catch_entry = catch_handler_new(s); + begin_pos = s->pc; + + /* evaluate main expression */ + codegen(s, cast->expression, val); + if (val) pop(); + + lp->type = LOOP_RESCUE; + end_pos = s->pc; + int noexc = genjmp_0(s, OP_JMP); + catch_handler_set(s, catch_entry, MRC_CATCH_RESCUE, begin_pos, end_pos, s->pc); + + /* rescue expression */ + int exc = cursp(); + genop_1(s, OP_EXCEPT, exc); + push(); + pop(); + codegen(s, cast->rescue_expression, val); + if (val) pop(); + + dispatch(s, noexc); + if (val) push(); + loop_pop(s, NOVAL); + break; + } + case PM_BLOCK_ARGUMENT_NODE: + { + CAST(block_argument); + if (!cast->expression) { + mrc_sym and = MRC_OPSYM_2(and); + int idx = lv_idx(s, and); + if (idx == 0) { + int depth = search_upvar(s, and, &idx); + gen_getupvar(s, cursp(), and, depth); + } + else { + gen_move(s, cursp(), idx, val); + } + if (val) push(); + } + else { + codegen(s, cast->expression, val); + } + break; + } + case PM_POST_EXECUTION_NODE: + { + mrc_diagnostic_list_append(s->c, tree->location.start, "END not supported", MRC_GENERATOR_ERROR); + break; + } + case PM_MATCH_REQUIRED_NODE: + { + CAST(match_required); + if (nint(cast->pattern) != PM_LOCAL_VARIABLE_TARGET_NODE) { + mrc_diagnostic_list_append(s->c, cast->pattern->location.start, "expecting a local variable", MRC_GENERATOR_ERROR); + break; + } + pm_local_variable_target_node_t *lvar = (pm_local_variable_target_node_t *)cast->pattern; + gen_assignment(s, (mrc_node *)lvar, (mrc_node *)cast->value, 0, val); + break; + } + case PM_RANGE_NODE: + { + CAST(range); + codegen(s, cast->left, val); + codegen(s, cast->right, val); + if (val) { + mrc_code op; + if (cast->base.flags & PM_RANGE_FLAGS_EXCLUDE_END) { + op = OP_RANGE_EXC; + } + else { + op = OP_RANGE_INC; + } + pop(); pop(); + genop_1(s, op, cursp()); + push(); + } + break; + } + case PM_SOURCE_FILE_NODE: + { + if (val) { + CAST(source_file); + char *p = (char *)cast->filepath.source; + mrc_int len = cast->filepath.length; + int off = new_lit_str(s, p, len); + genop_2(s, OP_STRING, cursp(), off); + push(); + } + break; + } + case PM_SOURCE_ENCODING_NODE: + { + genop_3(s, OP_SSEND, cursp(), new_sym(s, MRC_SYM_1(__ENCODING__)), 0); + push(); + { // Workaround: increase nregs in case __ENCODING__ called alone + // (maybe it is a useless use of a literal in void context) + push(); + pop(); + } + break; + } + case PM_FORWARDING_ARGUMENTS_NODE: + { + //CAST(forwarding_arguments); + if (val) { + int idx; + genop_1(s, OP_LOADNIL, cursp()); + push(); + // * + idx = lv_idx(s, MRC_OPSYM_2(mul)); + assert(idx != 0); + gen_move(s, cursp(), idx, val); + pop(); + genop_1(s, OP_ARYCAT, cursp()); + push(); + // ** + genop_2(s, OP_HASH, cursp(), 0); + push(); + idx = lv_idx(s, MRC_OPSYM_2(pow)); + assert(idx != 0); + gen_move(s, cursp(), idx, val); + pop(); + genop_1(s, OP_HASHCAT, cursp()); + push(); + // & + idx = lv_idx(s, MRC_OPSYM_2(and)); + assert(idx != 0); + gen_move(s, cursp(), idx, val); + } + break; + } + case PM_DEFINED_NODE: + { + CAST(defined); + push(); + codegen(s, cast->value, VAL); + pop(); + pop(); + genop_3(s, OP_SSEND, cursp(), new_sym(s, MRC_SYM_2(defined_p)), 1); + push(); + break; + } + default: + { + char buf[256]; + snprintf(buf, sizeof(buf), "Not implemented: %s", pm_node_type_to_str(nt)); + codegen_error(s, buf); + break; + } + } + exit: + s->rlev = rlev; +} diff --git a/vendor/mruby-compiler2/src/compile.c b/vendor/mruby-compiler2/src/compile.c new file mode 100644 index 0000000..358333f --- /dev/null +++ b/vendor/mruby-compiler2/src/compile.c @@ -0,0 +1,350 @@ +// #ifdef _MSC_VER +#include +// #endif + +typedef intptr_t ssize_t; +static ssize_t nigger; + +#include "../include/mrc_parser_util.h" +#include "../include/mrc_irep.h" +#include "../include/mrc_ccontext.h" +#include "../include/mrc_codegen.h" +#include "../include/mrc_dump.h" +#include "../include/mrc_codedump.h" +#include "../include/mrc_opcode.h" +#include "../include/mrc_presym.h" +#include "../include/mrc_diagnostic.h" + +#if defined(PICORB_VM_MRUBY) +#include "../include/mrc_proc.h" +#endif + +static mrc_irep * +mrc_load_exec(mrc_ccontext *c, mrc_node *ast) +{ + mrc_irep *irep; + /* parse error */ + if (0 < c->p->error_list.size) { + pm_diagnostic_t *e = (pm_diagnostic_t *)c->p->error_list.head; + while (e) { + mrc_diagnostic_list_append(c, e->location.start, e->message, MRC_PARSER_ERROR); + e = (pm_diagnostic_t *)e->node.next; + } + return NULL; + } + /* parse warning */ + if (0 < c->p->warning_list.size) { + pm_diagnostic_t *w = (pm_diagnostic_t *)c->p->warning_list.head; + while (w) { + mrc_diagnostic_list_append(c, w->location.start, w->message, MRC_PARSER_WARNING); + w = (pm_diagnostic_t *)w->node.next; + } + } +#if defined(MRC_DUMP_PRETTY) && !defined(MRC_NO_STDIO) + if (c->dump_result) { + pm_buffer_t buffer = { 0 }; + pm_prettyprint(&buffer, c->p, ast); + fprintf(stderr, "%s\n", buffer.value); + pm_buffer_free(&buffer); + } +#endif + irep = mrc_generate_code(c, ast); + if (c->capture_errors) { + return NULL; + } + if (c->dump_result) { + mrc_codedump_all(c, irep); + } + + return irep; +} + +static void +partial_hook(void *data, pm_parser_t *p, pm_token_t *token) +{ + mrc_ccontext *c = (mrc_ccontext *)data; + if (c->current_filename_index + 1 == c->filename_table_length) { + return; + } + uint32_t token_pos = (uint32_t)(token->start - p->start); + if (token_pos < c->filename_table[c->current_filename_index].start) { + return; + } + if (c->filename_table[c->current_filename_index + 1].start <= token_pos) { + c->current_filename_index++; + pm_string_t filename_string; + pm_string_constant_init( + &filename_string, + c->filename_table[c->current_filename_index].filename, + strlen(c->filename_table[c->current_filename_index].filename)); + p->filepath = filename_string; + } +} + +#if defined(PICORB_VM_MRUBY) +static void +mrc_pm_options_init(mrc_ccontext *cc) +{ + if (cc->options) return; + if (cc->upper == NULL) return; + + struct RProc *u; + + pm_options_t *options = (pm_options_t *)mrc_calloc(cc, 1, sizeof(pm_options_t)); + pm_string_t *encoding = &options->encoding; + pm_string_constant_init(encoding, "UTF-8", 5); + + u = (struct RProc *)cc->upper; + size_t scopes_count = 1; + while (u->upper) { + scopes_count++; + u = (struct RProc *)u->upper; + } + + pm_options_scopes_init(options, scopes_count + 1); // Prism requires one more scope + + u = (struct RProc *)cc->upper; + pm_options_scope_t *scope; + size_t nlocals; + for (; 0 < scopes_count; scopes_count--) { + scope = &options->scopes[scopes_count - 1]; + const struct mrc_irep *ir = u->body.irep; + nlocals = ir->nlocals; + pm_options_scope_init(scope, nlocals); + const mrc_sym *v = ir->lv; + if (v) { + const char *name; + for (size_t j = 0; j < nlocals; j++, v++) { + name = mrb_sym_name(cc->mrb, *v); + if (name) { // TODO: This happens in eval? + pm_string_constant_init(&scope->locals[j], name, strlen(name)); + } + } + } + u = (struct RProc *)u->upper; + } + + cc->options = options; +} +#endif + +static void +mrc_pm_parser_init(mrc_parser_state *p, uint8_t **source, size_t size, mrc_ccontext *cc) +{ + pm_lex_callback_t *cb = (pm_lex_callback_t *)mrc_malloc(cc, sizeof(pm_lex_callback_t)); + cb->data = cc; + cb->callback = partial_hook; +#if defined(PICORB_VM_MRUBY) + mrc_pm_options_init(cc); +#endif + pm_parser_init(p, *source, size, cc->options); + p->lex_callback = cb; + mrc_init_presym(&p->constant_pool); + if (cc->filename_table) { + pm_string_t filename_string; + pm_string_constant_init(&filename_string, cc->filename_table[0].filename, + strlen(cc->filename_table[0].filename)); + p->filepath = filename_string; + } +} + +#ifndef MRC_NO_STDIO + +#define INITIAL_BUF_SIZE 1024 +static ssize_t +append_from_stdin(mrc_ccontext *c, uint8_t **source, size_t source_length) +{ + uint8_t *buffer = mrc_malloc(c, INITIAL_BUF_SIZE); + if (buffer == NULL) return -1; + + int capacity = INITIAL_BUF_SIZE; + size_t length = 0; + + while (1) { + int ch = getchar(); + if (ch == EOF) { + buffer[length] = '\0'; + if (*source == NULL) + *source = (uint8_t *)mrc_malloc(c, source_length + length + 1); + else + *source = (uint8_t *)mrc_realloc(c, *source, source_length + length + 1); + memccpy(*source + source_length, buffer, 1, length); + mrc_free(c, buffer); + return length; + } + + buffer[length++] = (uint8_t)ch; + + if (capacity <= length) { + capacity *= 2; + uint8_t *new_buffer = mrc_realloc(c, buffer, capacity); + if (new_buffer == NULL) { + mrc_free(c, buffer); + return -1; + } + buffer = new_buffer; + } + } +} + +static ssize_t +read_input_files(mrc_ccontext *c, const char **filenames, uint8_t **source, mrc_filename_table *filename_table) +{ + int i = 0; + size_t pos = 0; + ssize_t length = 0; + ssize_t each_size; + FILE *file; + const char *filename = filenames[0]; + while (filename) { + filename_table[i].filename = filenames[i]; + filename_table[i].start = pos; + if (filename[0] == '-' && filename[1] == '\0') { + each_size = append_from_stdin(c, source, length); + if (each_size < 0) { + fprintf(stderr, "compile.c: cannot read from stdin\n"); + return -1; + } + length += each_size; + } + else { + file = NULL; + file = fopen(filename, "rb"); + if (!file) { + fprintf(stderr, "compile.c: cannot open program file. (%s)\n", filename); + return -1; + } + fseek(file, 0, SEEK_END); + each_size = ftell(file); + fseek(file, 0, SEEK_SET); + length += each_size; + if (*source == NULL) { + *source = (uint8_t *)mrc_malloc(c, length + 1); + } + else { + *source = (uint8_t *)mrc_realloc(c, *source, length + 1); + } + if (fread(*source + pos, sizeof(char), each_size, file) != each_size) { + fprintf(stderr, "compile.c: cannot read program file. (%s)\n", filename); + fclose(file); + return -1; + } + fclose(file); + (*source)[length] = '\0'; + } + pos += each_size; + filename = filenames[++i]; + } + return length; +} + +static mrc_node * +mrc_pm_parse(mrc_ccontext *cc) +{ + mrc_node *node = pm_parse(cc->p); + +#if defined(PICORB_VM_MRUBYC) + // Workaround: save top-level locals for PicoRuby(mruby/c) IRB + pm_program_node_t *program = (pm_program_node_t *)node; + uint32_t nlocals = program->locals.size; + pm_options_t *options = (pm_options_t *)mrc_malloc(cc, sizeof(pm_options_t)); + memset(options, 0, sizeof(pm_options_t)); + pm_string_t *encoding = &options->encoding; + pm_string_constant_init(encoding, "UTF-8", 5); + pm_options_scopes_init(options, 1); + pm_options_scope_t *options_scope = &options->scopes[0]; + pm_options_scope_init(options_scope, nlocals); + pm_constant_id_t id; + pm_constant_t *local; + pm_string_t *scope_local; + char *allocated; + for (int i = 0; i < nlocals; i++) { + scope_local = &options_scope->locals[i]; + id = program->locals.ids[i]; + local = pm_constant_pool_id_to_constant(&cc->p->constant_pool, id); + allocated = (char *)mrc_malloc(cc, local->length); + memcpy(allocated, local->start, local->length); + pm_string_constant_init(scope_local, (const char *)allocated, local->length); + } + if (cc->options && cc->options->scopes) { + for (int i = 0; i < cc->options->scopes[0].locals_count; i++) { + mrc_free(cc, (void *)cc->options->scopes[0].locals[i].source); + } + mrc_free(cc, cc->options); + } + cc->options = options; +#endif + + return node; +} + + +static mrc_node * +mrc_parse_file_cxt(mrc_ccontext *c, const char **filenames, uint8_t **source) +{ + size_t filecount = 0; + while (filenames[filecount]) { + filecount++; + } + c->filename_table = (mrc_filename_table *)mrc_malloc(c, sizeof(mrc_filename_table) * filecount); + c->filename_table_length = filecount; + c->current_filename_index = 0; + ssize_t length = read_input_files(c, filenames, source, c->filename_table); + if (length < 0) { + fprintf(stderr, "Cannot open files: "); + for (size_t i = 0; i < filecount; i++) { + fprintf(stderr, "%s ", filenames[i]); + } + fprintf(stderr, "\n"); + return NULL; + } + mrc_pm_parser_init(c->p, source, length, c); + return mrc_pm_parse(c); +} + +MRC_API mrc_irep * +mrc_load_file_cxt(mrc_ccontext *c, const char **filenames, uint8_t **source) +{ + mrc_node *root = mrc_parse_file_cxt(c, filenames, source); + if (root == NULL) { + return NULL; + } + mrc_irep *irep = mrc_load_exec(c, root); + pm_node_destroy(c->p, root); + return irep; +} +#endif + +static mrc_node * +mrc_parse_string_cxt(mrc_ccontext *c, const uint8_t **source, size_t length) +{ + c->filename_table = (mrc_filename_table *)mrc_malloc(c, sizeof(mrc_filename_table)); + c->filename_table[0].filename = "-e"; + c->filename_table[0].start = 0; + c->filename_table_length = 1; + c->current_filename_index = 0; + mrc_pm_parser_init(c->p, (uint8_t **)source, length, c); + return mrc_pm_parse(c); +} + +MRC_API mrc_irep * +mrc_load_string_cxt(mrc_ccontext *c, const uint8_t **source, size_t length) +{ + mrc_node *root = mrc_parse_string_cxt(c, source, length); + mrc_irep *irep = mrc_load_exec(c, root); + return irep; +} + +#if defined(MRC_TARGET_MRUBY) + +MRC_API void +mrb_mruby_compiler2_gem_init(mrb_state *mrb) +{ +} + +MRC_API void +mrb_mruby_compiler2_gem_final(mrb_state *mrb) +{ +} + +#endif diff --git a/vendor/mruby-compiler2/src/debug.c b/vendor/mruby-compiler2/src/debug.c new file mode 100644 index 0000000..e8574ed --- /dev/null +++ b/vendor/mruby-compiler2/src/debug.c @@ -0,0 +1,224 @@ +#include +#include +#include "../include/mrc_debug.h" + +static mrc_irep_debug_info_file* +get_file(mrc_irep_debug_info *info, uint32_t pc) +{ + if (pc >= info->pc_count) { return NULL; } + /* get upper bound */ + mrc_irep_debug_info_file **ret = info->files; + int32_t count = info->flen; + + while (count > 0) { + int32_t step = count / 2; + mrc_irep_debug_info_file **it = ret + step; + if (!(pc < (*it)->start_pos)) { + ret = it + 1; + count -= step + 1; + } + else { count = step; } + } + + --ret; + + /* check returning file exists inside debug info */ + mrc_assert(info->files <= ret && ret < (info->files + info->flen)); + /* check pc is within the range of returning file */ + mrc_assert((*ret)->start_pos <= pc && + pc < (((ret + 1 - info->files) < info->flen) + ? (*(ret+1))->start_pos : info->pc_count)); + + return *ret; +} + +static size_t +mrc_packed_int_len(uint32_t num) +{ + size_t llen = 0; + + do { + llen++; + } while (num >>= 7); + return llen; +} + +static size_t +mrc_packed_int_encode(uint32_t num, uint8_t *p) +{ + size_t llen = 0; + + do { + uint8_t byte = num & 0x7f; + num >>= 7; + if (num != 0) byte |= 0x80; + *p++ = byte; + llen++; + } while (num != 0); + + return llen; +} + +static uint32_t +mrc_packed_int_decode(const uint8_t *p, const uint8_t **newpos) +{ + size_t i = 0, shift = 0; + uint32_t n = 0; + + do { + n |= ((uint32_t)(p[i] & 0x7f)) << shift; + i++; + shift += 7; + } while (shift < sizeof(uint32_t) * 8 && (p[i - 1] & 0x80)); + if (newpos) *newpos = p + i; + return n; +} + +static int32_t +debug_get_line(mrc_irep_debug_info_file* f, uint32_t pc) +{ + if (f == NULL) return -1; + switch (f->line_type) { + case mrc_debug_line_ary: + case mrc_debug_line_flat_map: + default: + break; + + case mrc_debug_line_packed_map: + { + const uint8_t *p = f->lines.packed_map; + const uint8_t *pend = p + f->line_entry_count; + uint32_t pos = 0, line = 0; + while (p < pend) { + pos += mrc_packed_int_decode(p, &p); + uint32_t line_diff = mrc_packed_int_decode(p, &p); + if (pc < pos) break; + line += line_diff; + } + return line; + } + } + return -1; +} + +int32_t +mrc_debug_get_line(mrc_ccontext *c, const mrc_irep *irep, uint32_t pc) +{ + if (irep && pc < irep->ilen) { + if (!irep->debug_info) return -1; + return debug_get_line(get_file(irep->debug_info, pc), pc); + } + return -1; +} + +static char const* +debug_get_filename(mrc_ccontext *c, mrc_irep_debug_info_file* f) +{ + if (f == NULL) return NULL; + pm_constant_t *fn_constant = pm_constant_pool_id_to_constant(&c->p->constant_pool, f->filename_sym); + return (const char *)fn_constant->start; +} + +const char * +mrc_debug_get_filename(mrc_ccontext *c, const mrc_irep *irep, uint32_t pc) +{ + if (irep && pc < irep->ilen) { + if (!irep->debug_info) return NULL; + return debug_get_filename(c, get_file(irep->debug_info, pc)); + } + return NULL; +} + +mrc_irep_debug_info* +mrc_debug_info_alloc(mrc_ccontext *c, mrc_irep *irep) +{ + static const mrc_irep_debug_info initial = { 0, 0, NULL }; + + mrc_assert(!irep->debug_info); + mrc_irep_debug_info *ret = (mrc_irep_debug_info*)mrc_malloc(c, sizeof(*ret)); + *ret = initial; + irep->debug_info = ret; + return ret; +} + +mrc_irep_debug_info_file* +mrc_debug_info_append_file(mrc_ccontext *c, mrc_irep_debug_info *d, + const char *filename, uint16_t *lines, + uint32_t start_pos, uint32_t end_pos) +{ + if (!d) return NULL; + if (start_pos == end_pos) return NULL; + + mrc_assert(filename); + mrc_assert(lines); + + if (d->flen > 0) { + //const char *fn = mrc_sym_name_len(mrb, d->files[d->flen - 1]->filename_sym, NULL); + //if (strcmp(filename, fn) == 0) + // return NULL; + pm_constant_t *fn_constant = pm_constant_pool_id_to_constant(&c->p->constant_pool, d->files[d->flen - 1]->filename_sym); + mrc_assert(fn_constant); + if (strlen(filename) == fn_constant->length && + strncmp(filename, (const char *)fn_constant->start, fn_constant->length) == 0) + { + return NULL; + } + } + + mrc_irep_debug_info_file *f = (mrc_irep_debug_info_file*)mrc_malloc(c, sizeof(*f)); + d->files = (mrc_irep_debug_info_file**)mrc_realloc(c, d->files, sizeof(mrc_irep_debug_info_file*) * (d->flen + 1)); + d->files[d->flen++] = f; + + uint32_t file_pc_count = end_pos - start_pos; + f->start_pos = start_pos; + d->pc_count = end_pos; + + size_t fn_len = strlen(filename); + //f->filename_sym = mrc_intern(mrb, filename, fn_len); + f->filename_sym = pm_constant_pool_insert_constant(&c->p->constant_pool, (const uint8_t *)filename, fn_len); + + f->line_type = mrc_debug_line_packed_map; + f->lines.ptr = NULL; + + uint16_t prev_line = 0; + uint32_t prev_pc = 0; + size_t packed_size = 0; + uint8_t *p; + + for (uint32_t i = 0; i < file_pc_count; i++) { + if (lines[start_pos + i] == prev_line) continue; + packed_size += mrc_packed_int_len(start_pos+i-prev_pc); + prev_pc = start_pos+i; + packed_size += mrc_packed_int_len(lines[start_pos+i]-prev_line); + prev_line = lines[start_pos + i]; + } + f->lines.packed_map = p = (uint8_t*)mrc_malloc(c, packed_size); + prev_line = 0; prev_pc = 0; + for (uint32_t i = 0; i < file_pc_count; i++) { + if (lines[start_pos + i] == prev_line) continue; + p += mrc_packed_int_encode(start_pos+i-prev_pc, p); + prev_pc = start_pos + i; + p += mrc_packed_int_encode(lines[start_pos + i]-prev_line, p); + prev_line = lines[start_pos + i]; + } + f->line_entry_count = (uint32_t)packed_size; + + return f; +} + +void +mrc_debug_info_free(mrc_ccontext *c, mrc_irep_debug_info *d) +{ + if (!d) { return; } + + if (d->files) { + for (uint32_t i = 0; i < d->flen; i++) { + if (d->files[i]) { + mrc_free(c, d->files[i]->lines.ptr); + mrc_free(c, d->files[i]); + } + } + mrc_free(c, d->files); + } + mrc_free(c, d); +} diff --git a/vendor/mruby-compiler2/src/diagnostic.c b/vendor/mruby-compiler2/src/diagnostic.c new file mode 100644 index 0000000..ce1da9c --- /dev/null +++ b/vendor/mruby-compiler2/src/diagnostic.c @@ -0,0 +1,102 @@ +#include "../include/mrc_diagnostic.h" + +static void +line_and_column_by_start_and_offset(const uint8_t *source_start, const uint8_t *location_start, uint32_t *line, uint32_t *column) +{ + if (!location_start) { + *line = 0; + *column = 0; + return; + } + uint32_t start_offset = (uint32_t)(location_start - source_start); + uint32_t l = 1, c = 1; + const uint8_t *p = source_start; + while (p < source_start + start_offset) { + if (*p == '\n') { + l++; + c = 1; + } else { + c++; + } + p++; + } + *line = l; + *column = c; +} + +/* + const char *level; + switch (diagnostic->level) { + case PM_ERROR_LEVEL_SYNTAX: + level = "syntax"; + break; + case PM_ERROR_LEVEL_ARGUMENT: + level = "argument"; + break; + case PM_ERROR_LEVEL_LOAD: + level = "load"; + break; + default: + abort(); + } +*/ + +const char * +mrc_diagnostic_code_to_string(mrc_diagnostic_code code) +{ + switch (code) { + case MRC_PARSER_ERROR: + return "syntax error"; + case MRC_GENERATOR_ERROR: + return "generator error"; + case MRC_PARSER_WARNING: + return "syntax warning"; + case MRC_GENERATOR_WARNING: + return "generator warning"; + default: + return "unknown"; + } +} + +void +mrc_diagnostic_list_append(mrc_ccontext *c, const uint8_t * location_start, const char *message, mrc_diagnostic_code code) +{ + mrc_diagnostic_list *list = mrc_calloc(c, 1, sizeof(mrc_diagnostic_list)); + line_and_column_by_start_and_offset(c->p->start, location_start, &list->line, &list->column); + char buf[256]; + const char *diagnostic_code_str = mrc_diagnostic_code_to_string(code); + snprintf(buf, sizeof(buf), "%s, %s", diagnostic_code_str, message); + size_t len = strlen(buf); + list->message = (char *)mrc_malloc(c, len + 1); + strcpy(list->message, buf); + list->message[len] = '\0'; + list->code = code; + + if (c->diagnostic_list == NULL) { + c->diagnostic_list = list; + } else { + mrc_diagnostic_list *p = c->diagnostic_list; + while (p->next) { + p = p->next; + } + p->next = list; + } + + if (code == MRC_PARSER_ERROR || code == MRC_GENERATOR_ERROR) { + c->capture_errors = TRUE; + } +} + +void +mrc_diagnostic_list_free(mrc_ccontext *c) +{ + mrc_diagnostic_list *p = c->diagnostic_list; + while (p) { + mrc_diagnostic_list *next = p->next; + mrc_free(c, p->message); + mrc_free(c, p); + p = next; + } + c->diagnostic_list = NULL; +} + diff --git a/vendor/mruby-compiler2/src/dump.c b/vendor/mruby-compiler2/src/dump.c new file mode 100644 index 0000000..a1da7da --- /dev/null +++ b/vendor/mruby-compiler2/src/dump.c @@ -0,0 +1,929 @@ +#include +#include "../include/mrc_ccontext.h" +#include "../include/mrc_irep.h" +#include "../include/mrc_dump.h" +#include "../include/mrc_parser_util.h" +#include "../include/mrc_debug.h" +#include "../include/mrc_irep_pool_type.h" + +#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER__) +# define BYTE_ORDER __BYTE_ORDER__ +#endif +#if !defined(BIG_ENDIAN) && defined(__ORDER_BIG_ENDIAN__) +# define BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#endif +#if !defined(LITTLE_ENDIAN) && defined(__ORDER_LITTLE_ENDIAN__) +# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#endif + +#ifdef BYTE_ORDER +# if BYTE_ORDER == BIG_ENDIAN +# define littleendian 0 +# elif BYTE_ORDER == LITTLE_ENDIAN +# define littleendian 1 +# endif +#endif +#ifndef littleendian +/* can't distinguish endian in compile time */ +static inline int +check_little_endian(void) +{ + unsigned int n = 1; + return (*(unsigned char*)&n == 1); +} +# define littleendian check_little_endian() +#endif + + +static size_t get_irep_record_size_1(mrc_ccontext *c, const mrc_irep *irep); + +#if UINT32_MAX > SIZE_MAX +# error This code cannot be built on your environment. +#endif + +static size_t +get_irep_header_size(mrc_ccontext *c) +{ + size_t size = 0; + + size += sizeof(uint32_t) * 1; + size += sizeof(uint16_t) * 3; + + return size; +} + +static ptrdiff_t +write_irep_header(mrc_ccontext *c, const mrc_irep *irep, uint8_t *buf) +{ + uint8_t *cur = buf; + + cur += mrc_uint32_to_bin((uint32_t)get_irep_record_size_1(c, irep), cur); /* record size */ + cur += mrc_uint16_to_bin((uint16_t)irep->nlocals, cur); /* number of local variable */ + cur += mrc_uint16_to_bin((uint16_t)irep->nregs, cur); /* number of register variable */ + cur += mrc_uint16_to_bin((uint16_t)irep->rlen, cur); /* number of child irep */ + + return cur - buf; +} + +static size_t +get_iseq_block_size(mrc_ccontext *c, const mrc_irep *irep) +{ + size_t size = 0; + + size += sizeof(uint16_t); /* clen */ + size += sizeof(uint32_t); /* ilen */ + size += irep->ilen * sizeof(mrc_code); /* iseq(n) */ + size += irep->clen * sizeof(struct mrc_irep_catch_handler); + + return size; +} + +static ptrdiff_t +write_iseq_block(mrc_ccontext *c, const mrc_irep *irep, uint8_t *buf, uint8_t flags) +{ + uint8_t *cur = buf; + size_t seqlen = irep->ilen * sizeof(mrc_code) + + irep->clen * sizeof(struct mrc_irep_catch_handler); + + cur += mrc_uint16_to_bin(irep->clen, cur); /* number of catch handlers */ + cur += mrc_uint32_to_bin(irep->ilen, cur); /* number of opcode */ + memcpy(cur, irep->iseq, seqlen); + cur += seqlen; + + return cur - buf; +} + +#ifndef MRC_NO_FLOAT +static void +dump_float(mrc_ccontext *c, uint8_t *buf, mrc_float f) +{ + /* dump IEEE754 binary in little endian */ + union { + double f; + char s[sizeof(double)]; + } u = {.f = (double)f}; + + if (littleendian) { + memcpy(buf, u.s, sizeof(double)); + } + else { + for (size_t i=0; iplen * sizeof(uint8_t); /* len(n) */ + + for (pool_no = 0; pool_no < irep->plen; pool_no++) { + int ai = mrc_gc_arena_save(c); + + switch (irep->pool[pool_no].tt) { + case IREP_TT_INT64: +#if defined(MRC_64BIT) || defined(MRC_INT64) + { + int64_t i = irep->pool[pool_no].u.i64; + + if (i < INT32_MIN || INT32_MAX < i) + size += 8; + else + size += 4; + } + break; +#else + /* fall through */ +#endif + case IREP_TT_INT32: + size += 4; /* 32bits = 4bytes */ + break; + + case IREP_TT_BIGINT: + { + mrc_int len = irep->pool[pool_no].u.str[0]; + mrc_assert_int_fit(mrc_int, len, size_t, SIZE_MAX); + size += (size_t)len+2; + } + break; + + case IREP_TT_FLOAT: +#ifndef MRC_NO_FLOAT + { + size += sizeof(double); + } +#endif + break; + + default: /* packed IREP_TT_STRING */ + { + mrc_int len = irep->pool[pool_no].tt >> 2; /* unpack length */ + mrc_assert_int_fit(mrc_int, len, size_t, SIZE_MAX); + size += sizeof(uint16_t); + size += (size_t)len+1; + } + break; + } + mrc_gc_arena_restore(c, ai); + } + + return size; +} + +static ptrdiff_t +write_pool_block(mrc_ccontext *c, const mrc_irep *irep, uint8_t *buf) +{ + int pool_no; + uint8_t *cur = buf; + mrc_int len; + const char *ptr; + + cur += mrc_uint16_to_bin(irep->plen, cur); /* number of pool */ + + for (pool_no = 0; pool_no < irep->plen; pool_no++) { + int ai = mrc_gc_arena_save(c); + + switch (irep->pool[pool_no].tt) { + case IREP_TT_INT64: +#if defined(MRC_64BIT) || defined(MRC_INT64) + { + int64_t i = irep->pool[pool_no].u.i64; + if (i < INT32_MIN || INT32_MAX < i) { + cur += mrc_uint8_to_bin(IREP_TT_INT64, cur); /* data type */ + cur += mrc_uint32_to_bin((uint32_t)((i>>32) & 0xffffffff), cur); /* i64 hi */ + cur += mrc_uint32_to_bin((uint32_t)((i ) & 0xffffffff), cur); /* i64 lo */ + } + else { + cur += mrc_uint8_to_bin(IREP_TT_INT32, cur); /* data type */ + cur += mrc_uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */ + } + } + break; +#endif + case IREP_TT_INT32: + cur += mrc_uint8_to_bin(IREP_TT_INT32, cur); /* data type */ + cur += mrc_uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */ + break; + + case IREP_TT_BIGINT: + cur += mrc_uint8_to_bin(IREP_TT_BIGINT, cur); /* data type */ + len = irep->pool[pool_no].u.str[0]; + memcpy(cur, irep->pool[pool_no].u.str, (size_t)len+2); + cur += len+2; + break; + + case IREP_TT_FLOAT: + cur += mrc_uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ +#ifndef MRC_NO_FLOAT + { + dump_float(c, cur,irep->pool[pool_no].u.f); + cur += sizeof(double); + } +#else + cur += mrc_uint16_to_bin(0, cur); /* zero length */ +#endif + break; + + default: /* string */ + cur += mrc_uint8_to_bin(IREP_TT_STR, cur); /* data type */ + ptr = irep->pool[pool_no].u.str; + len = irep->pool[pool_no].tt>>2; + mrc_assert_int_fit(mrc_int, len, uint16_t, UINT16_MAX); + cur += mrc_uint16_to_bin((uint16_t)len, cur); /* data length */ + memcpy(cur, ptr, (size_t)len); + cur += len; + *cur++ = '\0'; + break; + } + mrc_gc_arena_restore(c, ai); + } + + return cur - buf; +} + +static size_t +get_syms_block_size(mrc_ccontext *c, const mrc_irep *irep) +{ + size_t size = 0; + int sym_no; + mrc_int len; + + size += sizeof(uint16_t); /* slen */ + for (sym_no = 0; sym_no < irep->slen; sym_no++) { + size += sizeof(uint16_t); /* snl(n) */ + if (irep->syms[sym_no] != 0) { + mrc_sym_name_len(c, irep->syms[sym_no], &len); + size += len + 1; /* sn(n) + null char */ + } + } + + return size; +} + +static ptrdiff_t +write_syms_block(mrc_ccontext *c, const mrc_irep *irep, uint8_t *buf) +{ + int sym_no; + uint8_t *cur = buf; + const char *name; + + cur += mrc_uint16_to_bin(irep->slen, cur); /* number of symbol */ + + for (sym_no = 0; sym_no < irep->slen; sym_no++) { + if (irep->syms[sym_no] != 0) { + mrc_int len; + + name = mrc_sym_name_len(c, irep->syms[sym_no], &len); + + mrc_assert_int_fit(mrc_int, len, uint16_t, UINT16_MAX); + cur += mrc_uint16_to_bin((uint16_t)len, cur); /* length of symbol name */ + memcpy(cur, name, len); /* symbol name */ + cur += (uint16_t)len; + *cur++ = '\0'; + } + else { + cur += mrc_uint16_to_bin(MRC_DUMP_NULL_SYM_LEN, cur); /* length of symbol name */ + } + } + + return cur - buf; +} + +static size_t +get_irep_record_size_1(mrc_ccontext *c, const mrc_irep *irep) +{ + size_t size = 0; + + size += get_irep_header_size(c); + size += get_iseq_block_size(c, irep); + size += get_pool_block_size(c, irep); + size += get_syms_block_size(c, irep); + return size; +} + +static size_t +get_irep_record_size(mrc_ccontext *c, const mrc_irep *irep) +{ + size_t size = 0; + + size = get_irep_record_size_1(c, irep); + for (int irep_no = 0; irep_no < irep->rlen; irep_no++) { + size += get_irep_record_size(c, irep->reps[irep_no]); + } + return size; +} + +static int +write_irep_record(mrc_ccontext *c, const mrc_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags) +{ + uint8_t *src = bin; + + if (irep == NULL) { + return MRC_DUMP_INVALID_IREP; + } + + bin += write_irep_header(c, irep, bin); + bin += write_iseq_block(c, irep, bin, flags); + bin += write_pool_block(c, irep, bin); + bin += write_syms_block(c, irep, bin); + + for (int i = 0; i < irep->rlen; i++) { + int result; + size_t rsize; + + result = write_irep_record(c, irep->reps[i], bin, &rsize, flags); + if (result != MRC_DUMP_OK) { + return result; + } + bin += rsize; + } + *irep_record_size = bin - src; + return MRC_DUMP_OK; +} + +static uint32_t +write_footer(mrc_ccontext *c, uint8_t *bin) +{ + struct rite_binary_footer footer; + + memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident)); + mrc_uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size); + memcpy(bin, &footer, sizeof(struct rite_binary_footer)); + + return sizeof(struct rite_binary_footer); +} + + +static int +write_section_irep_header(mrc_ccontext *c, size_t section_size, uint8_t *bin) +{ + struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin; + + memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident)); + + mrc_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX); + mrc_uint32_to_bin((uint32_t)section_size, header->section_size); + memcpy(header->rite_version, RITE_VM_VER, sizeof(header->rite_version)); + + return MRC_DUMP_OK; +} + +static int +write_section_irep(mrc_ccontext *c, const mrc_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags) +{ + int result; + size_t rsize = 0; + uint8_t *cur = bin; + + if (c == NULL || bin == NULL) { + return MRC_DUMP_INVALID_ARGUMENT; + } + + cur += sizeof(struct rite_section_irep_header); + + result = write_irep_record(c, irep, cur, &rsize, flags); + if (result != MRC_DUMP_OK) { + return result; + } + mrc_assert(rsize == get_irep_record_size(c, irep)); + *len_p = cur - bin + rsize; + write_section_irep_header(c, *len_p, bin); + + return MRC_DUMP_OK; +} + +static size_t +get_debug_record_size(mrc_ccontext *c, const mrc_irep *irep) +{ + size_t ret = 0; + uint16_t f_idx; + + ret += sizeof(uint32_t); /* record size */ + ret += sizeof(uint16_t); /* file count */ + + for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { + mrc_irep_debug_info_file const* file = irep->debug_info->files[f_idx]; + + ret += sizeof(uint32_t); /* position */ + ret += sizeof(uint16_t); /* filename index */ + + /* lines */ + ret += sizeof(uint32_t); /* entry count */ + ret += sizeof(uint8_t); /* line type */ + switch (file->line_type) { + case mrc_debug_line_ary: + ret += sizeof(uint16_t) * (size_t)(file->line_entry_count); + break; + + case mrc_debug_line_flat_map: + ret += (sizeof(uint32_t) + sizeof(uint16_t)) * (size_t)(file->line_entry_count); + break; + + case mrc_debug_line_packed_map: + ret += (size_t)(file->line_entry_count); + break; + + default: mrc_assert(0); break; + } + } + for (int i=0; irlen; i++) { + ret += get_debug_record_size(c, irep->reps[i]); + } + + return ret; +} + +static int +find_filename_index(const mrc_sym *ary, int ary_len, mrc_sym s) +{ + for (int i = 0; i < ary_len; i++) { + if (ary[i] == s) { return i; } + } + return -1; +} + +static size_t +get_filename_table_size(mrc_ccontext *c, const mrc_irep *irep, mrc_sym **fp, uint16_t *lp) +{ + mrc_sym *filenames = *fp; + size_t size = 0; + const mrc_irep_debug_info *di = irep->debug_info; + + mrc_assert(lp); + for (int i = 0; i < di->flen; i++) { + mrc_irep_debug_info_file *file; + mrc_int filename_len; + + file = di->files[i]; + if (find_filename_index(filenames, *lp, file->filename_sym) == -1) { + /* register filename */ + *lp += 1; + *fp = filenames = (mrc_sym*)mrc_realloc(c, filenames, sizeof(mrc_sym) * (*lp)); + filenames[*lp - 1] = file->filename_sym; + + /* filename */ + mrc_sym_name_len(c, file->filename_sym, &filename_len); + size += sizeof(uint16_t) + (size_t)filename_len; + } + } + for (int i=0; irlen; i++) { + size += get_filename_table_size(c, irep->reps[i], fp, lp); + } + return size; +} + +static size_t +write_debug_record_1(mrc_ccontext *c, const mrc_irep *irep, uint8_t *bin, mrc_sym const* filenames, uint16_t filenames_len) +{ + uint8_t *cur; + ptrdiff_t ret; + + cur = bin + sizeof(uint32_t); /* skip record size */ + cur += mrc_uint16_to_bin(irep->debug_info->flen, cur); /* file count */ + + for (int f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { + int filename_idx; + const mrc_irep_debug_info_file *file = irep->debug_info->files[f_idx]; + + /* position */ + cur += mrc_uint32_to_bin(file->start_pos, cur); + + /* filename index */ + filename_idx = find_filename_index(filenames, filenames_len, + file->filename_sym); + mrc_assert_int_fit(int, filename_idx, uint16_t, UINT16_MAX); + cur += mrc_uint16_to_bin((uint16_t)filename_idx, cur); + + /* lines */ + cur += mrc_uint32_to_bin(file->line_entry_count, cur); + cur += mrc_uint8_to_bin(file->line_type, cur); + switch (file->line_type) { + case mrc_debug_line_ary: { + uint32_t l; + for (l = 0; l < file->line_entry_count; ++l) { + cur += mrc_uint16_to_bin(file->lines.ary[l], cur); + } + } break; + + case mrc_debug_line_flat_map: { + uint32_t line; + for (line = 0; line < file->line_entry_count; ++line) { + cur += mrc_uint32_to_bin(file->lines.flat_map[line].start_pos, cur); + cur += mrc_uint16_to_bin(file->lines.flat_map[line].line, cur); + } + } break; + + case mrc_debug_line_packed_map: { + memcpy(cur, file->lines.packed_map, file->line_entry_count); + cur += file->line_entry_count; + } break; + + default: mrc_assert(0); break; + } + } + + ret = cur - bin; + mrc_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX); + mrc_uint32_to_bin((uint32_t)ret, bin); + + mrc_assert_int_fit(ptrdiff_t, ret, size_t, SIZE_MAX); + return (size_t)ret; +} + +static size_t +write_debug_record(mrc_ccontext *c, const mrc_irep *irep, uint8_t *bin, mrc_sym const* filenames, uint16_t filenames_len) +{ + size_t size = write_debug_record_1(c, irep, bin, filenames, filenames_len); + + bin += size; + for (int irep_no = 0; irep_no < irep->rlen; irep_no++) { + size_t len = write_debug_record(c, irep->reps[irep_no], bin, filenames, filenames_len); + bin += len; + size += len; + } + + mrc_assert(size == get_debug_record_size(c, irep)); + return size; +} + +static int +write_section_debug(mrc_ccontext *c, const mrc_irep *irep, uint8_t *cur, mrc_sym const *filenames, uint16_t filenames_len) +{ + size_t section_size = 0; + const uint8_t *bin = cur; + struct rite_section_debug_header *header; + size_t dlen; + + if (c == NULL || cur == NULL) { + return MRC_DUMP_INVALID_ARGUMENT; + } + + header = (struct rite_section_debug_header*)bin; + cur += sizeof(struct rite_section_debug_header); + section_size += sizeof(struct rite_section_debug_header); + + /* filename table */ + cur += mrc_uint16_to_bin(filenames_len, cur); + section_size += sizeof(uint16_t); + for (int i = 0; i < filenames_len; i++) { + char const *sym; + mrc_int sym_len; + + sym = mrc_sym_name_len(c, filenames[i], &sym_len); + mrc_assert(sym); + cur += mrc_uint16_to_bin((uint16_t)sym_len, cur); + memcpy(cur, sym, sym_len); + cur += sym_len; + section_size += sizeof(uint16_t) + sym_len; + } + + /* debug records */ + dlen = write_debug_record(c, irep, cur, filenames, filenames_len); + section_size += dlen; + + memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident)); + mrc_assert(section_size <= INT32_MAX); + mrc_uint32_to_bin((uint32_t)section_size, header->section_size); + + return MRC_DUMP_OK; +} + +static void +create_lv_sym_table(mrc_ccontext *c, const mrc_irep *irep, mrc_sym **syms, uint32_t *syms_len) +{ + pm_constant_id_t null_mark = pm_constant_pool_find(&c->p->constant_pool, NULL, 0); + + if (*syms == NULL) { + *syms = (mrc_sym*)mrc_malloc(c, sizeof(mrc_sym) * 1); + } + + for (int i = 0; i + 1 < irep->nlocals; i++) { + mrc_sym const name = irep->lv[i]; + if (name == null_mark) continue; + if (find_filename_index(*syms, *syms_len, name) != -1) continue; + + ++(*syms_len); + *syms = (mrc_sym*)mrc_realloc(c, *syms, sizeof(mrc_sym) * (*syms_len)); + (*syms)[*syms_len - 1] = name; + } + + for (int i = 0; i < irep->rlen; i++) { + create_lv_sym_table(c, irep->reps[i], syms, syms_len); + } +} + +static int +write_lv_sym_table(mrc_ccontext *c, uint8_t **start, mrc_sym const *syms, uint32_t syms_len) +{ + uint8_t *cur = *start; + const char *str; + mrc_int str_len; + + cur += mrc_uint32_to_bin(syms_len, cur); + + for (uint32_t i = 0; i < syms_len; i++) { + str = mrc_sym_name_len(c, syms[i], &str_len); + cur += mrc_uint16_to_bin((uint16_t)str_len, cur); + memcpy(cur, str, str_len); + cur += str_len; + } + + *start = cur; + + return MRC_DUMP_OK; +} + +static int +write_lv_record(mrc_ccontext *c, const mrc_irep *irep, uint8_t **start, mrc_sym const *syms, uint32_t syms_len) +{ + uint8_t *cur = *start; + + pm_constant_id_t null_mark = pm_constant_pool_find(&c->p->constant_pool, NULL, 0); + + for (int i = 0; i + 1 < irep->nlocals; i++) { + if (irep->lv[i] == null_mark) { + cur += mrc_uint16_to_bin(RITE_LV_NULL_MARK, cur); + } + else { + int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i]); + mrc_assert(sym_idx != -1); /* local variable name must be in syms */ + + cur += mrc_uint16_to_bin(sym_idx, cur); + } + } + + for (int i = 0; i < irep->rlen; i++) { + write_lv_record(c, irep->reps[i], &cur, syms, syms_len); + } + + *start = cur; + + return MRC_DUMP_OK; +} + +static size_t +get_lv_record_size(mrc_ccontext *c, const mrc_irep *irep) +{ + size_t ret = sizeof(uint16_t) * (irep->nlocals - 1); + + for (int i = 0; i < irep->rlen; i++) { + ret += get_lv_record_size(c, irep->reps[i]); + } + + return ret; +} + +static size_t +get_lv_section_size(mrc_ccontext *c, const mrc_irep *irep, mrc_sym const *syms, uint32_t syms_len) +{ + size_t ret = sizeof(uint32_t); /* syms_len */ + ret += sizeof(uint16_t) * syms_len; /* symbol name lengths */ + for (uint32_t i = 0; i < syms_len; i++) { + mrc_int str_len; + mrc_sym_name_len(c, syms[i], &str_len); + ret += str_len; + } + + ret += get_lv_record_size(c, irep); + + return ret; +} + +static int +write_section_lv(mrc_ccontext *c, const mrc_irep *irep, uint8_t *start, mrc_sym const *syms, uint32_t const syms_len) +{ + uint8_t *cur = start; + struct rite_section_lv_header *header; + ptrdiff_t diff; + int result = MRC_DUMP_OK; + + if (c == NULL || cur == NULL) { + return MRC_DUMP_INVALID_ARGUMENT; + } + + header = (struct rite_section_lv_header*)cur; + cur += sizeof(struct rite_section_lv_header); + + result = write_lv_sym_table(c, &cur, syms, syms_len); + if (result != MRC_DUMP_OK) { + goto lv_section_exit; + } + + result = write_lv_record(c, irep, &cur, syms, syms_len); + if (result != MRC_DUMP_OK) { + goto lv_section_exit; + } + + memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident)); + + diff = cur - start; + mrc_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); + mrc_uint32_to_bin((uint32_t)diff, header->section_size); + +lv_section_exit: + return result; +} + +static int +write_rite_binary_header(mrc_ccontext *c, size_t binary_size, uint8_t *bin, uint8_t flags) +{ + struct rite_binary_header *header = (struct rite_binary_header*)bin; + + memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)); + memcpy(header->major_version, RITE_BINARY_MAJOR_VER, sizeof(header->major_version)); + memcpy(header->minor_version, RITE_BINARY_MINOR_VER, sizeof(header->minor_version)); + memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name)); + memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version)); + mrc_assert(binary_size <= UINT32_MAX); + mrc_uint32_to_bin((uint32_t)binary_size, header->binary_size); + + return MRC_DUMP_OK; +} + +static mrc_bool +debug_info_defined_p(const mrc_irep *irep) +{ + if (!irep->debug_info) return FALSE; + for (int i=0; irlen; i++) { + if (!debug_info_defined_p(irep->reps[i])) return FALSE; + } + return TRUE; +} + +static mrc_bool +lv_defined_p(const mrc_irep *irep) +{ + if (irep->lv && 0 < ((pm_constant_id_list_t *)irep->lv)->size) { return TRUE; } + for (int i = 0; i < irep->rlen; i++) { + if (lv_defined_p(irep->reps[i])) { return TRUE; } + } + return FALSE; +} + +int +mrc_dump_irep(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) +{ + int result = MRC_DUMP_GENERAL_FAILURE; + size_t malloc_size; + size_t section_irep_size; + size_t section_lineno_size = 0, section_lv_size = 0; + uint8_t *cur = NULL; + mrc_bool const debug_info_defined = debug_info_defined_p(irep), lv_defined = lv_defined_p(irep); + mrc_sym *lv_syms = NULL; uint32_t lv_syms_len = 0; + mrc_sym *filenames = NULL; uint16_t filenames_len = 0; + + if (c == NULL) { + *bin = NULL; + return MRC_DUMP_GENERAL_FAILURE; + } + + section_irep_size = sizeof(struct rite_section_irep_header); + section_irep_size += get_irep_record_size(c, irep); + + /* DEBUG section size */ + if (flags & MRC_DUMP_DEBUG_INFO) { + if (debug_info_defined) { + section_lineno_size += sizeof(struct rite_section_debug_header); + /* filename table */ + filenames = (mrc_sym*)mrc_malloc(c, sizeof(mrc_sym) + 1); + + /* filename table size */ + section_lineno_size += sizeof(uint16_t); + section_lineno_size += get_filename_table_size(c, irep, &filenames, &filenames_len); + + section_lineno_size += get_debug_record_size(c, irep); + } + } + + if (lv_defined) { + section_lv_size += sizeof(struct rite_section_lv_header); + create_lv_sym_table(c, irep, &lv_syms, &lv_syms_len); + section_lv_size += get_lv_section_size(c, irep, lv_syms, lv_syms_len); + } + + malloc_size = sizeof(struct rite_binary_header) + + section_irep_size + section_lineno_size + section_lv_size + + sizeof(struct rite_binary_footer); + cur = *bin = (uint8_t*)mrc_malloc(c, malloc_size); + cur += sizeof(struct rite_binary_header); + + result = write_section_irep(c, irep, cur, §ion_irep_size, flags); + if (result != MRC_DUMP_OK) { + goto error_exit; + } + cur += section_irep_size; + *bin_size = sizeof(struct rite_binary_header) + + section_irep_size + section_lineno_size + section_lv_size + + sizeof(struct rite_binary_footer); + + /* write DEBUG section */ + if (flags & MRC_DUMP_DEBUG_INFO) { + if (debug_info_defined) { + result = write_section_debug(c, irep, cur, filenames, filenames_len); + if (result != MRC_DUMP_OK) { + goto error_exit; + } + } + cur += section_lineno_size; + } + + if (lv_defined) { + result = write_section_lv(c, irep, cur, lv_syms, lv_syms_len); + if (result != MRC_DUMP_OK) { + goto error_exit; + } + cur += section_lv_size; + } + + write_footer(c, cur); + write_rite_binary_header(c, *bin_size, *bin, flags); + +error_exit: + if (result != MRC_DUMP_OK) { + mrc_free(c, *bin); + *bin = NULL; + } + mrc_free(c, lv_syms); + mrc_free(c, filenames); + return result; +} + +#ifndef MRC_NO_STDIO + +int +mrc_dump_irep_binary(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, FILE* fp) +{ + uint8_t *bin = NULL; + size_t bin_size = 0; + int result; + + if (fp == NULL) { + return MRC_DUMP_INVALID_ARGUMENT; + } + + result = mrc_dump_irep(c, irep, flags, &bin, &bin_size); + if (result == MRC_DUMP_OK) { + if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) { + result = MRC_DUMP_WRITE_FAULT; + } + } + + mrc_free(c, bin); + return result; +} + +int +mrc_dump_irep_cfunc(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, FILE *fp, const char *initname) +{ + uint8_t *bin = NULL; + + if (fp == NULL || initname == NULL || initname[0] == '\0') { + return MRC_DUMP_INVALID_ARGUMENT; + } + size_t bin_size, bin_idx = 0; + int result = mrc_dump_irep(c, irep, flags, &bin, &bin_size); + if (result == MRC_DUMP_OK) { + if (fprintf(fp, "#include \n") < 0) { /* for uint8_t under at least Darwin */ + mrc_free(c, bin); + return MRC_DUMP_WRITE_FAULT; + } + if (fprintf(fp, + "%s\n" + "const uint8_t %s[] = {", + (flags & MRC_DUMP_STATIC) ? "static" + : "#ifdef __cplusplus\n" + "extern\n" + "#endif", + initname) < 0) { + mrc_free(c, bin); + return MRC_DUMP_WRITE_FAULT; + } + while (bin_idx < bin_size) { + if (bin_idx % 16 == 0) { + if (fputs("\n", fp) == EOF) { + mrc_free(c, bin); + return MRC_DUMP_WRITE_FAULT; + } + } + if (fprintf(fp, "0x%02x,", bin[bin_idx++]) < 0) { + mrc_free(c, bin); + return MRC_DUMP_WRITE_FAULT; + } + } + if (fputs("\n};\n", fp) == EOF) { + mrc_free(c, bin); + return MRC_DUMP_WRITE_FAULT; + } + } + + mrc_free(c, bin); + return result; +} + +#endif // MRC_NO_STDIO diff --git a/vendor/mruby-compiler2/src/irep.c b/vendor/mruby-compiler2/src/irep.c new file mode 100644 index 0000000..92108fe --- /dev/null +++ b/vendor/mruby-compiler2/src/irep.c @@ -0,0 +1,53 @@ +#include "../include/mrc_irep.h" +#include "../include/mrc_debug.h" +#include "../include/mrc_irep_pool_type.h" + +void +mrc_irep_remove_lv(mrc_ccontext *c, mrc_irep *irep) +{ + int i; + + if (irep->flags & MRC_IREP_NO_FREE) return; + if (irep->lv) { + mrc_free(c, (void*)irep->lv); + irep->lv = NULL; + } + if (!irep->reps) return; + for (i = 0; i < irep->rlen; i++) { + mrc_irep_remove_lv(c, (mrc_irep*)irep->reps[i]); + } +} + +void +mrc_irep_free(mrc_ccontext *c, mrc_irep *irep) +{ + int i; + + if (irep->flags & MRC_IREP_NO_FREE) return; + if (!(irep->flags & MRC_ISEQ_NO_FREE)) + mrc_free(c, (void*)irep->iseq); + if (irep->pool) { + for (i=0; iplen; i++) { + if ((irep->pool[i].tt & 3) == IREP_TT_STR || + irep->pool[i].tt == IREP_TT_BIGINT) { + mrc_free(c, (void*)irep->pool[i].u.str); + } + } + mrc_free(c, (void*)irep->pool); + } + mrc_free(c, (void*)irep->syms); + if (irep->reps) { + for (i=0; irlen; i++) { +// if (irep->reps[i]) +// mrb_irep_decref((mrb_irep*)irep->reps[i]); + mrc_irep_free(c, (mrc_irep*)irep->reps[i]); + } + mrc_free(c, (void*)irep->reps); + } + mrc_free(c, (void*)irep->lv); + mrc_debug_info_free(c, irep->debug_info); +#ifdef MRC_DEBUG + memset(irep, -1, sizeof(*irep)); +#endif + mrc_free(c, irep); +} diff --git a/vendor/mruby-compiler2/src/mrc_presym.c b/vendor/mruby-compiler2/src/mrc_presym.c new file mode 100644 index 0000000..c6c4ac8 --- /dev/null +++ b/vendor/mruby-compiler2/src/mrc_presym.c @@ -0,0 +1,40 @@ +#include +#include "mrc_ccontext.h" + +typedef struct { + int index; + const char *lit; +} mrc_sym_entry; + +static mrc_sym_entry symTable[] = { +#define MRC_OPSYM_2(name, lit, num) {num, #lit}, +#define MRC_SYM_1(lit, num) {num, #lit}, +#define MRC_SYM_2(name, lit, num) {num, #lit}, +#include "mrc_presym.inc" +#undef MRC_OPSYM_2 +#undef MRC_SYM_1 +#undef MRC_SYM_2 + {0, NULL} // sentinel +}; + +static uint32_t offset = 0; + +mrc_sym mrc_sym_offset(mrc_sym sym) +{ + return sym + offset; +} + +void +mrc_init_presym(pm_constant_pool_t *pool) +{ + offset = pool->size; + for (int i = 0; ; i++) { + if (symTable[i].lit == NULL) { break; } +#ifdef MRC_DEBUG + pm_constant_id_t id = pm_constant_pool_insert_constant(pool, (const uint8_t *)symTable[i].lit, strlen(symTable[i].lit)); + mrc_assert(id == symTable[i].index + offset); +#else + pm_constant_pool_insert_constant(pool, (const uint8_t *)symTable[i].lit, strlen(symTable[i].lit)); +#endif + } +} diff --git a/vendor/mruby-compiler2/src/parser_util.c b/vendor/mruby-compiler2/src/parser_util.c new file mode 100644 index 0000000..72b4ff9 --- /dev/null +++ b/vendor/mruby-compiler2/src/parser_util.c @@ -0,0 +1,13 @@ +#include "../include/mrc_ccontext.h" +#include "../include/mrc_parser_util.h" + +const char* +mrc_sym_name_len(mrc_ccontext *c, mrc_sym sym, mrc_int *lenp) +{ + pm_constant_t *constant = pm_constant_pool_id_to_constant(&c->p->constant_pool, sym); + if (constant) { + *lenp = constant->length; + return (const char*)constant->start; + } + return NULL; +} diff --git a/vendor/mruby-compiler2/src/pool.c b/vendor/mruby-compiler2/src/pool.c new file mode 100644 index 0000000..7675297 --- /dev/null +++ b/vendor/mruby-compiler2/src/pool.c @@ -0,0 +1,152 @@ +/* +** pool.c - memory pool +** +** See Copyright Notice in mruby.h +*/ + +#include "../include/mrc_ccontext.h" + +/* configuration section */ +/* allocated memory address should be multiple of POOL_ALIGNMENT */ +/* or undef it if alignment does not matter */ +#ifndef POOL_ALIGNMENT +#if INTPTR_MAX == INT64_MAX +#define POOL_ALIGNMENT 8 +#else +#define POOL_ALIGNMENT 4 +#endif +#endif +/* page size of memory pool */ +#ifndef POOL_PAGE_SIZE +#define POOL_PAGE_SIZE 1024 +#endif +/* end of configuration section */ + +/* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array + * in struct/union" when in C++ mode */ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4200) +#endif + +struct mrc_pool_page { + struct mrc_pool_page *next; + size_t offset; + size_t len; + void *last; + char page[]; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef POOL_ALIGNMENT +# define ALIGN_PADDING(x) ((SIZE_MAX - (x) + 1) & (POOL_ALIGNMENT - 1)) +#else +# define ALIGN_PADDING(x) (0) +#endif + +MRC_API mrc_pool* +mrc_pool_open(mrc_ccontext *c) +{ + mrc_pool *pool = (mrc_pool*)mrc_malloc(c, sizeof(mrc_pool)); + + if (pool) { + pool->c = c; + pool->pages = NULL; + } + return pool; +} + +MRC_API void +mrc_pool_close(mrc_pool *pool) +{ + struct mrc_pool_page *page; + + if (!pool) return; + page = pool->pages; + while (page) { + struct mrc_pool_page *tmp = page; + page = page->next; + mrc_free(pool->c, tmp); + } + mrc_free(pool->c, pool); +} + +static struct mrc_pool_page* +page_alloc(mrc_pool *pool, size_t len) +{ +#if defined(MRC_TARGET_MRUBY) + mrc_ccontext *c = pool->c; +#endif + struct mrc_pool_page *page; + + if (len < POOL_PAGE_SIZE) + len = POOL_PAGE_SIZE; + page = (struct mrc_pool_page*)mrc_malloc(c, sizeof(struct mrc_pool_page)+len); + if (page) { + page->offset = 0; + page->len = len; + } + + return page; +} + +MRC_API void* +mrc_pool_alloc(mrc_pool *pool, size_t len) +{ + struct mrc_pool_page *page; + + if (!pool) return NULL; + len += ALIGN_PADDING(len); + for (page = pool->pages; page; page = page->next) { + if (page->offset + len <= page->len) { + size_t n = page->offset; + page->offset += len; + page->last = (void*)(page->page+n); + return page->last; + } + } + page = page_alloc(pool, len); + if (!page) return NULL; + page->offset = len; + page->next = pool->pages; + pool->pages = page; + + page->last = (void*)page->page; + return page->last; +} + +MRC_API void* +mrc_pool_realloc(mrc_pool *pool, void *p, size_t oldlen, size_t newlen) +{ + if (!pool) return NULL; + if (newlen < oldlen) return p; + oldlen += ALIGN_PADDING(oldlen); + newlen += ALIGN_PADDING(newlen); + for (struct mrc_pool_page *page = pool->pages; page; page = page->next) { + if (page->last == p) { + /* if p is a last allocation from the page */ + size_t beg = (char*)p - page->page; + /* check beg + oldlen points bottom */ + /* assert(beg + oldlen == page->offset) */ + if (beg + oldlen != page->offset) break; + if (beg + newlen > page->len) { + /* new allocation need more space */ + /* abandon this space */ + page->offset = beg; + break; + } + page->offset = beg + newlen; + return p; + } + } + void *np = mrc_pool_alloc(pool, newlen); + if (np == NULL) { + return NULL; + } + memcpy(np, p, oldlen); + return np; +} +