From 5eb2fea1b771660516c45390038804c641e8f2eb Mon Sep 17 00:00:00 2001 From: bash0C7 Date: Thu, 10 Sep 2026 11:07:47 +0000 Subject: [PATCH 1/2] Vendor mruby-compiler2 that emits RITE0400 Copied picoruby/mruby-compiler2 6d88acf (lib/prism c0e3781, generated with templates/template.rb) over vendor/mruby-compiler2, so the compiler emits RITE0400 instead of RITE0300. The new mrc_common.h falls back to unless the target macros are set, so bindgen needs the same defines and include path cc already gets. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FfoC7uMVr3d3VaxWFnaPFT --- build.rs | 7 + vendor/mruby-compiler2/LICENSE | 2 +- vendor/mruby-compiler2/README.md | 135 +- vendor/mruby-compiler2/include/mrc_ccontext.h | 15 +- vendor/mruby-compiler2/include/mrc_cdump.h | 1 - vendor/mruby-compiler2/include/mrc_codedump.h | 1 - vendor/mruby-compiler2/include/mrc_codegen.h | 1 - vendor/mruby-compiler2/include/mrc_common.h | 65 +- vendor/mruby-compiler2/include/mrc_debug.h | 1 - .../mruby-compiler2/include/mrc_diagnostic.h | 1 + vendor/mruby-compiler2/include/mrc_dump.h | 14 +- vendor/mruby-compiler2/include/mrc_endian.h | 1 - vendor/mruby-compiler2/include/mrc_irep.h | 1 + .../include/mrc_irep_pool_type.h | 5 + vendor/mruby-compiler2/include/mrc_opcode.h | 5 + vendor/mruby-compiler2/include/mrc_ops.h | 21 +- vendor/mruby-compiler2/include/mrc_pool.h | 1 - vendor/mruby-compiler2/include/mrc_presym.h | 9 +- vendor/mruby-compiler2/include/mrc_presym.inc | 25 + vendor/mruby-compiler2/include/mrc_proc.h | 16 +- vendor/mruby-compiler2/include/mrc_throw.h | 2 +- .../include/prism_xallocator.h | 138 +- vendor/mruby-compiler2/lib/prism/CHANGELOG.md | 104 +- vendor/mruby-compiler2/lib/prism/README.md | 4 +- .../mruby-compiler2/lib/prism/include/prism.h | 55 +- .../lib/prism/include/prism/ast.h | 438 +- .../lib/prism/include/prism/diagnostic.h | 7 + .../lib/prism/include/prism/options.h | 52 +- .../lib/prism/include/prism/parser.h | 3 + .../lib/prism/include/prism/regexp.h | 4 +- .../lib/prism/include/prism/util/pm_buffer.h | 8 + .../lib/prism/include/prism/util/pm_integer.h | 4 + .../lib/prism/include/prism/util/pm_list.h | 6 + .../lib/prism/include/prism/util/pm_string.h | 14 +- .../lib/prism/include/prism/version.h | 4 +- .../mruby-compiler2/lib/prism/prism.gemspec | 16 +- .../lib/prism/src/diagnostic.c | 14 +- .../mruby-compiler2/lib/prism/src/encoding.c | 239 +- vendor/mruby-compiler2/lib/prism/src/node.c | 11 + .../mruby-compiler2/lib/prism/src/options.c | 24 +- .../lib/prism/src/prettyprint.c | 18 + vendor/mruby-compiler2/lib/prism/src/prism.c | 3571 +++++----- .../mruby-compiler2/lib/prism/src/serialize.c | 10 +- .../lib/prism/src/token_type.c | 74 +- .../lib/prism/src/util/pm_constant_pool.c | 2 +- .../lib/prism/src/util/pm_string.c | 14 +- vendor/mruby-compiler2/mrbgem.rake | 182 +- vendor/mruby-compiler2/src/ccontext.c | 153 +- vendor/mruby-compiler2/src/cdump.c | 13 +- vendor/mruby-compiler2/src/codedump.c | 246 +- vendor/mruby-compiler2/src/codegen.c | 5968 ++++++++++++++++- vendor/mruby-compiler2/src/codegen_prism.inc | 2957 -------- vendor/mruby-compiler2/src/compile.c | 260 +- vendor/mruby-compiler2/src/debug.c | 4 - vendor/mruby-compiler2/src/diagnostic.c | 22 +- vendor/mruby-compiler2/src/dump.c | 26 +- vendor/mruby-compiler2/src/irep.c | 2 - vendor/mruby-compiler2/src/mrc_presym.c | 31 +- vendor/mruby-compiler2/src/mruby_compat.c | 702 ++ vendor/mruby-compiler2/src/parser_util.c | 1 + vendor/mruby-compiler2/src/pool.c | 1 - 61 files changed, 9966 insertions(+), 5765 deletions(-) delete mode 100644 vendor/mruby-compiler2/src/codegen_prism.inc create mode 100644 vendor/mruby-compiler2/src/mruby_compat.c diff --git a/build.rs b/build.rs index 56a88fd..7706f94 100644 --- a/build.rs +++ b/build.rs @@ -36,6 +36,13 @@ fn main() { let mut builder = bindgen::Builder::default() .header("./vendor/mruby-compiler2/include/mruby_compiler.h") .header("./vendor/mruby-compiler2/include/mrc_codedump.h") + .clang_arg("-DMRB_NO_PRESYM") + .clang_arg("-DMRB_INT64=1") + .clang_arg("-DPRISM_XALLOCATOR") + .clang_arg("-DPRISM_BUILD_MINIMAL") + .clang_arg("-DPICORB_VM_MRUBYC") + .clang_arg("-DMRBC_ALLOC_LIBC") + .clang_arg("-I./vendor/include") .clang_arg("-I./vendor/mruby-compiler2/include") .clang_arg("-I./vendor/mruby-compiler2/lib/prism/include") .blocklist_item("FP_NAN") diff --git a/vendor/mruby-compiler2/LICENSE b/vendor/mruby-compiler2/LICENSE index a3c85e4..cc4339c 100644 --- a/vendor/mruby-compiler2/LICENSE +++ b/vendor/mruby-compiler2/LICENSE @@ -1,4 +1,4 @@ -mruby-compiler2 +mruby-compiler-prism / mruby-compiler2 Copyright (c) HASUMI Hitoshi 2024 diff --git a/vendor/mruby-compiler2/README.md b/vendor/mruby-compiler2/README.md index 5308b9c..cca8ddd 100644 --- a/vendor/mruby-compiler2/README.md +++ b/vendor/mruby-compiler2/README.md @@ -1,20 +1,137 @@ -# mruby-compiler2 +# mruby-compiler / mruby-compiler2 -New mruby compiler based on Prism. +This project is a Prism-based Ruby parser and bytecode compiler for mruby, PicoRuby, and FemtoRuby. -## install by mrbgems +Read this note first: -- add conf.gem line to `build_config.rb` +- If this file is under `mruby/mrbgems/mruby-compiler`, you are reading the canonical mruby core mgem. This is where development happens. +- If this file is at the top of `picoruby/mruby-compiler2`, you are reading the standalone mirror. That repository exists for projects that need to use the compiler independently from mruby and pin it at an arbitrary commit. +- Please send patches to `mruby/mruby`, not to `picoruby/mruby-compiler2`. The standalone repository is synchronized from mruby after changes land there. + +The compiler must remain usable without fundamentally depending on mruby. mruby uses it as `mruby-compiler`; PicoRuby and FemtoRuby use the same compiler through the standalone `mruby-compiler2` mirror. + +## Using mruby-compiler2 as a standalone mgem + +Projects that want to pin the compiler independently from mruby can use: ```ruby MRuby::Build.new do |conf| + conf.gem github: "picoruby/mruby-compiler2", commit: "..." +end +``` - # ... (snip) ... +`picoruby/mruby-compiler2` is synchronized from +`mruby/mruby/mrbgems/mruby-compiler`. It may lag behind mruby until the sync workflow opens and merges a mirror PR. - conf.gem :github => 'picoruby/mruby-compiler2' -end +## Using Prism in mruby + +Prism support in mruby is opt-in: + +```sh +MRB_COMPILER_PRISM=yes rake test +``` + +The top-level mruby `Rakefile` maps `MRB_COMPILER_PRISM=yes` to `MRUBY_CONFIG=prism` only when neither `MRUBY_CONFIG` nor `CONFIG` is already set. Explicit config selection continues to win: + +```sh +MRUBY_CONFIG=ci/gcc-clang rake test +MRUBY_CONFIG=prism rake test +``` + +This keeps the normal mruby build and CI path compatible with the existing `mruby-compiler` gem while Prism support is still being aligned. + +The Prism route currently includes: + +- `mruby-compiler` +- `mruby-bin-mrbc` +- `mruby-bin-mruby` +- `mruby-bin-mirb` +- `mruby-eval` + +## Prism submodule and bootstrap + +`lib/prism` is a git submodule pointing at `ruby/prism`. + +The gem bootstrap runs before `mrbgem.rake` collects Prism C sources with `Dir.glob`. This matters for fresh checkouts, where the submodule may not yet be initialized and generated Prism files may be absent. + +During normal build setup, `mrbgem.rake` does the following: + +1. If `lib/prism/templates/template.rb` is missing, run: + + ```sh + git submodule update --init lib/prism + ``` + +2. If generated Prism files such as `src/node.c`, `src/serialize.c`, `include/prism/ast.h`, or `include/prism/diagnostic.h` are missing, run: + + ```sh + ruby templates/template.rb + ``` + +This bootstrap belongs in the compiler gem because the same source tree is used from mruby, PicoRuby, FemtoRuby, and the standalone mirror. + +## Compatibility requirements + +The compiler is shared by multiple runtimes. Changes made for mruby must preserve these boundaries. + +1. Do not introduce a fundamental dependency on mruby. + + The compiler should continue to build for both `MRC_TARGET_MRUBY` and `MRC_TARGET_MRUBYC`. mruby-only compatibility code must stay behind `MRC_TARGET_MRUBY`. + +2. The compiler library must not define `global_mrb`. + + The executable or embedding runtime owns `global_mrb` when the mruby allocator path needs it. This avoids duplicate-symbol conflicts in PicoRuby and r2p2. In standalone mruby, `mrbc-prism`, `mruby-prism`, `mirb-prism`, and `mrbtest` provide the owner when building the Prism route. + +3. `PICORB_VM_MRUBY` and `PICORB_VM_MRUBYC` must remain respected. + + PicoRuby builds the mruby VM path. FemtoRuby builds the mruby/c VM path. The gem configuration must not accidentally select `MRC_TARGET_MRUBY` while building FemtoRuby. + +4. Public compiler headers are the API boundary for PicoRuby and FemtoRuby. + + Keep the C API usable without depending on mruby internals: + - `mrc_common.h` + - `mrc_ccontext.h` + - `mrc_compile.h` + - `mrc_dump.h` + +5. Do not hard-code the PicoRuby submodule path. + + PicoRuby intentionally uses top-level gems such as `mrbgems/mruby-compiler-prism` or the standalone `mruby-compiler2` mirror and synchronizes them at chosen times. + +6. `mruby-eval-prism` is for the mruby VM path. + + FemtoRuby uses `picoruby-eval`, loaded as a prebuilt gem. On FemtoRuby, `eval` is available after: + + ```ruby + require "eval" + ``` + +## Current status + +`MRB_COMPILER_PRISM=yes rake test:build` currently passes in the local port. The Prism build links: + +- `mrbc-prism` +- `mruby-prism` +- `mirb-prism` +- `mrbtest` + +Basic execution works: + +```sh +build/host/bin/mruby-prism -e 'p 1 + 2' +#=> 3 + +build/host/bin/mruby-prism -e 'p eval("1 + 2")' +#=> 3 ``` +Known remaining work: + +- `MRB_COMPILER_PRISM=yes rake test:run:lib` reached `mrbtest` locally but failed in the socket tests with an AF_UNIX bind error. This should be rechecked in upstream CI before treating it as a Prism compiler issue. +- `MRB_COMPILER_PRISM=yes rake test:run:bin` still has Prism-specific bintest failures around diagnostics, verbose dump output, top-level locals, and `mirb-prism` multi-line behavior. +- `mruby-bin-strip-prism` does not exist yet. +- Some non-Prism gems and gemboxes still refer directly to `mruby-compiler`. + ## License -under the MIT License: -- see LICENSE file + +MIT License. See `LICENSE`. diff --git a/vendor/mruby-compiler2/include/mrc_ccontext.h b/vendor/mruby-compiler2/include/mrc_ccontext.h index 11a2982..052d4ff 100644 --- a/vendor/mruby-compiler2/include/mrc_ccontext.h +++ b/vendor/mruby-compiler2/include/mrc_ccontext.h @@ -35,7 +35,9 @@ typedef struct mrc_ccontext { char *filename; uint16_t lineno; struct RClass *target_class; - mrc_bool capture_errors:1; + mrc_bool capture_errors:1; /* output: an error was recorded */ + mrc_bool quiet_errors:1; /* input: caller reports them itself (eval) */ + mrc_bool dump_ast:1; mrc_bool dump_result:1; mrc_bool no_exec:1; mrc_bool keep_lv:1; @@ -59,6 +61,17 @@ typedef struct mrc_ccontext { uint16_t filename_table_length; uint16_t current_filename_index; #endif + + /* The arena everything Prism allocates for this context is taken from, and + the arena of the context this one was made inside of, put back when this + one is freed. Unused where Prism allocates through libc; see + prism_xallocator.h for what the arena is for. */ + void *prism_arena; + void *prism_arena_outer; + + /* How deep the brackets stand where the lexer is, so that a nesting Prism + would recurse through is refused instead. See src/compile.c. */ + uint32_t nesting; } mrc_ccontext; /* compiler context */ #ifdef MRC_TARGET_MRUBY diff --git a/vendor/mruby-compiler2/include/mrc_cdump.h b/vendor/mruby-compiler2/include/mrc_cdump.h index 6d7c36d..6d862fb 100644 --- a/vendor/mruby-compiler2/include/mrc_cdump.h +++ b/vendor/mruby-compiler2/include/mrc_cdump.h @@ -11,4 +11,3 @@ 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 index 9f7146f..bc3c0b4 100644 --- a/vendor/mruby-compiler2/include/mrc_codedump.h +++ b/vendor/mruby-compiler2/include/mrc_codedump.h @@ -14,4 +14,3 @@ 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 index cf7de55..d67984a 100644 --- a/vendor/mruby-compiler2/include/mrc_codegen.h +++ b/vendor/mruby-compiler2/include/mrc_codegen.h @@ -12,4 +12,3 @@ 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 index 7c17cd4..734f33a 100644 --- a/vendor/mruby-compiler2/include/mrc_common.h +++ b/vendor/mruby-compiler2/include/mrc_common.h @@ -1,8 +1,6 @@ #ifndef MRC_COMMON_H #define MRC_COMMON_H -#include - #define MRC_STRINGIZE0(expr) #expr #define MRC_STRINGIZE(expr) MRC_STRINGIZE0(expr) @@ -10,38 +8,62 @@ #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) */ +/* mruby.h must be included before (it enforces this ordering on + some platforms) and it carries the core API's linkage, so include it up + front -- and outside the extern "C" wrap below. prism.h pulls mruby.h in + transitively through prism_xallocator.h; keeping it out of the wrap means + that under MRB_USE_CXX_ABI the core keeps its C++ linkage while only Prism + gets C linkage. */ +#if defined(MRC_TARGET_MRUBY) + #include +#elif defined(MRC_TARGET_MRUBYC) + #include + #define mrb_state void +#else + /* May be building standalone mrbc. mruby.h would declare a core API this + binary does not link, but mrbconf.h on its own is self-contained, and it + is what settles the target's mrb_int width -- which mrc_int has to match, + because this mrbc dumps irep for that target (see MRC_INT32 below). */ + #include + #include #define mrb_state void #endif +#include + #if !defined(PRISM_XALLOCATOR) #define PRISM_XALLOCATOR #endif +/* Prism is a vendored C library and is always compiled as C (its generated + code uses C constructs -- designated initializers, implicit void* casts -- + that a C++ compiler cannot build). When this header is included from a C++ + translation unit -- e.g. an MRB_USE_CXX_ABI build -- its declarations must + use C linkage so they match the C-compiled Prism objects. */ +#ifdef __cplusplus +extern "C" { +#endif #include "prism.h" - -#ifndef PICORUBY_VERSION - #define MRC_VERSION "unknown (standalone)" -#else - #define MRC_VERSION PICORUBY_VERSION +#ifdef __cplusplus +} #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) +#ifndef MRC_COMMIT_TIMESTAMP + #define MRC_COMMIT_TIMESTAMP "unknown" +#endif +#ifndef MRC_COMMIT_BRANCH + #define MRC_COMMIT_BRANCH "unknown" +#endif +#ifndef MRC_COMMIT_HASH + #define MRC_COMMIT_HASH "unknown" +#endif +#define MRC_BUILD_INFO MRC_COMMIT_TIMESTAMP " " MRC_COMMIT_BRANCH " " MRC_COMMIT_HASH #ifdef MRB_USE_CXX_ABI #define MRC_USE_CXX_ABI @@ -99,6 +121,13 @@ typedef uint8_t mrc_bool; # endif #endif +/* mrc_int must be as wide as the VM's mrb_int and no wider: the pool literals + and the constant folding below are dumped for a target whose loader rejects + an IREP_TT_INT64 entry unless it was built with MRB_INT64 (src/load.c). */ +#if defined(MRB_INT32) && !defined(MRC_INT32) +#define MRC_INT32 1 +#endif + #if !defined(MRC_INT32) #define MRC_INT64 1 #endif diff --git a/vendor/mruby-compiler2/include/mrc_debug.h b/vendor/mruby-compiler2/include/mrc_debug.h index 9b2533b..5afaf7e 100644 --- a/vendor/mruby-compiler2/include/mrc_debug.h +++ b/vendor/mruby-compiler2/include/mrc_debug.h @@ -75,4 +75,3 @@ 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 index fc48db6..8085aec 100644 --- a/vendor/mruby-compiler2/include/mrc_diagnostic.h +++ b/vendor/mruby-compiler2/include/mrc_diagnostic.h @@ -15,6 +15,7 @@ typedef enum { typedef struct mrc_diagnostic_list { mrc_diagnostic_code code; char *message; + const char *filename; uint32_t line; uint32_t column; struct mrc_diagnostic_list *next; diff --git a/vendor/mruby-compiler2/include/mrc_dump.h b/vendor/mruby-compiler2/include/mrc_dump.h index 7e64f8a..81639b4 100644 --- a/vendor/mruby-compiler2/include/mrc_dump.h +++ b/vendor/mruby-compiler2/include/mrc_dump.h @@ -36,7 +36,7 @@ int mrc_dump_irep(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, uint8_t /* 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_MAJOR_VER "04" #define RITE_BINARY_MINOR_VER "00" #define RITE_BINARY_FORMAT_VER RITE_BINARY_MAJOR_VER RITE_BINARY_MINOR_VER #if defined(RITE_COMPILER_NAME) @@ -46,7 +46,7 @@ int mrc_dump_irep(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, uint8_t #define RITE_PARSER_NAME "Prism" #define RITE_COMPILER_VERSION "0000" -#define RITE_VM_VER "0300" +#define RITE_VM_VER "0400" #define RITE_BINARY_EOF "END\0" #define RITE_SECTION_IREP_IDENT "IREP" @@ -56,6 +56,11 @@ int mrc_dump_irep(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, uint8_t #define MRC_DUMP_DEFAULT_STR_LEN 128 #define MRC_DUMP_ALIGNMENT sizeof(uint32_t) +/* The RITE structs below are identical to the ones in mruby's + . Guard them so both headers can coexist in one + translation unit (e.g. the amalgamated build). */ +#ifndef MRUBY_DUMP_H + /* binary header */ struct rite_binary_header { uint8_t binary_ident[4]; /* Binary Identifier */ @@ -95,6 +100,8 @@ struct rite_binary_footer { RITE_SECTION_HEADER; }; +#endif /* !MRUBY_DUMP_H */ + static inline size_t mrc_uint8_to_bin(uint8_t s, uint8_t *bin) { @@ -145,10 +152,9 @@ mrc_bin_to_uint8(const uint8_t *bin) static inline const char* mrc_description(void) { - return MRC_VERSION " (" MRC_RELEASE_DATE ") Parser: " RITE_PARSER_NAME ", RITE: " RITE_BINARY_FORMAT_VER; + return "RITE" RITE_BINARY_FORMAT_VER " (" MRC_BUILD_INFO ") Parser: " RITE_PARSER_NAME "-" PRISM_VERSION; } 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 index a30e720..4f1b50d 100644 --- a/vendor/mruby-compiler2/include/mrc_endian.h +++ b/vendor/mruby-compiler2/include/mrc_endian.h @@ -42,4 +42,3 @@ check_little_endian(void) 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 index 3ae3ea8..16438c8 100644 --- a/vendor/mruby-compiler2/include/mrc_irep.h +++ b/vendor/mruby-compiler2/include/mrc_irep.h @@ -92,6 +92,7 @@ void mrc_irep_free(mrc_ccontext *c, mrc_irep *irep); #define MRC_ASPEC_KEY(a) (((a) >> 2) & 0x1f) #define MRC_ASPEC_KDICT(a) (((a) >> 1) & 0x1) #define MRC_ASPEC_BLOCK(a) ((a) & 1) +#define MRC_ASPEC_NOBLOCK(a) (((a) >> 23) & 0x1) MRC_END_DECL diff --git a/vendor/mruby-compiler2/include/mrc_irep_pool_type.h b/vendor/mruby-compiler2/include/mrc_irep_pool_type.h index c0d861a..312723b 100644 --- a/vendor/mruby-compiler2/include/mrc_irep_pool_type.h +++ b/vendor/mruby-compiler2/include/mrc_irep_pool_type.h @@ -3,6 +3,10 @@ MRC_BEGIN_DECL +/* This enum is identical to the one in mruby's . Guard it + so both headers can coexist in one translation unit (e.g. the + amalgamated build). */ +#ifndef MRUBY_IREP_H enum irep_pool_type { IREP_TT_STR = 0, /* string (need free) */ IREP_TT_SSTR = 2, /* string (static) */ @@ -11,6 +15,7 @@ enum irep_pool_type { IREP_TT_BIGINT = 7, /* big integer (not yet supported) */ IREP_TT_FLOAT = 5, /* float (double/float) */ }; +#endif /* !MRUBY_IREP_H */ MRC_END_DECL diff --git a/vendor/mruby-compiler2/include/mrc_opcode.h b/vendor/mruby-compiler2/include/mrc_opcode.h index bfcee66..b46f9f9 100644 --- a/vendor/mruby-compiler2/include/mrc_opcode.h +++ b/vendor/mruby-compiler2/include/mrc_opcode.h @@ -13,6 +13,11 @@ enum mrb_insn { #undef OPCODE }; +/* backward compatibility aliases */ +#define OP_LOADI OP_LOADI8 +#define OP_LOADT OP_LOADTRUE +#define OP_LOADF OP_LOADFALSE + #define OP_L_STRICT 1 #define OP_L_CAPTURE 2 #define OP_L_METHOD OP_L_STRICT diff --git a/vendor/mruby-compiler2/include/mrc_ops.h b/vendor/mruby-compiler2/include/mrc_ops.h index cda796f..5ded349 100644 --- a/vendor/mruby-compiler2/include/mrc_ops.h +++ b/vendor/mruby-compiler2/include/mrc_ops.h @@ -15,7 +15,7 @@ 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(LOADI8, 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) */ @@ -31,8 +31,8 @@ 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(LOADTRUE, B) /* R[a] = true */ +OPCODE(LOADFALSE, 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]] */ @@ -48,6 +48,7 @@ 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(GETIDX0, BB) /* R[a] = R[b][0]; a+1 for method call */ OPCODE(SETIDX, B) /* R[a][R[a+1]] = R[a+2] */ OPCODE(JMP, S) /* pc+=a */ OPCODE(JMPIF, BS) /* if R[a] pc+=b */ @@ -57,25 +58,35 @@ 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(MATCHERR, B) /* raise NoMatchingPatternError unless 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(SSEND0, BB) /* R[a] = self.send(Syms[b]) (no args) */ 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(SEND0, BB) /* R[a] = R[a].send(Syms[b]) (no args) */ 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(BLKCALL, BB) /* R[a] = R[a].call(R[a+1],... ,R[a+b]); direct block call */ 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(ENTER, W) /* arg setup according to flags (24=n1: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(RETSELF, Z) /* return self */ +OPCODE(RETNIL, Z) /* return nil */ +OPCODE(RETTRUE, Z) /* return true */ +OPCODE(RETFALSE, Z) /* return false */ 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(ADDILV, BBB) /* R[a] = R[a]+mrb_int(c); R[b],R[b+1] for method call */ +OPCODE(SUBILV, BBB) /* R[a] = R[a]-mrb_int(c); R[b],R[b+1] for method call */ 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] */ @@ -108,6 +119,8 @@ 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(TDEF, BBB) /* target_class.newmethod(Syms[b],Irep[c]); R[a] = Syms[b] */ +OPCODE(SDEF, BBB) /* R[a].singleton_class.newmethod(Syms[b],Irep[c]); 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 */ diff --git a/vendor/mruby-compiler2/include/mrc_pool.h b/vendor/mruby-compiler2/include/mrc_pool.h index b093b43..9b904a5 100644 --- a/vendor/mruby-compiler2/include/mrc_pool.h +++ b/vendor/mruby-compiler2/include/mrc_pool.h @@ -20,4 +20,3 @@ MRC_API void *mrc_pool_realloc(mrc_pool *pool, void *p, size_t oldlen, size_t ne 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 index 62aed84..144eea9 100644 --- a/vendor/mruby-compiler2/include/mrc_presym.h +++ b/vendor/mruby-compiler2/include/mrc_presym.h @@ -13,14 +13,13 @@ enum mrc_opsym { #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) +#define MRC_OPSYM_2(name) mrc_presym_id(MRC_OPSYM_2__##name) +#define MRC_SYM_1(name) mrc_presym_id(MRC_SYM_1__##name) +#define MRC_SYM_2(name) mrc_presym_id(MRC_SYM_2__##name) void mrc_init_presym(pm_constant_pool_t *pool); -mrc_sym mrc_sym_offset(mrc_sym sym); +mrc_sym mrc_presym_id(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 index e204890..c114a66 100644 --- a/vendor/mruby-compiler2/include/mrc_presym.inc +++ b/vendor/mruby-compiler2/include/mrc_presym.inc @@ -31,3 +31,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) +MRC_SYM_1(deconstruct, 33) +MRC_SYM_1(deconstruct_keys, 34) +MRC_SYM_1(size, 35) +MRC_SYM_2(has_key_p, has_key?, 36) +MRC_SYM_1(__pat_values, 37) +MRC_SYM_1(__except, 38) +MRC_SYM_1(dup, 39) +MRC_SYM_2(defined_const_q, __defined_const?, 40) +MRC_SYM_2(defined_method_q, __defined_method?, 41) +MRC_SYM_2(defined_ivar_q, __defined_ivar?, 42) +MRC_SYM_2(defined_yield_q, __defined_yield?, 43) +MRC_SYM_2(defined_gvar_q, __defined_gvar?, 44) +MRC_SYM_2(defined_cvar_q, __defined_cvar?, 45) +MRC_SYM_2(defined_super_q, __defined_super?, 46) +MRC_SYM_2(defined_const_path_q, __defined_const_path?, 47) +MRC_SYM_2(defined_method_on_q, __defined_method_on?, 48) +MRC_SYM_2(last_match, $~, 49) +MRC_SYM_1(__pre_match, 50) +MRC_SYM_1(__post_match, 51) +MRC_SYM_1(__last_group, 52) +MRC_SYM_1(__group, 53) +MRC_SYM_2(errinfo, $!, 54) +MRC_SYM_1(freeze, 55) +MRC_SYM_2(respond_to_p, respond_to?, 56) +MRC_SYM_1(Exception, 57) diff --git a/vendor/mruby-compiler2/include/mrc_proc.h b/vendor/mruby-compiler2/include/mrc_proc.h index 61aa263..777bc0f 100644 --- a/vendor/mruby-compiler2/include/mrc_proc.h +++ b/vendor/mruby-compiler2/include/mrc_proc.h @@ -5,12 +5,15 @@ 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 +/* This struct mirrors RProc in mruby's (mrc_irep is + layout-compatible with mrb_irep there). Guard it so both headers can + coexist in one translation unit (e.g. the amalgamated build). */ +#ifndef MRUBY_PROC_H struct RProc { MRC_OBJECT_HEADER; union { @@ -24,6 +27,17 @@ struct RProc { struct REnv *env; } e; }; +#endif /* !MRUBY_PROC_H */ + +/* The flags of that struct, mirrored the same way. */ +#define MRC_PROC_CFUNC_FL 128 +#define MRC_PROC_CFUNC_P(p) (((p)->flags & MRC_PROC_CFUNC_FL) != 0) +#define MRC_PROC_ENVSET 1024 +#define MRC_PROC_ENV_P(p) (((p)->flags & MRC_PROC_ENVSET) != 0) +#define MRC_PROC_SCOPE 2048 +#define MRC_PROC_SCOPE_P(p) (((p)->flags & MRC_PROC_SCOPE) != 0) +/* MRB_PROC_LVAR_BOUNDARY_P() in mruby/proc.h */ +#define MRC_PROC_LVAR_BOUNDARY_P(p) (MRC_PROC_SCOPE_P(p) && !MRC_PROC_ENV_P(p)) MRC_END_DECL diff --git a/vendor/mruby-compiler2/include/mrc_throw.h b/vendor/mruby-compiler2/include/mrc_throw.h index 73dbd49..df6b5ad 100644 --- a/vendor/mruby-compiler2/include/mrc_throw.h +++ b/vendor/mruby-compiler2/include/mrc_throw.h @@ -33,7 +33,7 @@ typedef void *mrc_jmpbuf_impl; #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define MRC_SETJMP _setjmp #define MRC_LONGJMP _longjmp -#elif defined(__MINGW64__) && defined(__GNUC__) && __GNUC__ >= 4 +#elif defined(__MINGW64__) && !defined(_M_ARM64) && defined(__GNUC__) && __GNUC__ >= 4 #define MRC_SETJMP __builtin_setjmp #define MRC_LONGJMP __builtin_longjmp #else diff --git a/vendor/mruby-compiler2/include/prism_xallocator.h b/vendor/mruby-compiler2/include/prism_xallocator.h index a387ec0..a6506db 100644 --- a/vendor/mruby-compiler2/include/prism_xallocator.h +++ b/vendor/mruby-compiler2/include/prism_xallocator.h @@ -3,17 +3,134 @@ #if defined(MRC_TARGET_MRUBY) #include "mruby.h" - extern mrb_state *global_mrb; + #include - #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) + #if defined(MRC_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) + #elif defined(MRC_PRISM_ARENA) + extern mrb_state *global_mrb; - #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) + /* mrb_malloc()/mrb_calloc() answer a size of zero with NULL, where + malloc() and calloc() answer with a pointer. Prism is written against + the latter: it hands what it gets straight to memcpy() and stores it in + the constant pool without a NULL check, so a zero-length allocation + arrives at memcpy() and later at memcmp() as a null pointer, which is + undefined even for a length of zero. A byte is asked for instead by + the allocators below. xrealloc() needs no such thing, since + realloc(p, 0) frees and answers NULL in C too. */ + + /* Everything Prism allocates for one compiler context is taken from an + arena and given back in one piece. Freeing the tree a node at a time + costs a C frame per level of it, and the level is how deep the source + was written, so a program can ask for more stack than there is; the + arena is freed without walking anything. A block records the one + before it, which is all the walking that giving it back takes. + + The arena is opened before Prism allocates anything for a context and + closed after the last of it is given back, so every pointer these four + see is arena memory. That is what lets free() do nothing and realloc() + read the old size out of the chunk header. A rule that asked instead + whether an arena happens to be open would answer differently for one + pointer at different times: a pointer taken before the arena was open + would reach realloc() with a malloc header where the chunk header is + meant to be, and reach free() as memory the arena is holding. */ + /* C linkage: a C++ ABI build compiles the compiler glue as C++ and Prism + as C, and these are what the two share. Everything else the glue + exports keeps the linkage its build gives it. */ +#ifdef __cplusplus + extern "C" { +#endif + struct mrc_prism_arena_block { + struct mrc_prism_arena_block *prev; + }; + extern struct mrc_prism_arena_block *mrc_prism_arena; + + void *mrc_prism_arena_alloc(size_t size); + void *mrc_prism_arena_realloc(void *ptr, size_t size); +#ifdef __cplusplus + } +#endif + + static inline void* + mrc_prism_alloc(size_t size) + { + return mrc_prism_arena_alloc(size ? size : 1); + } + + static inline void* + mrc_prism_alloc_zero(size_t nmemb, size_t size) + { + if (nmemb == 0 || size == 0) { nmemb = 1; size = 1; } + void *p = mrc_prism_arena_alloc(nmemb * size); + if (p) memset(p, 0, nmemb * size); + return p; + } + + static inline void* + mrc_prism_realloc(void *ptr, size_t size) + { + return mrc_prism_arena_realloc(ptr, size); + } + + static inline void + mrc_prism_free(void *ptr) + { + /* A piece of an arena is not given back on its own: the whole of it + goes at mrc_ccontext_free(). */ + (void)ptr; + } + + #define xmalloc(size) mrc_prism_alloc(size) + #define xcalloc(nmemb,size) mrc_prism_alloc_zero(nmemb, size) + #define xrealloc(ptr,size) mrc_prism_realloc(ptr, size) + #define xfree(ptr) mrc_prism_free(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) + #else + extern mrb_state *global_mrb; + + /* mrb_malloc()/mrb_calloc() answer a size of zero with NULL, where + malloc() and calloc() answer with a pointer. Prism is written against + the latter: it hands what it gets straight to memcpy() and stores it in + the constant pool without a NULL check, so a zero-length allocation + arrives at memcpy() and later at memcmp() as a null pointer, which is + undefined even for a length of zero. A byte is asked for instead. + xrealloc() needs no such thing, since realloc(p, 0) frees and answers + NULL in C too. */ + static inline void* + mrc_prism_malloc(size_t size) + { + return mrb_malloc(global_mrb, size ? size : 1); + } + + static inline void* + mrc_prism_calloc(size_t nmemb, size_t size) + { + if (nmemb == 0 || size == 0) { nmemb = 1; size = 1; } + return mrb_calloc(global_mrb, nmemb, size); + } + + #define xmalloc(size) mrc_prism_malloc(size) + #define xcalloc(nmemb,size) mrc_prism_calloc(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) + #endif #elif defined(MRC_TARGET_MRUBYC) #include "mrubyc.h" #if defined(MRBC_ALLOC_LIBC) @@ -57,7 +174,7 @@ #endif #else - // for picorbc + // for standalone mrbc in PicoRuby #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) @@ -70,4 +187,3 @@ #endif #endif - diff --git a/vendor/mruby-compiler2/lib/prism/CHANGELOG.md b/vendor/mruby-compiler2/lib/prism/CHANGELOG.md index aa17d50..1f384df 100644 --- a/vendor/mruby-compiler2/lib/prism/CHANGELOG.md +++ b/vendor/mruby-compiler2/lib/prism/CHANGELOG.md @@ -6,6 +6,101 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## [Unreleased] +## [1.9.0] - 2026-01-27 + +### Added + +- Lots of work on the Ripper translation layer to make it more compatible and efficient. +- Alias `Prism::Node#breadth_first_search` to `Prism::Node#find`. +- Add `Prism::Node#breadth_first_search_all`/`Prism::Node#find_all` for finding all nodes matching a condition. + +### Changed + +- Fixed location of opening tokens when invalid syntax is parsed. +- Fix RBI for parsing options. + +## [1.8.0] - 2026-01-12 + +### Added + +- Optimize ruby visitor. +- Report unterminated construct errors at opening token. + +### Changed + +- Correctly expose ripper state. +- Use one file for versioned parser classes. +- Fix denominator of rational float literal. +- Decouple ripper translator from ripper library. +- Sync Prism::Translation::ParserCurrent with Ruby 4.0. + +## [1.7.0] - 2025-12-18 + +### Added + +- Support `4.1` as a version option. +- Add `equal_loc` to `CallNode`. +- Add `len()`/`is_empty()` to `ConstantList` and `NodeList` in the Rust API. + +### Changed + +- Rename version `3.5` to version `4.0`. +- Fix compiling the gem from source on Windows. +- Fix parsing of unary method calls like `42.~@`. +- Reject `def f a, (b) = 1`. +- Reject endless method as a block parameter default. +- Reject variable capture in alternative pattern. +- Many fixes in regards to memory safety, found through fuzzing. +- Many fixes to better handle invalid syntax, also found through fuzzing. +- Fix the ruby version used by the `ripper` translator. +- Fix `ruby_parser` translation comment processing. + +## [1.6.0] - 2025-10-16 + +### Added + +- Add support for passing `"current"` as the version option to `Prism.*` APIs. + +### Changed + +- Remove a compiler warning for a missing unsigned cast for a shift value. + +## [1.5.2] - 2025-10-09 + +### Changed + +- Fix character literal forced encoding when a unicode escape sequence is used. +- Reject `1 if foo = bar baz`. +- Clear static literal flag on interpolated strings. +- Reject optional argument/endless method definition ambiguity. + +## [1.5.1] - 2025-09-13 + +### Changed + +- Revert of a bug introduced with static literal flags on interpolated strings. + +## [1.5.0] - 2025-09-12 + +### Added + +- Add `Prism::Translation::ParserCurrent`. +- Add `Integer::to_u32_digits` for the Rust API. +- Add `pm_comment_type_t` field for the Rust API. +- Support leading logical operators for CRuby 3.5+. + +### Changed + +- Mark Prism as ractor-safe. +- Enforce a minimum version for the parser translation layer. +- Many fixes to the parser translation layer. +- Accept a newline after the `defined?` keyword. +- Reject `true && not true`. +- Make `it = it` assign nil to match parse.y behavior [Bug #21139]. +- Some fixes to the ruby parser translation layer. +- Ensure call nodes have the correct ending location. +- Reject `foo && return bar`. + ## [1.4.0] - 2025-03-18 ### Added @@ -649,7 +744,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a - 🎉 Initial release! 🎉 -[unreleased]: https://github.com/ruby/prism/compare/v1.4.0...HEAD +[unreleased]: https://github.com/ruby/prism/compare/v1.9.0...HEAD +[1.9.0]: https://github.com/ruby/prism/compare/v1.8.0...v1.9.0 +[1.8.0]: https://github.com/ruby/prism/compare/v1.7.0...v1.8.0 +[1.7.0]: https://github.com/ruby/prism/compare/v1.6.0...v1.7.0 +[1.6.0]: https://github.com/ruby/prism/compare/v1.5.2...v1.6.0 +[1.5.2]: https://github.com/ruby/prism/compare/v1.5.1...v1.5.2 +[1.5.1]: https://github.com/ruby/prism/compare/v1.5.0...v1.5.1 +[1.5.0]: https://github.com/ruby/prism/compare/v1.4.0...v1.5.0 [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 diff --git a/vendor/mruby-compiler2/lib/prism/README.md b/vendor/mruby-compiler2/lib/prism/README.md index b601ee5..e92ef7b 100644 --- a/vendor/mruby-compiler2/lib/prism/README.md +++ b/vendor/mruby-compiler2/lib/prism/README.md @@ -116,7 +116,7 @@ Prism has been integrated into the majority of Ruby runtimes, many libraries, an * [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) +* [TruffleRuby](https://github.com/truffleruby/truffleruby/issues/3117) (via Java) ### Libraries @@ -135,7 +135,9 @@ Prism has been integrated into the majority of Ruby runtimes, many libraries, an * [sorbet-eraser](https://github.com/kddnewton/sorbet-eraser/pull/25) * [synvert](https://github.com/xinminlabs/synvert-core-ruby) * [typeprof](https://github.com/ruby/typeprof) +* [unparser](https://github.com/mbj/unparser) (via parser translator) ### Applications * [gem.sh](https://github.com/marcoroth/gem.sh/pull/96) +* [Sorbet](https://github.com/sorbet/sorbet) diff --git a/vendor/mruby-compiler2/lib/prism/include/prism.h b/vendor/mruby-compiler2/lib/prism/include/prism.h index 317568a..c468db1 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism.h @@ -49,10 +49,15 @@ PRISM_EXPORTED_FUNCTION const char * pm_version(void); /** * Initialize a parser with the given start and end pointers. * + * The resulting parser must eventually be freed with `pm_parser_free()`. + * * @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. + * @param options The optional options to use when parsing. These options must + * live for the whole lifetime of this parser. + * + * \public \memberof pm_parser */ PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options); @@ -62,13 +67,20 @@ PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t * * * @param parser The parser to register the callback with. * @param callback The callback to register. + * + * \public \memberof pm_parser */ 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. * + * This does not free the `pm_options_t` object that was used to initialize the + * parser. + * * @param parser The parser to free. + * + * \public \memberof pm_parser */ PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser); @@ -77,16 +89,25 @@ PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser); * * @param parser The parser to use. * @return The AST representing the source. + * + * \public \memberof pm_parser */ 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 + * 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); +/** + * This function is used in pm_parse_stream to check whether a stream is EOF. + * It closely mirrors that of feof so that feof can be used as the + * default implementation. + */ +typedef int (pm_parse_stream_feof_t)(void *stream); + /** * Parse a stream of Ruby source and return the tree. * @@ -94,10 +115,13 @@ typedef char * (pm_parse_stream_fgets_t)(char *string, int size, void *stream); * @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 stream_feof The function to use to determine if the stream has hit eof. * @param options The optional options to use when parsing. * @return The AST representing the source. + * + * \public \memberof pm_parser */ -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); +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, pm_parse_stream_feof_t *stream_feof, 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 @@ -111,9 +135,10 @@ PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buff * @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 stream_feof The function to use to tell if the stream has hit eof. * @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); +PRISM_EXPORTED_FUNCTION void pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const char *data); /** * Serialize the given list of comments to the given buffer. @@ -289,7 +314,7 @@ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint * 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), + * [TruffleRuby](https://github.com/truffleruby/truffleruby), * [Sorbet](https://github.com/sorbet/sorbet), and * [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree). * @@ -307,10 +332,10 @@ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint * 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 + * * `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: * @@ -327,8 +352,8 @@ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint * } * ``` * - * 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 + * 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 @@ -340,9 +365,9 @@ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint * 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 + * * `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: * @@ -360,7 +385,7 @@ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint * @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: + * can do this with the `pm_prettyprint()` function, which you would use like: * * ```c * void prettyprint(const uint8_t *source, size_t length) { diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/ast.h b/vendor/mruby-compiler2/lib/prism/include/prism/ast.h index 9c4ad63..6911006 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/ast.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/ast.h @@ -1,3 +1,5 @@ +/* :markup: markdown */ + /*----------------------------------------------------------------------------*/ /* This file is generated by the templates/template.rb script and should not */ /* be modified manually. See */ @@ -10,6 +12,8 @@ * @file ast.h * * The abstract syntax tree. + * + * -- */ #ifndef PRISM_AST_H #define PRISM_AST_H @@ -30,11 +34,53 @@ 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, + /** } */ + PM_TOKEN_BRACE_RIGHT, - /** a token that was not present but it is okay */ - PM_TOKEN_NOT_PROVIDED, + /** , */ + PM_TOKEN_COMMA, + + /** } */ + PM_TOKEN_EMBEXPR_END, + + /** do */ + PM_TOKEN_KEYWORD_DO, + + /** else */ + PM_TOKEN_KEYWORD_ELSE, + + /** elsif */ + PM_TOKEN_KEYWORD_ELSIF, + + /** end */ + PM_TOKEN_KEYWORD_END, + + /** ensure */ + PM_TOKEN_KEYWORD_ENSURE, + + /** in */ + PM_TOKEN_KEYWORD_IN, + + /** rescue */ + PM_TOKEN_KEYWORD_RESCUE, + + /** then */ + PM_TOKEN_KEYWORD_THEN, + + /** when */ + PM_TOKEN_KEYWORD_WHEN, + + /** a newline character outside of other tokens */ + PM_TOKEN_NEWLINE, + + /** ) */ + PM_TOKEN_PARENTHESIS_RIGHT, + + /** | */ + PM_TOKEN_PIPE, + + /** ; */ + PM_TOKEN_SEMICOLON, /** & */ PM_TOKEN_AMPERSAND, @@ -69,9 +115,6 @@ typedef enum pm_token_type { /** { */ PM_TOKEN_BRACE_LEFT, - /** } */ - PM_TOKEN_BRACE_RIGHT, - /** [ */ PM_TOKEN_BRACKET_LEFT, @@ -105,9 +148,6 @@ typedef enum pm_token_type { /** :: */ PM_TOKEN_COLON_COLON, - /** , */ - PM_TOKEN_COMMA, - /** a comment */ PM_TOKEN_COMMENT, @@ -135,9 +175,6 @@ typedef enum pm_token_type { /** #{ */ PM_TOKEN_EMBEXPR_BEGIN, - /** } */ - PM_TOKEN_EMBEXPR_END, - /** # */ PM_TOKEN_EMBVAR, @@ -237,27 +274,12 @@ typedef enum pm_token_type { /** 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, @@ -270,9 +292,6 @@ typedef enum pm_token_type { /** if in the modifier form */ PM_TOKEN_KEYWORD_IF_MODIFIER, - /** in */ - PM_TOKEN_KEYWORD_IN, - /** module */ PM_TOKEN_KEYWORD_MODULE, @@ -291,9 +310,6 @@ typedef enum pm_token_type { /** redo */ PM_TOKEN_KEYWORD_REDO, - /** rescue */ - PM_TOKEN_KEYWORD_RESCUE, - /** rescue in the modifier form */ PM_TOKEN_KEYWORD_RESCUE_MODIFIER, @@ -309,9 +325,6 @@ typedef enum pm_token_type { /** super */ PM_TOKEN_KEYWORD_SUPER, - /** then */ - PM_TOKEN_KEYWORD_THEN, - /** true */ PM_TOKEN_KEYWORD_TRUE, @@ -330,9 +343,6 @@ typedef enum pm_token_type { /** until in the modifier form */ PM_TOKEN_KEYWORD_UNTIL_MODIFIER, - /** when */ - PM_TOKEN_KEYWORD_WHEN, - /** while */ PM_TOKEN_KEYWORD_WHILE, @@ -387,9 +397,6 @@ typedef enum pm_token_type { /** -> */ 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, @@ -399,9 +406,6 @@ typedef enum pm_token_type { /** ( for a parentheses node */ PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES, - /** ) */ - PM_TOKEN_PARENTHESIS_RIGHT, - /** % */ PM_TOKEN_PERCENT, @@ -423,9 +427,6 @@ typedef enum pm_token_type { /** %W */ PM_TOKEN_PERCENT_UPPER_W, - /** | */ - PM_TOKEN_PIPE, - /** |= */ PM_TOKEN_PIPE_EQUAL, @@ -450,9 +451,6 @@ typedef enum pm_token_type { /** the end of a regular expression */ PM_TOKEN_REGEXP_END, - /** ; */ - PM_TOKEN_SEMICOLON, - /** / */ PM_TOKEN_SLASH, @@ -519,6 +517,12 @@ typedef enum pm_token_type { /** marker for the point in the file at which the parser should stop */ PM_TOKEN___END__, + /** 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, + /** The maximum token value. */ PM_TOKEN_MAXIMUM, } pm_token_type_t; @@ -1046,22 +1050,6 @@ typedef uint16_t pm_node_flags_t; 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. @@ -1092,6 +1080,32 @@ typedef struct pm_node { pm_location_t location; } pm_node_t; +/** + * Cast the given node to the base pm_node_t type. + */ +#define PM_NODE_UPCAST(node_) ((pm_node_t *) (node_)) + +/** + * 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 the flags associated with the given node. + */ +#define PM_NODE_FLAGS(node_) (PM_NODE_UPCAST(node_)->flags) + +/** + * Return true if the given flag is set on the given node. + */ +#define PM_NODE_FLAG_P(node_, flag_) ((PM_NODE_FLAGS(node_) & (flag_)) != 0) + /** * AliasGlobalVariableNode * @@ -1420,6 +1434,17 @@ typedef struct pm_array_pattern_node { /** * ArrayPatternNode#constant + * + * Represents the optional constant preceding the Array + * + * foo in Bar[] + * ^^^ + * + * foo in Bar[1, 2, 3] + * ^^^ + * + * foo in Bar::Baz[1, 2, 3] + * ^^^^^^^^ */ struct pm_node *constant; @@ -1801,20 +1826,20 @@ typedef struct pm_block_node { /** * BlockNode#opening_loc * - * Represents the location of the opening `|`. + * Represents the location of the opening `{` or `do`. * * [1, 2, 3].each { |i| puts x } - * ^ + * ^ */ pm_location_t opening_loc; /** * BlockNode#closing_loc * - * Represents the location of the closing `|`. + * Represents the location of the closing `}` or `end`. * * [1, 2, 3].each { |i| puts x } - * ^ + * ^ */ pm_location_t closing_loc; } pm_block_node_t; @@ -2199,6 +2224,19 @@ typedef struct pm_call_node { */ pm_location_t closing_loc; + /** + * CallNode#equal_loc + * + * Represents the location of the equal sign, in the case that this is an attribute write. + * + * foo.bar = value + * ^ + * + * foo[bar] = value + * ^ + */ + pm_location_t equal_loc; + /** * CallNode#block * @@ -2623,7 +2661,7 @@ typedef struct pm_case_node { * 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; @@ -2692,6 +2730,11 @@ typedef struct pm_class_node { /** * ClassNode#class_keyword_loc + * + * Represents the location of the `class` keyword. + * + * class Foo end + * ^^^^^ */ pm_location_t class_keyword_loc; @@ -2702,26 +2745,51 @@ typedef struct pm_class_node { /** * ClassNode#inheritance_operator_loc + * + * Represents the location of the `<` operator. + * + * class Foo < Bar + * ^ */ pm_location_t inheritance_operator_loc; /** * ClassNode#superclass + * + * Represents the superclass of the class. + * + * class Foo < Bar + * ^^^ */ struct pm_node *superclass; /** * ClassNode#body + * + * Represents the body of the class. + * + * class Foo + * foo + * ^^^ */ struct pm_node *body; /** * ClassNode#end_keyword_loc + * + * Represents the location of the `end` keyword. + * + * class Foo end + * ^^^ */ pm_location_t end_keyword_loc; /** * ClassNode#name + * + * The name of the class. + * + * class Foo end # name `:Foo` */ pm_constant_id_t name; } pm_class_node_t; @@ -3758,6 +3826,9 @@ typedef struct pm_false_node { * foo in Foo(*bar, baz, *qux) * ^^^^^^^^^^^^^^^^^^^^ * + * foo => *bar, baz, *qux + * ^^^^^^^^^^^^^^^ + * * Type: ::PM_FIND_PATTERN_NODE * * @extends pm_node_t @@ -3769,31 +3840,76 @@ typedef struct pm_find_pattern_node { /** * FindPatternNode#constant + * + * Represents the optional constant preceding the pattern + * + * foo in Foo(*bar, baz, *qux) + * ^^^ */ struct pm_node *constant; /** * FindPatternNode#left + * + * Represents the first wildcard node in the pattern. + * + * foo in *bar, baz, *qux + * ^^^^ + * + * foo in Foo(*bar, baz, *qux) + * ^^^^ */ struct pm_splat_node *left; /** * FindPatternNode#requireds + * + * Represents the nodes in between the wildcards. + * + * foo in *bar, baz, *qux + * ^^^ + * + * foo in Foo(*bar, baz, 1, *qux) + * ^^^^^^ */ struct pm_node_list requireds; /** * FindPatternNode#right + * + * Represents the second wildcard node in the pattern. + * + * foo in *bar, baz, *qux + * ^^^^ + * + * foo in Foo(*bar, baz, *qux) + * ^^^^ */ struct pm_node *right; /** * FindPatternNode#opening_loc + * + * The location of the opening brace. + * + * foo in [*bar, baz, *qux] + * ^ + * + * foo in Foo(*bar, baz, *qux) + * ^ */ pm_location_t opening_loc; /** * FindPatternNode#closing_loc + * + * The location of the closing brace. + * + * foo in [*bar, baz, *qux] + * ^ + * + * foo in Foo(*bar, baz, *qux) + * ^ */ pm_location_t closing_loc; } pm_find_pattern_node_t; @@ -3991,11 +4107,16 @@ typedef struct pm_forwarding_parameter_node { /** * ForwardingSuperNode * - * Represents the use of the `super` keyword without parentheses or arguments. + * Represents the use of the `super` keyword without parentheses or arguments, but which might have a block. * * super * ^^^^^ * + * super { 123 } + * ^^^^^^^^^^^^^ + * + * If it has any other arguments, it would be a `SuperNode` instead. + * * Type: ::PM_FORWARDING_SUPER_NODE * * @extends pm_node_t @@ -4007,6 +4128,8 @@ typedef struct pm_forwarding_super_node { /** * ForwardingSuperNode#block + * + * All other arguments are forwarded as normal, except the original block is replaced with the new block. */ struct pm_block_node *block; } pm_forwarding_super_node_t; @@ -4306,6 +4429,12 @@ typedef struct pm_hash_node { * foo => { a: 1, b: 2, **c } * ^^^^^^^^^^^^^^^^^^^ * + * foo => Bar[a: 1, b: 2] + * ^^^^^^^^^^^^^^^ + * + * foo in { a: 1, b: 2 } + * ^^^^^^^^^^^^^^ + * * Type: ::PM_HASH_PATTERN_NODE * * @extends pm_node_t @@ -4317,26 +4446,66 @@ typedef struct pm_hash_pattern_node { /** * HashPatternNode#constant + * + * Represents the optional constant preceding the Hash. + * + * foo => Bar[a: 1, b: 2] + * ^^^ + * + * foo => Bar::Baz[a: 1, b: 2] + * ^^^^^^^^ */ struct pm_node *constant; /** * HashPatternNode#elements + * + * Represents the explicit named hash keys and values. + * + * foo => { a: 1, b:, ** } + * ^^^^^^^^ */ struct pm_node_list elements; /** * HashPatternNode#rest + * + * Represents the rest of the Hash keys and values. This can be named, unnamed, or explicitly forbidden via `**nil`, this last one results in a `NoKeywordsParameterNode`. + * + * foo => { a: 1, b:, **c } + * ^^^ + * + * foo => { a: 1, b:, ** } + * ^^ + * + * foo => { a: 1, b:, **nil } + * ^^^^^ */ struct pm_node *rest; /** * HashPatternNode#opening_loc + * + * The location of the opening brace. + * + * foo => { a: 1 } + * ^ + * + * foo => Bar[a: 1] + * ^ */ pm_location_t opening_loc; /** * HashPatternNode#closing_loc + * + * The location of the closing brace. + * + * foo => { a: 1 } + * ^ + * + * foo => Bar[a: 1] + * ^ */ pm_location_t closing_loc; } pm_hash_pattern_node_t; @@ -5618,6 +5787,9 @@ typedef struct pm_local_variable_read_node { * foo, bar = baz * ^^^ ^^^ * + * foo => baz + * ^^^ + * * Type: ::PM_LOCAL_VARIABLE_TARGET_NODE * * @extends pm_node_t @@ -5820,16 +5992,70 @@ typedef struct pm_match_required_node { /** * MatchRequiredNode#value + * + * Represents the left-hand side of the operator. + * + * foo => bar + * ^^^ */ struct pm_node *value; /** * MatchRequiredNode#pattern + * + * Represents the right-hand side of the operator. The type of the node depends on the expression. + * + * Anything that looks like a local variable name (including `_`) will result in a `LocalVariableTargetNode`. + * + * foo => a # This is equivalent to writing `a = foo` + * ^ + * + * Using an explicit `Array` or combining expressions with `,` will result in a `ArrayPatternNode`. This can be preceded by a constant. + * + * foo => [a] + * ^^^ + * + * foo => a, b + * ^^^^ + * + * foo => Bar[a, b] + * ^^^^^^^^^ + * + * If the array pattern contains at least two wildcard matches, a `FindPatternNode` is created instead. + * + * foo => *, 1, *a + * ^^^^^ + * + * Using an explicit `Hash` or a constant with square brackets and hash keys in the square brackets will result in a `HashPatternNode`. + * + * foo => { a: 1, b: } + * + * foo => Bar[a: 1, b:] + * + * foo => Bar[**] + * + * To use any variable that needs run time evaluation, pinning is required. This results in a `PinnedVariableNode` + * + * foo => ^a + * ^^ + * + * Similar, any expression can be used with pinning. This results in a `PinnedExpressionNode`. + * + * foo => ^(a + 1) + * + * Anything else will result in the regular node for that expression, for example a `ConstantReadNode`. + * + * foo => CONST */ struct pm_node *pattern; /** * MatchRequiredNode#operator_loc + * + * The location of the operator. + * + * foo => bar + * ^^ */ pm_location_t operator_loc; } pm_match_required_node_t; @@ -6487,21 +6713,41 @@ typedef struct pm_pinned_expression_node { /** * PinnedExpressionNode#expression + * + * The expression used in the pinned expression + * + * foo in ^(bar) + * ^^^ */ struct pm_node *expression; /** * PinnedExpressionNode#operator_loc + * + * The location of the `^` operator + * + * foo in ^(bar) + * ^ */ pm_location_t operator_loc; /** * PinnedExpressionNode#lparen_loc + * + * The location of the opening parenthesis. + * + * foo in ^(bar) + * ^ */ pm_location_t lparen_loc; /** * PinnedExpressionNode#rparen_loc + * + * The location of the closing parenthesis. + * + * foo in ^(bar) + * ^ */ pm_location_t rparen_loc; } pm_pinned_expression_node_t; @@ -6525,11 +6771,21 @@ typedef struct pm_pinned_variable_node { /** * PinnedVariableNode#variable + * + * The variable used in the pinned expression + * + * foo in ^bar + * ^^^ */ struct pm_node *variable; /** * PinnedVariableNode#operator_loc + * + * The location of the `^` operator + * + * foo in ^bar + * ^ */ pm_location_t operator_loc; } pm_pinned_variable_node_t; @@ -7313,6 +7569,8 @@ typedef struct pm_string_node { * super foo, bar * ^^^^^^^^^^^^^^ * + * If no arguments are provided (except for a block), it would be a `ForwardingSuperNode` instead. + * * Type: ::PM_SUPER_NODE * * @extends pm_node_t @@ -7334,6 +7592,8 @@ typedef struct pm_super_node { /** * SuperNode#arguments + * + * Can be only `nil` when there are empty parentheses, like `super()`. */ struct pm_arguments_node *arguments; @@ -7764,6 +8024,8 @@ typedef enum pm_arguments_node_flags { /** if the arguments contain multiple splats */ PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS = 64, + + PM_ARGUMENTS_NODE_FLAGS_LAST, } pm_arguments_node_flags_t; /** @@ -7772,6 +8034,8 @@ typedef enum pm_arguments_node_flags { typedef enum pm_array_node_flags { /** if array contains splat nodes */ PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT = 4, + + PM_ARRAY_NODE_FLAGS_LAST, } pm_array_node_flags_t; /** @@ -7789,6 +8053,8 @@ typedef enum pm_call_node_flags { /** a call that ignores method visibility */ PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY = 32, + + PM_CALL_NODE_FLAGS_LAST, } pm_call_node_flags_t; /** @@ -7800,6 +8066,8 @@ typedef enum pm_encoding_flags { /** internal bytes forced the encoding to binary */ PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING = 8, + + PM_ENCODING_FLAGS_LAST, } pm_encoding_flags_t; /** @@ -7817,6 +8085,8 @@ typedef enum pm_integer_base_flags { /** 0x prefix */ PM_INTEGER_BASE_FLAGS_HEXADECIMAL = 32, + + PM_INTEGER_BASE_FLAGS_LAST, } pm_integer_base_flags_t; /** @@ -7828,6 +8098,8 @@ typedef enum pm_interpolated_string_node_flags { /** 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_LAST, } pm_interpolated_string_node_flags_t; /** @@ -7836,6 +8108,8 @@ typedef enum pm_interpolated_string_node_flags { 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_LAST, } pm_keyword_hash_node_flags_t; /** @@ -7844,6 +8118,8 @@ typedef enum pm_keyword_hash_node_flags { 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_LAST, } pm_loop_flags_t; /** @@ -7852,6 +8128,8 @@ typedef enum pm_loop_flags { 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_LAST, } pm_parameter_flags_t; /** @@ -7860,6 +8138,8 @@ typedef enum pm_parameter_flags { typedef enum pm_parentheses_node_flags { /** parentheses that contain multiple potentially void statements */ PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS = 4, + + PM_PARENTHESES_NODE_FLAGS_LAST, } pm_parentheses_node_flags_t; /** @@ -7868,6 +8148,8 @@ typedef enum pm_parentheses_node_flags { typedef enum pm_range_flags { /** ... operator */ PM_RANGE_FLAGS_EXCLUDE_END = 4, + + PM_RANGE_FLAGS_LAST, } pm_range_flags_t; /** @@ -7906,6 +8188,8 @@ typedef enum pm_regular_expression_flags { /** internal bytes forced the encoding to US-ASCII */ PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING = 4096, + + PM_REGULAR_EXPRESSION_FLAGS_LAST, } pm_regular_expression_flags_t; /** @@ -7920,6 +8204,8 @@ typedef enum pm_shareable_constant_node_flags { /** 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_LAST, } pm_shareable_constant_node_flags_t; /** @@ -7937,6 +8223,8 @@ typedef enum pm_string_flags { /** mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` */ PM_STRING_FLAGS_MUTABLE = 32, + + PM_STRING_FLAGS_LAST, } pm_string_flags_t; /** @@ -7951,6 +8239,8 @@ typedef enum pm_symbol_flags { /** internal bytes forced the encoding to US-ASCII */ PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING = 16, + + PM_SYMBOL_FLAGS_LAST, } pm_symbol_flags_t; /** diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/diagnostic.h b/vendor/mruby-compiler2/lib/prism/include/prism/diagnostic.h index 2b1aa9b..9bf3ade 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/diagnostic.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/diagnostic.h @@ -1,3 +1,5 @@ +/* :markup: markdown */ + /*----------------------------------------------------------------------------*/ /* This file is generated by the templates/template.rb script and should not */ /* be modified manually. See */ @@ -89,6 +91,7 @@ typedef enum { PM_ERR_CONDITIONAL_WHILE_PREDICATE, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT, PM_ERR_DEF_ENDLESS, + PM_ERR_DEF_ENDLESS_PARAMETERS, PM_ERR_DEF_ENDLESS_SETTER, PM_ERR_DEF_NAME, PM_ERR_DEF_PARAMS_TERM, @@ -130,6 +133,8 @@ typedef enum { PM_ERR_EXPECT_FOR_DELIMITER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER, PM_ERR_EXPECT_IN_DELIMITER, + PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN, + PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER, PM_ERR_EXPECT_MESSAGE, PM_ERR_EXPECT_RBRACKET, @@ -245,6 +250,7 @@ typedef enum { PM_ERR_PARAMETER_WILD_LOOSE_COMMA, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS, PM_ERR_PATTERN_CAPTURE_DUPLICATE, + PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET, @@ -306,6 +312,7 @@ typedef enum { PM_ERR_UNEXPECTED_INDEX_KEYWORDS, PM_ERR_UNEXPECTED_LABEL, PM_ERR_UNEXPECTED_MULTI_WRITE, + PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE, PM_ERR_UNEXPECTED_RANGE_OPERATOR, PM_ERR_UNEXPECTED_SAFE_NAVIGATION, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/options.h b/vendor/mruby-compiler2/lib/prism/include/prism/options.h index 2f64701..c00c7bf 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/options.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/options.h @@ -82,14 +82,26 @@ typedef void (*pm_options_shebang_callback_t)(struct pm_options *options, const * 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, + /** If an explicit version is not provided, the current version of prism will be used. */ + PM_OPTIONS_VERSION_UNSET = 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_CRUBY_3_4 = 2, + + /** The vendored version of prism in CRuby 4.0.x. */ + PM_OPTIONS_VERSION_CRUBY_3_5 = 3, + + /** The vendored version of prism in CRuby 4.0.x. */ + PM_OPTIONS_VERSION_CRUBY_4_0 = 3, + + /** The vendored version of prism in CRuby 4.1.x. */ + PM_OPTIONS_VERSION_CRUBY_4_1 = 4, + + /** The current version of prism. */ + PM_OPTIONS_VERSION_LATEST = PM_OPTIONS_VERSION_CRUBY_4_1 } pm_options_version_t; /** @@ -231,6 +243,8 @@ static const uint8_t PM_OPTIONS_COMMAND_LINE_X = 0x20; * @param shebang_callback The shebang callback to set. * @param shebang_callback_data Any additional data that should be passed along * to the callback. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_shebang_callback_set(pm_options_t *options, pm_options_shebang_callback_t shebang_callback, void *shebang_callback_data); @@ -239,6 +253,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_shebang_callback_set(pm_options_t *optio * * @param options The options struct to set the filepath on. * @param filepath The filepath to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_filepath_set(pm_options_t *options, const char *filepath); @@ -247,6 +263,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_filepath_set(pm_options_t *options, cons * * @param options The options struct to set the line on. * @param line The line to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_line_set(pm_options_t *options, int32_t line); @@ -255,6 +273,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_line_set(pm_options_t *options, int32_t * * @param options The options struct to set the encoding on. * @param encoding The encoding to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_encoding_set(pm_options_t *options, const char *encoding); @@ -263,6 +283,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_encoding_set(pm_options_t *options, cons * * @param options The options struct to set the encoding_locked value on. * @param encoding_locked The encoding_locked value to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked); @@ -271,6 +293,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_encoding_locked_set(pm_options_t *option * * @param options The options struct to set the frozen string literal value on. * @param frozen_string_literal The frozen string literal value to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal); @@ -279,6 +303,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t * * * @param options The options struct to set the command line option on. * @param command_line The command_line value to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options, uint8_t command_line); @@ -291,6 +317,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options, * @param version The version to set. * @param length The length of the version string. * @return Whether or not the version was parsed successfully. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length); @@ -299,6 +327,8 @@ PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const * * @param options The options struct to set the main script value on. * @param main_script The main script value to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_main_script_set(pm_options_t *options, bool main_script); @@ -307,6 +337,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_main_script_set(pm_options_t *options, b * * @param options The options struct to set the partial script value on. * @param partial_script The partial script value to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_partial_script_set(pm_options_t *options, bool partial_script); @@ -315,6 +347,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_partial_script_set(pm_options_t *options * * @param options The options struct to set the freeze value on. * @param freeze The freeze value to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_freeze_set(pm_options_t *options, bool freeze); @@ -324,6 +358,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_freeze_set(pm_options_t *options, bool f * @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. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION bool pm_options_scopes_init(pm_options_t *options, size_t scopes_count); @@ -333,6 +369,8 @@ PRISM_EXPORTED_FUNCTION bool pm_options_scopes_init(pm_options_t *options, size_ * @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. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION const pm_options_scope_t * pm_options_scope_get(const pm_options_t *options, size_t index); @@ -343,6 +381,8 @@ PRISM_EXPORTED_FUNCTION const pm_options_scope_t * pm_options_scope_get(const pm * @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. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION bool pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count); @@ -352,6 +392,8 @@ PRISM_EXPORTED_FUNCTION bool pm_options_scope_init(pm_options_scope_t *scope, si * @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. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index); @@ -360,6 +402,8 @@ PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local_get(const pm_ * * @param scope The scope struct to set the forwarding on. * @param forwarding The forwarding value to set. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding); @@ -367,6 +411,8 @@ PRISM_EXPORTED_FUNCTION void pm_options_scope_forwarding_set(pm_options_scope_t * Free the internal memory associated with the options. * * @param options The options struct whose internal memory should be freed. + * + * \public \memberof pm_options */ PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options); diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/parser.h b/vendor/mruby-compiler2/lib/prism/include/prism/parser.h index 992729d..95d7aac 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/parser.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/parser.h @@ -299,6 +299,9 @@ typedef enum { /** a rescue else statement within a do..end block */ PM_CONTEXT_BLOCK_ELSE, + /** expressions in block parameters `foo do |...| end ` */ + PM_CONTEXT_BLOCK_PARAMETERS, + /** a rescue statement within a do..end block */ PM_CONTEXT_BLOCK_RESCUE, diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/regexp.h b/vendor/mruby-compiler2/lib/prism/include/prism/regexp.h index c0b3163..5366b5a 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/regexp.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/regexp.h @@ -17,12 +17,12 @@ #include /** - * This callback is called when a named capture group is found. + * This callback is called by pm_regexp_parse() 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. + * This callback is called by pm_regexp_parse() 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); 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 index f3c20ab..cb80f8b 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_buffer.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_buffer.h @@ -51,6 +51,8 @@ bool pm_buffer_init_capacity(pm_buffer_t *buffer, size_t capacity); * * @param buffer The buffer to initialize. * @returns True if the buffer was initialized successfully, false otherwise. + * + * \public \memberof pm_buffer_t */ PRISM_EXPORTED_FUNCTION bool pm_buffer_init(pm_buffer_t *buffer); @@ -59,6 +61,8 @@ PRISM_EXPORTED_FUNCTION bool pm_buffer_init(pm_buffer_t *buffer); * * @param buffer The buffer to get the value of. * @returns The value of the buffer. + * + * \public \memberof pm_buffer_t */ PRISM_EXPORTED_FUNCTION char * pm_buffer_value(const pm_buffer_t *buffer); @@ -67,6 +71,8 @@ PRISM_EXPORTED_FUNCTION char * pm_buffer_value(const pm_buffer_t *buffer); * * @param buffer The buffer to get the length of. * @returns The length of the buffer. + * + * \public \memberof pm_buffer_t */ PRISM_EXPORTED_FUNCTION size_t pm_buffer_length(const pm_buffer_t *buffer); @@ -222,6 +228,8 @@ void pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size * Free the memory associated with the buffer. * * @param buffer The buffer to free. + * + * \public \memberof pm_buffer_t */ PRISM_EXPORTED_FUNCTION void pm_buffer_free(pm_buffer_t *buffer); 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 index a9e2966..304665e 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_integer.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_integer.h @@ -112,6 +112,8 @@ void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator); * * @param buffer The buffer to append the string to. * @param integer The integer to convert to a string. + * + * \public \memberof pm_integer_t */ PRISM_EXPORTED_FUNCTION void pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer); @@ -120,6 +122,8 @@ PRISM_EXPORTED_FUNCTION void pm_integer_string(pm_buffer_t *buffer, const pm_int * the integer exceeds the size of a single node in the linked list. * * @param integer The integer to free. + * + * \public \memberof pm_integer_t */ PRISM_EXPORTED_FUNCTION void pm_integer_free(pm_integer_t *integer); 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 index 3512dee..f544bb2 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_list.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_list.h @@ -68,6 +68,8 @@ typedef struct { * * @param list The list to check. * @return True if the given list is empty, otherwise false. + * + * \public \memberof pm_list_t */ PRISM_EXPORTED_FUNCTION bool pm_list_empty_p(pm_list_t *list); @@ -76,6 +78,8 @@ PRISM_EXPORTED_FUNCTION bool pm_list_empty_p(pm_list_t *list); * * @param list The list to check. * @return The size of the list. + * + * \public \memberof pm_list_t */ PRISM_EXPORTED_FUNCTION size_t pm_list_size(pm_list_t *list); @@ -91,6 +95,8 @@ 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. + * + * \public \memberof pm_list_t */ PRISM_EXPORTED_FUNCTION void pm_list_free(pm_list_t *list); 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 index f99f1ab..d8456ff 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_string.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/util/pm_string.h @@ -45,11 +45,11 @@ typedef struct { /** 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`. */ + /** 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`. */ + /** This string is a memory-mapped file, and should be freed using `pm_string_free()`. */ PM_STRING_MAPPED #endif } type; @@ -130,6 +130,8 @@ typedef enum { * @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. + * + * \public \memberof pm_string_t */ PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_mapped_init(pm_string_t *string, const char *filepath); @@ -141,6 +143,8 @@ PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_mapped_init(pm_string_ * @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. + * + * \public \memberof pm_string_t */ PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_file_init(pm_string_t *string, const char *filepath); @@ -169,6 +173,8 @@ int pm_string_compare(const pm_string_t *left, const pm_string_t *right); * * @param string The string to get the length of. * @return The length of the string. + * + * \public \memberof pm_string_t */ PRISM_EXPORTED_FUNCTION size_t pm_string_length(const pm_string_t *string); @@ -177,6 +183,8 @@ PRISM_EXPORTED_FUNCTION size_t pm_string_length(const pm_string_t *string); * * @param string The string to get the start pointer of. * @return The start pointer of the string. + * + * \public \memberof pm_string_t */ PRISM_EXPORTED_FUNCTION const uint8_t * pm_string_source(const pm_string_t *string); @@ -184,6 +192,8 @@ PRISM_EXPORTED_FUNCTION const uint8_t * pm_string_source(const pm_string_t *stri * Free the associated memory of the given string. * * @param string The string to free. + * + * \public \memberof pm_string_t */ PRISM_EXPORTED_FUNCTION void pm_string_free(pm_string_t *string); diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/version.h b/vendor/mruby-compiler2/lib/prism/include/prism/version.h index 0a2a8c8..b95611f 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/version.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/version.h @@ -14,7 +14,7 @@ /** * The minor version of the Prism library as an int. */ -#define PRISM_VERSION_MINOR 4 +#define PRISM_VERSION_MINOR 9 /** * The patch version of the Prism library as an int. @@ -24,6 +24,6 @@ /** * The version of the Prism library as a constant string. */ -#define PRISM_VERSION "1.4.0" +#define PRISM_VERSION "1.9.0" #endif diff --git a/vendor/mruby-compiler2/lib/prism/prism.gemspec b/vendor/mruby-compiler2/lib/prism/prism.gemspec index 1947eed..20c66a5 100644 --- a/vendor/mruby-compiler2/lib/prism/prism.gemspec +++ b/vendor/mruby-compiler2/lib/prism/prism.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "prism" - spec.version = "1.4.0" + spec.version = "1.9.0" spec.authors = ["Shopify"] spec.email = ["ruby@shopify.com"] @@ -88,20 +88,23 @@ Gem::Specification.new do |spec| "lib/prism/pattern.rb", "lib/prism/polyfill/append_as_bytes.rb", "lib/prism/polyfill/byteindex.rb", + "lib/prism/polyfill/scan_byte.rb", "lib/prism/polyfill/unpack1.rb", + "lib/prism/polyfill/warn.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_current.rb", + "lib/prism/translation/parser_versions.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/filter.rb", + "lib/prism/translation/ripper/lexer.rb", "lib/prism/translation/ripper/sexp.rb", "lib/prism/translation/ripper/shim.rb", "lib/prism/translation/ruby_parser.rb", @@ -117,9 +120,7 @@ Gem::Specification.new do |spec| "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/parser_versions.rbi", "rbi/prism/translation/ripper.rbi", "rbi/prism/visitor.rbi", "sig/prism.rbs", @@ -134,6 +135,7 @@ Gem::Specification.new do |spec| "sig/prism/node.rbs", "sig/prism/pack.rbs", "sig/prism/parse_result.rbs", + "sig/prism/parse_result/comments.rbs", "sig/prism/pattern.rbs", "sig/prism/reflection.rbs", "sig/prism/relocation.rbs", diff --git a/vendor/mruby-compiler2/lib/prism/src/diagnostic.c b/vendor/mruby-compiler2/lib/prism/src/diagnostic.c index be1bd71..df0d564 100644 --- a/vendor/mruby-compiler2/lib/prism/src/diagnostic.c +++ b/vendor/mruby-compiler2/lib/prism/src/diagnostic.c @@ -1,3 +1,5 @@ +/* :markup: markdown */ + /*----------------------------------------------------------------------------*/ /* This file is generated by the templates/template.rb script and should not */ /* be modified manually. See */ @@ -8,7 +10,7 @@ #include "prism/diagnostic.h" -#define PM_DIAGNOSTIC_ID_MAX 319 +#define PM_DIAGNOSTIC_ID_MAX 324 /** This struct holds the data for each diagnostic. */ typedef struct { @@ -152,6 +154,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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_PARAMETERS] = { "could not parse the endless method parameters", 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 }, @@ -192,6 +195,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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_AFTER_NOT_LPAREN] = { "expected a `(` immediately after `not`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER] = { "expected a `(` after `not`", 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 }, @@ -306,6 +311,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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_CAPTURE_IN_ALTERNATIVE] = { "variable capture in alternative pattern", 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 }, @@ -366,6 +372,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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_PARAMETER_DEFAULT_VALUE] = { "unexpected %s; expected a default value for a parameter", 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 }, @@ -478,6 +485,7 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { 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_PARAMETERS: return "def_endless_parameters"; 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"; @@ -519,6 +527,8 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { 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_AFTER_NOT_LPAREN: return "expect_lparen_after_not_lparen"; + case PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER: return "expect_lparen_after_not_other"; 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"; @@ -634,6 +644,7 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { 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_CAPTURE_IN_ALTERNATIVE: return "pattern_capture_in_alternative"; 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"; @@ -695,6 +706,7 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { 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_PARAMETER_DEFAULT_VALUE: return "unexpected_parameter_default_value"; 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"; diff --git a/vendor/mruby-compiler2/lib/prism/src/encoding.c b/vendor/mruby-compiler2/lib/prism/src/encoding.c index a4aeed1..d7e5616 100644 --- a/vendor/mruby-compiler2/lib/prism/src/encoding.c +++ b/vendor/mruby-compiler2/lib/prism/src/encoding.c @@ -2,7 +2,7 @@ typedef uint32_t pm_unicode_codepoint_t; -#define UNICODE_ALPHA_CODEPOINTS_LENGTH 1450 +#define UNICODE_ALPHA_CODEPOINTS_LENGTH 1508 static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEPOINTS_LENGTH] = { 0x100, 0x2C1, 0x2C6, 0x2D1, @@ -10,7 +10,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x2EC, 0x2EC, 0x2EE, 0x2EE, 0x345, 0x345, - 0x370, 0x374, + 0x363, 0x374, 0x376, 0x377, 0x37A, 0x37D, 0x37F, 0x37F, @@ -50,7 +50,8 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x840, 0x858, 0x860, 0x86A, 0x870, 0x887, - 0x889, 0x88E, + 0x889, 0x88F, + 0x897, 0x897, 0x8A0, 0x8C9, 0x8D4, 0x8DF, 0x8E3, 0x8E9, @@ -140,7 +141,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0xC4A, 0xC4C, 0xC55, 0xC56, 0xC58, 0xC5A, - 0xC5D, 0xC5D, + 0xC5C, 0xC5D, 0xC60, 0xC63, 0xC80, 0xC83, 0xC85, 0xC8C, @@ -152,7 +153,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0xCC6, 0xCC8, 0xCCA, 0xCCC, 0xCD5, 0xCD6, - 0xCDD, 0xCDE, + 0xCDC, 0xCDE, 0xCE0, 0xCE3, 0xCF1, 0xCF3, 0xD00, 0xD0C, @@ -264,7 +265,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x1C00, 0x1C36, 0x1C4D, 0x1C4F, 0x1C5A, 0x1C7D, - 0x1C80, 0x1C88, + 0x1C80, 0x1C8A, 0x1C90, 0x1CBA, 0x1CBD, 0x1CBF, 0x1CE9, 0x1CEC, @@ -272,7 +273,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x1CF5, 0x1CF6, 0x1CFA, 0x1CFA, 0x1D00, 0x1DBF, - 0x1DE7, 0x1DF4, + 0x1DD3, 0x1DF4, 0x1E00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, @@ -352,11 +353,8 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0xA67F, 0xA6EF, 0xA717, 0xA71F, 0xA722, 0xA788, - 0xA78B, 0xA7CA, - 0xA7D0, 0xA7D1, - 0xA7D3, 0xA7D3, - 0xA7D5, 0xA7D9, - 0xA7F2, 0xA805, + 0xA78B, 0xA7DC, + 0xA7F1, 0xA805, 0xA807, 0xA827, 0xA840, 0xA873, 0xA880, 0xA8C3, @@ -446,6 +444,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x105A3, 0x105B1, 0x105B3, 0x105B9, 0x105BB, 0x105BC, + 0x105C0, 0x105F3, 0x10600, 0x10736, 0x10740, 0x10755, 0x10760, 0x10767, @@ -464,6 +463,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x108F4, 0x108F5, 0x10900, 0x10915, 0x10920, 0x10939, + 0x10940, 0x10959, 0x10980, 0x109B7, 0x109BE, 0x109BF, 0x10A00, 0x10A03, @@ -483,9 +483,14 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x10C80, 0x10CB2, 0x10CC0, 0x10CF2, 0x10D00, 0x10D27, + 0x10D4A, 0x10D65, + 0x10D69, 0x10D69, + 0x10D6F, 0x10D85, 0x10E80, 0x10EA9, 0x10EAB, 0x10EAC, 0x10EB0, 0x10EB1, + 0x10EC2, 0x10EC7, + 0x10EFA, 0x10EFC, 0x10F00, 0x10F1C, 0x10F27, 0x10F27, 0x10F30, 0x10F45, @@ -529,6 +534,17 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x11350, 0x11350, 0x11357, 0x11357, 0x1135D, 0x11363, + 0x11380, 0x11389, + 0x1138B, 0x1138B, + 0x1138E, 0x1138E, + 0x11390, 0x113B5, + 0x113B7, 0x113C0, + 0x113C2, 0x113C2, + 0x113C5, 0x113C5, + 0x113C7, 0x113CA, + 0x113CC, 0x113CD, + 0x113D1, 0x113D1, + 0x113D3, 0x113D3, 0x11400, 0x11441, 0x11443, 0x11445, 0x11447, 0x1144A, @@ -567,6 +583,8 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x11A50, 0x11A97, 0x11A9D, 0x11A9D, 0x11AB0, 0x11AF8, + 0x11B60, 0x11B67, + 0x11BC0, 0x11BE0, 0x11C00, 0x11C08, 0x11C0A, 0x11C36, 0x11C38, 0x11C3E, @@ -588,6 +606,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x11D90, 0x11D91, 0x11D93, 0x11D96, 0x11D98, 0x11D98, + 0x11DB0, 0x11DDB, 0x11EE0, 0x11EF6, 0x11F00, 0x11F10, 0x11F12, 0x11F3A, @@ -599,7 +618,9 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x12F90, 0x12FF0, 0x13000, 0x1342F, 0x13441, 0x13446, + 0x13460, 0x143FA, 0x14400, 0x14646, + 0x16100, 0x1612E, 0x16800, 0x16A38, 0x16A40, 0x16A5E, 0x16A70, 0x16ABE, @@ -608,16 +629,19 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x16B40, 0x16B43, 0x16B63, 0x16B77, 0x16B7D, 0x16B8F, + 0x16D40, 0x16D6C, 0x16E40, 0x16E7F, + 0x16EA0, 0x16EB8, + 0x16EBB, 0x16ED3, 0x16F00, 0x16F4A, 0x16F4F, 0x16F87, 0x16F8F, 0x16F9F, 0x16FE0, 0x16FE1, 0x16FE3, 0x16FE3, - 0x16FF0, 0x16FF1, - 0x17000, 0x187F7, - 0x18800, 0x18CD5, - 0x18D00, 0x18D08, + 0x16FF0, 0x16FF6, + 0x17000, 0x18CD5, + 0x18CFF, 0x18D1E, + 0x18D80, 0x18DF2, 0x1AFF0, 0x1AFF3, 0x1AFF5, 0x1AFFB, 0x1AFFD, 0x1AFFE, @@ -677,6 +701,11 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x1E290, 0x1E2AD, 0x1E2C0, 0x1E2EB, 0x1E4D0, 0x1E4EB, + 0x1E5D0, 0x1E5ED, + 0x1E5F0, 0x1E5F0, + 0x1E6C0, 0x1E6DE, + 0x1E6E0, 0x1E6F5, + 0x1E6FE, 0x1E6FF, 0x1E7E0, 0x1E7E6, 0x1E7E8, 0x1E7EB, 0x1E7ED, 0x1E7EE, @@ -722,16 +751,16 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x1F150, 0x1F169, 0x1F170, 0x1F189, 0x20000, 0x2A6DF, - 0x2A700, 0x2B739, - 0x2B740, 0x2B81D, - 0x2B820, 0x2CEA1, + 0x2A700, 0x2B81D, + 0x2B820, 0x2CEAD, 0x2CEB0, 0x2EBE0, + 0x2EBF0, 0x2EE5D, 0x2F800, 0x2FA1D, 0x30000, 0x3134A, - 0x31350, 0x323AF, + 0x31350, 0x33479, }; -#define UNICODE_ALNUM_CODEPOINTS_LENGTH 1528 +#define UNICODE_ALNUM_CODEPOINTS_LENGTH 1598 static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEPOINTS_LENGTH] = { 0x100, 0x2C1, 0x2C6, 0x2D1, @@ -739,7 +768,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x2EC, 0x2EC, 0x2EE, 0x2EE, 0x345, 0x345, - 0x370, 0x374, + 0x363, 0x374, 0x376, 0x377, 0x37A, 0x37D, 0x37F, 0x37F, @@ -778,7 +807,8 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x840, 0x858, 0x860, 0x86A, 0x870, 0x887, - 0x889, 0x88E, + 0x889, 0x88F, + 0x897, 0x897, 0x8A0, 0x8C9, 0x8D4, 0x8DF, 0x8E3, 0x8E9, @@ -872,7 +902,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0xC4A, 0xC4C, 0xC55, 0xC56, 0xC58, 0xC5A, - 0xC5D, 0xC5D, + 0xC5C, 0xC5D, 0xC60, 0xC63, 0xC66, 0xC6F, 0xC80, 0xC83, @@ -885,7 +915,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0xCC6, 0xCC8, 0xCCA, 0xCCC, 0xCD5, 0xCD6, - 0xCDD, 0xCDE, + 0xCDC, 0xCDE, 0xCE0, 0xCE3, 0xCE6, 0xCEF, 0xCF1, 0xCF3, @@ -1007,7 +1037,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1C00, 0x1C36, 0x1C40, 0x1C49, 0x1C4D, 0x1C7D, - 0x1C80, 0x1C88, + 0x1C80, 0x1C8A, 0x1C90, 0x1CBA, 0x1CBD, 0x1CBF, 0x1CE9, 0x1CEC, @@ -1015,7 +1045,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1CF5, 0x1CF6, 0x1CFA, 0x1CFA, 0x1D00, 0x1DBF, - 0x1DE7, 0x1DF4, + 0x1DD3, 0x1DF4, 0x1E00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, @@ -1094,11 +1124,8 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0xA67F, 0xA6EF, 0xA717, 0xA71F, 0xA722, 0xA788, - 0xA78B, 0xA7CA, - 0xA7D0, 0xA7D1, - 0xA7D3, 0xA7D3, - 0xA7D5, 0xA7D9, - 0xA7F2, 0xA805, + 0xA78B, 0xA7DC, + 0xA7F1, 0xA805, 0xA807, 0xA827, 0xA840, 0xA873, 0xA880, 0xA8C3, @@ -1191,6 +1218,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x105A3, 0x105B1, 0x105B3, 0x105B9, 0x105BB, 0x105BC, + 0x105C0, 0x105F3, 0x10600, 0x10736, 0x10740, 0x10755, 0x10760, 0x10767, @@ -1209,6 +1237,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x108F4, 0x108F5, 0x10900, 0x10915, 0x10920, 0x10939, + 0x10940, 0x10959, 0x10980, 0x109B7, 0x109BE, 0x109BF, 0x10A00, 0x10A03, @@ -1229,9 +1258,14 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x10CC0, 0x10CF2, 0x10D00, 0x10D27, 0x10D30, 0x10D39, + 0x10D40, 0x10D65, + 0x10D69, 0x10D69, + 0x10D6F, 0x10D85, 0x10E80, 0x10EA9, 0x10EAB, 0x10EAC, 0x10EB0, 0x10EB1, + 0x10EC2, 0x10EC7, + 0x10EFA, 0x10EFC, 0x10F00, 0x10F1C, 0x10F27, 0x10F27, 0x10F30, 0x10F45, @@ -1278,6 +1312,17 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x11350, 0x11350, 0x11357, 0x11357, 0x1135D, 0x11363, + 0x11380, 0x11389, + 0x1138B, 0x1138B, + 0x1138E, 0x1138E, + 0x11390, 0x113B5, + 0x113B7, 0x113C0, + 0x113C2, 0x113C2, + 0x113C5, 0x113C5, + 0x113C7, 0x113CA, + 0x113CC, 0x113CD, + 0x113D1, 0x113D1, + 0x113D3, 0x113D3, 0x11400, 0x11441, 0x11443, 0x11445, 0x11447, 0x1144A, @@ -1297,6 +1342,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x11680, 0x116B5, 0x116B8, 0x116B8, 0x116C0, 0x116C9, + 0x116D0, 0x116E3, 0x11700, 0x1171A, 0x1171D, 0x1172A, 0x11730, 0x11739, @@ -1322,6 +1368,9 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x11A50, 0x11A97, 0x11A9D, 0x11A9D, 0x11AB0, 0x11AF8, + 0x11B60, 0x11B67, + 0x11BC0, 0x11BE0, + 0x11BF0, 0x11BF9, 0x11C00, 0x11C08, 0x11C0A, 0x11C36, 0x11C38, 0x11C3E, @@ -1346,6 +1395,8 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x11D93, 0x11D96, 0x11D98, 0x11D98, 0x11DA0, 0x11DA9, + 0x11DB0, 0x11DDB, + 0x11DE0, 0x11DE9, 0x11EE0, 0x11EF6, 0x11F00, 0x11F10, 0x11F12, 0x11F3A, @@ -1358,7 +1409,10 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x12F90, 0x12FF0, 0x13000, 0x1342F, 0x13441, 0x13446, + 0x13460, 0x143FA, 0x14400, 0x14646, + 0x16100, 0x1612E, + 0x16130, 0x16139, 0x16800, 0x16A38, 0x16A40, 0x16A5E, 0x16A60, 0x16A69, @@ -1370,16 +1424,20 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x16B50, 0x16B59, 0x16B63, 0x16B77, 0x16B7D, 0x16B8F, + 0x16D40, 0x16D6C, + 0x16D70, 0x16D79, 0x16E40, 0x16E7F, + 0x16EA0, 0x16EB8, + 0x16EBB, 0x16ED3, 0x16F00, 0x16F4A, 0x16F4F, 0x16F87, 0x16F8F, 0x16F9F, 0x16FE0, 0x16FE1, 0x16FE3, 0x16FE3, - 0x16FF0, 0x16FF1, - 0x17000, 0x187F7, - 0x18800, 0x18CD5, - 0x18D00, 0x18D08, + 0x16FF0, 0x16FF6, + 0x17000, 0x18CD5, + 0x18CFF, 0x18D1E, + 0x18D80, 0x18DF2, 0x1AFF0, 0x1AFF3, 0x1AFF5, 0x1AFFB, 0x1AFFD, 0x1AFFE, @@ -1394,6 +1452,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1BC80, 0x1BC88, 0x1BC90, 0x1BC99, 0x1BC9E, 0x1BC9E, + 0x1CCF0, 0x1CCF9, 0x1D400, 0x1D454, 0x1D456, 0x1D49C, 0x1D49E, 0x1D49F, @@ -1443,6 +1502,11 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1E2F0, 0x1E2F9, 0x1E4D0, 0x1E4EB, 0x1E4F0, 0x1E4F9, + 0x1E5D0, 0x1E5ED, + 0x1E5F0, 0x1E5FA, + 0x1E6C0, 0x1E6DE, + 0x1E6E0, 0x1E6F5, + 0x1E6FE, 0x1E6FF, 0x1E7E0, 0x1E7E6, 0x1E7E8, 0x1E7EB, 0x1E7ED, 0x1E7EE, @@ -1490,16 +1554,16 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1F170, 0x1F189, 0x1FBF0, 0x1FBF9, 0x20000, 0x2A6DF, - 0x2A700, 0x2B739, - 0x2B740, 0x2B81D, - 0x2B820, 0x2CEA1, + 0x2A700, 0x2B81D, + 0x2B820, 0x2CEAD, 0x2CEB0, 0x2EBE0, + 0x2EBF0, 0x2EE5D, 0x2F800, 0x2FA1D, 0x30000, 0x3134A, - 0x31350, 0x323AF, + 0x31350, 0x33479, }; -#define UNICODE_ISUPPER_CODEPOINTS_LENGTH 1302 +#define UNICODE_ISUPPER_CODEPOINTS_LENGTH 1320 static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_CODEPOINTS_LENGTH] = { 0x100, 0x100, 0x102, 0x102, @@ -1774,6 +1838,7 @@ static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_C 0x10C7, 0x10C7, 0x10CD, 0x10CD, 0x13A0, 0x13F5, + 0x1C89, 0x1C89, 0x1C90, 0x1CBA, 0x1CBD, 0x1CBF, 0x1E00, 0x1E00, @@ -2103,9 +2168,15 @@ static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_C 0xA7C2, 0xA7C2, 0xA7C4, 0xA7C7, 0xA7C9, 0xA7C9, + 0xA7CB, 0xA7CC, + 0xA7CE, 0xA7CE, 0xA7D0, 0xA7D0, + 0xA7D2, 0xA7D2, + 0xA7D4, 0xA7D4, 0xA7D6, 0xA7D6, 0xA7D8, 0xA7D8, + 0xA7DA, 0xA7DA, + 0xA7DC, 0xA7DC, 0xA7F5, 0xA7F5, 0xFF21, 0xFF3A, 0x10400, 0x10427, @@ -2115,8 +2186,10 @@ static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_C 0x1058C, 0x10592, 0x10594, 0x10595, 0x10C80, 0x10CB2, + 0x10D50, 0x10D65, 0x118A0, 0x118BF, 0x16E40, 0x16E5F, + 0x16EA0, 0x16EB8, 0x1D400, 0x1D419, 0x1D434, 0x1D44D, 0x1D468, 0x1D481, @@ -2304,6 +2377,10 @@ pm_encoding_utf_8_char_width(const uint8_t *b, ptrdiff_t n) { */ size_t pm_encoding_utf_8_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) ? 1 : 0; } @@ -2324,6 +2401,10 @@ pm_encoding_utf_8_alpha_char(const uint8_t *b, ptrdiff_t n) { */ size_t pm_encoding_utf_8_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; } @@ -2344,6 +2425,10 @@ pm_encoding_utf_8_alnum_char(const uint8_t *b, ptrdiff_t n) { */ bool pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false; } @@ -2362,7 +2447,8 @@ pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n) { static pm_unicode_codepoint_t pm_cesu_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) { - if (b[0] < 0x80) { + + if ((n > 0) && (b[0] < 0x80)) { *width = 1; return (pm_unicode_codepoint_t) b[0]; } @@ -2401,6 +2487,10 @@ pm_cesu_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) { static size_t pm_encoding_cesu_8_char_width(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + size_t width; pm_cesu_8_codepoint(b, n, &width); return width; @@ -2408,6 +2498,10 @@ pm_encoding_cesu_8_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_cesu_8_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) ? 1 : 0; } @@ -2424,6 +2518,10 @@ pm_encoding_cesu_8_alpha_char(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_cesu_8_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; } @@ -2440,6 +2538,10 @@ pm_encoding_cesu_8_alnum_char(const uint8_t *b, ptrdiff_t n) { static bool pm_encoding_cesu_8_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false; } @@ -3855,14 +3957,14 @@ static const uint8_t pm_encoding_windows_874_table[256] = { }; #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 ## _alpha_char(const uint8_t *b, ptrdiff_t n) { \ + return ((n > 0) && (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 size_t pm_encoding_ ##name ## _alnum_char(const uint8_t *b, ptrdiff_t n) { \ + return ((n > 0) && (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); \ + static bool pm_encoding_ ##name ## _isupper_char(const uint8_t *b, ptrdiff_t n) { \ + return ((n > 0) && (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_UPPERCASE_BIT)); \ } PRISM_ENCODING_TABLE(cp850) @@ -3931,8 +4033,8 @@ PRISM_ENCODING_TABLE(windows_874) * 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; +pm_encoding_ascii_char_width(const uint8_t *b, ptrdiff_t n) { + return ((n > 0) && (*b < 0x80)) ? 1 : 0; } /** @@ -3940,8 +4042,8 @@ pm_encoding_ascii_char_width(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t * 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); +pm_encoding_ascii_alpha_char(const uint8_t *b, ptrdiff_t n) { + return (n > 0) ? (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) : 0; } /** @@ -3951,7 +4053,7 @@ pm_encoding_ascii_alpha_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t */ 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 ((n > 0) && (*b < 0x80)) ? pm_encoding_ascii_alpha_char(b, n) : 0; } /** @@ -3959,8 +4061,8 @@ pm_encoding_ascii_alpha_char_7bit(const uint8_t *b, ptrdiff_t n) { * 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; +pm_encoding_ascii_alnum_char(const uint8_t *b, ptrdiff_t n) { + return ((n > 0) && (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; } /** @@ -3970,7 +4072,7 @@ pm_encoding_ascii_alnum_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t */ 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 ((n > 0) && (*b < 0x80)) ? pm_encoding_ascii_alnum_char(b, n) : 0; } /** @@ -3978,8 +4080,8 @@ pm_encoding_ascii_alnum_char_7bit(const uint8_t *b, ptrdiff_t n) { * 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); +pm_encoding_ascii_isupper_char(const uint8_t *b, ptrdiff_t n) { + return (n > 0) && (pm_encoding_ascii_table[*b] & PRISM_ENCODING_UPPERCASE_BIT); } /** @@ -3998,7 +4100,7 @@ pm_encoding_single_char_width(PRISM_ATTRIBUTE_UNUSED const uint8_t *b, PRISM_ATT 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) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4042,6 +4144,9 @@ pm_encoding_euc_jp_isupper_char(const uint8_t *b, ptrdiff_t n) { */ static size_t pm_encoding_shift_jis_char_width(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } // These are the single byte characters. if (b[0] < 0x80 || (b[0] >= 0xA1 && b[0] <= 0xDF)) { return 1; @@ -4105,7 +4210,7 @@ pm_encoding_shift_jis_isupper_char(const uint8_t *b, ptrdiff_t n) { */ 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); + return (n > 0) && (*b < 0x80) && pm_encoding_ascii_isupper_char(b, n); } /** @@ -4115,7 +4220,7 @@ pm_encoding_ascii_isupper_char_7bit(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_big5_char_width(const uint8_t *b, ptrdiff_t n) { // These are the single byte characters. - if (*b < 0x80) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4134,7 +4239,7 @@ pm_encoding_big5_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_cp949_char_width(const uint8_t *b, ptrdiff_t n) { // These are the single byte characters - if (*b <= 0x80) { + if ((n > 0) && (*b <= 0x80)) { return 1; } @@ -4153,7 +4258,7 @@ pm_encoding_cp949_char_width(const uint8_t *b, ptrdiff_t n) { 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) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4196,7 +4301,7 @@ pm_encoding_emacs_mule_char_width(const uint8_t *b, ptrdiff_t n) { 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) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4215,7 +4320,7 @@ pm_encoding_euc_kr_char_width(const uint8_t *b, ptrdiff_t n) { 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) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4239,7 +4344,7 @@ pm_encoding_euc_tw_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_gb18030_char_width(const uint8_t *b, ptrdiff_t n) { // These are the 1 byte characters. - if (*b < 0x80) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4263,7 +4368,7 @@ pm_encoding_gb18030_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_gbk_char_width(const uint8_t *b, ptrdiff_t n) { // These are the single byte characters. - if (*b <= 0x80) { + if ((n > 0) && (*b <= 0x80)) { return 1; } diff --git a/vendor/mruby-compiler2/lib/prism/src/node.c b/vendor/mruby-compiler2/lib/prism/src/node.c index 9c666b8..e60ea19 100644 --- a/vendor/mruby-compiler2/lib/prism/src/node.c +++ b/vendor/mruby-compiler2/lib/prism/src/node.c @@ -1,3 +1,5 @@ +/* :markup: markdown */ + /*----------------------------------------------------------------------------*/ /* This file is generated by the templates/template.rb script and should not */ /* be modified manually. See */ @@ -3803,6 +3805,15 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no 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 block field pm_buffer_append_byte(buffer, ','); pm_buffer_append_string(buffer, "\"block\":", 8); diff --git a/vendor/mruby-compiler2/lib/prism/src/options.c b/vendor/mruby-compiler2/lib/prism/src/options.c index a457178..09d2a65 100644 --- a/vendor/mruby-compiler2/lib/prism/src/options.c +++ b/vendor/mruby-compiler2/lib/prism/src/options.c @@ -88,27 +88,37 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length return true; } - if (strncmp(version, "3.5", 3) == 0) { - options->version = PM_OPTIONS_VERSION_LATEST; + if (strncmp(version, "3.5", 3) == 0 || strncmp(version, "4.0", 3) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_0; + return true; + } + + if (strncmp(version, "4.1", 3) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_1; return true; } return false; } - if (length >= 4) { - if (strncmp(version, "3.3.", 4) == 0 && is_number(version + 4, length - 4)) { + if (length >= 4 && is_number(version + 4, length - 4)) { + if (strncmp(version, "3.3.", 4) == 0) { options->version = PM_OPTIONS_VERSION_CRUBY_3_3; return true; } - if (strncmp(version, "3.4.", 4) == 0 && is_number(version + 4, length - 4)) { + if (strncmp(version, "3.4.", 4) == 0) { 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; + if (strncmp(version, "3.5.", 4) == 0 || strncmp(version, "4.0.", 4) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_0; + return true; + } + + if (strncmp(version, "4.1.", 4) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_1; return true; } } diff --git a/vendor/mruby-compiler2/lib/prism/src/prettyprint.c b/vendor/mruby-compiler2/lib/prism/src/prettyprint.c index d322400..ad25c0a 100644 --- a/vendor/mruby-compiler2/lib/prism/src/prettyprint.c +++ b/vendor/mruby-compiler2/lib/prism/src/prettyprint.c @@ -1,3 +1,5 @@ +/* :markup: markdown */ + /*----------------------------------------------------------------------------*/ /* This file is generated by the templates/template.rb script and should not */ /* be modified manually. See */ @@ -1266,6 +1268,22 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm } } + // 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); + } + } + // block { pm_buffer_concat(output_buffer, prefix_buffer); diff --git a/vendor/mruby-compiler2/lib/prism/src/prism.c b/vendor/mruby-compiler2/lib/prism/src/prism.c index c4cab8f..b158e50 100644 --- a/vendor/mruby-compiler2/lib/prism/src/prism.c +++ b/vendor/mruby-compiler2/lib/prism/src/prism.c @@ -18,6 +18,24 @@ pm_version(void) { #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) +/******************************************************************************/ +/* Helpful AST-related macros */ +/******************************************************************************/ + +#define FL PM_NODE_FLAGS +#define UP PM_NODE_UPCAST + +#define PM_TOKEN_START(token_) ((token_)->start) +#define PM_TOKEN_END(token_) ((token_)->end) + +#define PM_NODE_START(node_) (UP(node_)->location.start) +#define PM_NODE_END(node_) (UP(node_)->location.end) + +#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 = PM_TOKEN_START(token_), .end = PM_TOKEN_END(token_) }) +#define PM_LOCATION_NODE_VALUE(node_) ((pm_location_t) { .start = PM_NODE_START(node_), .end = PM_NODE_END(node_) }) +#define PM_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == PM_TOKEN_NOT_PROVIDED ? ((pm_location_t) { 0 }) : PM_LOCATION_TOKEN_VALUE(token)) + /******************************************************************************/ /* Lex mode manipulations */ /******************************************************************************/ @@ -1049,25 +1067,25 @@ pm_check_value_expression(pm_parser_t *parser, pm_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); + pm_node_t *vn = pm_check_value_expression(parser, UP(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); + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); if (vn != NULL) return vn; } - node = (pm_node_t *) cast->ensure_clause; + node = UP(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); + pm_node_t *vn = pm_check_value_expression(parser, UP(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); + pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements)); if (vn == NULL) { void_node = NULL; break; @@ -1078,24 +1096,24 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) { } if (cast->else_clause != NULL) { - node = (pm_node_t *) cast->else_clause; + node = UP(cast->else_clause); } else { return void_node; } } else { - node = (pm_node_t *) cast->statements; + node = UP(cast->statements); } break; } case PM_ENSURE_NODE: { pm_ensure_node_t *cast = (pm_ensure_node_t *) node; - node = (pm_node_t *) cast->statements; + node = UP(cast->statements); break; } case PM_PARENTHESES_NODE: { pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; - node = (pm_node_t *) cast->body; + node = UP(cast->body); break; } case PM_STATEMENTS_NODE: { @@ -1108,7 +1126,7 @@ pm_check_value_expression(pm_parser_t *parser, pm_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); + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); if (vn == NULL) { return NULL; } @@ -1123,19 +1141,19 @@ pm_check_value_expression(pm_parser_t *parser, pm_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); + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); if (vn == NULL) { return NULL; } if (void_node == NULL) { void_node = vn; } - node = (pm_node_t *) cast->else_clause; + node = UP(cast->else_clause); break; } case PM_ELSE_NODE: { pm_else_node_t *cast = (pm_else_node_t *) node; - node = (pm_node_t *) cast->statements; + node = UP(cast->statements); break; } case PM_AND_NODE: { @@ -1409,7 +1427,7 @@ pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) { 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); + 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); } } @@ -1559,13 +1577,6 @@ 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 @@ -1635,7 +1646,7 @@ pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_b // 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); + pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_UNEXPECTED_BLOCK); } /******************************************************************************/ @@ -1928,8 +1939,23 @@ pm_node_alloc(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { return memory; } -#define PM_NODE_ALLOC(parser, type) (type *) pm_node_alloc(parser, sizeof(type)) -#define PM_NODE_IDENTIFY(parser) (++parser->node_id) +#define PM_NODE_ALLOC(parser_, type_) (type_ *) pm_node_alloc(parser_, sizeof(type_)) +#define PM_NODE_INIT(parser_, type_, flags_, start_, end_) (pm_node_t) { \ + .type = (type_), \ + .flags = (flags_), \ + .node_id = ++(parser_)->node_id, \ + .location = { .start = (start_), .end = (end_) } \ +} + +#define PM_NODE_INIT_UNSET(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, NULL, NULL) +#define PM_NODE_INIT_BASE(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, (parser_)->start, (parser_)->start) +#define PM_NODE_INIT_TOKEN(parser_, type_, flags_, token_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(token_), PM_TOKEN_END(token_)) +#define PM_NODE_INIT_NODE(parser_, type_, flags_, node_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(node_), PM_NODE_END(node_)) + +#define PM_NODE_INIT_TOKENS(parser_, type_, flags_, left_, right_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(left_), PM_TOKEN_END(right_)) +#define PM_NODE_INIT_NODES(parser_, type_, flags_, left_, right_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(left_), PM_NODE_END(right_)) +#define PM_NODE_INIT_TOKEN_NODE(parser_, type_, flags_, token_, node_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(token_), PM_NODE_END(node_)) +#define PM_NODE_INIT_NODE_TOKEN(parser_, type_, flags_, node_, token_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(node_), PM_TOKEN_END(token_)) /** * Allocate a new MissingNode node. @@ -1938,11 +1964,9 @@ 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 } - }}; + *node = (pm_missing_node_t) { + .base = PM_NODE_INIT(parser, PM_MISSING_NODE, 0, start, end) + }; return node; } @@ -1956,14 +1980,7 @@ pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyw 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 - }, - }, + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_ALIAS_GLOBAL_VARIABLE_NODE, 0, keyword, old_name), .new_name = new_name, .old_name = old_name, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) @@ -1981,14 +1998,7 @@ pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_n 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 - }, - }, + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_ALIAS_METHOD_NODE, 0, keyword, old_name), .new_name = new_name, .old_name = old_name, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) @@ -2005,14 +2015,7 @@ pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node 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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_ALTERNATION_PATTERN_NODE, 0, left, right), .left = left, .right = right, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -2031,14 +2034,7 @@ pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *opera 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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_AND_NODE, 0, left, right), .left = left, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .right = right @@ -2055,11 +2051,7 @@ 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) - }, + .base = PM_NODE_INIT_BASE(parser, PM_ARGUMENTS_NODE, 0), .arguments = { 0 } }; @@ -2083,14 +2075,17 @@ pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argumen node->base.location.start = argument->location.start; } - node->base.location.end = argument->location.end; + if (node->base.location.end < argument->location.end) { + 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); + pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS); } else { - pm_node_flag_set((pm_node_t *) node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT); + pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT); } } } @@ -2103,12 +2098,7 @@ 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_ARRAY_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .elements = { 0 } @@ -2132,11 +2122,11 @@ pm_array_node_elements_append(pm_array_node_t *node, pm_node_t *element) { // 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); + pm_node_flag_unset(UP(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); + pm_node_flag_set(UP(node), PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT); } } @@ -2159,20 +2149,13 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node 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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_ARRAY_PATTERN_NODE, 0, nodes->nodes[0], nodes->nodes[nodes->size - 1]), .constant = NULL, .rest = NULL, .requireds = { 0 }, .posts = { 0 }, - .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .opening_loc = { 0 }, + .closing_loc = { 0 } }; // For now we're going to just copy over each pointer manually. This could be @@ -2202,17 +2185,13 @@ 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, - }, + .base = PM_NODE_INIT_NODE(parser, PM_ARRAY_PATTERN_NODE, 0, rest), .constant = NULL, .rest = rest, .requireds = { 0 }, .posts = { 0 }, - .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .opening_loc = { 0 }, + .closing_loc = { 0 } }; return node; @@ -2227,14 +2206,7 @@ pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, 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 - }, - }, + .base = PM_NODE_INIT_NODE_TOKEN(parser, PM_ARRAY_PATTERN_NODE, 0, constant, closing), .constant = constant, .rest = NULL, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -2255,14 +2227,7 @@ pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *openin 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_ARRAY_PATTERN_NODE, 0, opening, closing), .constant = NULL, .rest = NULL, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -2313,15 +2278,7 @@ pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *oper } *node = (pm_assoc_node_t) { - { - .type = PM_ASSOC_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = key->location.start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_ASSOC_NODE, flags, key->location.start, end), .key = key, .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), .value = value @@ -2339,14 +2296,11 @@ pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token 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 - }, - }, + .base = ( + (value == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_ASSOC_SPLAT_NODE, 0, operator) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_ASSOC_SPLAT_NODE, 0, operator, value) + ), .value = value, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) }; @@ -2363,11 +2317,7 @@ pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) 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), - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_BACK_REFERENCE_READ_NODE, 0, name), .name = pm_parser_constant_id_token(parser, name) }; @@ -2382,17 +2332,14 @@ pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_st 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 - }, - }, + .base = ( + (statements == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_BEGIN_NODE, 0, begin_keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_BEGIN_NODE, 0, begin_keyword, statements) + ), .begin_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(begin_keyword), .statements = statements, - .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .end_keyword_loc = { 0 } }; return node; @@ -2448,14 +2395,11 @@ pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, p 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 - }, - }, + .base = ( + (expression == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator, expression) + ), .expression = expression, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) }; @@ -2471,11 +2415,7 @@ pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p 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 }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_BLOCK_NODE, 0, opening, closing), .locals = *locals, .parameters = parameters, .body = body, @@ -2495,14 +2435,11 @@ pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, cons 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) - }, - }, + .base = ( + (name->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN(parser, PM_BLOCK_PARAMETER_NODE, 0, operator) + : PM_NODE_INIT_TOKENS(parser, PM_BLOCK_PARAMETER_NODE, 0, operator, name) + ), .name = pm_parser_optional_constant_id_token(parser, name), .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -2537,17 +2474,10 @@ pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *param } *node = (pm_block_parameters_node_t) { - { - .type = PM_BLOCK_PARAMETERS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = start, - .end = end - } - }, + .base = PM_NODE_INIT(parser, PM_BLOCK_PARAMETERS_NODE, 0, start, end), .parameters = parameters, .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = { 0 }, .locals = { 0 } }; @@ -2573,11 +2503,7 @@ 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), - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_BLOCK_LOCAL_VARIABLE_NODE, 0, name), .name = pm_parser_constant_id_token(parser, name) }; @@ -2589,7 +2515,7 @@ pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) */ 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); + pm_node_list_append(&node->locals, UP(local)); if (node->base.location.start == NULL) node->base.location.start = local->base.location.start; node->base.location.end = local->base.location.end; @@ -2604,14 +2530,11 @@ pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument 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) - }, - }, + .base = ( + (arguments == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_BREAK_NODE, 0, keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_BREAK_NODE, 0, keyword, arguments) + ), .arguments = arguments, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) }; @@ -2622,10 +2545,11 @@ pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument // 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; +static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2); + +static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1); +static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2); +static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3); /** * Allocate and initialize a new CallNode node. This sets everything to NULL or @@ -2637,18 +2561,14 @@ 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), - }, + .base = PM_NODE_INIT_BASE(parser, PM_CALL_NODE, flags), .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, + .call_operator_loc = { 0 }, + .message_loc = { 0 }, + .opening_loc = { 0 }, .arguments = NULL, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = { 0 }, + .equal_loc = { 0 }, .block = NULL, .name = 0 }; @@ -2720,6 +2640,8 @@ pm_call_node_binary_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t return node; } +static const uint8_t * parse_operator_symbol_name(const pm_token_t *); + /** * Allocate and initialize a new CallNode node from a call expression. */ @@ -2745,10 +2667,14 @@ pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *o 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); + pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION); } - node->name = pm_parser_constant_id_token(parser, message); + /** + * If the final character is `@` as is the case for `foo.~@`, + * we should ignore the @ in the same way we do for symbols. + */ + node->name = pm_parser_constant_id_location(parser, message->start, parse_operator_symbol_name(message)); return node; } @@ -2854,7 +2780,7 @@ pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token 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); + pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION); } node->name = pm_parser_constant_id_constant(parser, "call", 4); @@ -2942,15 +2868,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CALL_AND_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -2976,7 +2894,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const */ 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 (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) { 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) { @@ -3005,15 +2923,7 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_AND_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -3041,15 +2951,7 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CALL_OPERATOR_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -3081,15 +2983,7 @@ pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OPERATOR_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -3119,15 +3013,7 @@ pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CALL_OR_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -3159,15 +3045,7 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OR_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -3195,18 +3073,24 @@ 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 - }, + .base = PM_NODE_INIT_NODE(parser, PM_CALL_TARGET_NODE, FL(target), target), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .name = target->name, .message_loc = target->message_loc }; + /* It is possible to get here where we have parsed an invalid syntax tree + * where the call operator was not present. In that case we will have a + * problem because it is a required location. In this case we need to fill + * it in with a fake location so that the syntax tree remains valid. */ + if (node->call_operator_loc.start == NULL) { + node->call_operator_loc = (pm_location_t) { + .start = target->base.location.start, + .end = target->base.location.start + }; + } + // 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. @@ -3222,18 +3106,12 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { 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 - }, + .base = PM_NODE_INIT_NODE(parser, PM_INDEX_TARGET_NODE, FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, target), .receiver = target->receiver, .opening_loc = target->opening_loc, .arguments = target->arguments, @@ -3257,14 +3135,7 @@ pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_local_v 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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_CAPTURE_PATTERN_NODE, 0, value, target), .value = value, .target = target, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -3281,14 +3152,7 @@ pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_CASE_NODE, 0, case_keyword, end_keyword), .predicate = predicate, .else_clause = NULL, .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword), @@ -3336,14 +3200,7 @@ pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, p 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_CASE_MATCH_NODE, 0, case_keyword, end_keyword), .predicate = predicate, .else_clause = NULL, .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword), @@ -3391,11 +3248,7 @@ pm_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p 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 }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_CLASS_NODE, 0, class_keyword, end_keyword), .locals = *locals, .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword), .constant_path = constant_path, @@ -3418,14 +3271,7 @@ pm_class_variable_and_write_node_create(pm_parser_t *parser, pm_class_variable_r 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_AND_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3443,14 +3289,7 @@ pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_varia 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3470,14 +3309,7 @@ pm_class_variable_or_write_node_create(pm_parser_t *parser, pm_class_variable_re 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_OR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3496,11 +3328,7 @@ pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_CLASS_VARIABLE_READ_NODE, 0, token), .name = pm_parser_constant_id_token(parser, token) }; @@ -3527,19 +3355,12 @@ pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) { 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); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_WRITE_NODE, flags, read_node, value), .name = read_node->name, - .name_loc = PM_LOCATION_NODE_VALUE((pm_node_t *) read_node), + .name_loc = PM_LOCATION_NODE_VALUE(UP(read_node)), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value }; @@ -3556,14 +3377,7 @@ pm_constant_path_and_write_node_create(pm_parser_t *parser, pm_constant_path_nod 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_AND_WRITE_NODE, 0, target, value), .target = target, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value @@ -3580,14 +3394,7 @@ pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_pat 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, 0, target, value), .target = target, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -3606,14 +3413,7 @@ pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_node 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_OR_WRITE_NODE, 0, target, value), .target = target, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value @@ -3635,20 +3435,23 @@ pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_to 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) - }; + if (parent == NULL) { + *node = (pm_constant_path_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_CONSTANT_PATH_NODE, 0, delimiter, name_token), + .parent = parent, + .name = name, + .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), + .name_loc = PM_LOCATION_TOKEN_VALUE(name_token) + }; + } else { + *node = (pm_constant_path_node_t) { + .base = PM_NODE_INIT_NODE_TOKEN(parser, PM_CONSTANT_PATH_NODE, 0, parent, name_token), + .parent = parent, + .name = name, + .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), + .name_loc = PM_LOCATION_TOKEN_VALUE(name_token) + }; + } return node; } @@ -3659,17 +3462,10 @@ pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_to 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); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_WRITE_NODE, flags, target, value), .target = target, .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), .value = value @@ -3687,14 +3483,7 @@ pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t * 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_AND_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3712,14 +3501,7 @@ pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_nod 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_OPERATOR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3739,14 +3521,7 @@ pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *t 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_OR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3765,11 +3540,7 @@ pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) { 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_CONSTANT_READ_NODE, 0, name), .name = pm_parser_constant_id_token(parser, name) }; @@ -3782,17 +3553,10 @@ pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) { 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); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_WRITE_NODE, flags, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), @@ -3810,7 +3574,7 @@ 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); + if (cast->statements != NULL) pm_def_node_receiver_check(parser, UP(cast->statements)); break; } case PM_PARENTHESES_NODE: { @@ -3866,24 +3630,17 @@ pm_def_node_create( 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)) { + if (receiver != NULL) { 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 }, - }, + .base = ( + (end_keyword->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN_NODE(parser, PM_DEF_NODE, 0, def_keyword, body) + : PM_NODE_INIT_TOKENS(parser, PM_DEF_NODE, 0, def_keyword, end_keyword) + ), .name = name, .name_loc = PM_LOCATION_TOKEN_VALUE(name_loc), .receiver = receiver, @@ -3905,22 +3662,19 @@ pm_def_node_create( * 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_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t *value, const pm_token_t *rparen, const pm_token_t *keyword) { 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) - }, - }, + .base = ( + (rparen->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN_NODE(parser, PM_DEFINED_NODE, 0, keyword, value) + : PM_NODE_INIT_TOKENS(parser, PM_DEFINED_NODE, 0, keyword, rparen) + ), .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen), .value = value, .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen), - .keyword_loc = *keyword_loc + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) }; return node; @@ -3932,22 +3686,13 @@ pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t 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, - }, - }, + .base = ( + ((end_keyword->type == PM_TOKEN_NOT_PROVIDED) && (statements != NULL)) + ? PM_NODE_INIT_TOKEN_NODE(parser, PM_ELSE_NODE, 0, else_keyword, statements) + : PM_NODE_INIT_TOKENS(parser, PM_ELSE_NODE, 0, else_keyword, end_keyword) + ), .else_keyword_loc = PM_LOCATION_TOKEN_VALUE(else_keyword), .statements = statements, .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) @@ -3964,14 +3709,7 @@ pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *openin 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 - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_EMBEDDED_STATEMENTS_NODE, 0, opening, closing), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .statements = statements, .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) @@ -3988,14 +3726,7 @@ pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator 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 - } - }, + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_EMBEDDED_VARIABLE_NODE, 0, operator, variable), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .variable = variable }; @@ -4011,14 +3742,7 @@ pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_ 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_ENSURE_NODE, 0, ensure_keyword, end_keyword), .ensure_keyword_loc = PM_LOCATION_TOKEN_VALUE(ensure_keyword), .statements = statements, .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword) @@ -4035,12 +3759,9 @@ 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) - }}; + *node = (pm_false_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_FALSE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) + }; return node; } @@ -4060,7 +3781,7 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { pm_node_t *right; if (nodes->size == 1) { - right = (pm_node_t *) pm_missing_node_create(parser, left->location.end, left->location.end); + right = UP(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)); @@ -4074,20 +3795,13 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { 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, - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_FIND_PATTERN_NODE, 0, left, right), .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 + .opening_loc = { 0 }, + .closing_loc = { 0 } }; // For now we're going to just copy over each pointer manually. This could be @@ -4182,12 +3896,7 @@ pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) { 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_FLOAT_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), .value = pm_double_parse(parser, token) }; @@ -4203,17 +3912,12 @@ pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) { 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) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), + .numeric = UP(pm_float_node_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT, .start = token->start, .end = token->end - 1 - })) + }))) }; return node; @@ -4228,12 +3932,7 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_RATIONAL_NODE, PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, token), .numerator = { 0 }, .denominator = { 0 } }; @@ -4253,7 +3952,7 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { const uint8_t *point = memchr(start, '.', length); assert(point && "should have a decimal point"); - uint8_t *digits = malloc(length); + uint8_t *digits = xmalloc(length); if (digits == NULL) { fputs("[pm_float_node_rational_create] Failed to allocate memory", stderr); abort(); @@ -4263,10 +3962,14 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1)); pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1); + size_t fract_length = 0; + for (const uint8_t *fract = point; fract < end; ++fract) { + if (*fract != '_') ++fract_length; + } 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); + if (fract_length > 1) memset(digits + 1, '0', fract_length - 1); + pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + fract_length); + xfree(digits); pm_integers_reduce(&node->numerator, &node->denominator); return node; @@ -4282,17 +3985,12 @@ pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *t 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) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), + .numeric = UP(pm_float_node_rational_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT_RATIONAL, .start = token->start, .end = token->end - 1 - })) + }))) }; return node; @@ -4315,14 +4013,7 @@ pm_for_node_create( 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_FOR_NODE, 0, for_keyword, end_keyword), .index = index, .collection = collection, .statements = statements, @@ -4343,11 +4034,9 @@ 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) - }}; + *node = (pm_forwarding_arguments_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_ARGUMENTS_NODE, 0, token) + }; return node; } @@ -4360,11 +4049,9 @@ 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) - }}; + *node = (pm_forwarding_parameter_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_PARAMETER_NODE, 0, token) + }; return node; } @@ -4384,14 +4071,11 @@ pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm } *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 - }, - }, + .base = ( + (block == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_SUPER_NODE, 0, token) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_FORWARDING_SUPER_NODE, 0, token, block) + ), .block = block }; @@ -4407,14 +4091,7 @@ pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_HASH_PATTERN_NODE, 0, opening, closing), .constant = NULL, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), @@ -4437,8 +4114,8 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme if (elements->size > 0) { if (rest) { - start = elements->nodes[0]->location.start; - end = rest->location.end; + start = MIN(rest->location.start, elements->nodes[0]->location.start); + end = MAX(rest->location.end, elements->nodes[elements->size - 1]->location.end); } else { start = elements->nodes[0]->location.start; end = elements->nodes[elements->size - 1]->location.end; @@ -4450,26 +4127,15 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme } *node = (pm_hash_pattern_node_t) { - { - .type = PM_HASH_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_HASH_PATTERN_NODE, 0, start, end), .constant = NULL, .elements = { 0 }, .rest = rest, - .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .opening_loc = { 0 }, + .closing_loc = { 0 } }; - pm_node_t *element; - PM_NODE_LIST_FOREACH(elements, index, element) { - pm_node_list_append(&node->elements, element); - } - + pm_node_list_concat(&node->elements, elements); return node; } @@ -4502,14 +4168,7 @@ pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_AND_WRITE_NODE, 0, target, value), .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4527,14 +4186,7 @@ pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *ta 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4554,14 +4206,7 @@ pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_OR_WRITE_NODE, 0, target, value), .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4579,11 +4224,7 @@ 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), - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, name), .name = pm_parser_constant_id_token(parser, name) }; @@ -4598,11 +4239,7 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant 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) - }, + .base = PM_NODE_INIT_BASE(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0), .name = name }; @@ -4615,17 +4252,10 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant 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); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, flags, target, value), .name = pm_global_variable_write_name(parser, target), .name_loc = PM_LOCATION_NODE_VALUE(target), .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), @@ -4643,11 +4273,7 @@ pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constan 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) - }, + .base = PM_NODE_INIT_BASE(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, 0), .name = name, .name_loc = PM_LOCATION_NULL_VALUE(parser), .operator_loc = PM_LOCATION_NULL_VALUE(parser), @@ -4666,12 +4292,7 @@ pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) { 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_HASH_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_NULL_VALUE(parser), .elements = { 0 } @@ -4696,7 +4317,7 @@ pm_hash_node_elements_append(pm_hash_node_t *hash, pm_node_t *element) { } if (!static_literal) { - pm_node_flag_unset((pm_node_t *)hash, PM_NODE_FLAG_STATIC_LITERAL); + pm_node_flag_unset(UP(hash), PM_NODE_FLAG_STATIC_LITERAL); } } @@ -4733,15 +4354,7 @@ pm_if_node_create(pm_parser_t *parser, } *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 - }, - }, + .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, if_keyword->start, end), .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), .predicate = predicate, .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword), @@ -4765,21 +4378,13 @@ pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_t 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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate), .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), .predicate = predicate, - .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .then_keyword_loc = { 0 }, .statements = statements, .subsequent = NULL, - .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .end_keyword_loc = { 0 } }; return node; @@ -4805,21 +4410,13 @@ pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_to 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, + .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, predicate, false_expression), + .if_keyword_loc = { 0 }, .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 + .subsequent = UP(else_node), + .end_keyword_loc = { 0 } }; return node; @@ -4846,11 +4443,7 @@ 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 - }, + .base = PM_NODE_INIT_NODE(parser, PM_IMPLICIT_NODE, 0, value), .value = value }; @@ -4867,11 +4460,7 @@ pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) { 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) - } + .base = PM_NODE_INIT_TOKEN(parser, PM_IMPLICIT_REST_NODE, 0, token) }; return node; @@ -4886,12 +4475,7 @@ pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_INTEGER_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token), .value = { 0 } }; @@ -4918,17 +4502,12 @@ pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, cons 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) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), + .numeric = UP(pm_integer_node_create(parser, base, &((pm_token_t) { .type = PM_TOKEN_INTEGER, .start = token->start, .end = token->end - 1 - })) + }))) }; return node; @@ -4944,12 +4523,7 @@ pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_RATIONAL_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token), .numerator = { 0 }, .denominator = { .value = 1, 0 } }; @@ -4978,17 +4552,12 @@ pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t b 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) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), + .numeric = UP(pm_integer_node_rational_create(parser, base, &((pm_token_t) { .type = PM_TOKEN_INTEGER_RATIONAL, .start = token->start, .end = token->end - 1 - })) + }))) }; return node; @@ -5011,14 +4580,7 @@ pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t } *node = (pm_in_node_t) { - { - .type = PM_IN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = in_keyword->start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_IN_NODE, 0, in_keyword->start, end), .pattern = pattern, .statements = statements, .in_loc = PM_LOCATION_TOKEN_VALUE(in_keyword), @@ -5037,14 +4599,7 @@ pm_instance_variable_and_write_node_create(pm_parser_t *parser, pm_instance_vari 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_AND_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -5062,14 +4617,7 @@ pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -5089,14 +4637,7 @@ pm_instance_variable_or_write_node_create(pm_parser_t *parser, pm_instance_varia 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_OR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -5115,11 +4656,7 @@ pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *tok 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_INSTANCE_VARIABLE_READ_NODE, 0, token), .name = pm_parser_constant_id_token(parser, token) }; @@ -5133,18 +4670,12 @@ pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *tok 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); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); + *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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_WRITE_NODE, flags, read_node, value), .name = read_node->name, - .name_loc = PM_LOCATION_NODE_BASE_VALUE(read_node), + .name_loc = PM_LOCATION_NODE_VALUE(read_node), .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), .value = value }; @@ -5186,7 +4717,7 @@ pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *p break; } case PM_EMBEDDED_VARIABLE_NODE: - pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL); + pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL); break; default: assert(false && "unexpected node type"); @@ -5204,15 +4735,7 @@ pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_tok 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, - }, - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_TOKEN_VALUE(opening), .parts = { 0 } @@ -5230,14 +4753,14 @@ pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expressio node->base.location.end = part->location.end; } - pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); + pm_interpolated_node_append(UP(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)); + pm_node_flag_set(UP(node), pm_regular_expression_flags_create(parser, closing)); } /** @@ -5266,10 +4789,10 @@ pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_inte 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)) + node->base.flags = (pm_node_flags_t) (FL(node) & ~(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); + node->base.flags = (pm_node_flags_t) ((FL(node) | 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; @@ -5279,6 +4802,12 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_ switch (PM_NODE_TYPE(part)) { case PM_STRING_NODE: + // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags, + // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for + // as long as this interpolation only consists of other string literals. + if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) { + pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL); + } 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: @@ -5330,8 +4859,10 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_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. + case PM_SYMBOL_NODE: + case PM_INTERPOLATED_SYMBOL_NODE: + // These will only happen in error cases. But we want to handle it + // here so that we don't fail the assertion. CLEAR_FLAGS(node); break; default: @@ -5363,15 +4894,7 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin } *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, - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_STRING_NODE, flags, opening, closing), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), .parts = { 0 } @@ -5402,7 +4925,7 @@ pm_interpolated_symbol_node_append(pm_interpolated_symbol_node_t *node, pm_node_ node->base.location.start = part->location.start; } - pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); + pm_interpolated_node_append(UP(node), &node->parts, part); node->base.location.end = MAX(node->base.location.end, part->location.end); } @@ -5420,15 +4943,7 @@ pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *openin 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, - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening, closing), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), .parts = { 0 } @@ -5452,14 +4967,7 @@ pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *openi 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_X_STRING_NODE, 0, opening, closing), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), .parts = { 0 } @@ -5470,7 +4978,7 @@ pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *openi 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); + pm_interpolated_node_append(UP(node), &node->parts, part); node->base.location.end = part->location.end; } @@ -5488,11 +4996,7 @@ pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *nam 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) - } + .base = PM_NODE_INIT_TOKEN(parser, PM_IT_LOCAL_VARIABLE_READ_NODE, 0, name), }; return node; @@ -5506,14 +5010,7 @@ pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, con 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 - } - } + .base = PM_NODE_INIT_TOKENS(parser, PM_IT_PARAMETERS_NODE, 0, opening, closing), }; return node; @@ -5527,12 +5024,7 @@ 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 - }, + .base = PM_NODE_INIT_UNSET(parser, PM_KEYWORD_HASH_NODE, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS), .elements = { 0 } }; @@ -5547,7 +5039,7 @@ pm_keyword_hash_node_elements_append(pm_keyword_hash_node_t *hash, pm_node_t *el // 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_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS); } pm_node_list_append(&hash->elements, element); @@ -5565,14 +5057,7 @@ pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t 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 - }, - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_REQUIRED_KEYWORD_PARAMETER_NODE, 0, name), .name = pm_parser_constant_id_location(parser, name->start, name->end - 1), .name_loc = PM_LOCATION_TOKEN_VALUE(name), }; @@ -5588,14 +5073,7 @@ pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t 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 - }, - }, + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_OPTIONAL_KEYWORD_PARAMETER_NODE, 0, name, value), .name = pm_parser_constant_id_location(parser, name->start, name->end - 1), .name_loc = PM_LOCATION_TOKEN_VALUE(name), .value = value @@ -5612,14 +5090,11 @@ pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *ope 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) - }, - }, + .base = ( + (name->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator) + : PM_NODE_INIT_TOKENS(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator, name) + ), .name = pm_parser_optional_constant_id_token(parser, name), .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5644,14 +5119,7 @@ pm_lambda_node_create( 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_LAMBDA_NODE, 0, operator, closing), .locals = *locals, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -5673,14 +5141,7 @@ pm_local_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_AND_WRITE_NODE, 0, target, value), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -5699,14 +5160,7 @@ pm_local_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *tar 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), .name_loc = target->location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -5728,14 +5182,7 @@ pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, c 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_OR_WRITE_NODE, 0, target, value), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -5756,11 +5203,7 @@ pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_tok 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_LOCAL_VARIABLE_READ_NODE, 0, name), .name = name_id, .depth = depth }; @@ -5793,17 +5236,10 @@ pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t 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); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *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 - } - }, + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_LOCAL_VARIABLE_WRITE_NODE, flags, name_loc, value), .name = name, .depth = depth, .value = value, @@ -5852,11 +5288,7 @@ pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *l 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 - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_LOCAL_VARIABLE_TARGET_NODE, 0, location), .name = name, .depth = depth }; @@ -5874,14 +5306,7 @@ pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_MATCH_PREDICATE_NODE, 0, value, pattern), .value = value, .pattern = pattern, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5900,14 +5325,7 @@ pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t * 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_MATCH_REQUIRED_NODE, 0, value, pattern), .value = value, .pattern = pattern, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5924,11 +5342,7 @@ 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 - }, + .base = PM_NODE_INIT_NODE(parser, PM_MATCH_WRITE_NODE, 0, call), .call = call, .targets = { 0 } }; @@ -5944,14 +5358,7 @@ pm_module_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const 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 - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_MODULE_NODE, 0, module_keyword, end_keyword), .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, @@ -5971,16 +5378,12 @@ 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 } - }, + .base = PM_NODE_INIT_UNSET(parser, PM_MULTI_TARGET_NODE, 0), .lefts = { 0 }, .rest = NULL, .rights = { 0 }, - .lparen_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .rparen_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .lparen_loc = { 0 }, + .rparen_loc = { 0 } }; return node; @@ -6044,17 +5447,10 @@ pm_multi_target_node_closing_set(pm_multi_target_node_t *node, const pm_token_t 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); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_MULTI_WRITE_NODE, flags, target, value), .lefts = target->lefts, .rest = target->rest, .rights = target->rights, @@ -6080,14 +5476,11 @@ pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments 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) - } - }, + .base = ( + (arguments == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_NEXT_NODE, 0, keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_NEXT_NODE, 0, keyword, arguments) + ), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .arguments = arguments }; @@ -6103,12 +5496,9 @@ 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) - }}; + *node = (pm_nil_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_NIL_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) + }; return node; } @@ -6123,14 +5513,7 @@ pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *oper 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 - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_NO_KEYWORDS_PARAMETER_NODE, 0, operator, keyword), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) }; @@ -6146,11 +5529,7 @@ pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_location_t *loc 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 - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_NUMBERED_PARAMETERS_NODE, 0, location), .maximum = maximum }; @@ -6215,11 +5594,7 @@ pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *na 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), - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_NUMBERED_REFERENCE_READ_NODE, 0, name), .number = pm_numbered_reference_read_node_number(parser, name) }; @@ -6234,14 +5609,7 @@ pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, c 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 - } - }, + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_OPTIONAL_PARAMETER_NODE, 0, name, value), .name = pm_parser_constant_id_token(parser, name), .name_loc = PM_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -6261,14 +5629,7 @@ pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operat 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_OR_NODE, 0, left, right), .left = left, .right = right, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -6285,11 +5646,7 @@ 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) - }, + .base = PM_NODE_INIT_UNSET(parser, PM_PARAMETERS_NODE, 0), .rest = NULL, .keyword_rest = NULL, .block = NULL, @@ -6334,8 +5691,8 @@ pm_parameters_node_requireds_append(pm_parameters_node_t *params, pm_node_t *par */ 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); + pm_parameters_node_location_set(params, UP(param)); + pm_node_list_append(¶ms->optionals, UP(param)); } /** @@ -6381,7 +5738,7 @@ pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *par 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); + pm_parameters_node_location_set(params, UP(param)); params->block = param; } @@ -6393,14 +5750,7 @@ pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_st 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 - } - }, + .base = PM_NODE_INIT_NODE(parser, PM_PROGRAM_NODE, 0, statements), .locals = *locals, .statements = statements }; @@ -6416,15 +5766,7 @@ pm_parentheses_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_no 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 - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_PARENTHESES_NODE, flags, opening, closing), .body = body, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) @@ -6441,14 +5783,7 @@ pm_pinned_expression_node_create(pm_parser_t *parser, pm_node_t *expression, con 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 - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_PINNED_EXPRESSION_NODE, 0, operator, rparen), .expression = expression, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .lparen_loc = PM_LOCATION_TOKEN_VALUE(lparen), @@ -6466,14 +5801,7 @@ pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, 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 - } - }, + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_PINNED_VARIABLE_NODE, 0, operator, variable), .variable = variable, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) }; @@ -6489,14 +5817,7 @@ pm_post_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, co 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 - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_POST_EXECUTION_NODE, 0, keyword, closing), .statements = statements, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -6514,14 +5835,7 @@ pm_pre_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, con 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 - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_PRE_EXECUTION_NODE, 0, keyword, closing), .statements = statements, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -6558,15 +5872,7 @@ pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *ope } *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) - } - }, + .base = PM_NODE_INIT(parser, PM_RANGE_NODE, flags, (left == NULL ? operator->start : left->location.start), (right == NULL ? operator->end : right->location.end)), .left = left, .right = right, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -6583,11 +5889,9 @@ 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) - }}; + *node = (pm_redo_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_REDO_NODE, 0, token) + }; return node; } @@ -6599,17 +5903,10 @@ pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) { 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); + pm_node_flags_t flags = pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL; *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) - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_REGULAR_EXPRESSION_NODE, flags, opening, closing), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .content_loc = PM_LOCATION_TOKEN_VALUE(content), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), @@ -6635,11 +5932,7 @@ 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_REQUIRED_PARAMETER_NODE, 0, token), .name = pm_parser_constant_id_token(parser, token) }; @@ -6654,14 +5947,7 @@ pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const 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 - } - }, + .base = PM_NODE_INIT_NODES(parser, PM_RESCUE_MODIFIER_NODE, 0, expression, rescue_expression), .expression = expression, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .rescue_expression = rescue_expression @@ -6678,14 +5964,10 @@ 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) - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_RESCUE_NODE, 0, 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, + .operator_loc = { 0 }, + .then_keyword_loc = { 0 }, .reference = NULL, .statements = NULL, .subsequent = NULL, @@ -6746,14 +6028,11 @@ pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, c 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) - } - }, + .base = ( + (name->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN(parser, PM_REST_PARAMETER_NODE, 0, operator) + : PM_NODE_INIT_TOKENS(parser, PM_REST_PARAMETER_NODE, 0, operator, name) + ), .name = pm_parser_optional_constant_id_token(parser, name), .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -6770,11 +6049,9 @@ 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) - }}; + *node = (pm_retry_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_RETRY_NODE, 0, token) + }; return node; } @@ -6787,14 +6064,11 @@ pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argumen 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) - } - }, + .base = ( + (arguments == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_RETURN_NODE, 0, keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_RETURN_NODE, 0, keyword, arguments) + ), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .arguments = arguments }; @@ -6810,11 +6084,9 @@ 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) - }}; + *node = (pm_self_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_SELF_NODE, 0, token) + }; return node; } @@ -6827,12 +6099,7 @@ pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shar 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) - }, + .base = PM_NODE_INIT_NODE(parser, PM_SHAREABLE_CONSTANT_NODE, (pm_node_flags_t) value, write), .write = write }; @@ -6847,14 +6114,7 @@ pm_singleton_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *local 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 - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_SINGLETON_CLASS_NODE, 0, class_keyword, end_keyword), .locals = *locals, .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -6874,12 +6134,9 @@ 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) - }}; + *node = (pm_source_encoding_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_ENCODING_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) + }; return node; } @@ -6904,12 +6161,7 @@ pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) } *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), - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_FILE_NODE, flags, file_keyword), .filepath = parser->filepath }; @@ -6924,12 +6176,9 @@ 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) - }}; + *node = (pm_source_line_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_LINE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) + }; return node; } @@ -6942,14 +6191,11 @@ pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t 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) - } - }, + .base = ( + (expression == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_SPLAT_NODE, 0, operator) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_SPLAT_NODE, 0, operator, expression) + ), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .expression = expression }; @@ -6965,11 +6211,7 @@ 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) - }, + .base = PM_NODE_INIT_BASE(parser, PM_STATEMENTS_NODE, 0), .body = { 0 } }; @@ -7061,16 +6303,11 @@ pm_string_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, break; } + const uint8_t *start = (opening->type == PM_TOKEN_NOT_PROVIDED ? content->start : opening->start); + const uint8_t *end = (closing->type == PM_TOKEN_NOT_PROVIDED ? content->end : closing->end); + *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) - } - }, + .base = PM_NODE_INIT(parser, PM_STRING_NODE, flags, start, end), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .content_loc = PM_LOCATION_TOKEN_VALUE(content), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -7113,14 +6350,7 @@ pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument } *node = (pm_super_node_t) { - { - .type = PM_SUPER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = end, - } - }, + .base = PM_NODE_INIT(parser, PM_SUPER_NODE, 0, keyword->start, end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .lparen_loc = arguments->opening_loc, .arguments = arguments->arguments, @@ -7349,16 +6579,11 @@ 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); + const uint8_t *start = (opening->type == PM_TOKEN_NOT_PROVIDED ? value->start : opening->start); + const uint8_t *end = (closing->type == PM_TOKEN_NOT_PROVIDED ? value->end : closing->end); + *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) - } - }, + .base = PM_NODE_INIT(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | flags, start, end), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .value_loc = PM_LOCATION_TOKEN_VALUE(value), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -7403,7 +6628,7 @@ pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) { 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)); + pm_node_flag_set(UP(node), parse_symbol_encoding(parser, &label, &node->unescaped, false)); break; } @@ -7432,12 +6657,7 @@ 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) - }, + .base = PM_NODE_INIT_BASE(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING), .value_loc = PM_LOCATION_NULL_VALUE(parser), .unescaped = { 0 } }; @@ -7475,15 +6695,7 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const 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 - } - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening, closing), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .value_loc = node->content_loc, .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -7491,7 +6703,7 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const }; 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)); + pm_node_flag_set(UP(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 @@ -7519,12 +6731,7 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { } *new_node = (pm_string_node_t) { - { - .type = PM_STRING_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = node->base.location - }, + .base = PM_NODE_INIT_NODE(parser, PM_STRING_NODE, flags, node), .opening_loc = node->opening_loc, .content_loc = node->value_loc, .closing_loc = node->closing_loc, @@ -7547,12 +6754,9 @@ 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) - }}; + *node = (pm_true_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) + }; return node; } @@ -7564,12 +6768,9 @@ 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 } - }}; + *node = (pm_true_node_t) { + .base = PM_NODE_INIT_BASE(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL) + }; return node; } @@ -7583,11 +6784,7 @@ pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) { 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), - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_UNDEF_NODE, 0, token), .keyword_loc = PM_LOCATION_TOKEN_VALUE(token), .names = { 0 } }; @@ -7610,31 +6807,18 @@ pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) { 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; - } + pm_unless_node_t *node = PM_NODE_ALLOC(parser, pm_unless_node_t); + pm_node_t *end = statements == NULL ? predicate : UP(statements); *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 - }, - }, + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, keyword, 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 + .end_keyword_loc = { 0 } }; return node; @@ -7652,21 +6836,13 @@ pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const 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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate), .keyword_loc = PM_LOCATION_TOKEN_VALUE(unless_keyword), .predicate = predicate, - .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .then_keyword_loc = { 0 }, .statements = statements, .else_clause = NULL, - .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .end_keyword_loc = { 0 } }; return node; @@ -7710,15 +6886,7 @@ pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to 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, - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_UNTIL_NODE, flags, keyword, closing), .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), @@ -7739,18 +6907,10 @@ pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm 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, - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_UNTIL_NODE, flags, statements, predicate), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), - .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .do_keyword_loc = { 0 }, + .closing_loc = { 0 }, .predicate = predicate, .statements = statements }; @@ -7766,17 +6926,10 @@ 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 - } - }, + .base = PM_NODE_INIT_TOKEN(parser, PM_WHEN_NODE, 0, keyword), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .statements = NULL, - .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .then_keyword_loc = { 0 }, .conditions = { 0 } }; @@ -7822,15 +6975,7 @@ pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_WHILE_NODE, flags, keyword, closing), .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), @@ -7851,18 +6996,10 @@ pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm 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 - }, - }, + .base = PM_NODE_INIT_NODES(parser, PM_WHILE_NODE, flags, statements, predicate), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), - .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .do_keyword_loc = { 0 }, + .closing_loc = { 0 }, .predicate = predicate, .statements = statements }; @@ -7878,11 +7015,7 @@ pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_s 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) - }, + .base = PM_NODE_INIT_BASE(parser, PM_WHILE_NODE, 0), .keyword_loc = PM_LOCATION_NULL_VALUE(parser), .do_keyword_loc = PM_LOCATION_NULL_VALUE(parser), .closing_loc = PM_LOCATION_NULL_VALUE(parser), @@ -7902,15 +7035,7 @@ pm_xstring_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, 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 - }, - }, + .base = PM_NODE_INIT_TOKENS(parser, PM_X_STRING_NODE, PM_STRING_FLAGS_FROZEN, opening, closing), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .content_loc = PM_LOCATION_TOKEN_VALUE(content), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), @@ -7947,14 +7072,7 @@ pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_lo } *node = (pm_yield_node_t) { - { - .type = PM_YIELD_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_YIELD_NODE, 0, keyword->start, end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .lparen_loc = *lparen_loc, .arguments = arguments, @@ -7964,9 +7082,6 @@ pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_lo 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. @@ -8429,7 +7544,7 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) { if (*cursor == '\\' && (cursor + 1 < end)) cursor++; } value_end = cursor; - if (*cursor == '"') cursor++; + if (cursor < end && *cursor == '"') cursor++; } else { value_start = cursor; while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++; @@ -8570,97 +7685,79 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) { 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; + magic_comment->value_length = value_length; + pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment); + } } - return false; + return result; +} + +/******************************************************************************/ +/* Context manipulations */ +/******************************************************************************/ + +static const uint32_t context_terminators[] = { + [PM_CONTEXT_NONE] = 0, + [PM_CONTEXT_BEGIN] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BEGIN_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BEGIN_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BEGIN_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BLOCK_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT), + [PM_CONTEXT_BLOCK_KEYWORDS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_BLOCK_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BLOCK_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BLOCK_PARAMETERS] = (1U << PM_TOKEN_PIPE), + [PM_CONTEXT_BLOCK_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_CASE_WHEN] = (1U << PM_TOKEN_KEYWORD_WHEN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE), + [PM_CONTEXT_CASE_IN] = (1U << PM_TOKEN_KEYWORD_IN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE), + [PM_CONTEXT_CLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_CLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_CLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_CLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_DEF] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_DEF_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_DEF_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_DEF_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_DEF_PARAMS] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_DEFINED] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_DEFAULT_PARAMS] = (1U << PM_TOKEN_COMMA) | (1U << PM_TOKEN_PARENTHESIS_RIGHT), + [PM_CONTEXT_ELSE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_ELSIF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_EMBEXPR] = (1U << PM_TOKEN_EMBEXPR_END), + [PM_CONTEXT_FOR] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_FOR_INDEX] = (1U << PM_TOKEN_KEYWORD_IN), + [PM_CONTEXT_IF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_LAMBDA_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT), + [PM_CONTEXT_LAMBDA_DO_END] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_LAMBDA_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_LAMBDA_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_LAMBDA_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_LOOP_PREDICATE] = (1U << PM_TOKEN_KEYWORD_DO) | (1U << PM_TOKEN_KEYWORD_THEN), + [PM_CONTEXT_MAIN] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_MODULE] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_MODULE_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_MODULE_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_MODULE_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_MULTI_TARGET] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_PARENS] = (1U << PM_TOKEN_PARENTHESIS_RIGHT), + [PM_CONTEXT_POSTEXE] = (1U << PM_TOKEN_BRACE_RIGHT), + [PM_CONTEXT_PREDICATE] = (1U << PM_TOKEN_KEYWORD_THEN) | (1U << PM_TOKEN_NEWLINE) | (1U << PM_TOKEN_SEMICOLON), + [PM_CONTEXT_PREEXE] = (1U << PM_TOKEN_BRACE_RIGHT), + [PM_CONTEXT_RESCUE_MODIFIER] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_SCLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_SCLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_SCLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_SCLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_TERNARY] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_UNLESS] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_UNTIL] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_WHILE] = (1U << PM_TOKEN_KEYWORD_END), +}; + +static inline bool +context_terminator(pm_context_t context, pm_token_t *token) { + return token->type < 32 && (context_terminators[context] & (1U << token->type)); } /** @@ -8761,6 +7858,7 @@ context_human(pm_context_t context) { 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_BLOCK_PARAMETERS: return "'|'..'|' block parameter"; case PM_CONTEXT_CASE_WHEN: return "'when' clause"; case PM_CONTEXT_CASE_IN: return "'in' clause"; case PM_CONTEXT_CLASS: return "class definition"; @@ -9109,7 +8207,7 @@ lex_global_variable(pm_parser_t *parser) { } 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_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); } @@ -9146,7 +8244,7 @@ lex_global_variable(pm_parser_t *parser) { } 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; + 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); } @@ -9519,7 +8617,7 @@ escape_hexadecimal_digit(const uint8_t value) { * validated. */ static inline uint32_t -escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length) { +escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location) { uint32_t value = 0; for (size_t index = 0; index < length; index++) { if (index != 0) value <<= 4; @@ -9529,7 +8627,11 @@ escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length) { // 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); + if (error_location != NULL) { + pm_parser_err(parser, error_location->start, error_location->end, PM_ERR_ESCAPE_INVALID_UNICODE); + } else { + pm_parser_err(parser, string, string + length, PM_ERR_ESCAPE_INVALID_UNICODE); + } return 0xFFFD; } @@ -9829,7 +8931,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre extra_codepoints_start = unicode_start; } - uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length); + uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL); 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); @@ -9870,7 +8972,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre 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); + uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL); if (flags & PM_ESCAPE_FLAG_REGEXP) { pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start)); @@ -10173,7 +9275,7 @@ lex_at_variable(pm_parser_t *parser) { } } 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) { + 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; } @@ -10849,14 +9951,37 @@ parser_lex(pm_parser_t *parser) { 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 the lex state was ignored, we will lex the + // ignored newline. + if (lex_state_ignored_p(parser)) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lexed_comment = false; + goto lex_next_token; + } + + // If we hit a '.' or a '&.' we will lex the ignored + // newline. + if (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 are parsing as CRuby 4.0 or later and we + // hit a '&&' or a '||' then 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) == '.') - )) + (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) && + following && ( + (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') || + (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') || + (peek_at(parser, following) == 'a' && peek_at(parser, following + 1) == 'n' && peek_at(parser, following + 2) == 'd' && !char_is_identifier(parser, following + 3, parser->end - (following + 3))) || + (peek_at(parser, following) == 'o' && peek_at(parser, following + 1) == 'r' && !char_is_identifier(parser, following + 2, parser->end - (following + 2))) + ) ) { if (!lexed_comment) parser_lex_ignored_newline(parser); lexed_comment = false; @@ -10896,6 +10021,63 @@ parser_lex(pm_parser_t *parser) { parser->next_start = NULL; LEX(PM_TOKEN_AMPERSAND_DOT); } + + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { + // If we hit an && then we are in a logical chain + // and we need to return the logical 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_BEG); + parser->current.start = next_content; + parser->current.end = next_content + 2; + parser->next_start = NULL; + LEX(PM_TOKEN_AMPERSAND_AMPERSAND); + } + + // If we hit a || then we are in a logical chain and + // we need to return the logical 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_BEG); + parser->current.start = next_content; + parser->current.end = next_content + 2; + parser->next_start = NULL; + LEX(PM_TOKEN_PIPE_PIPE); + } + + // If we hit an 'and' then we are in a logical chain + // and we need to return the logical operator. + if ( + peek_at(parser, next_content) == 'a' && + peek_at(parser, next_content + 1) == 'n' && + peek_at(parser, next_content + 2) == 'd' && + !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3)) + ) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->current.start = next_content; + parser->current.end = next_content + 3; + parser->next_start = NULL; + parser->command_start = true; + LEX(PM_TOKEN_KEYWORD_AND); + } + + // If we hit a 'or' then we are in a logical chain + // and we need to return the logical operator. + if ( + peek_at(parser, next_content) == 'o' && + peek_at(parser, next_content + 1) == 'r' && + !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2)) + ) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->current.start = next_content; + parser->current.end = next_content + 2; + parser->next_start = NULL; + parser->command_start = true; + LEX(PM_TOKEN_KEYWORD_OR); + } + } } // At this point we know this is a regular newline, and we can set the @@ -12170,7 +11352,13 @@ parser_lex(pm_parser_t *parser) { 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); + + // Track the newline if we're not in a heredoc that + // would have already have added the newline to the + // list. + if (parser->heredoc_end == NULL) { + pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + } } else { parser->current.end = breakpoint + 1; } @@ -12424,7 +11612,13 @@ parser_lex(pm_parser_t *parser) { 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); + + // Track the newline if we're not in a heredoc that + // would have already have added the newline to the + // list. + if (parser->heredoc_end == NULL) { + pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + } } else { parser->current.end = breakpoint + 1; } @@ -12436,6 +11630,13 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_LABEL_END); } + // When the delimiter itself is a newline, we won't + // get a chance to flush heredocs in the usual places since + // the newline is already consumed. + if (term == '\n' && parser->heredoc_end) { + parser_flush_heredoc_end(parser); + } + lex_state_set(parser, PM_LEX_STATE_END); lex_mode_pop(parser); LEX(PM_TOKEN_STRING_END); @@ -12841,7 +12042,10 @@ parser_lex(pm_parser_t *parser) { // 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); + + if (parser->heredoc_end == NULL) { + pm_newline_list_append(&parser->newline_list, end); + } // Here we want the buffer to only // include up to the backslash. @@ -13142,14 +12346,6 @@ match8(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, 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: @@ -13230,6 +12426,22 @@ expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ide } } +/** + * A special expect1 that attaches the error to the opening token location + * rather than the current position. This is useful for errors about missing + * closing tokens, where we want to point to the line with the opening token + * (e.g., `def`, `class`, `if`, `{`) rather than the end of the file. + */ +static void +expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id, const pm_token_t *opening) { + if (accept1(parser, type)) return; + + pm_parser_err(parser, opening->start, opening->end, diag_id); + + 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); @@ -13327,12 +12539,87 @@ parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, 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 UP(pm_splat_node_create(parser, &operator, expression)); } return parse_value_expression(parser, binding_power, accepts_command_call, false, diag_id, depth); } +static bool +pm_node_unreference_each(const pm_node_t *node, void *data) { + switch (PM_NODE_TYPE(node)) { + /* When we are about to destroy a set of nodes that could potentially + * contain block exits for the current scope, we need to check if they + * are contained in the list of block exits and remove them if they are. + */ + case PM_BREAK_NODE: + case PM_NEXT_NODE: + case PM_REDO_NODE: { + pm_parser_t *parser = (pm_parser_t *) data; + size_t index = 0; + + while (index < parser->current_block_exits->size) { + pm_node_t *block_exit = parser->current_block_exits->nodes[index]; + + if (block_exit == node) { + if (index + 1 < parser->current_block_exits->size) { + memmove( + &parser->current_block_exits->nodes[index], + &parser->current_block_exits->nodes[index + 1], + (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *) + ); + } + parser->current_block_exits->size--; + + /* Note returning true here because these nodes could have + * arguments that are themselves block exits. */ + return true; + } + + index++; + } + + return true; + } + /* When an implicit local variable is written to or targeted, it becomes + * a regular, named local variable. This branch removes it from the list + * of implicit parameters when that happens. */ + case PM_LOCAL_VARIABLE_READ_NODE: + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_parser_t *parser = (pm_parser_t *) data; + 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) { + memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *)); + } + + implicit_parameters->size--; + break; + } + } + + return false; + } + default: + return true; + } +} + +/** + * When we are about to destroy a set of nodes that could potentially be + * referenced by one or more lists on the parser, then remove them from those + * lists so we don't get a use-after-free. + */ +static void +pm_node_unreference(pm_parser_t *parser, const pm_node_t *node) { + pm_visit_node(node, pm_node_unreference_each, parser); +} + /** * Convert the name of a method into the corresponding write method name. For * example, foo would be turned into foo=. @@ -13380,31 +12667,7 @@ parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) { 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; - } - } + return UP(result); } /** @@ -13464,7 +12727,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p 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); + pm_node_unreference(parser, target); } const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target; @@ -13479,9 +12742,9 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p } 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); + pm_node_t *node = UP(pm_local_variable_target_node_create(parser, &target->location, name, 0)); - parse_target_implicit_parameter(parser, target); + pm_node_unreference(parser, target); pm_node_destroy(parser, target); return node; @@ -13505,7 +12768,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p splat->expression = parse_target(parser, splat->expression, multiple, true); } - return (pm_node_t *) splat; + return UP(splat); } case PM_CALL_NODE: { pm_call_node_t *call = (pm_call_node_t *) target; @@ -13536,16 +12799,16 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p 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); + return UP(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 (peek_at(parser, 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); + return UP(pm_call_target_node_create(parser, call)); } } @@ -13553,7 +12816,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p // 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); + return UP(pm_index_target_node_create(parser, call)); } } PRISM_FALLTHROUGH @@ -13596,7 +12859,7 @@ 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 UP(pm_shareable_constant_node_create(parser, write, shareable_constant)); } return write; @@ -13614,10 +12877,10 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod 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; + return UP(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); + pm_node_t *node = UP(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); @@ -13626,7 +12889,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod 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); + pm_node_t *node = UP(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); @@ -13642,7 +12905,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod 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; + return UP(node); } case PM_LOCAL_VARIABLE_READ_NODE: { pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target; @@ -13656,30 +12919,30 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod 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_node_unreference(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); + return UP(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); + pm_node_t *node = UP(pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator)); - parse_target_implicit_parameter(parser, target); + pm_node_unreference(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_t *write_node = UP(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); + return UP(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; @@ -13688,9 +12951,9 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod } 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); + pm_multi_target_node_targets_append(parser, multi_target, UP(splat)); - return (pm_node_t *) pm_multi_write_node_create(parser, multi_target, operator, value); + return UP(pm_multi_write_node_create(parser, multi_target, operator, value)); } case PM_CALL_NODE: { pm_call_node_t *call = (pm_call_node_t *) target; @@ -13722,7 +12985,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod 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); + target = UP(pm_local_variable_write_node_create(parser, constant_id, 0, value, &message, operator)); pm_refute_numbered_parameter(parser, message.start, message.end); return target; @@ -13744,11 +13007,12 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod pm_arguments_node_arguments_append(arguments, value); call->base.location.end = arguments->base.location.end; + call->equal_loc = PM_LOCATION_TOKEN_VALUE(operator); 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)); + pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)); - return (pm_node_t *) call; + return UP(call); } } @@ -13765,19 +13029,26 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod // Replace the name with "[]=". call->name = pm_parser_constant_id_constant(parser, "[]=", 3); + call->equal_loc = PM_LOCATION_TOKEN_VALUE(operator); // 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)); + pm_node_flag_set(UP(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 + // 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. + // + // Since it is possible for the value to contain an implicit + // parameter somewhere in its subtree, we need to walk it and remove + // any implicit parameters from the list of implicit parameters for + // the current scope. + pm_node_unreference(parser, value); pm_node_destroy(parser, value); } PRISM_FALLTHROUGH @@ -13813,7 +13084,7 @@ parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t 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; + return UP(result); } /** @@ -13850,7 +13121,7 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b name = parse_target(parser, name, true, true); } - pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name); + pm_node_t *splat = UP(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)) { @@ -13868,13 +13139,13 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b } 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_node_t *rest = UP(pm_implicit_rest_node_create(parser, &parser->previous)); pm_multi_target_node_targets_append(parser, result, rest); break; } } - return (pm_node_t *) result; + return UP(result); } /** @@ -14068,7 +13339,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod pm_parser_scope_forwarding_keywords_check(parser, &operator); } - element = (pm_node_t *) pm_assoc_splat_node_create(parser, value, &operator); + element = UP(pm_assoc_splat_node_create(parser, value, &operator)); contains_keyword_splat = true; break; } @@ -14076,7 +13347,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod pm_token_t label = parser->current; parser_lex(parser); - pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &label); + pm_node_t *key = UP(pm_symbol_node_label_create(parser, &label)); pm_hash_key_static_literals_add(parser, literals, key); pm_token_t operator = not_provided(parser); @@ -14087,7 +13358,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod } 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); + value = UP(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 }; @@ -14099,17 +13370,17 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod } if (depth == -1) { - value = (pm_node_t *) pm_call_node_variable_call_create(parser, &identifier); + value = UP(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 = UP(pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth)); } } value->location.end++; - value = (pm_node_t *) pm_implicit_node_create(parser, value); + value = UP(pm_implicit_node_create(parser, value)); } - element = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value); + element = UP(pm_assoc_node_create(parser, key, &operator, value)); break; } default: { @@ -14132,7 +13403,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod } 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); + element = UP(pm_assoc_node_create(parser, key, &operator, value)); break; } } @@ -14161,6 +13432,30 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod return contains_keyword_splat; } +static inline bool +argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) { + if (pm_symbol_node_label_p(argument)) { + return true; + } + + switch (PM_NODE_TYPE(argument)) { + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *) argument; + if (cast->opening_loc.start == NULL && cast->arguments != NULL) { + if (PM_NODE_FLAG_P(cast->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS | PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) { + return false; + } + if (cast->block != NULL) { + return false; + } + } + break; + } + default: break; + } + return accept1(parser, PM_TOKEN_EQUAL_GREATER); +} + /** * Append an argument to a list of arguments. */ @@ -14210,16 +13505,16 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for } pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser); - argument = (pm_node_t *) hash; + argument = UP(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)); + bool contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(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_node_flag_set(UP(arguments->arguments), flags); pm_static_literals_free(&hash_keys); parsed_bare_hash = true; @@ -14237,7 +13532,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for pm_parser_scope_forwarding_block_check(parser, &operator); } - argument = (pm_node_t *) pm_block_argument_node_create(parser, &operator, expression); + argument = UP(pm_block_argument_node_create(parser, &operator, expression)); if (parsed_block_argument) { parse_arguments_append(parser, arguments, argument); } else { @@ -14257,7 +13552,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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); + argument = UP(pm_splat_node_create(parser, &operator, NULL)); if (parsed_bare_hash) { pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT); } @@ -14268,7 +13563,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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); + argument = UP(pm_splat_node_create(parser, &operator, expression)); } parse_arguments_append(parser, arguments, argument); @@ -14293,16 +13588,16 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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); + argument = UP(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); + argument = UP(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); + pm_node_flag_set(UP(arguments->arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING); arguments->has_forwarding = true; parsed_forwarding_arguments = true; break; @@ -14318,7 +13613,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for bool contains_keywords = false; bool contains_keyword_splat = false; - if (pm_symbol_node_label_p(argument) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) { + if (argument_allowed_for_bare_hash(parser, argument)){ if (parsed_bare_hash) { pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH); } @@ -14339,17 +13634,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for // 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); + argument = UP(pm_assoc_node_create(parser, argument, &operator, value)); pm_keyword_hash_node_elements_append(bare_hash, argument); - argument = (pm_node_t *) bare_hash; + argument = UP(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)); + contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(bare_hash), (uint16_t) (depth + 1)); } pm_static_literals_free(&hash_keys); @@ -14361,7 +13656,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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); + pm_node_flag_set(UP(arguments->arguments), flags); break; } @@ -14390,6 +13685,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for if (accepted_newline) { pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); } + + // If this is a command call and an argument takes a block, + // there can be no further arguments. For example, + // `foo(bar 1 do end, 2)` should be rejected. + if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) { + pm_call_node_t *call = (pm_call_node_t *) argument; + if (call->opening_loc.start == NULL && call->arguments != NULL && call->block != NULL) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + break; + } + } } 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. @@ -14427,33 +13733,33 @@ parse_required_destructured_parameter(pm_parser_t *parser) { // 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); + param = UP(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); + param = UP(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); + value = UP(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); + param = UP(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); + param = UP(pm_required_parameter_node_create(parser, &name)); if (pm_parser_parameter_name_check(parser, &name)) { pm_node_flag_set_repeated_parameter(param); } @@ -14566,7 +13872,7 @@ parse_parameters( 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); + pm_node_t *param = UP(parse_required_destructured_parameter(parser)); if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { pm_parameters_node_requireds_append(params, param); @@ -14595,13 +13901,13 @@ parse_parameters( 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); + pm_node_flag_set_repeated_parameter(UP(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); + pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_BLOCK_MULTI); + pm_parameters_node_posts_append(params, UP(param)); } break; @@ -14626,7 +13932,7 @@ parse_parameters( params->keyword_rest = NULL; } - pm_parameters_node_keyword_rest_set(params, (pm_node_t *) param); + pm_parameters_node_keyword_rest_set(params, UP(param)); break; } case PM_TOKEN_CLASS_VARIABLE: @@ -14671,7 +13977,7 @@ parse_parameters( 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; + 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)); @@ -14680,14 +13986,14 @@ parse_parameters( 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_node_flag_set_repeated_parameter(UP(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)) { + 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); } @@ -14703,15 +14009,15 @@ parse_parameters( } 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_node_flag_set_repeated_parameter(UP(param)); } - pm_parameters_node_requireds_append(params, (pm_node_t *) param); + pm_parameters_node_requireds_append(params, UP(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_node_flag_set_repeated_parameter(UP(param)); } - pm_parameters_node_posts_append(params, (pm_node_t *) param); + pm_parameters_node_posts_append(params, UP(param)); } break; @@ -14742,7 +14048,7 @@ parse_parameters( case PM_TOKEN_PIPE: { context_pop(parser); - pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name)); if (repeated) { pm_node_flag_set_repeated_parameter(param); } @@ -14759,7 +14065,7 @@ parse_parameters( break; } - pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name)); if (repeated) { pm_node_flag_set_repeated_parameter(param); } @@ -14772,20 +14078,20 @@ parse_parameters( 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; + 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)) { + 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); + param = UP(pm_optional_keyword_parameter_node_create(parser, &name, value)); } else { - param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + param = UP(pm_required_keyword_parameter_node_create(parser, &name)); } if (repeated) { @@ -14826,7 +14132,7 @@ parse_parameters( parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS; } - pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name); + pm_node_t *param = UP(pm_rest_parameter_node_create(parser, &operator, &name)); if (repeated) { pm_node_flag_set_repeated_parameter(param); } @@ -14854,7 +14160,7 @@ parse_parameters( 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); + param = UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous)); } else { pm_token_t name; @@ -14868,7 +14174,7 @@ parse_parameters( parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS; } - param = (pm_node_t *) pm_keyword_rest_parameter_node_create(parser, &operator, &name); + param = UP(pm_keyword_rest_parameter_node_create(parser, &operator, &name)); if (repeated) { pm_node_flag_set_repeated_parameter(param); } @@ -14888,13 +14194,13 @@ parse_parameters( 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); + pm_node_t *param = UP(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); + pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI); + pm_parameters_node_posts_append(params, UP(param)); } } else { pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); @@ -14931,7 +14237,7 @@ parse_parameters( // 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); + pm_node_destroy(parser, UP(params)); return NULL; } @@ -15203,7 +14509,7 @@ parse_rescues(pm_parser_t *parser, size_t opening_newline_index, const pm_token_ // 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 (current == NULL) pm_parser_err_node(parser, UP(else_clause), PM_ERR_BEGIN_LONELY_ELSE); } if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) { @@ -15277,6 +14583,9 @@ parse_block_parameters( ) { pm_parameters_node_t *parameters = NULL; if (!match1(parser, PM_TOKEN_SEMICOLON)) { + if (!is_lambda_literal) { + context_push(parser, PM_CONTEXT_BLOCK_PARAMETERS); + } parameters = parse_parameters( parser, is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX, @@ -15287,6 +14596,9 @@ parse_block_parameters( true, (uint16_t) (depth + 1) ); + if (!is_lambda_literal) { + context_pop(parser); + } } pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening); @@ -15321,7 +14633,7 @@ parse_block_parameters( 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); + if (repeated) pm_node_flag_set_repeated_parameter(UP(local)); pm_block_parameters_node_append_local(block_parameters, local); } while (accept1(parser, PM_TOKEN_COMMA)); @@ -15425,11 +14737,11 @@ parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_ } const pm_location_t location = { .start = opening->start, .end = closing->end }; - return (pm_node_t *) pm_numbered_parameters_node_create(parser, &location, numbered_parameter); + return UP(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 UP(pm_it_parameters_node_create(parser, opening, closing)); } return NULL; @@ -15469,30 +14781,30 @@ parse_block(pm_parser_t *parser, uint16_t depth) { 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)); + statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1))); } - expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE); + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening); } 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)); + statements = UP(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)); + statements = UP(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); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening); } 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_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &opening, &parser->previous); pm_parser_scope_pop(parser); pm_accepts_block_stack_pop(parser); @@ -15564,9 +14876,9 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept if (block != NULL) { if (arguments->block == NULL && !arguments->has_forwarding) { - arguments->block = (pm_node_t *) block; + arguments->block = UP(block); } else { - pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI); + pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_BLOCK_MULTI); if (arguments->block != NULL) { if (arguments->arguments == NULL) { @@ -15574,7 +14886,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept } pm_arguments_node_arguments_append(arguments->arguments, arguments->block); } - arguments->block = (pm_node_t *) block; + arguments->block = UP(block); } } } @@ -15642,6 +14954,7 @@ parse_return(pm_parser_t *parser, pm_node_t *node) { case PM_CONTEXT_BLOCK_ENSURE: case PM_CONTEXT_BLOCK_KEYWORDS: case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_BLOCK_PARAMETERS: case PM_CONTEXT_DEF_ELSE: case PM_CONTEXT_DEF_ENSURE: case PM_CONTEXT_DEF_PARAMS: @@ -15661,7 +14974,7 @@ parse_return(pm_parser_t *parser, pm_node_t *node) { break; } } - if (in_sclass) { + if (in_sclass && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) { pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID); } } @@ -15678,6 +14991,7 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node) { case PM_CONTEXT_BLOCK_KEYWORDS: case PM_CONTEXT_BLOCK_ELSE: case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_BLOCK_PARAMETERS: case PM_CONTEXT_BLOCK_RESCUE: case PM_CONTEXT_DEFINED: case PM_CONTEXT_FOR: @@ -15860,10 +15174,10 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl switch (context) { case PM_CONTEXT_IF: - parent = (pm_node_t *) pm_if_node_create(parser, &keyword, predicate, &then_keyword, statements, NULL, &end_keyword); + parent = UP(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); + parent = UP(pm_unless_node_create(parser, &keyword, predicate, &then_keyword, statements)); break; default: assert(false && "unreachable"); @@ -15891,7 +15205,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl 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_node_t *elsif = UP(pm_if_node_create(parser, &elsif_keyword, predicate, &then_keyword, statements, NULL, &end_keyword)); ((pm_if_node_t *) current)->subsequent = elsif; current = elsif; } @@ -15910,13 +15224,13 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl 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); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE, &keyword); 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; + ((pm_if_node_t *) current)->subsequent = UP(else_node); break; case PM_CONTEXT_UNLESS: ((pm_unless_node_t *) parent)->else_clause = else_node; @@ -15927,7 +15241,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl } } else { parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false); - expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM, &keyword); } // Set the appropriate end location for all of the nodes in the subtree. @@ -16074,7 +15388,7 @@ parse_string_part(pm_parser_t *parser, uint16_t depth) { 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_t *node = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); pm_node_flag_set(node, parse_unescaped_encoding(parser)); parser_lex(parser); @@ -16120,7 +15434,7 @@ parse_string_part(pm_parser_t *parser, uint16_t depth) { 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); + return UP(pm_embedded_statements_node_create(parser, &opening, statements, &closing)); } // Here the lexer has returned the beginning of an embedded variable. @@ -16145,42 +15459,42 @@ parse_string_part(pm_parser_t *parser, uint16_t depth) { // 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); + variable = UP(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); + variable = UP(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); + variable = UP(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); + variable = UP(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); + variable = UP(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); + variable = UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); break; } - return (pm_node_t *) pm_embedded_variable_node_create(parser, &operator, variable); + return UP(pm_embedded_variable_node_create(parser, &operator, variable)); } default: parser_lex(parser); @@ -16217,9 +15531,9 @@ parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_sta 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); + pm_node_flag_set(UP(symbol), PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING); - return (pm_node_t *) symbol; + return UP(symbol); } /** @@ -16257,9 +15571,9 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s 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)); + pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); - return (pm_node_t *) symbol; + return UP(symbol); } if (lex_mode->as.string.interpolation) { @@ -16270,7 +15584,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s 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); + return UP(pm_symbol_node_create(parser, &opening, &content, &closing)); } // Now we can parse the first part of the symbol. @@ -16282,7 +15596,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s 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); + return UP(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); @@ -16302,7 +15616,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s } pm_interpolated_symbol_node_closing_loc_set(symbol, &parser->previous); - return (pm_node_t *) symbol; + return UP(symbol); } pm_token_t content; @@ -16326,10 +15640,10 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening); pm_token_t bounds = not_provided(parser); - pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &unescaped); + pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &unescaped)); pm_interpolated_symbol_node_append(symbol, part); - part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &parser->current, &bounds, &parser->current_string); + part = UP(pm_string_node_create_unescaped(parser, &bounds, &parser->current, &bounds, &parser->current_string)); pm_interpolated_symbol_node_append(symbol, part); if (next_state != PM_LEX_STATE_NONE) { @@ -16340,7 +15654,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s 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; + return UP(symbol); } } else { content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end }; @@ -16357,7 +15671,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s 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)); + return UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, false))); } /** @@ -16382,9 +15696,9 @@ parse_undef_argument(pm_parser_t *parser, uint16_t depth) { 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)); + pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); - return (pm_node_t *) symbol; + return UP(symbol); } case PM_TOKEN_SYMBOL_BEGIN: { pm_lex_mode_t lex_mode = *parser->lex_modes.current; @@ -16394,7 +15708,7 @@ parse_undef_argument(pm_parser_t *parser, uint16_t depth) { } 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); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); } } @@ -16423,9 +15737,9 @@ parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) { 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)); + pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); - return (pm_node_t *) symbol; + return UP(symbol); } case PM_TOKEN_SYMBOL_BEGIN: { pm_lex_mode_t lex_mode = *parser->lex_modes.current; @@ -16435,16 +15749,16 @@ parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) { } case PM_TOKEN_BACK_REFERENCE: parser_lex(parser); - return (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + return UP(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); + return UP(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); + return UP(pm_global_variable_read_node_create(parser, &parser->previous)); default: pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT); - return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); } } @@ -16459,7 +15773,7 @@ parse_variable(pm_parser_t *parser) { 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); + return UP(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; @@ -16478,12 +15792,12 @@ parse_variable(pm_parser_t *parser) { 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_t *node = UP(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); + } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) { + pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous)); pm_node_list_append(¤t_scope->implicit_parameters, node); return node; @@ -16507,9 +15821,9 @@ parse_variable_call(pm_parser_t *parser) { } pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous); - pm_node_flag_set((pm_node_t *)node, flags); + pm_node_flag_set(UP(node), flags); - return (pm_node_t *) node; + return UP(node); } /** @@ -16658,7 +15972,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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; + node = UP(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 @@ -16667,7 +15981,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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; + node = UP(symbol); if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); } else if (!lex_interpolation) { @@ -16700,32 +16014,32 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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_t *part = UP(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); + part = UP(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); + node = UP(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)); + node = UP(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); + node = UP(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); + node = UP(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); + node = UP(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 @@ -16737,7 +16051,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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); + node = UP(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 @@ -16753,7 +16067,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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)); + node = UP(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 @@ -16762,7 +16076,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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_t *part = UP(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); @@ -16773,14 +16087,14 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 } if (accept1(parser, PM_TOKEN_LABEL_END)) { - node = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); + node = UP(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); + node = UP(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); + node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous)); } pm_node_list_free(&parts); @@ -16799,14 +16113,14 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 } if (accept1(parser, PM_TOKEN_LABEL_END)) { - node = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); + node = UP(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); + node = UP(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); + node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous)); } pm_node_list_free(&parts); @@ -16843,7 +16157,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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; + current = UP(container); } pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, node); @@ -16868,7 +16182,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag 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 (peek_at(parser, location->start) == '_') return; if (pm_constant_id_list_includes(captures, capture)) { pm_parser_err(parser, location->start, location->end, PM_ERR_PATTERN_CAPTURE_DUPLICATE); @@ -16887,7 +16201,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures 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); + node = UP(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 @@ -16908,7 +16222,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures 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); + expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening); } closing = parser->previous; @@ -16920,7 +16234,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures 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); + expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening); } closing = parser->previous; @@ -16929,7 +16243,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures 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); + return UP(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, @@ -16948,7 +16262,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -16964,7 +16278,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -16980,7 +16294,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -16994,7 +16308,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures // 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; + return UP(pattern_node); } /** @@ -17019,12 +16333,12 @@ parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) { } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier)); - name = (pm_node_t *) pm_local_variable_target_node_create( + name = UP(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. @@ -17043,7 +16357,7 @@ parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) 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); + return UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous)); } if (accept1(parser, PM_TOKEN_IDENTIFIER)) { @@ -17055,15 +16369,15 @@ parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); - value = (pm_node_t *) pm_local_variable_target_node_create( + value = UP(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); + return UP(pm_assoc_splat_node_create(parser, value, &operator)); } /** @@ -17126,7 +16440,7 @@ parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *ca (uint32_t) (depth == -1 ? 0 : depth) ); - return (pm_node_t *) pm_implicit_node_create(parser, (pm_node_t *) target); + return UP(pm_implicit_node_create(parser, UP(target))); } /** @@ -17170,7 +16484,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node } 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_t *assoc = UP(pm_assoc_node_create(parser, first_node, &operator, value)); pm_node_list_append(&assocs, assoc); break; @@ -17185,8 +16499,8 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node 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_t *value = UP(pm_missing_node_create(parser, first_node->location.start, first_node->location.end)); + pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, &operator, value)); pm_node_list_append(&assocs, assoc); break; @@ -17227,20 +16541,24 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node } } else { expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA); - key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); + key = UP(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); + if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) { + value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key); + } else { + value = UP(pm_missing_node_create(parser, key->location.end, key->location.end)); + } } 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); + pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, &operator, value)); if (rest != NULL) { pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); @@ -17274,12 +16592,12 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); - return (pm_node_t *) pm_local_variable_target_node_create( + return UP(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; @@ -17288,7 +16606,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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); + return UP(pm_array_pattern_node_empty_create(parser, &opening, &parser->previous)); } // Otherwise, we'll parse the inner pattern, then deal with it depending @@ -17296,7 +16614,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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); + expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening); pm_token_t closing = parser->previous; switch (PM_NODE_TYPE(inner)) { @@ -17309,7 +16627,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -17323,7 +16641,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -17334,7 +16652,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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; + return UP(node); } case PM_TOKEN_BRACE_LEFT: { bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; @@ -17354,7 +16672,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm switch (parser->current.type) { case PM_TOKEN_LABEL: parser_lex(parser); - first_node = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); + first_node = UP(pm_symbol_node_label_create(parser, &parser->previous)); break; case PM_TOKEN_USTAR_STAR: first_node = parse_pattern_keyword_rest(parser, captures); @@ -17366,7 +16684,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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); + first_node = UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); break; } } @@ -17374,7 +16692,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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); + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE, &opening); pm_token_t closing = parser->previous; node->base.location.start = opening.start; @@ -17385,7 +16703,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm } parser->pattern_matching_newlines = previous_pattern_matching_newlines; - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_UDOT_DOT: case PM_TOKEN_UDOT_DOT_DOT: { @@ -17397,12 +16715,12 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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); + return UP(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); + pm_node_t *right = UP(pm_missing_node_create(parser, operator.start, operator.end)); + return UP(pm_range_node_create(parser, NULL, &operator, right)); } } } @@ -17412,6 +16730,16 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm // If we found a label, we need to immediately return to the caller. if (pm_symbol_node_label_p(node)) return node; + // Call nodes (arithmetic operations) are not allowed in patterns + if (PM_NODE_TYPE(node) == PM_CALL_NODE) { + pm_parser_err_node(parser, node, diag_id); + pm_missing_node_t *missing_node = pm_missing_node_create(parser, node->location.start, node->location.end); + + pm_node_unreference(parser, node); + pm_node_destroy(parser, node); + return UP(missing_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; @@ -17422,10 +16750,10 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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); + return UP(pm_range_node_create(parser, node, &operator, right)); } default: - return (pm_node_t *) pm_range_node_create(parser, node, &operator, NULL); + return UP(pm_range_node_create(parser, node, &operator, NULL)); } } @@ -17440,44 +16768,44 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm switch (parser->current.type) { case PM_TOKEN_IDENTIFIER: { parser_lex(parser); - pm_node_t *variable = (pm_node_t *) parse_variable(parser); + pm_node_t *variable = UP(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); + variable = UP(pm_local_variable_read_node_missing_create(parser, &parser->previous, 0)); } - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(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); + pm_node_t *variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(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); + pm_node_t *variable = UP(pm_class_variable_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(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); + pm_node_t *variable = UP(pm_global_variable_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(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); + pm_node_t *variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(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); + pm_node_t *variable = UP(pm_back_reference_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); } case PM_TOKEN_PARENTHESIS_LEFT: { bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; @@ -17490,15 +16818,15 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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); + expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &lparen); + return UP(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); + pm_node_t *variable = UP(pm_missing_node_create(parser, operator.start, operator.end)); + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); } } } @@ -17509,21 +16837,41 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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)); + return parse_pattern_constant_path(parser, captures, UP(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); + pm_node_t *node = UP(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); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); + } +} + +static bool +parse_pattern_alternation_error_each(const pm_node_t *node, void *data) { + switch (PM_NODE_TYPE(node)) { + case PM_LOCAL_VARIABLE_TARGET_NODE: + pm_parser_err((pm_parser_t *) data, node->location.start, node->location.end, PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE); + return false; + default: + return true; } } +/** + * When we get here, we know that we already have a syntax error, because we + * know we have captured a variable and that we are in an alternation. + */ +static void +parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) { + pm_visit_node(node, parse_pattern_alternation_error_each, parser); +} + /** * Parse any number of primitives joined by alternation and ended optionally by * assignment. @@ -17531,9 +16879,12 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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; + bool alternation = false; - while ((node == NULL) || accept1(parser, PM_TOKEN_PIPE)) { - pm_token_t operator = parser->previous; + while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) { + if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) { + parse_pattern_alternation_error(parser, node); + } switch (parser->current.type) { case PM_TOKEN_IDENTIFIER: @@ -17545,41 +16896,47 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p case PM_TOKEN_UDOT_DOT: case PM_TOKEN_UDOT_DOT_DOT: case PM_CASE_PRIMITIVE: { - if (node == NULL) { + if (!alternation) { node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1)); } else { + pm_token_t operator = parser->previous; 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); + + if (captures->size) parse_pattern_alternation_error(parser, right); + node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator)); } break; } case PM_TOKEN_PARENTHESIS_LEFT: case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { + pm_token_t operator = parser->previous; 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); + expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening); + pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0)); - if (node == NULL) { + if (!alternation) { node = right; } else { - node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); + if (captures->size) parse_pattern_alternation_error(parser, right); + node = UP(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); + pm_node_t *right = UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); - if (node == NULL) { + if (!alternation) { node = right; } else { - node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); + if (captures->size) parse_pattern_alternation_error(parser, right); + node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous)); } break; @@ -17608,7 +16965,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p (uint32_t) (depth == -1 ? 0 : depth) ); - node = (pm_node_t *) pm_capture_pattern_node_create(parser, node, target, &operator); + node = UP(pm_capture_pattern_node_create(parser, node, target, &operator)); } return node; @@ -17627,8 +16984,8 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag 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)); + pm_node_t *key = UP(pm_symbol_node_label_create(parser, &parser->previous)); + node = UP(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); @@ -17638,7 +16995,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } 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)); + node = UP(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); @@ -17652,7 +17009,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag 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)); + node = UP(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); @@ -17667,7 +17024,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag 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); + node = UP(parse_pattern_rest(parser, captures)); leading_rest = true; break; } @@ -17681,7 +17038,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag // 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)); + return UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1))); } if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) { @@ -17694,15 +17051,15 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag // 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); + if (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_AND, PM_TOKEN_KEYWORD_OR)) { + node = UP(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); + node = UP(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, @@ -17724,13 +17081,13 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag // 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); + node = UP(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); + node = UP(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); @@ -17741,7 +17098,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } 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); + node = UP(pm_array_pattern_node_rest_create(parser, node)); } return node; @@ -17887,6 +17244,7 @@ parse_retry(pm_parser_t *parser, const pm_node_t *node) { case PM_CONTEXT_BEGIN: case PM_CONTEXT_BLOCK_BRACES: case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_BLOCK_PARAMETERS: case PM_CONTEXT_CASE_IN: case PM_CONTEXT_CASE_WHEN: case PM_CONTEXT_DEFAULT_PARAMS: @@ -17967,6 +17325,7 @@ parse_yield(pm_parser_t *parser, const pm_node_t *node) { case PM_CONTEXT_BLOCK_KEYWORDS: case PM_CONTEXT_BLOCK_ELSE: case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_BLOCK_PARAMETERS: case PM_CONTEXT_BLOCK_RESCUE: case PM_CONTEXT_CASE_IN: case PM_CONTEXT_CASE_WHEN: @@ -18117,13 +17476,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + element = UP(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); + element = UP(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)) { @@ -18152,10 +17511,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } 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_node_t *assoc = UP(pm_assoc_node_create(parser, element, &operator, value)); pm_keyword_hash_node_elements_append(hash, assoc); - element = (pm_node_t *) hash; + element = UP(hash); if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) { parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1)); } @@ -18180,7 +17539,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_array_node_close_set(array, &parser->previous); pm_accepts_block_stack_pop(parser); - return (pm_node_t *) array; + return UP(array); } case PM_TOKEN_PARENTHESIS_LEFT: case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { @@ -18207,7 +17566,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, flags)); } // Otherwise, we're going to parse the first statement in the list @@ -18276,10 +17635,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); accept1(parser, PM_TOKEN_NEWLINE); } else { - result = (pm_node_t *) multi_target; + result = UP(multi_target); } if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) { @@ -18308,7 +17667,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, flags)); } // If we have more than one statement in the set of parentheses, @@ -18373,16 +17732,16 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; + statement = UP(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); + pm_node_t *value = UP(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); + statement = UP(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); @@ -18393,7 +17752,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, flags)); } case PM_TOKEN_BRACE_LEFT: { // If we were passed a current_hash_keys via the parser, then that @@ -18409,14 +17768,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_accepts_block_stack_push(parser, true); parser_lex(parser); - pm_hash_node_t *node = pm_hash_node_create(parser, &parser->previous); + pm_token_t opening = parser->previous; + pm_hash_node_t *node = pm_hash_node_create(parser, &opening); 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)); + parse_assocs(parser, current_hash_keys, UP(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)); + parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1)); pm_static_literals_free(&hash_keys); } @@ -18424,26 +17784,34 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_accepts_block_stack_pop(parser); - expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM); + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening); pm_hash_node_closing_loc_set(node, &parser->previous); - return (pm_node_t *) node; + return UP(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_t *node = UP(pm_string_node_create_current_string( + parser, + &(pm_token_t) { + .type = PM_TOKEN_STRING_BEGIN, + .start = parser->current.start, + .end = parser->current.start + 1 + }, + &(pm_token_t) { + .type = PM_TOKEN_STRING_CONTENT, + .start = parser->current.start + 1, + .end = parser->current.end + }, + &closing + )); + pm_node_flag_set(node, parse_unescaped_encoding(parser)); + // Skip past the character literal here, since now we have handled + // parser->explicit_encoding correctly. + parser_lex(parser); + // Characters can be followed by strings in which case they are // automatically concatenated. if (match1(parser, PM_TOKEN_STRING_BEGIN)) { @@ -18454,7 +17822,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } case PM_TOKEN_CLASS_VARIABLE: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(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)); @@ -18476,10 +17844,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b ) { 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); + return UP(pm_call_node_fcall_create(parser, &constant, &arguments)); } - pm_node_t *node = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(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 @@ -18494,7 +17862,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pm_node_t *node = UP(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)); @@ -18517,23 +17885,23 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR); } - return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + return UP(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); + return UP(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); + return UP(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); + return UP(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); + return UP(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); + pm_node_t *node = UP(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)); @@ -18543,7 +17911,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } case PM_TOKEN_GLOBAL_VARIABLE: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(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)); @@ -18553,7 +17921,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } case PM_TOKEN_BACK_REFERENCE: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(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)); @@ -18578,24 +17946,18 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pm_node_flag_unset(UP(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; + const uint8_t *end = pm_arguments_end(&arguments); + if (!end) { + end = call->message_loc.end; } + call->base.location.end = end; } } else { // Otherwise, we know the identifier is in the local table. This @@ -18614,7 +17976,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + pm_node_unreference(parser, node); } else { // Otherwise, we're about to convert a regular local // variable read into a method call, in which case we @@ -18623,7 +17985,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pm_node_unreference(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); @@ -18631,7 +17993,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_destroy(parser, node); - return (pm_node_t *) fcall; + return UP(fcall); } } @@ -18663,9 +18025,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + node = UP(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 = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY)); } node->location.end = opening.end; @@ -18676,7 +18038,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // // 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); + node = UP(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 @@ -18698,7 +18060,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parse_heredoc_dedent_string(&cast->unescaped, common_whitespace); } - node = (pm_node_t *) cast; + node = UP(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, @@ -18723,7 +18085,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_xstring_node_closing_set(cast, &parser->previous); cast->base.location = cast->opening_loc; - node = (pm_node_t *) cast; + node = UP(cast); } else { pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening); pm_node_list_free(&parts); @@ -18732,7 +18094,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_string_node_closing_set(cast, &parser->previous); cast->base.location = cast->opening_loc; - node = (pm_node_t *) cast; + node = UP(cast); } // If this is a heredoc that is indented with a ~, then we need @@ -18757,7 +18119,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } case PM_TOKEN_INSTANCE_VARIABLE: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(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)); @@ -18768,32 +18130,32 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(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); + return UP(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); + return UP(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); + return UP(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); + return UP(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); + return UP(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); + return UP(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); @@ -18817,7 +18179,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name)); } case PM_SYMBOL_NODE: case PM_INTERPOLATED_SYMBOL_NODE: { @@ -18827,7 +18189,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } PRISM_FALLTHROUGH default: - return (pm_node_t *) pm_alias_method_node_create(parser, &keyword, new_name, old_name); + return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name)); } } case PM_TOKEN_KEYWORD_CASE: { @@ -18860,7 +18222,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(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 @@ -18888,7 +18250,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pm_when_node_conditions_append(when_node, UP(splat_node)); if (PM_NODE_TYPE_P(expression, PM_MISSING_NODE)) break; } else { @@ -18927,7 +18289,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } - pm_case_node_condition_append(case_node, (pm_node_t *) when_node); + pm_case_node_condition_append(case_node, UP(when_node)); } // If we didn't parse any conditions (in or when) then we need @@ -18937,7 +18299,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_static_literals_free(&literals); - node = (pm_node_t *) case_node; + node = UP(case_node); } else { pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate, &end_keyword); @@ -18974,11 +18336,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pattern = UP(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); + pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate)); } // Now we need to check for the terminator of the in node's @@ -19007,7 +18369,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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_node_t *condition = UP(pm_in_node_create(parser, pattern, statements, &in_keyword, &then_keyword)); pm_case_match_node_condition_append(case_node, condition); } @@ -19017,7 +18379,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS); } - node = (pm_node_t *) case_node; + node = UP(case_node); } accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); @@ -19039,7 +18401,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false); - expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword); if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) { pm_case_node_end_keyword_loc_set((pm_case_node_t *) node, &parser->previous); @@ -19072,7 +18434,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword); begin_node->base.location.end = parser->previous.end; pm_begin_node_end_keyword_set(begin_node, &parser->previous); @@ -19080,7 +18442,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pop_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) begin_node; + return UP(begin_node); } case PM_TOKEN_KEYWORD_BEGIN_UPCASE: { pm_node_list_t current_block_exits = { 0 }; @@ -19097,7 +18459,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening); 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); @@ -19106,7 +18468,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous)); } case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_NEXT: @@ -19123,29 +18485,35 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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) { + pm_token_t next = parser->current; parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, (uint16_t) (depth + 1)); + + // Reject `foo && return bar`. + if (!accepts_command_call && arguments.arguments != NULL) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(next.type)); + } } } switch (keyword.type) { case PM_TOKEN_KEYWORD_BREAK: { - pm_node_t *node = (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments); + pm_node_t *node = UP(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); + pm_node_t *node = UP(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); + pm_node_t *node = UP(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); + return UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); } } case PM_TOKEN_KEYWORD_SUPER: { @@ -19160,10 +18528,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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 UP(pm_forwarding_super_node_create(parser, &keyword, &arguments)); } - return (pm_node_t *) pm_super_node_create(parser, &keyword, &arguments); + return UP(pm_super_node_create(parser, &keyword, &arguments)); } case PM_TOKEN_KEYWORD_YIELD: { parser_lex(parser); @@ -19178,11 +18546,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // yield node. if (arguments.block != NULL) { pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT); + pm_node_unreference(parser, arguments.block); 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); + pm_node_t *node = UP(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; @@ -19209,18 +18578,18 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + statements = UP(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)); + statements = UP(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); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword); pm_constant_id_list_t locals; pm_locals_order(parser, &parser->current_scope->locals, &locals, false); @@ -19231,7 +18600,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(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)); @@ -19267,18 +18636,18 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + statements = UP(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)); + statements = UP(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); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword); if (context_def_p(parser)) { pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD); @@ -19297,7 +18666,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(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 }; @@ -19374,37 +18743,37 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b switch (identifier.type) { case PM_TOKEN_CONSTANT: - receiver = (pm_node_t *) pm_constant_read_node_create(parser, &identifier); + receiver = UP(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); + receiver = UP(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); + receiver = UP(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); + receiver = UP(pm_global_variable_read_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD_NIL: - receiver = (pm_node_t *) pm_nil_node_create(parser, &identifier); + receiver = UP(pm_nil_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD_SELF: - receiver = (pm_node_t *) pm_self_node_create(parser, &identifier); + receiver = UP(pm_self_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD_TRUE: - receiver = (pm_node_t *) pm_true_node_create(parser, &identifier); + receiver = UP(pm_true_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD_FALSE: - receiver = (pm_node_t *) pm_false_node_create(parser, &identifier); + receiver = UP(pm_false_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD___FILE__: - receiver = (pm_node_t *) pm_source_file_node_create(parser, &identifier); + receiver = UP(pm_source_file_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD___LINE__: - receiver = (pm_node_t *) pm_source_line_node_create(parser, &identifier); + receiver = UP(pm_source_line_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD___ENCODING__: - receiver = (pm_node_t *) pm_source_encoding_node_create(parser, &identifier); + receiver = UP(pm_source_encoding_node_create(parser, &identifier)); break; default: break; @@ -19439,7 +18808,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + receiver = UP(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. @@ -19458,6 +18827,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t rparen; pm_parameters_node_t *params; + bool accept_endless_def = true; switch (parser->current.type) { case PM_TOKEN_PARENTHESIS_LEFT: { parser_lex(parser); @@ -19493,6 +18863,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b rparen = not_provided(parser); params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, false, false, true, true, false, (uint16_t) (depth + 1)); + // Reject `def * = 1` and similar. We have to specifically check + // for them because they create ambiguity with optional arguments. + accept_endless_def = false; + context_pop(parser); break; } @@ -19514,13 +18888,30 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (token_is_setter_name(&name)) { pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER); } + if (!accept_endless_def) { + pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS); + } + if ( + parser->current_context->context == PM_CONTEXT_DEFAULT_PARAMS && + parser->current_context->prev->context == PM_CONTEXT_BLOCK_PARAMETERS + ) { + PM_PARSER_ERR_FORMAT(parser, def_keyword.start, parser->previous.end, PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE, "endless method definition"); + } 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); + statements = UP(pm_statements_node_create(parser)); + + bool allow_command_call; + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { + allow_command_call = accepts_command_call; + } else { + // Allow `def foo = puts "Hello"` but not `private def foo = puts "Hello"` + allow_command_call = binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION; + } - 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)); + pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_command_call, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1)); if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); @@ -19529,7 +18920,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value)); } pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false); @@ -19552,13 +18943,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + statements = UP(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)); + statements = UP(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); } @@ -19566,7 +18957,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_accepts_block_stack_pop(parser); pm_do_loop_stack_pop(parser); - expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword); end_keyword = parser->previous; } @@ -19575,7 +18966,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_scope_pop(parser); /** - * If the final character is @. As is the case when defining + * 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. */ @@ -19584,7 +18975,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b flush_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_def_node_create( + return UP(pm_def_node_create( parser, name_id, &name, @@ -19598,7 +18989,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b &rparen, &equal, &end_keyword - ); + )); } case PM_TOKEN_KEYWORD_DEFINED: { parser_lex(parser); @@ -19607,18 +18998,27 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t lparen; pm_token_t rparen; pm_node_t *expression; + context_push(parser, PM_CONTEXT_DEFINED); + bool newline = accept1(parser, PM_TOKEN_NEWLINE); 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) { + if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0)); + lparen = not_provided(parser); rparen = not_provided(parser); } else { - accept1(parser, PM_TOKEN_NEWLINE); - expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); - rparen = 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); @@ -19627,13 +19027,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } context_pop(parser); - return (pm_node_t *) pm_defined_node_create( + return UP(pm_defined_node_create( parser, &lparen, expression, &rparen, - &PM_LOCATION_TOKEN_VALUE(&keyword) - ); + &keyword + )); } case PM_TOKEN_KEYWORD_END_UPCASE: { if (binding_power != PM_BINDING_POWER_STATEMENT) { @@ -19651,12 +19051,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening); + return UP(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); + return UP(pm_false_node_create(parser, &parser->previous)); case PM_TOKEN_KEYWORD_FOR: { size_t opening_newline_index = token_newline_index(parser); parser_lex(parser); @@ -19675,12 +19075,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + index = UP(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); + index = UP(pm_missing_node_create(parser, for_keyword.start, for_keyword.end)); } // Now, if there are multiple index expressions, parse them out. @@ -19715,9 +19115,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false); - expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword); - return (pm_node_t *) pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, &do_keyword, &parser->previous); + return UP(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)) { @@ -19757,7 +19157,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } - return (pm_node_t *) undef; + return UP(undef); } case PM_TOKEN_KEYWORD_NOT: { parser_lex(parser); @@ -19766,13 +19166,27 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_arguments_t arguments = { 0 }; pm_node_t *receiver = NULL; + // If we do not accept a command call, then we also do not accept a + // not without parentheses. In this case we need to reject this + // syntax. + if (!accepts_command_call && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) { + pm_parser_err(parser, parser->previous.end, parser->previous.end + 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN); + } else { + accept1(parser, PM_TOKEN_NEWLINE); + pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER); + } + + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); + } + 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); + receiver = UP(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)); @@ -19787,7 +19201,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(pm_call_node_not_create(parser, receiver, &message, &arguments)); } case PM_TOKEN_KEYWORD_UNLESS: { size_t opening_newline_index = token_newline_index(parser); @@ -19813,14 +19227,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(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); + constant_path = UP(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, @@ -19837,13 +19251,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + statements = UP(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)); + statements = UP(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); } @@ -19852,7 +19266,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword); if (context_def_p(parser)) { pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD); @@ -19861,15 +19275,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + return UP(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); + return UP(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); + pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous)); if (!parser->partial_script) parse_block_exit(parser, node); return node; @@ -19877,17 +19291,17 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b case PM_TOKEN_KEYWORD_RETRY: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_retry_node_create(parser, &parser->previous); + pm_node_t *node = UP(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); + return UP(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); + return UP(pm_true_node_create(parser, &parser->previous)); case PM_TOKEN_KEYWORD_UNTIL: { size_t opening_newline_index = token_newline_index(parser); @@ -19918,9 +19332,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); - expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword); - return (pm_node_t *) pm_until_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0); + return UP(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); @@ -19952,26 +19366,60 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); - expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword); - return (pm_node_t *) pm_while_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0); + return UP(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); + pm_node_t *current = NULL; 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)) { + // Interpolation is not possible but nested heredocs can still lead to + // consecutive (disjoint) string tokens when the final newline is escaped. + while (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)); + + // Record the string node, moving to interpolation if needed. + if (current == NULL) { + current = UP(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)) { + pm_node_t *string = UP(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)) { + 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 = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped)); + pm_node_t *second_string = UP(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 = UP(interpolated); + } else { + assert(false && "unreachable"); + } } - expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT); + if (current) { + pm_array_node_elements_append(array, current); + current = NULL; + } else { + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT); + } } pm_token_t closing = parser->current; @@ -19983,7 +19431,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_array_node_close_set(array, &closing); - return (pm_node_t *) array; + return UP(array); } case PM_TOKEN_PERCENT_UPPER_I: { parser_lex(parser); @@ -20018,13 +19466,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + current = UP(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); + pm_node_t *string = UP(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); @@ -20036,8 +19484,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped)); + pm_node_t *second_string = UP(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); @@ -20045,7 +19493,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_append(interpolated, second_string); xfree(current); - current = (pm_node_t *) interpolated; + current = UP(interpolated); } else { assert(false && "unreachable"); } @@ -20060,7 +19508,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + current = UP(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 @@ -20069,11 +19517,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + current = UP(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; + current = UP(interpolated); } else { // If we hit an embedded variable and the current node is an // interpolated string, then we'll just add the embedded variable. @@ -20094,7 +19542,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + current = UP(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 @@ -20104,11 +19552,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + current = UP(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; + current = UP(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. @@ -20144,29 +19592,48 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_array_node_close_set(array, &closing); - return (pm_node_t *) array; + return UP(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); + pm_node_t *current = NULL; 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)) { + // Interpolation is not possible but nested heredocs can still lead to + // consecutive (disjoint) string tokens when the final newline is escaped. + while (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); + pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); + + // Record the string node, moving to interpolation if needed. + if (current == NULL) { + current = string; + } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, string); + } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { + 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 = UP(interpolated); + } else { + assert(false && "unreachable"); + } + parser_lex(parser); } - expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT); + if (current) { + pm_array_node_elements_append(array, current); + current = NULL; + } else { + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT); + } } pm_token_t closing = parser->current; @@ -20178,7 +19645,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_array_node_close_set(array, &closing); - return (pm_node_t *) array; + return UP(array); } case PM_TOKEN_PERCENT_UPPER_W: { parser_lex(parser); @@ -20214,7 +19681,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); pm_node_flag_set(string, parse_unescaped_encoding(parser)); parser_lex(parser); @@ -20237,7 +19704,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; + current = UP(interpolated); } else { assert(false && "unreachable"); } @@ -20252,7 +19719,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + current = UP(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 @@ -20262,7 +19729,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; + current = UP(interpolated); } else { // If we hit an embedded variable and the current // node is an interpolated string, then we'll just @@ -20281,7 +19748,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + current = UP(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 @@ -20291,7 +19758,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; + current = UP(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 @@ -20325,7 +19792,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_array_node_close_set(array, &closing); - return (pm_node_t *) array; + return UP(array); } case PM_TOKEN_REGEXP_BEGIN: { pm_token_t opening = parser->current; @@ -20343,7 +19810,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_regular_expression_node_create(parser, &opening, &content, &parser->previous); + pm_node_t *node = UP(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; @@ -20375,8 +19842,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; + pm_node_flag_set(UP(node), parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, FL(node))); + return UP(node); } // If we get here, then we have interpolation so we'll need to create @@ -20385,7 +19852,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_t *part = UP(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 @@ -20420,7 +19887,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing); - return (pm_node_t *) interpolated; + return UP(interpolated); } case PM_TOKEN_BACKTICK: case PM_TOKEN_PERCENT_LOWER_X: { @@ -20442,7 +19909,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b }; parser_lex(parser); - return (pm_node_t *) pm_xstring_node_create(parser, &opening, &content, &parser->previous); + return UP(pm_xstring_node_create(parser, &opening, &content, &parser->previous)); } pm_interpolated_x_string_node_t *node; @@ -20457,7 +19924,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_t *node = UP(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; @@ -20470,7 +19937,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_t *part = UP(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); @@ -20497,7 +19964,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_interpolated_xstring_node_closing_set(node, &closing); - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_USTAR: { parser_lex(parser); @@ -20507,7 +19974,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + return UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); } pm_token_t operator = parser->previous; @@ -20517,7 +19984,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pm_node_t *splat = UP(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)); @@ -20537,7 +20004,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; + return UP(node); } case PM_TOKEN_TILDE: { if (binding_power > PM_BINDING_POWER_UNARY) { @@ -20549,7 +20016,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; + return UP(node); } case PM_TOKEN_UMINUS: { if (binding_power > PM_BINDING_POWER_UNARY) { @@ -20561,7 +20028,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; + return UP(node); } case PM_TOKEN_UMINUS_NUM: { parser_lex(parser); @@ -20572,8 +20039,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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, "-@"); + node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0)); + node = UP(pm_call_node_unary_create(parser, &operator, node, "-@")); } else { switch (PM_NODE_TYPE(node)) { case PM_INTEGER_NODE: @@ -20583,7 +20050,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parse_negative_numeric(node); break; default: - node = (pm_node_t *) pm_call_node_unary_create(parser, &operator, node, "-@"); + node = UP(pm_call_node_unary_create(parser, &operator, node, "-@")); break; } } @@ -20641,39 +20108,39 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + body = UP(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); + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening); } 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)); + body = UP(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)); + body = UP(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); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator); } 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_node_t *parameters = parse_blocklike_parameters(parser, UP(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); + return UP(pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body)); } case PM_TOKEN_UPLUS: { if (binding_power > PM_BINDING_POWER_UNARY) { @@ -20685,7 +20152,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; + return UP(node); } case PM_TOKEN_STRING_BEGIN: return parse_strings(parser, NULL, accepts_label, (uint16_t) (depth + 1)); @@ -20721,7 +20188,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_prefix(parser, diag_id); } - return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + return UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); } } } @@ -20750,7 +20217,7 @@ parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_ 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 UP(pm_rescue_modifier_node_create(parser, value, &rescue, right)); } return value; @@ -20809,7 +20276,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding 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)); + 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_MODIFIER, 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); @@ -20822,7 +20289,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding pm_array_node_t *array = pm_array_node_create(parser, &opening); pm_array_node_elements_append(array, value); - value = (pm_node_t *) array; + value = UP(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)); @@ -20856,7 +20323,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding 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 UP(pm_rescue_modifier_node_create(parser, value, &rescue, right)); } return value; @@ -20873,13 +20340,15 @@ 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); + pm_node_unreference(parser, UP(call_node->arguments)); + pm_node_destroy(parser, UP(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); + pm_node_unreference(parser, UP(call_node->block)); + pm_node_destroy(parser, UP(call_node->block)); call_node->block = NULL; } } @@ -20950,7 +20419,7 @@ pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, con } 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) { +pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end, const pm_location_t *error_location) { const uint8_t *start = cursor - 1; cursor++; @@ -20961,7 +20430,7 @@ pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, con if (*cursor != '{') { size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4)); - uint32_t value = escape_unicode(parser, cursor, length); + uint32_t value = escape_unicode(parser, cursor, length, error_location); if (!pm_buffer_append_unicode_codepoint(unescaped, value)) { pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start)); @@ -20981,7 +20450,10 @@ pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, con } size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor); - uint32_t value = escape_unicode(parser, cursor, length); + if (length == 0) { + break; + } + uint32_t value = escape_unicode(parser, cursor, length, error_location); (void) pm_buffer_append_unicode_codepoint(unescaped, value); cursor += length; @@ -20991,7 +20463,7 @@ pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, con } 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) { +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 pm_location_t *error_location) { const uint8_t *end = source + length; pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source)); @@ -21009,7 +20481,7 @@ pm_named_capture_escape(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8 cursor = pm_named_capture_escape_octal(unescaped, cursor, end); break; case 'u': - cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end); + cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location); break; default: pm_buffer_append_byte(unescaped, '\\'); @@ -21052,7 +20524,7 @@ parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { // 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); + pm_named_capture_escape(parser, &unescaped, source, length, cursor, callback_data->shared ? NULL : &call->receiver->location); source = (const uint8_t *) pm_buffer_value(&unescaped); length = pm_buffer_length(&unescaped); } @@ -21111,7 +20583,7 @@ parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { // 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_t *target = UP(pm_local_variable_target_node_create(parser, &location, name, depth == -1 ? 0 : (uint32_t) depth)); pm_node_list_append(&callback_data->match->targets, target); } @@ -21142,9 +20614,9 @@ parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t * pm_constant_id_list_free(&callback_data.names); if (callback_data.match != NULL) { - return (pm_node_t *) callback_data.match; + return UP(callback_data.match); } else { - return (pm_node_t *) call; + return UP(call); } } @@ -21167,6 +20639,13 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } PRISM_FALLTHROUGH case PM_CASE_WRITABLE: { + // When we have `it = value`, we need to add `it` as a local + // variable before parsing the value, in case the value + // references the variable. + if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) { + pm_parser_local_add_location(parser, node->location.start, node->location.end, 0); + } + 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)); @@ -21182,7 +20661,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return parse_write(parser, UP(multi_target), &token, value); } case PM_SOURCE_ENCODING_NODE: case PM_FALSE_NODE: @@ -21216,7 +20695,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_global_variable_and_write_node_create(parser, node, &token, value)); pm_node_destroy(parser, node); return result; @@ -21225,7 +20704,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -21234,7 +20713,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + pm_node_t *write = UP(pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value)); return parse_shareable_constant_write(parser, write); } @@ -21242,7 +20721,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *write = UP(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); @@ -21251,7 +20730,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -21261,23 +20740,23 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0)); - parse_target_implicit_parameter(parser, node); + pm_node_unreference(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_node_unreference(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_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, cast->name, cast->depth)); pm_node_destroy(parser, node); return result; @@ -21296,9 +20775,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &token, value, constant_id, 0)); - pm_node_destroy(parser, (pm_node_t *) cast); + pm_node_destroy(parser, UP(cast)); return result; } @@ -21311,7 +20790,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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); + return UP(pm_index_and_write_node_create(parser, cast, &token, value)); } // If this node cannot be writable, then we have an error. @@ -21323,7 +20802,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(pm_call_and_write_node_create(parser, cast, &token, value)); } case PM_MULTI_WRITE_NODE: { parser_lex(parser); @@ -21350,7 +20829,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_global_variable_or_write_node_create(parser, node, &token, value)); pm_node_destroy(parser, node); return result; @@ -21359,7 +20838,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -21368,7 +20847,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + pm_node_t *write = UP(pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value)); return parse_shareable_constant_write(parser, write); } @@ -21376,7 +20855,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *write = UP(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); @@ -21385,7 +20864,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -21395,23 +20874,23 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0)); - parse_target_implicit_parameter(parser, node); + pm_node_unreference(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_node_unreference(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_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, cast->name, cast->depth)); pm_node_destroy(parser, node); return result; @@ -21430,9 +20909,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &token, value, constant_id, 0)); - pm_node_destroy(parser, (pm_node_t *) cast); + pm_node_destroy(parser, UP(cast)); return result; } @@ -21445,7 +20924,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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); + return UP(pm_index_or_write_node_create(parser, cast, &token, value)); } // If this node cannot be writable, then we have an error. @@ -21457,7 +20936,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(pm_call_or_write_node_create(parser, cast, &token, value)); } case PM_MULTI_WRITE_NODE: { parser_lex(parser); @@ -21494,7 +20973,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_global_variable_operator_write_node_create(parser, node, &token, value)); pm_node_destroy(parser, node); return result; @@ -21503,7 +20982,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -21512,7 +20991,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + pm_node_t *write = UP(pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value)); return parse_shareable_constant_write(parser, write); } @@ -21520,7 +20999,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *write = UP(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); @@ -21529,7 +21008,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -21539,23 +21018,23 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0)); - parse_target_implicit_parameter(parser, node); + pm_node_unreference(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_node_unreference(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_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, cast->name, cast->depth)); pm_node_destroy(parser, node); return result; @@ -21573,9 +21052,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &token, value, constant_id, 0)); - pm_node_destroy(parser, (pm_node_t *) cast); + pm_node_destroy(parser, UP(cast)); return result; } @@ -21584,7 +21063,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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); + return UP(pm_index_operator_write_node_create(parser, cast, &token, value)); } // If this node cannot be writable, then we have an error. @@ -21596,7 +21075,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(pm_call_operator_write_node_create(parser, cast, &token, value)); } case PM_MULTI_WRITE_NODE: { parser_lex(parser); @@ -21618,14 +21097,14 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(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); + return UP(pm_or_node_create(parser, node, &token, right)); } case PM_TOKEN_EQUAL_TILDE: { // Note that we _must_ parse the value before adding the local @@ -21640,7 +21119,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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; + pm_node_t *result = UP(call); // If the receiver of this =~ is a regular expression node, then we // need to introduce local variables for it based on its named @@ -21743,7 +21222,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } 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); + return UP(pm_call_node_binary_create(parser, node, &token, argument, 0)); } case PM_TOKEN_GREATER: case PM_TOKEN_GREATER_EQUAL: @@ -21755,7 +21234,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON)); } case PM_TOKEN_AMPERSAND_DOT: case PM_TOKEN_DOT: { @@ -21766,7 +21245,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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); + return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments)); } switch (PM_NODE_TYPE(node)) { @@ -21822,9 +21301,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); } else { - return (pm_node_t *) call; + return UP(call); } } case PM_TOKEN_DOT_DOT: @@ -21836,21 +21315,21 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(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); + return UP(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); + return UP(pm_unless_node_modifier_create(parser, node, &keyword, predicate)); } case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: { parser_lex(parser); @@ -21858,7 +21337,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(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); @@ -21866,7 +21345,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(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); @@ -21886,13 +21365,13 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_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); + pm_node_t *false_expression = UP(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); + return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression)); } accept1(parser, PM_TOKEN_NEWLINE); @@ -21905,7 +21384,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression)); } case PM_TOKEN_COLON_COLON: { parser_lex(parser); @@ -21930,10 +21409,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + path = UP(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); + path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous)); } // If this is followed by a comma then it is a multiple assignment. @@ -21958,10 +21437,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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 parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); } - return (pm_node_t *) call; + return UP(call); } case PM_TOKEN_PARENTHESIS_LEFT: { // If we have a parenthesis following a '::' operator, then it is the @@ -21969,11 +21448,11 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(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); + return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous)); } } } @@ -21985,7 +21464,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(pm_rescue_modifier_node_create(parser, node, &token, value)); } case PM_TOKEN_BRACKET_LEFT: { parser_lex(parser); @@ -22006,7 +21485,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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)); + return parse_targets_validate(parser, UP(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 @@ -22022,17 +21501,17 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t if (block != NULL) { if (arguments.block != NULL) { - pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_AFTER_BLOCK); + pm_parser_err_node(parser, UP(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; + arguments.block = UP(block); } - return (pm_node_t *) pm_call_node_aref_create(parser, node, &arguments); + return UP(pm_call_node_aref_create(parser, node, &arguments)); } case PM_TOKEN_KEYWORD_IN: { bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; @@ -22049,7 +21528,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(pm_match_predicate_node_create(parser, node, pattern, &operator)); } case PM_TOKEN_EQUAL_GREATER: { bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; @@ -22066,7 +21545,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + return UP(pm_match_required_node_create(parser, node, pattern, &operator)); } default: assert(false && "unreachable"); @@ -22103,7 +21582,7 @@ 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); + return UP(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); @@ -22160,6 +21639,12 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc ) { node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call, (uint16_t) (depth + 1)); + if (context_terminator(parser->current_context->context, &parser->current)) { + // If this token terminates the current context, then we need to + // stop parsing the expression, as it has become a statement. + return node; + } + switch (PM_NODE_TYPE(node)) { case PM_MULTI_WRITE_NODE: // Multi-write nodes are statements, and cannot be followed by @@ -22291,14 +21776,14 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { 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)) + UP(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( + pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create( parser, arguments, pm_parser_constant_id_constant(parser, "print", 5) - ), true); + )), true); } if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) { @@ -22310,46 +21795,46 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { 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)) + UP(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_call_node_t *call = pm_call_node_call_synthesized_create(parser, UP(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 + UP(call) ); - pm_statements_node_body_prepend(statements, (pm_node_t *) write); + pm_statements_node_body_prepend(statements, UP(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)) + UP(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( + pm_keyword_hash_node_elements_append(keywords, UP(pm_assoc_node_create( parser, - (pm_node_t *) pm_symbol_node_synthesized_create(parser, "chomp"), + UP(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) - )); + UP(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_arguments_node_arguments_append(arguments, UP(keywords)); + pm_node_flag_set(UP(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( + pm_statements_node_body_append(parser, wrapped_statements, UP(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)), + UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))), statements - ), true); + )), true); statements = wrapped_statements; } @@ -22393,9 +21878,10 @@ parse_program(pm_parser_t *parser) { statements = wrap_statements(parser, statements); } else { flush_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_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. @@ -22404,7 +21890,7 @@ parse_program(pm_parser_t *parser) { pm_statements_node_location_set(statements, parser->start, parser->start); } - return (pm_node_t *) pm_program_node_create(parser, &locals, statements); + return UP(pm_program_node_create(parser, &locals, statements)); } /******************************************************************************/ @@ -22424,7 +21910,7 @@ 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++) { + for (const char *max = big + big_length - little_length; big <= max; big++) { if (*big == *little && memcmp(big, little, little_length) == 0) return big; } @@ -22614,6 +22100,12 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm } } + // Now that we have established the user-provided options, check if + // a version was given and parse as the latest version otherwise. + if (parser->version == PM_OPTIONS_VERSION_UNSET) { + parser->version = PM_OPTIONS_VERSION_LATEST; + } + pm_accepts_block_stack_push(parser, true); // Skip past the UTF-8 BOM if it exists. @@ -22647,8 +22139,8 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm // 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); + const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end); + size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end); if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') { const char *engine; @@ -22667,7 +22159,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm } search_shebang = false; - } else if (options->main_script && !parser->parsing_eval) { + } else if (options != NULL && options->main_script && !parser->parsing_eval) { search_shebang = true; } } @@ -22807,7 +22299,7 @@ pm_parse(pm_parser_t *parser) { * otherwise return true. */ static bool -pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets) { +pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof) { #define LINE_SIZE 4096 char line[LINE_SIZE]; @@ -22843,6 +22335,12 @@ pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t if (strncmp(line, "__END__\r\n", 9) == 0) return false; break; } + + // All data should be read via gets. If the string returned by gets + // _doesn't_ end with a newline, then we assume we hit EOF condition. + if (stream_feof(stream)) { + break; + } } return true; @@ -22878,16 +22376,17 @@ pm_parse_stream_unterminated_heredoc_p(pm_parser_t *parser) { * 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_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const pm_options_t *options) { pm_buffer_init(buffer); - bool eof = pm_parse_stream_read(buffer, stream, stream_fgets); + bool eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof); + 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); + eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof); pm_parser_free(parser); pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options); @@ -22922,10 +22421,6 @@ pm_parse_success_p(const uint8_t *source, size_t size, const char *data) { #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 @@ -22979,13 +22474,13 @@ pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, cons * 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_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, 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_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, stream_fgets, stream_feof, &options); pm_serialize_header(buffer); pm_serialize_content(&parser, node, buffer); pm_buffer_append_byte(buffer, '\0'); diff --git a/vendor/mruby-compiler2/lib/prism/src/serialize.c b/vendor/mruby-compiler2/lib/prism/src/serialize.c index 8bfed8f..08ffb81 100644 --- a/vendor/mruby-compiler2/lib/prism/src/serialize.c +++ b/vendor/mruby-compiler2/lib/prism/src/serialize.c @@ -1,3 +1,5 @@ +/* :markup: markdown */ + /*----------------------------------------------------------------------------*/ /* This file is generated by the templates/template.rb script and should not */ /* be modified manually. See */ @@ -393,6 +395,12 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { pm_buffer_append_byte(buffer, 1); pm_serialize_location(parser, &((pm_call_node_t *)node)->closing_loc, buffer); } + if (((pm_call_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_call_node_t *)node)->equal_loc, buffer); + } if (((pm_call_node_t *)node)->block == NULL) { pm_buffer_append_byte(buffer, 0); } else { @@ -2175,7 +2183,7 @@ pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) // 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); + uint32_t owned_mask = 1U << 31; assert(content_offset < owned_mask); content_offset |= owned_mask; diff --git a/vendor/mruby-compiler2/lib/prism/src/token_type.c b/vendor/mruby-compiler2/lib/prism/src/token_type.c index ba2d751..32c20fe 100644 --- a/vendor/mruby-compiler2/lib/prism/src/token_type.c +++ b/vendor/mruby-compiler2/lib/prism/src/token_type.c @@ -1,3 +1,5 @@ +/* :markup: markdown */ + /*----------------------------------------------------------------------------*/ /* This file is generated by the templates/template.rb script and should not */ /* be modified manually. See */ @@ -18,10 +20,38 @@ 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_BRACE_RIGHT: + return "BRACE_RIGHT"; + case PM_TOKEN_COMMA: + return "COMMA"; + case PM_TOKEN_EMBEXPR_END: + return "EMBEXPR_END"; + case PM_TOKEN_KEYWORD_DO: + return "KEYWORD_DO"; + 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_ENSURE: + return "KEYWORD_ENSURE"; + case PM_TOKEN_KEYWORD_IN: + return "KEYWORD_IN"; + case PM_TOKEN_KEYWORD_RESCUE: + return "KEYWORD_RESCUE"; + case PM_TOKEN_KEYWORD_THEN: + return "KEYWORD_THEN"; + case PM_TOKEN_KEYWORD_WHEN: + return "KEYWORD_WHEN"; + case PM_TOKEN_NEWLINE: + return "NEWLINE"; + case PM_TOKEN_PARENTHESIS_RIGHT: + return "PARENTHESIS_RIGHT"; + case PM_TOKEN_PIPE: + return "PIPE"; + case PM_TOKEN_SEMICOLON: + return "SEMICOLON"; case PM_TOKEN_AMPERSAND: return "AMPERSAND"; case PM_TOKEN_AMPERSAND_AMPERSAND: @@ -44,8 +74,6 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -68,8 +96,6 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -88,8 +114,6 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -156,20 +180,10 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -178,8 +192,6 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -192,8 +204,6 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -204,8 +214,6 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -218,8 +226,6 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -256,16 +262,12 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -280,8 +282,6 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -298,8 +298,6 @@ pm_token_type_name(pm_token_type_t token_type) { 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: @@ -344,6 +342,10 @@ pm_token_type_name(pm_token_type_t token_type) { return "WORDS_SEP"; case PM_TOKEN___END__: return "__END__"; + case PM_TOKEN_MISSING: + return "MISSING"; + case PM_TOKEN_NOT_PROVIDED: + return "NOT_PROVIDED"; case PM_TOKEN_MAXIMUM: assert(false && "unreachable"); return ""; 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 index 38ea01a..922ce6a 100644 --- a/vendor/mruby-compiler2/lib/prism/src/util/pm_constant_pool.c +++ b/vendor/mruby-compiler2/lib/prism/src/util/pm_constant_pool.c @@ -264,7 +264,7 @@ pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t l // 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); + bucket->type = (unsigned int) (type & 0x3); } return bucket->id; diff --git a/vendor/mruby-compiler2/lib/prism/src/util/pm_string.c b/vendor/mruby-compiler2/lib/prism/src/util/pm_string.c index 75422fb..a7493c4 100644 --- a/vendor/mruby-compiler2/lib/prism/src/util/pm_string.c +++ b/vendor/mruby-compiler2/lib/prism/src/util/pm_string.c @@ -1,5 +1,7 @@ #include "prism/util/pm_string.h" +static const uint8_t empty_source[] = ""; + /** * Returns the size of the pm_string_t struct. This is necessary to allocate the * correct amount of memory in the FFI backend. @@ -133,8 +135,7 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) { // 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 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } @@ -182,8 +183,7 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) { if (size == 0) { close(fd); - const uint8_t source[] = ""; - *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } @@ -225,8 +225,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { // 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 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } @@ -278,8 +277,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { 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 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } diff --git a/vendor/mruby-compiler2/mrbgem.rake b/vendor/mruby-compiler2/mrbgem.rake index 0919b96..1056d59 100644 --- a/vendor/mruby-compiler2/mrbgem.rake +++ b/vendor/mruby-compiler2/mrbgem.rake @@ -1,66 +1,170 @@ -MRuby::Gem::Specification.new('mruby-compiler2') do |spec| +MRuby::Gem::Specification.new('mruby-compiler') do |spec| spec.license = 'MIT' - spec.author = 'HASUMI Hitoshi' - spec.summary = 'mruby compiler using a universal parser' + spec.author = 'mruby and PicoRuby developers' + spec.summary = 'mruby compiler using Prism' lib_dir = "#{dir}/lib" - cc.include_paths << "#{dir}/include" - prism_dir = "#{lib_dir}/prism" - ruby_dir = "#{lib_dir}/ruby" + prism_templates_dir = "#{prism_dir}/templates" - cc.defines.flatten! + cc.include_paths << "#{dir}/include" + cc.include_paths << "#{build.build_root}/prism/include" + cc.include_paths << "#{prism_dir}/include" - 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" + cc.defines.flatten! + cc.defines << 'PRISM_XALLOCATOR' + # Prism is a recursive-descent parser and mruby has no machine-stack-overflow + # backstop, so its default nesting cap of 10000 overflows the C stack (a + # crash) before the limit trips -- e.g. an ASan build dies around 400-500 + # deep. Cap nesting to match the codegen's own MRC_CODEGEN_LEVEL_MAX (256): + # the codegen cannot compile an expression nested deeper than that anyway, so + # this rejects nothing compilable while staying well below the stack limit. + # Targets on a tiny stack can lower it; those that raise it must also raise + # MRC_CODEGEN_LEVEL_MAX and have the C stack to match. + unless cc.defines.any? { |d| d.match?(/\APRISM_DEPTH_MAXIMUM(=|\z)/) } + cc.defines << 'PRISM_DEPTH_MAXIMUM=256' end - - if cc.defines.include?("PICORB_INT32") - cc.defines << "MRC_INT64" + if cc.defines.include?('PICORB_VM_MRUBY') + cc.defines << 'MRC_TARGET_MRUBY' + elsif cc.defines.include?('PICORB_VM_MRUBYC') + cc.defines << 'MRC_TARGET_MRUBYC' + elsif !cc.defines.include?('MRB_NO_GEMS') + cc.defines << 'MRC_TARGET_MRUBY' 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" + # Prism allocates the tree it parses, and the walk that gives the tree back + # costs a C frame per level of it; a tree deep enough to run that off the + # stack is written in ordinary source, so the tree is taken from an arena + # and given back in one piece instead (see include/prism_xallocator.h). + # + # A C++ ABI build takes the arena's blocks from libc. Prism is compiled as + # C there, so it reaches the arena through the C linkage the header gives + # it; what it must not reach is mrb_malloc(), which raises on failure and + # would throw through Prism's frames. Nothing is lost by it: that build + # already had every Prism allocation outside mrb_malloc(). + cc.defines << 'MRC_PRISM_ARENA' + cc.defines << 'MRC_PRISM_ARENA_LIBC' if build.cxx_abi_enabled? + cc.defines << 'MRC_DEBUG' if cc.has_define?('MRB_DEBUG') + cc.defines << 'PRISM_BUILD_MINIMAL' unless cc.defines.include?('MRC_DEBUG') + # PRISM_BUILD_MINIMAL stubs out pm_prettyprint(), so `mruby -v` can only dump + # the AST where it is compiled in + cc.defines << 'MRC_DUMP_PRETTY' if cc.defines.include?('MRC_DEBUG') + + # The compiler glue is built as C++ under MRB_USE_CXX_ABI, and mruby.h + # requires __STDC_LIMIT_MACROS / __STDC_CONSTANT_MACROS before in + # C++ mode -- some libc stdint.h (e.g. mingw) only define UINTPTR_MAX and + # friends when they are set. Define them on the command line so they apply no + # matter which header pulls in first. + if build.cxx_abi_enabled? + cc.defines += %w(__STDC_LIMIT_MACROS __STDC_CONSTANT_MACROS) end - prism_templates_dir = "#{lib_dir}/prism/templates" - cc.include_paths << "#{prism_dir}/include" + # Skip the Prism template generation and object registration only when the + # invocation is purely cleaning. Checking just the first task would also skip + # it for "rake clean test" (clean followed by a build in one process), which + # would leave the Prism objects out of objs and break the link. + next if (Rake.application.top_level_tasks - %w(clean deep_clean)).empty? + + prism_template_names = %w[ + ext/prism/api_node.c + include/prism/ast.h + include/prism/diagnostic.h + src/diagnostic.c + src/node.c + src/prettyprint.c + src/serialize.c + src/token_type.c + ] + # Written where the build writes, not into the submodule: a checkout is not + # the build's to change, and an out-of-source build left it holding the + # generated sources. `build_root` rather than `build_dir` because every + # target of the config reads the same ones, the mrbc sub-build included. + prism_gen_dir = "#{build.build_root}/prism" + prism_generated_files = prism_template_names.map { |path| "#{prism_gen_dir}/#{path}" } - next if %w(clean deep_clean).include?(Rake.application.top_level_tasks.first) + task :prism_submodule do + next if File.exist?("#{prism_dir}/templates/template.rb") - directory prism_dir do FileUtils.cd dir do - sh "git submodule update --init" + sh 'git submodule update --init lib/prism' end end - task :prism_templates => prism_dir do - FileUtils.cd prism_dir do - sh "templates/template.rb" + task prism_templates: :prism_submodule do + missing = prism_generated_files.reject { |path| File.exist?(path) } + unless missing.empty? + FileUtils.cd prism_dir do + prism_template_names.each do |name| + sh "#{RbConfig.ruby} templates/template.rb #{name} #{prism_gen_dir}/#{name}" + end + end + end + + # The templates write #line directives that name themselves against the + # root of the prism repository ("prism/templates/..."), a path that + # resolves to nothing from where mruby compiles: diagnostics point at a + # file the editor cannot open, and ccache drops its direct mode over the + # missing dependency. Rewrite the prefix to the name the compile is given + # for the gem's location, which is the one every other source is named + # by, and the gem's path where the build compiles by paths. + prism_compile_dir = build.compile_path(prism_dir) + prism_generated_files.each do |path| + source = File.binread(path) + rewritten = source.gsub(/^(#line \d+ ")prism\//) { "#{$1}#{prism_compile_dir}/" } + File.binwrite(path, rewritten) unless rewritten == source end end + Rake::Task[:prism_templates].invoke + %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| + dst = "#{prism_gen_dir}/src/#{name}.c" + file dst => ["#{prism_templates_dir}/src/#{name}.c.erb", "#{prism_templates_dir}/template.rb"] do 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 + # Prism is a vendored C library and must be compiled as C: neither g++ (its + # generated diagnostic table uses non-trivial designated initializers) nor + # clang++ (its implicit void* conversions) can build it as C++. In an + # MRB_USE_CXX_ABI build the rest of mruby compiles as C++, so strip the C++ + # compile flag here to keep these sources on the C compiler; mrc_common.h + # wraps the Prism header in extern "C" so the C++ glue links against them. + # Prism's allocator is the arena, which the header declares with C linkage + # so that these C objects resolve it; its blocks come from libc there, so + # nothing here reaches a C++-linkage symbol (see MRC_PRISM_ARENA_LIBC). + # The compiler is derived when a rule is first resolved (not here) so cc is + # already fully populated with the build's generated-header include flags. + # + # The objects go through the rules like every other object of the gem, so + # that a change to a Prism header or to the compile flags rebuilds them. + prism_src_dir = "#{prism_dir}/src" + prism_obj_dir = "#{build_dir}/lib" + prism_cc = nil + cc.define_rules(prism_obj_dir, prism_src_dir) do + prism_cc ||= if build.cxx_abi_enabled? + cc.clone.tap do |c| + c.flags = cc.flags.flatten - [cc.cxx_compile_flag].flatten + c.defines = cc.defines + end + else + cc end end - + prism_gen_src_dir = "#{prism_gen_dir}/src" + cc.define_rules(prism_obj_dir, prism_gen_src_dir) do + prism_cc ||= if build.cxx_abi_enabled? + cc.clone.tap do |c| + c.flags = cc.flags.flatten - [cc.cxx_compile_flag].flatten + c.defines = cc.defines + end + else + cc + end + end + Dir.glob("#{prism_src_dir}/**/*.c").each do |src| + objs << objfile(src.relative_path_from(prism_src_dir).pathmap("#{prism_obj_dir}/%X")) + end + Dir.glob("#{prism_gen_src_dir}/**/*.c").each do |src| + objs << objfile(src.relative_path_from(prism_gen_src_dir).pathmap("#{prism_obj_dir}/%X")) + end end - diff --git a/vendor/mruby-compiler2/src/ccontext.c b/vendor/mruby-compiler2/src/ccontext.c index f18b294..22f7130 100644 --- a/vendor/mruby-compiler2/src/ccontext.c +++ b/vendor/mruby-compiler2/src/ccontext.c @@ -2,14 +2,144 @@ #include "../include/mrc_ccontext.h" #include "../include/mrc_parser_util.h" +#if defined(MRC_TARGET_MRUBY) +/* The Prism xallocator routes allocations through this mrb_state. Define it + in the compiler library so every executable that links libmruby (not just + the mrbc/mruby/mirb front-ends) resolves the symbol. The front-ends assign + it unconditionally for the mruby target, so it must exist regardless of + MRC_ALLOC_LIBC even though only the non-libc allocator dereferences it. */ +#include + +mrb_state *global_mrb = NULL; + +#if defined(MRC_TARGET_MRUBY) && defined(MRC_PRISM_ARENA) +/* The arena prism allocates a parse from. Blocks are handed out by bumping a + pointer; giving the arena back walks the chain of blocks rather than the + tree, so a tree of any depth costs one loop and no C stack. See + prism_xallocator.h for why the tree is not walked. + + The blocks come from mrb_malloc(), so that a parse is on the allocator the + state was opened with, except where Prism is compiled as C beside a C++ + core: mrb_malloc() raises on failure and the throw would pass through + Prism's frames, so the blocks come from libc there. See the gem's + mrbgem.rake. */ +#if defined(MRC_PRISM_ARENA_LIBC) +# include +# define arena_block_alloc(size) malloc(size) +# define arena_block_free(ptr) free(ptr) +#else +# define arena_block_alloc(size) mrb_malloc(global_mrb, size) +# define arena_block_free(ptr) mrb_free(global_mrb, ptr) +#endif + +struct mrc_prism_arena_block *mrc_prism_arena = NULL; + +#define MRC_PRISM_ARENA_BLOCK (64 * 1024) + +struct arena_block { + struct mrc_prism_arena_block head; /* must be first: the public view */ + size_t used, size; +}; + +/* Every answer carries the size it was given, so that growing one knows how + much of it there is to copy. realloc() is told the new size only. */ +struct arena_chunk { + size_t size; + char body[]; +}; + +static struct arena_block * +arena_block_new(size_t need) +{ + size_t size = MRC_PRISM_ARENA_BLOCK; + while (size - sizeof(struct arena_block) < need) size *= 2; + struct arena_block *b = (struct arena_block *)arena_block_alloc(size); + b->head.prev = mrc_prism_arena; + b->used = sizeof(struct arena_block); + b->size = size; + mrc_prism_arena = &b->head; + return b; +} + +/* Open an arena for one compiler context, putting aside the arena of the + context this one is being made inside of. Contexts are made and freed in + the order of the calls that make them, so putting the outer one aside here + and back at mrc_ccontext_free() leaves each context taking from its own. */ +static void +arena_open(mrc_ccontext *c) +{ + c->prism_arena_outer = mrc_prism_arena; + mrc_prism_arena = NULL; + arena_block_new(0); + c->prism_arena = mrc_prism_arena; +} + +void * +mrc_prism_arena_alloc(size_t size) +{ + size_t need = (sizeof(struct arena_chunk) + size + 7u) & ~(size_t)7; + struct arena_block *b = (struct arena_block *)mrc_prism_arena; + + if (b == NULL || b->size - b->used < need) { + b = arena_block_new(need); + } + struct arena_chunk *chunk = (struct arena_chunk *)((char *)b + b->used); + b->used += need; + chunk->size = size; + return chunk->body; +} + +void * +mrc_prism_arena_realloc(void *ptr, size_t size) +{ + void *p = mrc_prism_arena_alloc(size); + + if (ptr != NULL && p != NULL) { + /* The old bytes are left where they are: the arena gives everything back + at once, so what a growing array leaves behind is reclaimed with it. + How much is left behind is bounded by the doubling the caller does. */ + struct arena_chunk *old = (struct arena_chunk *)((char *)ptr - offsetof(struct arena_chunk, body)); + size_t copy = old->size < size ? old->size : size; + memcpy(p, ptr, copy); + } + return p; +} + +/* Give back everything this context took, and make the arena of the context + it was made inside of the one that is open again. */ +static void +arena_close(mrc_ccontext *c) +{ + struct mrc_prism_arena_block *b = mrc_prism_arena; + + while (b != NULL) { + struct mrc_prism_arena_block *prev = b->prev; + arena_block_free(b); + b = prev; + } + c->prism_arena = NULL; + mrc_prism_arena = (struct mrc_prism_arena_block *)c->prism_arena_outer; + c->prism_arena_outer = NULL; +} +#endif +#endif + MRC_API mrc_ccontext * mrc_ccontext_new(mrb_state *mrb) { mrc_ccontext temp_c = {0}; +#if defined(MRC_TARGET_MRUBY) && defined(MRC_PRISM_ARENA) + global_mrb = mrb; +#endif 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; +#if defined(MRC_TARGET_MRUBY) && defined(MRC_PRISM_ARENA) + /* Before Prism is asked for anything on this context's behalf, so that + every pointer its allocator sees for this context is arena memory. */ + arena_open(c); +#endif return c; } @@ -20,7 +150,7 @@ 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++) { + for (size_t 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); @@ -47,9 +177,30 @@ mrc_ccontext_filename(mrc_ccontext *c, const char *s) MRC_API void mrc_ccontext_free(mrc_ccontext *c) { + if (c->options) { + /* pm_options_free() releases the scope and locals arrays but not the + per-local name copies (they are PM_STRING_CONSTANT, which pm_string_free + leaves alone) nor the options struct itself, so free those here. The + copies must go first, before pm_options_free() releases the arrays. */ + for (size_t s = 0; s < c->options->scopes_count; s++) { + pm_options_scope_t *scope = &c->options->scopes[s]; + for (size_t l = 0; l < scope->locals_count; l++) { + mrc_free(c, (void *)scope->locals[l].source); + } + } + pm_options_free(c->options); + mrc_free(c, c->options); + c->options = NULL; + } if (c->filename_table) mrc_free(c, c->filename_table); if (c->filename) mrc_free(c, c->filename); pm_parser_free(c->p); +#if defined(MRC_TARGET_MRUBY) && defined(MRC_PRISM_ARENA) + /* Everything Prism took for this context, the tree and what the parser + kept beside it, came from the arena and is given back here in one piece. + After pm_parser_free(), which reaches into that same arena. */ + arena_close(c); +#endif mrc_diagnostic_list_free(c); if (c->p->lex_callback) { mrc_free(c, c->p->lex_callback); diff --git a/vendor/mruby-compiler2/src/cdump.c b/vendor/mruby-compiler2/src/cdump.c index 51446df..61b0cd8 100644 --- a/vendor/mruby-compiler2/src/cdump.c +++ b/vendor/mruby-compiler2/src/cdump.c @@ -349,13 +349,20 @@ 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); + size_t 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); + int emitted = 0; for (int i=0; ipool, lenp+1); - memcpy(buf, name, lenp); - buf[lenp] = '\0'; - return buf; + size_t len = (size_t)lenp; + if (len == SIZE_MAX) { + return NULL; } + char *buf = (char*)mrc_pool_alloc(c->pool, len + 1); + if (!buf) { + return NULL; + } + memcpy(buf, name, len); + buf[len] = '\0'; + return buf; } static inline const struct mrc_irep_catch_handler * @@ -40,37 +45,46 @@ mrc_irep_catch_handler_table(const mrc_irep *irep) } #ifndef MRC_NO_STDIO +/* An anonymous local (a rest or block placeholder) is a pool entry with an + empty name here, where mruby leaves the symbol unset, so the name has to be + looked at rather than just the symbol. */ +static const char* +local_name(mrc_ccontext *c, const mrc_irep *irep, size_t n) +{ + if (n == 0) return NULL; + if (!irep->lv) return NULL; + if (n >= irep->nlocals) return NULL; + if (!irep->lv[n-1]) return NULL; + const char *s = mrc_sym_dump(c, irep->lv[n-1]); + if (!s || !s[0]) return NULL; + return s; +} + 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])); + const char *s = local_name(c, irep, n); + if (s) fprintf(out, " R%d:%s", (int)n, s); } 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; + if (local_name(c, irep, a)) { + fprintf(out, "\t;"); + print_r(c, irep, a, out); } - 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; + if (local_name(c, irep, a) || local_name(c, irep, b)) { + fprintf(out, "\t;"); + print_r(c, irep, a, out); + print_r(c, irep, b, out); } - fprintf(out, "\t;"); - if (a > 0) print_r(c, irep, a, out); - if (b > 0) print_r(c, irep, b, out); fprintf(out, "\n"); } @@ -121,7 +135,6 @@ print_args(uint16_t i, FILE *out) 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; @@ -134,7 +147,7 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) int head = FALSE; for (int i = 1; i < irep->nlocals; i++) { - char const *s = mrc_sym_dump(c, irep->lv[i - 1]); + char const *s = local_name(c, irep, i); if (s) { if (!head) { head = TRUE; @@ -195,7 +208,7 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) fprintf(out, "NOP\n"); break; CASE(OP_MOVE, BB): - fprintf(out, "MOVE\t\tR%d\tR%d\t", a, b); + fprintf(out, "MOVE\t\tR%d\tR%d", a, b); print_lv_ab(c, irep, a, b, out); break; @@ -220,24 +233,24 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) } print_lv_a(c, irep, a, out); break; - CASE(OP_LOADI, BB): - fprintf(out, "LOADI\t\tR%d\t%d\t", a, b); + CASE(OP_LOADI8, BB): + fprintf(out, "LOADI8\tR%d\t%d", a, b); print_lv_a(c, irep, a, out); break; CASE(OP_LOADINEG, BB): - fprintf(out, "LOADINEG\tR%d\t-%d\t", a, b); + fprintf(out, "LOADINEG\tR%d\t-%d", 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); + fprintf(out, "LOADI16\tR%d\t%d", 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)); + fprintf(out, "LOADI32\tR%d\t%d", 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); + fprintf(out, "LOADI__1\tR%d\t(-1)", a); print_lv_a(c, irep, a, out); break; CASE(OP_LOADI_0, B): goto L_LOADI; @@ -250,90 +263,93 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) 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); + fprintf(out, "LOADI_%d\tR%d\t(%d)", 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])); + fprintf(out, "LOADSYM\tR%d\t:%s", 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); + fprintf(out, "LOADNIL\tR%d\t(nil)", a); print_lv_a(c, irep, a, out); break; CASE(OP_LOADSELF, B): - fprintf(out, "LOADSELF\tR%d\t(R0)\t", a); + fprintf(out, "LOADSELF\tR%d\t(R0)", a); print_lv_a(c, irep, a, out); break; - CASE(OP_LOADT, B): - fprintf(out, "LOADT\t\tR%d\t(true)\t", a); + CASE(OP_LOADTRUE, B): + fprintf(out, "LOADTRUE\tR%d\t(true)", a); print_lv_a(c, irep, a, out); break; - CASE(OP_LOADF, B): - fprintf(out, "LOADF\t\tR%d\t(false)\t", a); + CASE(OP_LOADFALSE, B): + fprintf(out, "LOADFALSE\tR%d\t(false)", 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])); + fprintf(out, "GETGV\t\tR%d\t%s", 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); + fprintf(out, "SETGV\t\t%s\tR%d", 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])); + fprintf(out, "GETSV\t\tR%d\t%s", 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); + fprintf(out, "SETSV\t\t%s\tR%d", 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])); + fprintf(out, "GETCONST\tR%d\t%s", 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); + fprintf(out, "SETCONST\t%s\tR%d", 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])); + fprintf(out, "GETMCNST\tR%d\t(R%d)::%s", 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); + fprintf(out, "SETMCNST\t(R%d)::%s\tR%d", 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])); + fprintf(out, "GETIV\t\tR%d\t%s", 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); + fprintf(out, "SETIV\t\t%s\tR%d", 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); + fprintf(out, "GETUPVAR\tR%d\t%d\t%d", 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); + fprintf(out, "SETUPVAR\tR%d\t%d\t%d", 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])); + fprintf(out, "GETCV\t\tR%d\t%s", 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); + fprintf(out, "SETCV\t\t%s\tR%d", 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); + fprintf(out, "GETIDX\tR%d\t(R%d)\n", a, a+1); + break; + CASE(OP_GETIDX0, BB): + fprintf(out, "GETIDX0\tR%d\tR%d[0]\n", a, b); break; CASE(OP_SETIDX, B): - fprintf(out, "SETIDX\tR%d\tR%d\tR%d\n", a, a+1, a+2); + fprintf(out, "SETIDX\tR%d\t(R%d)\t(R%d)\n", a, a+1, a+2); break; CASE(OP_JMP, S): i = pc - irep->iseq; @@ -345,23 +361,26 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) break; CASE(OP_JMPIF, BS): i = pc - irep->iseq; - fprintf(out, "JMPIF\t\tR%d\t%03d\t", a, (int)i+(int16_t)b); + fprintf(out, "JMPIF\t\tR%d\t%03d", 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); + fprintf(out, "JMPNOT\tR%d\t%03d", 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); + fprintf(out, "JMPNIL\tR%d\t%03d", 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_SSEND0, BB): + fprintf(out, "SSEND0\tR%d\t:%s\n", a, mrc_sym_dump(c, irep->syms[b])); + 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); @@ -370,6 +389,9 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) fprintf(out, "SEND\t\tR%d\t:%s\t", a, mrc_sym_dump(c, irep->syms[b])); print_args(cc, out); break; + CASE(OP_SEND0, BB): + fprintf(out, "SEND0\t\tR%d\t:%s\n", a, mrc_sym_dump(c, irep->syms[b])); + 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); @@ -377,12 +399,15 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) CASE(OP_CALL, Z): fprintf(out, "CALL\n"); break; + CASE(OP_BLKCALL, BB): + fprintf(out, "BLKCALL\t\tR%d\t%d\n", a, b); + 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, + fprintf(out, "ARGARY\tR%d\t%d:%d:%d:%d (%d)", a, (b>>11)&0x3f, (b>>10)&0x1, (b>>5)&0x1f, @@ -391,40 +416,53 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) 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", + fprintf(out, "ENTER\t\t%d:%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); + MRC_ASPEC_BLOCK(a), + MRC_ASPEC_NOBLOCK(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])); + fprintf(out, "KEY_P\t\tR%d\t:%s", 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])); + fprintf(out, "KARG\t\tR%d\t:%s", 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); + fprintf(out, "RETURN\tR%d\t", a); print_lv_a(c, irep, a, out); break; CASE(OP_RETURN_BLK, B): - fprintf(out, "RETURN_BLK\tR%d\t\t", a); + fprintf(out, "RETURN_BLK\tR%d\t", a); print_lv_a(c, irep, a, out); break; + CASE(OP_RETSELF, Z): + fprintf(out, "RETSELF\n"); + break; + CASE(OP_RETNIL, Z): + fprintf(out, "RETNIL\n"); + break; + CASE(OP_RETTRUE, Z): + fprintf(out, "RETTRUE\n"); + break; + CASE(OP_RETFALSE, Z): + fprintf(out, "RETFALSE\n"); + break; CASE(OP_BREAK, B): - fprintf(out, "BREAK\t\tR%d\t\t", a); + fprintf(out, "BREAK\t\tR%d\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, + fprintf(out, "BLKPUSH\tR%d\t%d:%d:%d:%d (%d)", a, (b>>11)&0x3f, (b>>10)&0x1, (b>>5)&0x1f, @@ -448,7 +486,13 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) 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])); + fprintf(out, "DEF\t\tR%d\t:%s\t(R%d)\n", a, mrc_sym_dump(c, irep->syms[b]), a+1); + break; + CASE(OP_TDEF, BBB): + fprintf(out, "TDEF\t\tR%d\t:%s\tI[%d]\n", a, mrc_sym_dump(c, irep->syms[b]), cc); + break; + CASE(OP_SDEF, BBB): + fprintf(out, "SDEF\t\tR%d\t:%s\tI[%d]\n", a, mrc_sym_dump(c, irep->syms[b]), cc); break; CASE(OP_UNDEF, B): fprintf(out, "UNDEF\t\t:%s\n", mrc_sym_dump(c, irep->syms[a])); @@ -457,42 +501,50 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) 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); + fprintf(out, "ADD\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_ADDI, BB): - fprintf(out, "ADDI\t\tR%d\t%d\t", a, b); + fprintf(out, "ADDI\t\tR%d\t%d", 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); + fprintf(out, "SUB\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_SUBI, BB): - fprintf(out, "SUBI\t\tR%d\t%d\t", a, b); + fprintf(out, "SUBI\t\tR%d\t%d", a, b); + print_lv_a(c, irep, a, out); + break; + CASE(OP_ADDILV, BBB): + fprintf(out, "ADDILV\tR%d\tR%d\t%d", a, b, cc); + print_lv_a(c, irep, a, out); + break; + CASE(OP_SUBILV, BBB): + fprintf(out, "SUBILV\tR%d\tR%d\t%d", a, b, cc); print_lv_a(c, irep, a, out); break; CASE(OP_MUL, B): - fprintf(out, "MUL\t\tR%d\tR%d\n", a, a+1); + fprintf(out, "MUL\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_DIV, B): - fprintf(out, "DIV\t\tR%d\tR%d\n", a, a+1); + fprintf(out, "DIV\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_LT, B): - fprintf(out, "LT\t\tR%d\tR%d\n", a, a+1); + fprintf(out, "LT\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_LE, B): - fprintf(out, "LE\t\tR%d\tR%d\n", a, a+1); + fprintf(out, "LE\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_GT, B): - fprintf(out, "GT\t\tR%d\tR%d\n", a, a+1); + fprintf(out, "GT\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_GE, B): - fprintf(out, "GE\t\tR%d\tR%d\n", a, a+1); + fprintf(out, "GE\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_EQ, B): - fprintf(out, "EQ\t\tR%d\tR%d\n", a, a+1); + fprintf(out, "EQ\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_ARRAY, BB): - fprintf(out, "ARRAY\t\tR%d\tR%d\t%d", a, a, b); + fprintf(out, "ARRAY\t\tR%d\t%d", a, b); print_lv_a(c, irep, a, out); break; CASE(OP_ARRAY2, BBB): @@ -500,15 +552,15 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) print_lv_ab(c, irep, a, b, out); break; CASE(OP_ARYCAT, B): - fprintf(out, "ARYCAT\tR%d\tR%d\t", a, a+1); + fprintf(out, "ARYCAT\tR%d\t(R%d)", 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); + fprintf(out, "ARYPUSH\tR%d\t%d", a, b); print_lv_a(c, irep, a, out); break; CASE(OP_ARYSPLAT, B): - fprintf(out, "ARYSPLAT\tR%d\t", a); + fprintf(out, "ARYSPLAT\tR%d", a); print_lv_a(c, irep, a, out); break; CASE(OP_AREF, BBB): @@ -524,7 +576,7 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) print_lv_a(c, irep, a, out); break; CASE(OP_INTERN, B): - fprintf(out, "INTERN\tR%d\t\t", a); + fprintf(out, "INTERN\tR%d\t", a); print_lv_a(c, irep, a, out); break; CASE(OP_SYMBOL, BB): @@ -534,28 +586,31 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *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); + fprintf(out, "STRING\tR%d\tL[%d]", a, b); + if (irep->pool[b].u.str[0]) { + fprintf(out, "\t; %s", 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); + fprintf(out, "STRCAT\tR%d\t(R%d)", 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); + fprintf(out, "HASH\t\tR%d\t%d", a, b); print_lv_a(c, irep, a, out); break; CASE(OP_HASHADD, BB): - fprintf(out, "HASHADD\tR%d\t%d\t", a, b); + fprintf(out, "HASHADD\tR%d\t%d", 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); + fprintf(out, "HASHCAT\tR%d\t(R%d)", a, a+1); print_lv_a(c, irep, a, out); break; CASE(OP_OCLASS, B): - fprintf(out, "OCLASS\tR%d\t\t", a); + fprintf(out, "OCLASS\tR%d\t", a); print_lv_a(c, irep, a, out); break; CASE(OP_CLASS, BB): @@ -571,11 +626,11 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) print_lv_a(c, irep, a, out); break; CASE(OP_SCLASS, B): - fprintf(out, "SCLASS\tR%d\t", a); + fprintf(out, "SCLASS\tR%d", a); print_lv_a(c, irep, a, out); break; CASE(OP_TCLASS, B): - fprintf(out, "TCLASS\tR%d\t\t", a); + fprintf(out, "TCLASS\tR%d\t", a); print_lv_a(c, irep, a, out); break; CASE(OP_ERR, B): @@ -587,7 +642,7 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) } break; CASE(OP_EXCEPT, B): - fprintf(out, "EXCEPT\tR%d\t\t", a); + fprintf(out, "EXCEPT\tR%d\t", a); print_lv_a(c, irep, a, out); break; CASE(OP_RESCUE, BB): @@ -595,9 +650,12 @@ codedump(mrc_ccontext *c, const mrc_irep *irep, FILE *out) print_lv_ab(c, irep, a, b, out); break; CASE(OP_RAISEIF, B): - fprintf(out, "RAISEIF\tR%d\t\t", a); + fprintf(out, "RAISEIF\tR%d\t", a); print_lv_a(c, irep, a, out); break; + CASE(OP_MATCHERR, B): + fprintf(out, "MATCHERR\tR%d\n", a); + break; CASE(OP_DEBUG, BBB): fprintf(out, "DEBUG\t\t%d\t%d\t%d\n", a, b, cc); diff --git a/vendor/mruby-compiler2/src/codegen.c b/vendor/mruby-compiler2/src/codegen.c index 54e4aa2..b317232 100644 --- a/vendor/mruby-compiler2/src/codegen.c +++ b/vendor/mruby-compiler2/src/codegen.c @@ -11,7 +11,7 @@ #include "../include/mrc_debug.h" #include "../include/mrc_irep_pool_type.h" -#if defined(PICORB_VM_MRUBY) +#if defined(MRC_TARGET_MRUBY) #include "../include/mrc_proc.h" #endif @@ -91,95 +91,6 @@ #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 @@ -219,9 +130,9 @@ typedef struct scope { uint32_t lastlabel; uint16_t ainfo:15; mrc_bool mscope:1; + uint32_t aspec; /* the operand of this scope's `OP_ENTER` */ struct loopinfo *loop; - //mrc_sym filename_sym; const char *filename; uint16_t lineno; @@ -245,6 +156,7 @@ typedef struct scope { mrc_ccontext* c; int rlev; /* recursion levels */ + uint16_t for_depth; /* number of for-loop scopes above */ } mrc_codegen_scope; static void codegen(mrc_codegen_scope *s, mrc_node *tree, int val); @@ -258,14 +170,15 @@ codegen_error(mrc_codegen_scope *s, const char *message) 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); + if (!s->c->quiet_errors) { + 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; @@ -281,11 +194,12 @@ codegen_error(mrc_codegen_scope *s, const char *message) 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]); - //} + /* Compiler ireps are singly owned (refcnt is only ever set to 1), so + freeing each child outright is equivalent to decref-to-zero. */ + for (int i=0; iirep->rlen; i++) { + if (s->reps[i]) + mrc_irep_free(s->c, (mrc_irep*)s->reps[i]); + } mrc_free(s->c, s->reps); } mrc_free(s->c, s->lines); @@ -563,12 +477,28 @@ scope_new(mrc_ccontext *c, mrc_codegen_scope *prev, mrc_constant_id_list *nlv) 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) { + if (nlv == NULL) { + /* for-loop scope: empty lv so search_upvar skips this scope, + but mirror parent's register layout */ + s->lv = NULL; + s->sp = prev->nlocals; + s->nlocals = s->nregs = s->sp; + if (prev->irep->lv) { + size_t lv_size = sizeof(mrc_sym) * (s->nlocals - 1); + s->irep->lv = (mrc_sym *)mrc_malloc(c, lv_size); + memcpy(s->irep->lv, prev->irep->lv, lv_size); + } + else { + s->irep->lv = NULL; + } + } + else { + if (nlv->size >= UINT8_MAX) { + codegen_error(s, "too many local variables"); + } + s->lv = nlv; + s->sp += nlv->size + 1; /* add self */ + s->nlocals = s->nregs = s->sp; mrc_sym *lv; size_t size = sizeof(mrc_sym) * nlv->size; if (0 < size) { @@ -780,7 +710,7 @@ get_int_operand(mrc_codegen_scope *s, struct mrc_insn_data *data, mrc_int *n) *n = data->insn - OP_LOADI_0; return TRUE; - case OP_LOADI: + case OP_LOADI8: case OP_LOADI16: *n = (int16_t)data->b; return TRUE; @@ -865,7 +795,7 @@ genjmp2(mrc_codegen_scope *s, mrc_code i, uint16_t a, uint32_t pc, int val) } break; case OP_LOADNIL: - case OP_LOADF: + case OP_LOADFALSE: 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)) { @@ -876,7 +806,7 @@ genjmp2(mrc_codegen_scope *s, mrc_code i, uint16_t a, uint32_t pc, int val) } } break; - case OP_LOADT: case OP_LOADI: case OP_LOADINEG: case OP_LOADI__1: + case OP_LOADTRUE: case OP_LOADI8: 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) { @@ -922,7 +852,7 @@ gen_int(mrc_codegen_scope *s, uint16_t dst, mrc_int 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 <= 0xff) genop_2(s, OP_LOADI8, 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 { @@ -958,7 +888,7 @@ gen_move(mrc_codegen_scope *s, uint16_t dst, uint16_t src, int nopeep) return; } goto normal; - case OP_LOADNIL: case OP_LOADSELF: case OP_LOADT: case OP_LOADF: + case OP_LOADNIL: case OP_LOADSELF: case OP_LOADTRUE: case OP_LOADFALSE: 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: @@ -969,7 +899,7 @@ gen_move(mrc_codegen_scope *s, uint16_t dst, uint16_t src, int nopeep) case OP_HASH: if (data.b != 0) goto normal; /* fall through */ - case OP_LOADI: case OP_LOADINEG: + case OP_LOADI8: 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: @@ -1016,19 +946,10 @@ gen_move(mrc_codegen_scope *s, uint16_t dst, uint16_t src, int nopeep) 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; - } - } - } + /* ADDILV/SUBILV fusion: MOVE temp local; ADDI/SUBI temp imm; MOVE local temp */ + /* -> ADDILV/SUBILV local temp imm (temp is working space for method fallback) */ + genop_3(s, data.insn == OP_ADDI ? OP_ADDILV : OP_SUBILV, dst, data.a, data.b); + return; } genop_2(s, data.insn, dst, data.b); return; @@ -1052,11 +973,6 @@ lv_idx(mrc_codegen_scope *s, mrc_sym id) } -#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) { @@ -1072,16 +988,16 @@ search_upvar(mrc_codegen_scope *s, mrc_sym id, int *idx) up = up->prev; } -#if defined(PICORB_VM_MRUBY) +#if defined(MRC_TARGET_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) { + if (id != PM_CONSTANT_ID_UNSET && id <= s->c->p->constant_pool.size) { + 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); while (u && !MRC_PROC_CFUNC_P(u)) { - const struct mrc_irep *ir = u->body.irep; + const struct mrc_irep *ir = (const struct mrc_irep *)u->body.irep; uint_fast16_t n = ir->nlocals; int i; const mrc_sym *v = ir->lv; @@ -1093,7 +1009,7 @@ search_upvar(mrc_codegen_scope *s, mrc_sym id, int *idx) } } } - if (MRC_PROC_SCOPE_P(u)) break; + if (MRC_PROC_LVAR_BOUNDARY_P(u)) break; u = u->upper; lv++; } @@ -1116,13 +1032,11 @@ search_upvar(mrc_codegen_scope *s, mrc_sym id, int *idx) } static void -gen_getupvar(mrc_codegen_scope *s, uint16_t dst, mrc_sym id, int depth) +gen_getupvar(mrc_codegen_scope *s, uint16_t dst, mrc_sym id) { 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) { @@ -1134,14 +1048,16 @@ gen_getupvar(mrc_codegen_scope *s, uint16_t dst, mrc_sym id, int depth) } static void -gen_setupvar(mrc_codegen_scope *s, uint16_t dst, mrc_sym id, int depth) +gen_setupvar(mrc_codegen_scope *s, uint16_t dst, mrc_sym id, int val) { int idx; int lv = search_upvar(s, id, &idx); - mrc_assert(lv == depth-1); - - if (!no_peephole(s)) { + /* Folding the preceding `OP_MOVE dst, src` into `OP_SETUPVAR src` leaves + `dst` unwritten, so it is only sound where the assignment is a statement. + As an expression the value belongs in `dst`, and the store is the only + thing that would have put it there. */ + 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; @@ -1164,7 +1080,28 @@ gen_return(mrc_codegen_scope *s, uint8_t op, uint16_t src) rewind_pc(s); genop_1(s, op, data.b); } - else if (data.insn != OP_RETURN) { + else if (data.insn == OP_LOADSELF && src == data.a && op == OP_RETURN) { + /* LOADSELF + RETURN -> RETSELF */ + rewind_pc(s); + genop_0(s, OP_RETSELF); + } + else if (data.insn == OP_LOADNIL && src == data.a && op == OP_RETURN) { + /* LOADNIL + RETURN -> RETNIL */ + rewind_pc(s); + genop_0(s, OP_RETNIL); + } + else if (data.insn == OP_LOADTRUE && src == data.a && op == OP_RETURN) { + /* LOADTRUE + RETURN -> RETTRUE */ + rewind_pc(s); + genop_0(s, OP_RETTRUE); + } + else if (data.insn == OP_LOADFALSE && src == data.a && op == OP_RETURN) { + /* LOADFALSE + RETURN -> RETFALSE */ + rewind_pc(s); + genop_0(s, OP_RETFALSE); + } + else if (data.insn != OP_RETURN && data.insn != OP_RETSELF && data.insn != OP_RETNIL && + data.insn != OP_RETTRUE && data.insn != OP_RETFALSE) { genop_1(s, op, src); } } @@ -1226,6 +1163,12 @@ scope_finish(mrc_codegen_scope *s) static mrc_pool_value* lit_pool_extend(mrc_codegen_scope *s) { + /* `plen` is what the dump writes the pool count into, and it is 16 bits + wide there and here, so the 65536th entry would wrap it to zero and leave + every OP_LOADL past that pointing outside the pool. */ + if (s->irep->plen == 0xffff) { + codegen_error(s, "too many literals"); + } 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); @@ -1269,6 +1212,18 @@ new_sym(mrc_codegen_scope *s, mrc_sym sym) for (i=0; isyms[i] == sym) return i; } + { + /* The dump writes a symbol name's length in 16 bits, and then walks the + cursor by the truncated count while copying the name in full, so a + longer name leaves the rest of the symbol block written over itself. + 0xffff is one short of that, and spoken for: it is the length the dump + writes for a null symbol (MRC_DUMP_NULL_SYM_LEN), so a name that long + is read back as no symbol at all. */ + mrc_int nlen = 0; + if (mrc_sym_name_len(s->c, sym, &nlen) && nlen >= MRC_DUMP_NULL_SYM_LEN) { + codegen_error(s, "symbol name too long"); + } + } if (s->irep->slen >= s->scapa) { s->scapa *= 2; if (s->scapa > 0xffff) { @@ -1296,64 +1251,15 @@ gen_addsub(mrc_codegen_scope *s, uint8_t op, uint16_t dst) /* 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); + /* Fold to OP_ADDI/OP_SUBI only for non-negative 8-bit n; flipping op + for negative n would change the method sent on user override (#2557). + Two literals are not folded to their sum for the same reason: the + operator is a method of the receiver, and only the opcode can tell + whether it is still the builtin. */ + if (n < 0 || n > UINT8_MAX) goto normal; + rewind_pc(s); + if (op == OP_ADD) genop_2(s, OP_ADDI, dst, (uint16_t)n); + else genop_2(s, OP_SUBI, dst, (uint16_t)n); } } @@ -1372,10 +1278,9 @@ gen_uniop(mrc_codegen_scope *s, mrc_sym sym, uint16_t dst) if (n == MRC_INT_MIN) return FALSE; n = -n; } - else if (sym == MRC_OPSYM_2(neg)) { - n = ~n; - } else { + /* `~1` is a method call in CRuby too, and folding it would bypass a + redefined `Integer#~` */ return FALSE; } s->pc = addr_pc(s, data.addr); @@ -1388,55 +1293,22 @@ 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 { + /* GETIDX0 fusion: MOVE dst arr; LOADI_0 dst+1 -> GETIDX0 dst arr */ 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; + if (data.insn == OP_LOADI_0 && data.a == (uint32_t)(dst+1) && addr_pc(s, data.addr) != s->lastlabel) { + struct mrc_insn_data data0 = mrc_decode_insn(mrc_prev_pc(s, data.addr)); + if (data0.insn == OP_MOVE && data0.a == dst && data0.b != dst) { + s->pc = addr_pc(s, data0.addr); + genop_2(s, OP_GETIDX0, dst, data0.b); + return TRUE; } } - 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); + genop_1(s, OP_GETIDX, dst); return TRUE; } + else { + return FALSE; + } } #define JMPLINK_START UINT32_MAX @@ -1488,7 +1360,10 @@ new_litbint(mrc_codegen_scope *s, const char *p, int base, mrc_bool neg) 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) + /* str[1] encodes the sign as -base for negative values, so compare the + signed base; otherwise a negative literal would dedup onto a positive + one of the same magnitude and lose its sign. */ + if (len == plen && pv->u.str[1] == (neg ? -base : base) && memcmp(pv->u.str+2, p, len) == 0) return i; } @@ -1513,29 +1388,31 @@ new_lit_str(mrc_codegen_scope *s, const char *str, mrc_int len) int i; mrc_pool_value *pv; + /* The dump writes a pool string's length in 16 bits, so a longer one is + recorded truncated while its bytes are written in full, and every field + after it is read from the wrong offset. */ + if (len > UINT16_MAX) { + codegen_error(s, "string literal too long"); + } 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) + /* plen==0 means both are empty; skip memcmp so a NULL str (empty string + literal) is not passed to its nonnull argument (UB clang miscompiles). */ + if (plen == 0 || 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; - //} + char *p; + pv->tt = (uint32_t)(len<<2) | IREP_TT_STR; + p = (char*)mrc_realloc(s->c, NULL, len+1); + if (len) memcpy(p, str, len); /* str may be NULL for an empty literal */ + p[len] = '\0'; + pv->u.str = p; return i; } @@ -1620,7 +1497,6 @@ loop_push(mrc_codegen_scope *s, enum looptype t) return p; } -// Implementation in codegen_prism.inc static void gen_retval(mrc_codegen_scope *s, mrc_node *tree); static void @@ -1692,23 +1568,215 @@ loop_pop(mrc_codegen_scope *s, int val) if (val) push(); } +/* The method scope a `super`, a `zsuper` or a `yield` belongs to. */ +struct mscope { + int ainfo; /* the argument layout the forwarded arguments + and the block are read by; -1 when there + is no method scope */ + int lv; /* scopes between the method and the asker */ + uint32_t aspec; /* the operand of the method's `OP_ENTER` */ + const mrc_constant_id_list *names; /* its locals by register, when the + method is in this compile unit */ +#if defined(MRC_TARGET_MRUBY) + const mrc_irep *irep; /* its irep, when the method is on the proc + chain of the compile context instead */ +#endif +}; + +/* Read the method's local in register `reg` into `cursp()`. */ static void -gen_blkmove(mrc_codegen_scope *s, uint16_t ainfo, int lv) +gen_mscope_lvar(mrc_codegen_scope *s, const struct mscope *m, int reg) { - 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); + if (m->lv == 0) { + gen_move(s, cursp(), reg, 0); } else { - genop_3(s, OP_GETUPVAR, cursp(), off, lv); + /* `lv` counts the scopes between here and the method, while `OP_GETUPVAR` + counts the envs above this frame's own, and the method's env is the + first of those: one level fewer. */ + genop_3(s, OP_GETUPVAR, cursp(), reg, m->lv-1); } push(); } +static void +gen_blkmove(mrc_codegen_scope *s, const struct mscope *m) +{ + int m1 = (m->ainfo>>7)&0x3f; + int r = (m->ainfo>>6)&0x1; + int m2 = (m->ainfo>>1)&0x1f; + int kd = (m->ainfo)&0x1; + gen_mscope_lvar(s, m, m1+r+m2+kd+1); +} + +/* The operand `OP_ARGARY` and `OP_BLKPUSH` reach the method scope by. It has + sixteen bits for both the layout of the arguments to forward and the level + the method scope is at, four of them the level, and neither the mandatory + and optional parameters counted together nor the level is bounded anywhere + else. A `super` or a `yield` that outgrows either is refused rather than + sent to a frame it did not mean. */ +static uint16_t +mscope_operand(mrc_codegen_scope *s, const struct mscope *m) +{ + if (m->ainfo > 0xfff) { + codegen_error(s, "too many formal arguments"); + } + if (m->lv > 0xf) { + codegen_error(s, "too many nested blocks/methods"); + } + return (uint16_t)((m->ainfo<<4)|m->lv); +} + +static mrc_sym nsym(mrc_parser_state *p, const uint8_t *start, size_t length); + +/* The name of the method's local in register `reg`, as a symbol of this + compile unit. 0 when the method carries no names. */ +static mrc_sym +mscope_lvar_name(mrc_codegen_scope *s, const struct mscope *m, int reg) +{ + if (m->names) return m->names->ids[reg-1]; +#if defined(MRC_TARGET_MRUBY) + if (m->irep && m->irep->lv) { + const char *name = mrb_sym_name(s->c->mrb, m->irep->lv[reg-1]); + if (name) return nsym(s->c->p, (const uint8_t *)name, strlen(name)); + } +#endif + return 0; +} + +/* Build the keyword hash a bare `super` forwards, at `cursp()`. + + `OP_KARG` moves each keyword parameter into its local by deleting it from + the dictionary the frame received, so by the time `super` runs that + dictionary holds only what no parameter claimed, and it is the very object + that `**rest` names. Forwarding it as read hands the parent a dictionary + with the declared keywords missing, and lets the parent's `OP_KARG` delete + from the caller's `rest`. The keyword locals hold the current values, as + CRuby forwards them, so the hash is rebuilt from a copy of `rest` with + those written over it: the order CRuby's parent sees the keys in, and the + one that lets a declared keyword win over a key of its name in `rest`. + + Answers whether the hash was built; the registers above `cursp()` are + used as scratch, the block's slot among them. When the method carries no + local names there is nothing to build it from, and the dictionary stays + as read. */ +static mrc_bool +gen_zsuper_kwargs(mrc_codegen_scope *s, const struct mscope *m) +{ + uint32_t a = m->aspec; + int ka = MRC_ASPEC_KEY(a); + int kd = MRC_ASPEC_KDICT(a); + int kw_pos = MRC_ASPEC_REQ(a) + MRC_ASPEC_OPT(a) + MRC_ASPEC_REST(a) + MRC_ASPEC_POST(a) + 1; + /* the keyword locals follow the dictionary, the block's slot and the + block's name */ + int kw_reg = kw_pos + 2 + MRC_ASPEC_BLOCK(a); + + if (ka > 0 && mscope_lvar_name(s, m, kw_reg) == 0) return FALSE; + if (kd) { + genop_2(s, OP_HASH, cursp(), 0); + push(); + gen_mscope_lvar(s, m, kw_pos); + pop(); pop(); + genop_1(s, OP_HASHCAT, cursp()); + push(); + } + for (int i = 0; i < ka; i++) { + genop_2(s, OP_LOADSYM, cursp(), new_sym(s, mscope_lvar_name(s, m, kw_reg+i))); + push(); + gen_mscope_lvar(s, m, kw_reg+i); + } + if (ka > 0) { + pop_n(ka*2); + if (kd) { + pop(); + genop_2(s, OP_HASHADD, cursp(), ka); + } + else { + genop_2(s, OP_HASH, cursp(), ka); + } + push(); + } + pop(); + return TRUE; +} + +/* Whether a `return` here leaves a method that is not part of this compile + unit. A string compiled for `eval` holds no method scope of its own, and + `return` in it leaves the method that encloses the `eval` call, which is + the frame the proc chain reaches and the one `OP_RETURN_BLK` unwinds to. */ +static mrc_bool +return_leaves_upper_p(mrc_codegen_scope *s) +{ +#if defined(MRC_TARGET_MRUBY) + if (!s->c->upper) return FALSE; + for (mrc_codegen_scope *s2 = s; s2; s2 = s2->prev) { + if (s2->mscope) return FALSE; + } + return TRUE; +#else + (void)s; + return FALSE; +#endif +} + +/* Find the method scope that a `super`, a `zsuper` or a `yield` belongs to. + Fills `m` with its `ainfo`, the argument layout that the forwarded + arguments and the block are read by, the number of levels between it and + `s`, and where its locals are to be found. `ainfo` is -1 when there is + no method scope to find. */ +static void +search_mscope(mrc_codegen_scope *s, struct mscope *m) +{ + mrc_codegen_scope *s2 = s; + int lv = 0; + + memset(m, 0, sizeof(*m)); + m->ainfo = -1; + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + m->lv = lv; + if (s2) { + m->ainfo = (int)s2->ainfo; + m->aspec = s2->aspec; + m->names = s2->lv; + return; + } + +#if defined(MRC_TARGET_MRUBY) + /* A string compiled for `eval` has a scope chain of its own, and the method + that encloses the `eval` call is not on it: it is on the proc chain the + context carries. The walk above ended on the scope `generate_code()` + wraps a compile unit in, which is one level more than the string's own + scope and stands for no frame, so `c->upper` is the proc the level count + has now reached. Restate the `OP_ENTER` that the method scope emitted + for itself as the `ainfo` it would have answered. */ + const struct RProc *u = s->c->upper; + + m->lv--; + + while (u && !MRC_PROC_CFUNC_P(u)) { + if (MRC_PROC_SCOPE_P(u)) { + const struct mrc_irep *ir = (const struct mrc_irep *)u->body.irep; + if (!ir || ir->ilen == 0 || ir->iseq[0] != OP_ENTER) break; + uint32_t a = PEEK_W(ir->iseq + 1); + uint32_t ma = MRC_ASPEC_REQ(a) + MRC_ASPEC_OPT(a); + m->ainfo = (int)(((ma & 0x3f) << 7) + | (MRC_ASPEC_REST(a) << 6) + | ((MRC_ASPEC_POST(a) & 0x1f) << 1) + | ((MRC_ASPEC_KEY(a) || MRC_ASPEC_KDICT(a)) ? 1 : 0)); + m->aspec = a; + m->irep = ir; + return; + } + u = u->upper; + m->lv++; + } +#endif +} + static void gen_setxv(mrc_codegen_scope *s, uint8_t op, uint16_t dst, mrc_sym sym, int val) { @@ -1750,8 +1818,9 @@ generate_code(mrc_ccontext *c, mrc_node *node, int val) return irep; } MRC_CATCH(c->jmp) { - // TODO? - //mrc_irep_free(c, scope->irep); + /* scope->irep is the root irep (shared with the top-level scope). It is + NULL only if codegen failed before the first scope_add_irep(). */ + if (scope->irep) mrc_irep_free(c, scope->irep); mrc_pool_close(scope->mpool); c->jmp = prev_jmp; return NULL; @@ -1770,7 +1839,5346 @@ mrc_generate_code(mrc_ccontext *c, mrc_node *node) #define GEN_VAL_STACK_MAX 99 /*-------------------------------------------------------------------------- - * Parser dependent code + * Prism dependent code *------------------------------------------------------------------------*/ -#include "codegen_prism.inc" +#define nint(node) PM_NODE_TYPE(node) + +/* Names `defined?` will name in one constant path. The walk holds them on + the stack of a recursive codegen, so the bound stays well under what + OP_ARRAY could carry; a path deeper than this is answered nil. */ +#define DEFINED_PATH_MAX 32 + +#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 void gen_lvar(mrc_codegen_scope *s, mrc_sym name, int depth); +static void codegen_pattern(mrc_codegen_scope *s, mrc_node *pattern, int target, + uint32_t *fail_pos, int known_array_len, int cache); + +static mrc_sym +nsym(mrc_parser_state *p, const uint8_t *start, size_t length) +{ + if (length == 0) { + /* An empty name has no bytes to keep alive, and `start` can be NULL for + one. The pool compares colliding entries with `memcmp()`, which is + declared nonnull, so hand it an empty string instead. */ + return pm_constant_pool_insert_constant(&p->constant_pool, (const uint8_t *)"", 0); + } + if (start >= p->start && start < p->end) { + /* Source-backed bytes stay valid for the parser's lifetime. */ + return pm_constant_pool_insert_constant(&p->constant_pool, start, length); + } + /* Node-owned bytes (e.g. an unescaped symbol name containing escapes) are + freed together with the AST, before the generated irep is dumped; copy + them into pool-owned memory so the constant id stays valid. */ + uint8_t *copy = (uint8_t *)xmalloc(length); + memcpy(copy, start, length); + return pm_constant_pool_insert_owned(&p->constant_pool, copy, length); +} + +static int32_t +node_lineno(mrc_ccontext *c, mrc_node *node) +{ + pm_location_t *loc = &((pm_node_t *)node)->location; + int32_t abs_line = pm_newline_list_line(&c->p->newline_list, loc->start, 1); + int32_t line_offset = c->lineno > 0 ? c->lineno - 1 : 0; + uint32_t node_pos = (uint32_t)(loc->start - c->p->start); + uint32_t file_start = 0; + for (uint16_t i = 1; i < c->filename_table_length; i++) { + if (c->filename_table[i].start <= node_pos) { + file_start = c->filename_table[i].start; + } else { + break; + } + } + if (file_start == 0) return abs_line + line_offset; + int32_t file_start_line = pm_newline_list_line(&c->p->newline_list, c->p->start + file_start, 1); + return abs_line - file_start_line + 1 + line_offset; +} + +/* `alias` and `undef` take compile-time symbol indices, but prism hands them a + SymbolNode *or* an InterpolatedSymbolNode (`alias :"#{x}" :y`), plus + GlobalVariableReadNode / MissingNode on a parse error. Resolve the ones whose + name is known at compile time and report the rest instead of reading the + wrong node layout. */ +static mrc_sym +alias_sym(mrc_codegen_scope *s, mrc_node *tree) +{ + switch (nint(tree)) { + case PM_SYMBOL_NODE: + { + CAST3(symbol, tree, sym); + return new_sym(s, nsym(s->c->p, sym->unescaped.source, sym->unescaped.length)); + } + case PM_INTERPOLATED_SYMBOL_NODE: + /* The name is only known at run time, but OP_ALIAS and OP_UNDEF carry a + symbol index. Supporting it would mean interning and dispatching at run + time, which is a separate feature. */ + codegen_error(s, "dynamic symbol is not supported by alias/undef"); + return 0; + default: + /* GlobalVariableReadNode / MissingNode: prism produces these for + `alias a $b` and `alias a 42` and has already reported the error. */ + codegen_error(s, "invalid alias/undef argument"); + return 0; + } +} + +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) { + pm_splat_node_t *splat = (pm_splat_node_t *)cast->arguments.nodes[0]; + if (splat->expression) { + codegen(s, (mrc_node *)splat->expression, VAL); + } else { + /* anonymous splat: load local variable '*' */ + pm_constant_id_t astr = MRC_OPSYM_2(mul); + gen_lvar(s, astr, 0); + } + 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, val); + } +} + +/* Bind what a pattern captured to the local its target names. Prism records + how many scopes up that local lives, so a capture inside a block reaches a + local of the enclosing scope the way an assignment does. */ +static void +gen_pattern_bind(mrc_codegen_scope *s, pm_local_variable_target_node_t *var, int src) +{ + gen_assignment_lvar(s, src, var->name, var->depth + s->for_depth, 1); +} + +/* Load the anonymous forwarding variable `sym` (one of `*`, `**`, `&`) into + cursp(). When `...` is forwarded from inside a block the variable lives in + an enclosing method scope, so fall back to an upvar load instead of asserting + it is a local of the current scope. */ +static void +gen_forward_arg(mrc_codegen_scope *s, mrc_sym sym, int val) +{ + int idx = lv_idx(s, sym); + if (idx > 0) { + gen_move(s, cursp(), idx, val); + } + else { + gen_getupvar(s, cursp(), sym); + } +} + +/* The first `upto` of the arguments, which is all of them for gen_values() + below. An attribute write asks for one fewer: the last of its arguments + is the value being assigned, which it has to hold on to rather than let + into the array a splat gathers the rest into. */ +static int +gen_values_upto(mrc_codegen_scope *s, mrc_node *tree, int val, int limit, size_t upto) +{ + if (tree == NULL) return 0; /* no arguments (e.g. empty index `a[]`) */ + 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 < upto; i++) { + t = (mrc_node *)cast->arguments.nodes[i]; + codegen(s, t, NOVAL); + n++; + } + return n; + } + + for (size_t i = 0; i < upto; i++) { + t = (mrc_node *)cast->arguments.nodes[i]; + if (nint(t) == PM_KEYWORD_HASH_NODE) break; + int is_splat = nint(t) == PM_SPLAT_NODE; + int is_forwarding = nint(t) == PM_FORWARDING_ARGUMENTS_NODE; + + if (is_splat || is_forwarding || 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) { + pm_splat_node_t *splat = (pm_splat_node_t *)t; + if (splat->expression) { + CAST3(array, splat->expression, a); + codegen(s, (mrc_node *)a, val); + } else { + /* anonymous splat: load local variable '*' */ + pm_constant_id_t astr = MRC_OPSYM_2(mul); + gen_lvar(s, astr, 0); + } + pop(); pop(); + genop_1(s, OP_ARYCAT, cursp()); + push(); + } + else if (is_forwarding) { + /* ARYCAT rest args (*) into the flushed array */ + gen_forward_arg(s, MRC_OPSYM_2(mul), val); + pop(); + genop_1(s, OP_ARYCAT, cursp()); + push(); + /* ** keyword hash */ + genop_2(s, OP_HASH, cursp(), 0); + push(); + gen_forward_arg(s, MRC_OPSYM_2(pow), val); + pop(); + genop_1(s, OP_HASHCAT, cursp()); + push(); + /* & block */ + gen_forward_arg(s, MRC_OPSYM_2(and), val); + break; + } + 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 int +gen_values(mrc_codegen_scope *s, mrc_node *tree, int val, int limit) +{ + if (tree == NULL) return 0; + CAST(arguments); + return gen_values_upto(s, tree, val, limit, cast->arguments.size); +} + +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); + /* pm_required_parameter_node_t has no `depth` field (a parameter is + always a local in the current scope); reading cast->depth on it would + read past the node, yielding a garbage depth that sends the lookup to + search_upvar and fails with "Can't find local variables". */ + int depth = (nint(tree) == PM_REQUIRED_PARAMETER_NODE) ? 0 : (int)cast->depth; + gen_assignment_lvar(s, sp, cast->name, depth + s->for_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); + /* 13 rather than 14: the value to assign is an argument too, and a + count that reaches CALL_MAXARGS is the mark for arguments gathered + in an array rather than a count of them. */ + int n = gen_values(s, (mrc_node *)cast->arguments, VAL, 13); + if (n < 0) { + /* More indices than a count carries: gen_values() gathered them into + an array at cursp(), and the value joins them there. */ + push(); + genop_2(s, OP_MOVE, cursp(), sp); + push(); /* the value, which is also the block slot the send + reads after the array */ + pop(); + pop(); + genop_2(s, OP_ARYPUSH, cursp(), 1); + pop(); + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(aset)), CALL_MAXARGS); + break; + } + /* the value to assign lives in sp (set by the caller for multiple + assignment); cursp()-n*2+1 would point at an index register */ + genop_2(s, OP_MOVE, cursp(), sp); + push(); /* reserve the value register so nregs accounts for it */ + if (n == 1) { + pop_n(3); + genop_1(s, OP_SETIDX, cursp()); + } + else { + push(); pop(); /* touch block slot so nregs covers the OP_SEND */ + pop_n(n+2); + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(aset)), n+1); + } + break; + } + case PM_CALL_TARGET_NODE: + { + CAST(call_target); + /* a written `self` is a call on self, so OP_SSEND, which fills the + receiver register itself */ + int noself = nint(cast->receiver) == PM_SELF_NODE; + if (noself) { + push(); + } + else { + codegen(s, cast->receiver, VAL); + } + /* the value to assign lives in sp (set by the caller for multiple + assignment) and goes in the register after the receiver, which the + `aset` arm above reserves the same way: the OP_SEND below reads its + one argument from there and its block from the register after that, + so both have to be counted or `nregs` leaves the frame short of them */ + genop_2(s, OP_MOVE, cursp(), sp); + push(); /* reserve the value register so nregs accounts for it */ + push(); pop(); /* touch block slot so nregs covers the OP_SEND */ + pop_n(2); + genop_3(s, noself ? OP_SSEND : 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, cls); + nlv = &cls->locals; + statements = cls->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; + + 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(); + } + if (assocsplat->value) { + codegen(s, assocsplat->value, val); + } else if (val) { + /* anonymous keyword splat: load local variable '**' */ + pm_constant_id_t dastr = MRC_OPSYM_2(pow); + gen_lvar(s, dastr, 0); + } + 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; +} + +/* Attribute assignment (`recv.attr = v`, `recv[i] = v`) as an expression. + Prism bundles the RHS as the last positional argument of the call node. + The whole expression must evaluate to that RHS, not to the setter's + return value, so the RHS is copied into a reserved slot below the call + frame and used as the result while the SEND result is discarded. With + `recv_ready` the receiver has been evaluated already and sits at + cursp()-1. */ +static void +gen_call_assign(mrc_codegen_scope *s, mrc_node *tree, int val, int safe, int recv_ready) +{ + CAST(call); + const mrc_sym sym = cast->name; + int skip = 0, n = 0, noself = 0, noop = no_optimize(s); + int top, callsp, opt_op = 0; + + if (!noop && sym == MRC_OPSYM_2(aset)) opt_op = OP_SETIDX; + + if (recv_ready) { + /* the receiver's slot becomes the room for retval, and the receiver + moves up above it */ + top = cursp()-1; + gen_move(s, cursp(), top, 1); + push(); + callsp = cursp()-1; + } + else { + top = cursp(); + push(); /* room for retval */ + callsp = cursp(); + + /* receiver: a written `self` is a call on self, which OP_SSEND makes + so that a private setter is reachable as in CRuby; the register is + still loaded where an instruction reads it before the send, that + is for OP_SETIDX and for the `&.` nil check */ + if (cast->receiver == NULL) { + noself = 1; + push(); + } + else if (nint(cast->receiver) == PM_SELF_NODE) { + noself = 1; + if (opt_op || safe) { + codegen(s, cast->receiver, VAL); + } + else { + push(); + } + } + else { + codegen(s, cast->receiver, VAL); + } + } + if (safe) { + int recv = cursp()-1; + gen_move(s, cursp(), recv, 1); + skip = genjmp2_0(s, OP_JMPNIL, cursp(), val); + } + + /* The indices, then the RHS, which is the last of the arguments and is + generated apart from them: a splat among the indices gathers them into + an array, and the RHS has to be held back from it until it has been + copied to the result slot. 13 leaves room for the RHS under + CALL_MAXARGS, which is the mark for arguments gathered in an array + rather than a count of them. */ + CAST3(arguments, cast->arguments, arguments); + int gathered = 0; + if (arguments && 0 < arguments->arguments.size) { + size_t last = arguments->arguments.size - 1; + n = gen_values_upto(s, (mrc_node *)arguments, VAL, 13, last); + if (n < 0) { /* the indices are in an array at cursp() */ + gathered = 1; + push(); + } + codegen(s, (mrc_node *)arguments->arguments.nodes[last], VAL); + if (!gathered) n++; + } + if (val) { + /* nopeep: keep the RHS in its argument slot for the SEND, while also + copying it to the reserved result slot */ + gen_move(s, top, cursp()-1, 1); /* preserve the RHS as the result */ + } + + if (gathered) { + /* the RHS joins the indices in their array, which is the one argument */ + pop(); + pop(); + genop_2(s, OP_ARYPUSH, cursp(), 1); + push(); + n = CALL_MAXARGS; + } + + push(); pop(); + s->sp = callsp; + + if (opt_op == OP_SETIDX && n == 2) { + genop_1(s, OP_SETIDX, cursp()); + } + else if (noself) { + genop_3(s, OP_SSEND, cursp(), new_sym(s, sym), n); + } + else { + genop_3(s, OP_SEND, cursp(), new_sym(s, sym), n); + } + + if (safe) { + dispatch(s, skip); + } + + s->sp = top; + if (val) { + push(); + } +} + +/* Are the call arguments simple enough for gen_call_assign (no splat, + keyword hash, or forwarding that would obscure the RHS position)? */ +static mrc_bool +attr_assign_simple_args(pm_call_node_t *cast) +{ + if (cast->arguments == NULL) return FALSE; + pm_arguments_node_t *arguments = (pm_arguments_node_t *)cast->arguments; + if (arguments->arguments.size == 0) return FALSE; + for (size_t i = 0; i < arguments->arguments.size; i++) { + int t = nint((mrc_node *)arguments->arguments.nodes[i]); + /* A splat is gathered by gen_values_upto(); keywords and forwarding are + not what an index assignment can be written with. */ + if (t == PM_KEYWORD_HASH_NODE || t == PM_FORWARDING_ARGUMENTS_NODE) { + return FALSE; + } + } + return TRUE; +} + +static void +gen_call(mrc_codegen_scope *s, mrc_node *tree, int val, int safe, int recv_ready) +{ + CAST(call); + const mrc_sym sym = cast->name; + + if (val && (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && + attr_assign_simple_args(cast)) { + gen_call_assign(s, tree, val, safe, recv_ready); + return; + } + int skip = 0, n = 0, nk = 0, noop = no_optimize(s), noself = 0, blk = 0; + int sp_save = recv_ready ? cursp()-1 : cursp(); + + if (recv_ready) { + /* the receiver has been evaluated already and sits at cursp()-1 */ + } + else if (cast->receiver == NULL) { + noself = noop = 1; + push(); + } + else if (nint(cast->receiver) == PM_SELF_NODE) { + noself = noop = 1; + /* OP_SSEND fills the receiver register itself; only the nil check of + `self&.m` reads it before then, so it is loaded for that */ + if (safe) { + codegen(s, cast->receiver, VAL); + } + else { + 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) { + genop_1(s, OP_MUL, cursp()); + } + else if (!noop && sym == MRC_OPSYM_2(div) && n == 1) { + genop_1(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())) { + /* a literal absorbed its sign */ + } + else if (!noop && n == 1 && gen_binop(s, sym, cursp())) { + /* an index opcode was emitted */ + } + else if (noself) { + if (!blk && n == 0 && nk == 0) { + genop_2(s, OP_SSEND0, cursp(), new_sym(s, sym)); + } + else { + genop_3(s, blk ? OP_SSENDB : OP_SSEND, cursp(), new_sym(s, sym), n|(nk<<4)); + } + } + else if (!blk && n == 0 && nk == 0) { + genop_2(s, OP_SEND0, cursp(), new_sym(s, sym)); + } + 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(); + } +} + +/* The index OP_AREF reads an element with is the third operand of a BBB + instruction, and OP_EXT1 to OP_EXT3 widen only the first two, so a list of + more than 256 elements cannot name its later ones. Every 255 elements the + array is rebased on the ones not read yet and the index starts over. + Dropping what has already been read leaves the groups that follow anchored + where they were, so a rest and its post targets still choose the same + elements. `scratch` is a register reserved for the whole walk by + gen_aref_scratch(); `base` is what the index counts from now, and what this + returns is what it counts from next. */ +static int +gen_aref_rebase(mrc_codegen_scope *s, int base, int scratch) +{ + if (base != scratch) { + gen_move(s, scratch, base, 0); + } + genop_3(s, OP_APOST, scratch, 255, 0); + return scratch; +} + +/* The register gen_aref_rebase() rebases into, or -1 when `len` elements are + few enough to be read without one. */ +static int +gen_aref_scratch(mrc_codegen_scope *s, int len) +{ + if (len <= 255) return -1; + int scratch = cursp(); + push(); + return scratch; +} + +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; + int has_rest = cast->rest && nint(cast->rest) != PM_IMPLICIT_REST_NODE; + int base = rhs; /* the array the index below counts from */ + int idx = 0; /* how far into `base` the pre targets have come */ + int scratch; + + /* The post count is the third operand of OP_APOST and cannot be rebased + away: whether the post targets are filled from the front or from the back + depends on how long the array turns out to be, and each rebase would + decide that over again for the group it split off. */ + if (255 < post) { + codegen_error(s, "too many post-splat assignment targets"); + } + scratch = gen_aref_scratch(s, n); + if (0 < n) { /* pre */ + for (int i = 0; i < n; i++) { + if (idx == 255) { + base = gen_aref_rebase(s, base, scratch); + idx = 0; + } + int sp = cursp(); + genop_3(s, OP_AREF, sp, base, idx++); + push(); + gen_assignment(s, cast->lefts.nodes[i], NULL, sp, NOVAL); + pop(); + } + } + if (has_rest || 0 < post) { + gen_move(s, cursp(), base, val); + int sp = cursp(); + /* OP_APOST fills sp..sp+post, and the targets keep being read from there + while they are assigned: a call or index target assigns through a send + built from cursp() up, so those registers have to be reserved first */ + push_n(post+1); + genop_3(s, OP_APOST, sp, idx, post); + if (has_rest) { /* rest */ + pm_node_t *rest_expr = ((pm_splat_node_t *)cast->rest)->expression; + if (rest_expr) { + gen_assignment(s, rest_expr, NULL, sp, NOVAL); + } + } + for (int i = 0; i < post; i++) { + gen_assignment(s, cast->rights.nodes[i], NULL, sp+i+1, NOVAL); + } + pop_n(post+1); + if (0 <= scratch) { /* the value is the original array, not a rebased one */ + pop(); + scratch = -1; + } + if (val) { + gen_move(s, cursp(), rhs, 0); + } + } + if (0 <= scratch) { + pop(); + } +} + +/* Fail the match unless `value` answers `===` for the value at `target`: the + test the constant of `Const[...]` and `Const(...)` makes, and the one a pin + pattern makes over what `^` names. */ +static void +gen_pattern_eqq(mrc_codegen_scope *s, mrc_node *value, int target, uint32_t *fail_pos) +{ + codegen(s, value, VAL); + gen_move(s, cursp(), target, 0); + push(); push(); pop(); pop(); pop(); + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(eqq)), 1); + *fail_pos = genjmp2(s, OP_JMPNOT, cursp(), *fail_pos, 1); +} + +/* Fail the match unless the value at `target` answers `mid`. A pattern asks + before it sends, so a subject with no deconstruction hook simply does not + match, the way CRuby has it, rather than raising NoMethodError. */ +static void +gen_pattern_respond_to(mrc_codegen_scope *s, int target, mrc_sym mid, uint32_t *fail_pos, int cache) +{ + int reg = cursp(); + + gen_move(s, reg, target, 0); + push(); /* protect receiver */ + genop_2(s, OP_LOADSYM, cursp(), new_sym(s, mid)); + push(); /* protect the argument */ + push(); pop(); /* touch block slot */ + s->sp = reg; + genop_3(s, OP_SEND, reg, new_sym(s, MRC_SYM_2(respond_to_p)), 1); + if (cache) gen_move(s, cache, reg, 1); + *fail_pos = genjmp2(s, OP_JMPNOT, reg, *fail_pos, 1); +} + +/* Send `deconstruct` to `target` and leave the array in cursp(). `cache` is + the register an enclosing `case/in` keeps for the answer across its + clauses, or 0 for a pattern with none: `nil` until a clause asks, `false` + once the subject has turned out to have no hook, the array otherwise. A + later clause reads it instead of asking again, as CRuby does. */ +static void +gen_pattern_deconstruct(mrc_codegen_scope *s, int target, uint32_t *fail_pos, int cache) +{ + int reg = cursp(); + uint32_t ask = JMPLINK_START, have = JMPLINK_START; + + if (cache) { + ask = genjmp2(s, OP_JMPNIL, cache, JMPLINK_START, 1); + *fail_pos = genjmp2(s, OP_JMPNOT, cache, *fail_pos, 1); + gen_move(s, reg, cache, 1); + have = genjmp(s, OP_JMP, JMPLINK_START); + dispatch(s, ask); + } + gen_pattern_respond_to(s, target, MRC_SYM_1(deconstruct), fail_pos, cache); + gen_move(s, reg, target, 0); + push_n(2); pop_n(2); /* space for receiver and a block */ + genop_3(s, OP_SEND, reg, new_sym(s, MRC_SYM_1(deconstruct)), 0); + if (cache) { + gen_move(s, cache, reg, 1); + dispatch(s, have); + } +} + +/* Whether `pattern`, at the top of an `in` clause, would send `deconstruct` + to the subject: it is an array or find pattern, or holds one under a + guard, a capture or an alternative. */ +static int +pattern_deconstructs(mrc_node *pattern) +{ + for (;;) { + switch (nint(pattern)) { + case PM_IF_NODE: + { + pm_if_node_t *n = (pm_if_node_t *)pattern; + if (!n->statements || n->statements->body.size == 0) return FALSE; + pattern = n->statements->body.nodes[0]; + } + break; + case PM_UNLESS_NODE: + { + pm_unless_node_t *n = (pm_unless_node_t *)pattern; + if (!n->statements || n->statements->body.size == 0) return FALSE; + pattern = n->statements->body.nodes[0]; + } + break; + case PM_CAPTURE_PATTERN_NODE: + pattern = (mrc_node *)((pm_capture_pattern_node_t *)pattern)->value; + break; + case PM_ALTERNATION_PATTERN_NODE: + { + pm_alternation_pattern_node_t *n = (pm_alternation_pattern_node_t *)pattern; + if (pattern_deconstructs((mrc_node *)n->left)) return TRUE; + pattern = (mrc_node *)n->right; + } + break; + case PM_ARRAY_PATTERN_NODE: + case PM_FIND_PATTERN_NODE: + return TRUE; + default: + return FALSE; + } + } +} + +/* Generate pattern matching code for a single pattern. + * target: stack position of the value being matched + * fail_pos: linked list of jump positions for pattern match failure + * known_array_len: -1 if unknown, >= 0 if target is known to be an array of that length + * cache: register of the `case/in` that keeps what `deconstruct` answered, or 0 + */ +/* Put a jump that leaves a pattern on the chain the clause dispatches at its + end. genjmp2() answers JMPLINK_START when its peephole found the jump can + never be taken and emitted none, as it does for the `if true` of a guard; + the chain is then what it already was, and storing that answer as the chain + would drop every jump on it, leaving each one with the zero it was written + with, which is the start of the irep. */ +static void +gen_pattern_fail_jmp(mrc_codegen_scope *s, mrc_code op, uint16_t a, uint32_t *fail_pos, int val) +{ + uint32_t tmp = genjmp2(s, op, a, *fail_pos, val); + + if (tmp != JMPLINK_START) *fail_pos = tmp; +} + +static void +codegen_pattern_1(mrc_codegen_scope *s, mrc_node *pattern, int target, uint32_t *fail_pos, int known_array_len, int cache); + +/* A pattern is walked by codegen_pattern_1() and not by codegen(), so its + nesting is not on the count codegen() keeps against MRC_CODEGEN_LEVEL_MAX, + and source of any depth would recurse there until the C stack ran out. It + goes on the same count here: the two walks are the same resource, and how + deep either may go is a property of the compiler rather than of the machine + it was built for, which is what makes a count portable where a measure of + the stack in bytes is not. The walk below has exits of its own, so the + count is kept here, where there is one. */ +static void +codegen_pattern(mrc_codegen_scope *s, mrc_node *pattern, int target, uint32_t *fail_pos, int known_array_len, int cache) +{ + int rlev = s->rlev; + + s->rlev++; + if (s->rlev > MRC_CODEGEN_LEVEL_MAX) { + s->rlev = rlev; + codegen_error(s, "too complex pattern"); + } + codegen_pattern_1(s, pattern, target, fail_pos, known_array_len, cache); + s->rlev = rlev; +} + +static void +codegen_pattern_1(mrc_codegen_scope *s, mrc_node *pattern, int target, uint32_t *fail_pos, int known_array_len, int cache) +{ + uint32_t tmp; + + /* A pattern reads `target` more than once: it asks whether the value answers + the deconstruction hook before sending it, and a later alternative or a + later `in` clause reads it again. The instruction that produced the value + is therefore not the last use the peephole would take this pattern's first + read for, so label the point before generating any of the pattern. */ + new_label(s); + + /* Handle guard clause wrapper (PM_IF_NODE wrapping the actual pattern) */ + if (nint(pattern) == PM_IF_NODE) { + pm_if_node_t *if_n = (pm_if_node_t *)pattern; + /* In Prism: in pattern if condition + * The IfNode.statements contains the actual pattern + * The IfNode.predicate contains the guard condition */ + if (if_n->statements) { + pm_statements_node_t *stmts = if_n->statements; + if (stmts->body.size > 0) { + /* Extract and match the inner pattern first */ + mrc_node *inner_pattern = stmts->body.nodes[0]; + codegen_pattern(s, inner_pattern, target, fail_pos, known_array_len, cache); + } + } + /* Generate the guard condition */ + if (if_n->predicate) { + codegen(s, (mrc_node *)if_n->predicate, VAL); + pop(); + /* if guard: fail if guard is false */ + gen_pattern_fail_jmp(s, OP_JMPNOT, cursp(), fail_pos, 0); + } + return; + } + + /* Handle unless guard clause (PM_UNLESS_NODE wrapping the actual pattern) */ + if (nint(pattern) == PM_UNLESS_NODE) { + pm_unless_node_t *unless_n = (pm_unless_node_t *)pattern; + /* In Prism: in pattern unless condition + * The UnlessNode.statements contains the actual pattern + * The UnlessNode.predicate contains the guard condition */ + if (unless_n->statements) { + pm_statements_node_t *stmts = unless_n->statements; + if (stmts->body.size > 0) { + /* Extract and match the inner pattern first */ + mrc_node *inner_pattern = stmts->body.nodes[0]; + codegen_pattern(s, inner_pattern, target, fail_pos, known_array_len, cache); + } + } + /* Generate the guard condition */ + if (unless_n->predicate) { + codegen(s, (mrc_node *)unless_n->predicate, VAL); + pop(); + /* unless guard: fail if guard is true (inverted from if) */ + gen_pattern_fail_jmp(s, OP_JMPIF, cursp(), fail_pos, 0); + } + return; + } + + switch (nint(pattern)) { + /* Value patterns: literals */ + case PM_INTEGER_NODE: + case PM_FLOAT_NODE: + case PM_RATIONAL_NODE: + case PM_IMAGINARY_NODE: + case PM_STRING_NODE: + case PM_INTERPOLATED_STRING_NODE: + case PM_X_STRING_NODE: + case PM_SYMBOL_NODE: + case PM_INTERPOLATED_SYMBOL_NODE: + case PM_REGULAR_EXPRESSION_NODE: + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + case PM_RANGE_NODE: + case PM_TRUE_NODE: + case PM_FALSE_NODE: + case PM_NIL_NODE: + case PM_CONSTANT_READ_NODE: + case PM_CONSTANT_PATH_NODE: + { + /* Generate: pattern_value === target */ + codegen(s, pattern, VAL); + gen_move(s, cursp(), target, 0); + push(); push(); pop(); pop(); pop(); + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(eqq)), 1); + /* Jump to fail if not matched */ + tmp = genjmp2(s, OP_JMPNOT, cursp(), *fail_pos, 1); + *fail_pos = tmp; + } + break; + + case PM_LOCAL_VARIABLE_TARGET_NODE: + { + CAST3(local_variable_target, pattern, var_target); + gen_pattern_bind(s, var_target, target); + /* Variable pattern always matches */ + } + break; + + case PM_IMPLICIT_NODE: + { + /* Unwrap implicit node and process inner value */ + pm_implicit_node_t *implicit = (pm_implicit_node_t *)pattern; + codegen_pattern(s, (mrc_node *)implicit->value, target, fail_pos, known_array_len, 0); + } + break; + + case PM_ALTERNATION_PATTERN_NODE: + { + CAST3(alternation_pattern, pattern, pat_alt); + uint32_t left_fail = JMPLINK_START; + uint32_t success_pos = JMPLINK_START; + + /* Try left pattern */ + codegen_pattern(s, (mrc_node *)pat_alt->left, target, &left_fail, known_array_len, cache); + + /* Optimize JMPNOT+JMP to JMPIF when possible. + Only when the left pattern's tail is an OP_JMPNOT (BS format, so the + opcode sits at left_fail-2). Patterns that emit a plain OP_JMP (e.g. + unimplemented patterns falling to the default case) must not be + rewritten, or a neighboring byte would be corrupted. */ + if (nint(pat_alt->left) != PM_ALTERNATION_PATTERN_NODE && + left_fail != JMPLINK_START && left_fail >= 2 && left_fail + 2 == s->pc && + s->iseq[left_fail - 2] == OP_JMPNOT) { + /* Extract the previous link from the JMPNOT chain */ + int16_t prev_offset = (int16_t)PEEK_S(s->iseq + left_fail); + int32_t next_addr = (int32_t)(left_fail + 2) + prev_offset; + uint32_t prev_link = (next_addr == 0) ? JMPLINK_START : (uint32_t)next_addr; + /* Convert JMPNOT to JMPIF */ + s->iseq[left_fail - 2] = OP_JMPIF; + /* Clear offset to mark end of success chain */ + emit_S(s, left_fail, 0); + success_pos = left_fail; + /* Continue with remaining fail chain */ + left_fail = prev_link; + } + else { + /* Left succeeded - jump to success */ + tmp = genjmp(s, OP_JMP, success_pos); + success_pos = tmp; + } + + /* Left failed - try right pattern */ + if (left_fail != JMPLINK_START) { + dispatch_linked(s, left_fail); + } + codegen_pattern(s, (mrc_node *)pat_alt->right, target, fail_pos, known_array_len, cache); + + /* Dispatch success jumps */ + if (success_pos != JMPLINK_START) { + dispatch_linked(s, success_pos); + } + } + break; + + case PM_CAPTURE_PATTERN_NODE: + { + CAST3(capture_pattern, pattern, pat_as); + /* First match the inner pattern */ + codegen_pattern(s, (mrc_node *)pat_as->value, target, fail_pos, known_array_len, cache); + /* Then bind the value to the variable */ + CAST3(local_variable_target, pat_as->target, var_target); + gen_pattern_bind(s, var_target, target); + } + break; + + /* `^x`, `^@x`, `^@@x`, `^$x` and `^(expression)`: what the pin names is + read the way any other expression is read, then asked `===`. Reading it + through codegen is what lets the pin reach a variable of an enclosing + scope, an instance, class or global variable, and an expression. */ + case PM_PINNED_VARIABLE_NODE: + { + CAST3(pinned_variable, pattern, pat_pin); + gen_pattern_eqq(s, (mrc_node *)pat_pin->variable, target, fail_pos); + } + break; + + case PM_PINNED_EXPRESSION_NODE: + { + CAST3(pinned_expression, pattern, pat_pin); + gen_pattern_eqq(s, (mrc_node *)pat_pin->expression, target, fail_pos); + } + break; + + case PM_ARRAY_PATTERN_NODE: + { + CAST3(array_pattern, pattern, pat_arr); + int pre_len = pat_arr->requireds.size; + int post_len = pat_arr->posts.size; + int arr_reg; + int i; + + if (pat_arr->constant) { + gen_pattern_eqq(s, (mrc_node *)pat_arr->constant, target, fail_pos); + } + + /* Optimization: if we know the target is an array, skip deconstruct */ + if (known_array_len >= 0) { + /* Use target directly as array register */ + arr_reg = target; + + /* Compile-time size check */ + if (pat_arr->rest == NULL) { + /* No rest: exact length match required */ + if (known_array_len != pre_len + post_len) { + /* Size mismatch - always fail */ + tmp = genjmp(s, OP_JMP, *fail_pos); + *fail_pos = tmp; + break; + } + /* Size matches, no runtime check needed */ + } + else { + /* Has rest: minimum length check */ + int min_len = pre_len + post_len; + if (known_array_len < min_len) { + /* Size too small - always fail */ + tmp = genjmp(s, OP_JMP, *fail_pos); + *fail_pos = tmp; + break; + } + /* Size sufficient, no runtime check needed */ + } + + /* Match pre-rest elements using AREF */ + { + int base = arr_reg, idx = 0; + int scratch = gen_aref_scratch(s, pre_len); + for (i = 0; i < pre_len; i++) { + if (idx == 255) { + base = gen_aref_rebase(s, base, scratch); + idx = 0; + } + /* Get arr[i] using AREF */ + int sp = cursp(); + genop_3(s, OP_AREF, sp, base, idx++); + push(); + /* Element is now at sp */ + /* Match element pattern (elements are not known arrays) */ + codegen_pattern(s, pat_arr->requireds.nodes[i], sp, fail_pos, -1, 0); + pop(); + } + if (0 <= scratch) { + pop(); + } + } + + /* Bind rest elements if rest is a variable */ + if (pat_arr->rest && nint(pat_arr->rest) == PM_SPLAT_NODE) { + pm_splat_node_t *splat = (pm_splat_node_t *)pat_arr->rest; + if (splat->expression && nint(splat->expression) == PM_LOCAL_VARIABLE_TARGET_NODE) { + pm_local_variable_target_node_t *rest_var = (pm_local_variable_target_node_t *)splat->expression; + /* Generate: arr[pre_len..-(post_len+1)] or arr[pre_len..-1] if no post */ + int sp_save = cursp(); + gen_move(s, cursp(), arr_reg, 0); + push(); + gen_int(s, cursp(), pre_len); + push(); + if (post_len > 0) { + gen_int(s, cursp(), -(post_len + 1)); + } + else { + gen_int(s, cursp(), -1); + } + /* Create inclusive range */ + genop_1(s, OP_RANGE_INC, cursp() - 1); + push(); pop(); /* touch block slot */ + s->sp = sp_save; + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(aref)), 1); + gen_pattern_bind(s, rest_var, cursp()); + } + } + + /* Match post-rest elements using negative indices (GETIDX) */ + for (i = 0; i < post_len; i++) { + gen_move(s, cursp(), arr_reg, 0); + push(); + gen_int(s, cursp(), -(post_len - i)); + push(); pop(); /* space for the index */ + genop_1(s, OP_GETIDX, cursp() - 1); + /* Element is now at cursp-1 */ + codegen_pattern(s, pat_arr->posts.nodes[i], cursp() - 1, fail_pos, -1, 0); + pop(); + } + } + else { + gen_pattern_deconstruct(s, target, fail_pos, cache); + arr_reg = cursp(); + push(); /* protect arr_reg on stack */ + + /* Check if deconstruct returned nil */ + gen_pattern_fail_jmp(s, OP_JMPNIL, arr_reg, fail_pos, 0); + + /* Runtime size check: arr.size() == or >= expected */ + { + int chk = cursp(); + gen_move(s, chk, arr_reg, 0); + push_n(2); pop_n(2); /* space for receiver and a block */ + genop_3(s, OP_SEND, chk, new_sym(s, MRC_SYM_1(size)), 0); + /* R[chk] = size */ + gen_int(s, chk + 1, pre_len + post_len); + if (pat_arr->rest == NULL) { + genop_1(s, OP_EQ, chk); + } + else { + genop_1(s, OP_GE, chk); + } + tmp = genjmp2(s, OP_JMPNOT, chk, *fail_pos, 1); + *fail_pos = tmp; + } + + /* Match pre-rest elements */ + { + int base = arr_reg, idx = 0; + int scratch = gen_aref_scratch(s, pre_len); + for (i = 0; i < pre_len; i++) { + if (idx == 255) { + base = gen_aref_rebase(s, base, scratch); + idx = 0; + } + int sp = cursp(); + genop_3(s, OP_AREF, sp, base, idx++); + push(); + codegen_pattern(s, pat_arr->requireds.nodes[i], sp, fail_pos, -1, 0); + pop(); + } + if (0 <= scratch) { + pop(); + } + } + + /* Bind rest elements if rest is a variable */ + if (pat_arr->rest && nint(pat_arr->rest) == PM_SPLAT_NODE) { + pm_splat_node_t *splat = (pm_splat_node_t *)pat_arr->rest; + if (splat->expression && nint(splat->expression) == PM_LOCAL_VARIABLE_TARGET_NODE) { + pm_local_variable_target_node_t *rest_var = (pm_local_variable_target_node_t *)splat->expression; + int sp_save = cursp(); + gen_move(s, cursp(), arr_reg, 0); + push(); + gen_int(s, cursp(), pre_len); + push(); + if (post_len > 0) { + gen_int(s, cursp(), -(post_len + 1)); + } + else { + gen_int(s, cursp(), -1); + } + genop_1(s, OP_RANGE_INC, cursp() - 1); + push(); pop(); /* touch block slot */ + s->sp = sp_save; + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(aref)), 1); + /* Result at R[sp_save] */ + gen_pattern_bind(s, rest_var, cursp()); + } + } + + /* Match post-rest elements using negative indices (GETIDX) */ + for (i = 0; i < post_len; i++) { + gen_move(s, cursp(), arr_reg, 0); + push(); + gen_int(s, cursp(), -(post_len - i)); + push(); pop(); /* space for the index */ + genop_1(s, OP_GETIDX, cursp() - 1); + codegen_pattern(s, pat_arr->posts.nodes[i], cursp() - 1, fail_pos, -1, 0); + pop(); + } + pop(); /* release arr_reg */ + } + } + break; + + case PM_HASH_PATTERN_NODE: + { + CAST3(hash_pattern, pattern, pat_hash); + int hash_reg; + int num_keys = 0; + + if (pat_hash->constant) { + gen_pattern_eqq(s, (mrc_node *)pat_hash->constant, target, fail_pos); + } + + /* Count regular (non-rest) keys */ + for (size_t i = 0; i < pat_hash->elements.size; i++) { + if (nint(pat_hash->elements.nodes[i]) == PM_ASSOC_NODE) num_keys++; + } + + int has_rest = pat_hash->rest != NULL; + int has_double_nil = has_rest && nint(pat_hash->rest) == PM_NO_KEYWORDS_PARAMETER_NODE; + + /* Call target.deconstruct_keys(keys_array or nil). + * Pass keys_array only when no rest pattern (partial-match optimization). + * Pass nil when any ** is present so deconstruct_keys returns all keys. */ + gen_pattern_respond_to(s, target, MRC_SYM_1(deconstruct_keys), fail_pos, 0); + + hash_reg = cursp(); + gen_move(s, hash_reg, target, 0); + push(); /* protect receiver */ + + if (!has_rest && num_keys > 0) { + int keys_base = cursp(); + for (size_t i = 0; i < pat_hash->elements.size; i++) { + mrc_node *elem = pat_hash->elements.nodes[i]; + if (nint(elem) == PM_ASSOC_NODE) { + pm_assoc_node_t *assoc = (pm_assoc_node_t *)elem; + codegen(s, (mrc_node *)assoc->key, VAL); + } + } + pop_n(num_keys); + genop_2(s, OP_ARRAY, keys_base, num_keys); + push(); /* protect keys arg */ + push(); pop(); /* touch block slot */ + s->sp = hash_reg; + genop_3(s, OP_SEND, hash_reg, new_sym(s, MRC_SYM_1(deconstruct_keys)), 1); + } + else { + genop_1(s, OP_LOADNIL, cursp()); + push(); /* protect nil arg */ + push(); pop(); /* touch block slot */ + s->sp = hash_reg; + genop_3(s, OP_SEND, hash_reg, new_sym(s, MRC_SYM_1(deconstruct_keys)), 1); + } + push(); /* protect hash_reg */ + + /* Check all keys exist and get values via __pat_values. + * __pat_values(keys_array) returns an array of values in key order, + * or false if any key is missing. It runs even for a pattern with no + * keys, since it is also the type check: __pat_values is Hash's, and + * Object's raises the TypeError CRuby raises when #deconstruct_keys + * answers anything but a Hash, a nil included. */ + { + int vals_reg = cursp(); + gen_move(s, vals_reg, hash_reg, 0); + push(); /* protect receiver */ + + int keys_base = cursp(); + for (size_t i = 0; i < pat_hash->elements.size; i++) { + mrc_node *elem = pat_hash->elements.nodes[i]; + if (nint(elem) == PM_ASSOC_NODE) { + pm_assoc_node_t *assoc = (pm_assoc_node_t *)elem; + codegen(s, (mrc_node *)assoc->key, VAL); + } + } + pop_n(num_keys); + genop_2(s, OP_ARRAY, keys_base, num_keys); + push(); /* protect keys arg */ + push(); pop(); /* touch block slot */ + s->sp = vals_reg; + genop_3(s, OP_SEND, vals_reg, new_sym(s, MRC_SYM_1(__pat_values)), 1); + push(); /* protect vals_reg */ + + /* __pat_values returns false when a key is missing */ + tmp = genjmp2(s, OP_JMPNOT, vals_reg, *fail_pos, 1); + *fail_pos = tmp; + + /* Match each value against its sub-pattern using vals_reg[i] */ + int loop_sp = cursp(); + int key_idx = 0; + for (size_t i = 0; i < pat_hash->elements.size; i++) { + mrc_node *elem = pat_hash->elements.nodes[i]; + if (nint(elem) == PM_ASSOC_NODE) { + pm_assoc_node_t *assoc = (pm_assoc_node_t *)elem; + + int val_reg = cursp(); + gen_move(s, val_reg, vals_reg, 0); + push(); /* protect receiver */ + gen_int(s, cursp(), key_idx); + push(); /* protect index arg */ + push(); pop(); /* touch block slot */ + s->sp = val_reg; + genop_3(s, OP_SEND, val_reg, new_sym(s, MRC_OPSYM_2(aref)), 1); + + /* Prism gives the shorthand `{a:}` an implicit local target + as its value, so there is always a sub-pattern to match. */ + push(); /* keep the value below cursp() for the sub-pattern */ + codegen_pattern(s, (mrc_node *)assoc->value, val_reg, fail_pos, -1, 0); + s->sp = loop_sp; + key_idx++; + } + } + pop(); /* release vals_reg */ + } + + /* Handle rest pattern */ + if (has_double_nil || (!has_rest && num_keys == 0)) { + /* **nil or empty {}: exact match - verify hash.size == num_keys */ + int chk = cursp(); + gen_move(s, chk, hash_reg, 0); + push_n(2); pop_n(2); /* space for receiver and a block */ + genop_3(s, OP_SEND, chk, new_sym(s, MRC_SYM_1(size)), 0); + gen_int(s, chk + 1, num_keys); + genop_1(s, OP_EQ, chk); + tmp = genjmp2(s, OP_JMPNOT, chk, *fail_pos, 1); + *fail_pos = tmp; + } + else if (has_rest && !has_double_nil) { + if (nint(pat_hash->rest) == PM_ASSOC_SPLAT_NODE) { + pm_assoc_splat_node_t *splat = (pm_assoc_splat_node_t *)pat_hash->rest; + /* Named **var: capture remaining keys via hash.__except(keys_array) */ + if (splat->value && nint(splat->value) == PM_LOCAL_VARIABLE_TARGET_NODE) { + pm_local_variable_target_node_t *rest_var = (pm_local_variable_target_node_t *)splat->value; + { + int recv = cursp(); + gen_move(s, recv, hash_reg, 0); + push(); /* protect receiver */ + if (num_keys > 0) { + int keys_base = cursp(); + for (size_t i = 0; i < pat_hash->elements.size; i++) { + mrc_node *elem = pat_hash->elements.nodes[i]; + if (nint(elem) == PM_ASSOC_NODE) { + pm_assoc_node_t *assoc = (pm_assoc_node_t *)elem; + codegen(s, (mrc_node *)assoc->key, VAL); + } + } + pop_n(num_keys); + genop_2(s, OP_ARRAY, keys_base, num_keys); + push(); /* protect keys arg */ + push(); pop(); /* touch block slot */ + s->sp = recv; + genop_3(s, OP_SEND, recv, new_sym(s, MRC_SYM_1(__except)), 1); + } + else { + push(); pop(); /* touch block slot */ + s->sp = recv; + genop_3(s, OP_SEND, recv, new_sym(s, MRC_SYM_1(dup)), 0); + } + gen_pattern_bind(s, rest_var, recv); + } + } + /* Anonymous **: do nothing */ + } + } + + pop(); /* release hash_reg */ + } + break; + + case PM_FIND_PATTERN_NODE: + { + /* Find pattern: [*pre, elem1, elem2, ..., *post] + * Searches for elems anywhere in the array. */ + CAST3(find_pattern, pattern, pat_find); + int elems_len = pat_find->requireds.size; + int arr_reg = cursp(); + int idx_reg; + uint32_t loop_start, match_fail, loop_end; + + if (pat_find->constant) { + gen_pattern_eqq(s, (mrc_node *)pat_find->constant, target, fail_pos); + } + + gen_pattern_deconstruct(s, target, fail_pos, cache); + push(); /* protect arr_reg */ + + /* Check if deconstruct returned nil */ + gen_pattern_fail_jmp(s, OP_JMPNIL, arr_reg, fail_pos, 0); + + /* Check minimum length: arr.size >= elems_len */ + gen_move(s, cursp(), arr_reg, 0); + push_n(2); pop_n(2); /* space for receiver and a block, then for the GE operand */ + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_SYM_1(size)), 0); + gen_int(s, cursp() + 1, elems_len); + genop_1(s, OP_GE, cursp()); + tmp = genjmp2(s, OP_JMPNOT, cursp(), *fail_pos, 1); + *fail_pos = tmp; + + /* Initialize index to 0 */ + idx_reg = cursp(); + gen_int(s, idx_reg, 0); + push(); + + /* Loop: try matching at each position */ + loop_start = new_label(s); + match_fail = JMPLINK_START; + + /* Check if idx <= arr.size - elems_len */ + gen_move(s, cursp(), arr_reg, 0); + push_n(2); pop_n(2); /* space for receiver and a block, then for the SUB and GE operands */ + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_SYM_1(size)), 0); + gen_int(s, cursp() + 1, elems_len); + genop_1(s, OP_SUB, cursp()); + gen_move(s, cursp() + 1, idx_reg, 0); + genop_1(s, OP_GE, cursp()); + tmp = genjmp2(s, OP_JMPNOT, cursp(), *fail_pos, 1); + *fail_pos = tmp; + + /* Try to match each middle element at idx+offset */ + for (size_t i = 0; i < pat_find->requireds.size; i++) { + /* Get arr[idx + offset] */ + gen_move(s, cursp(), arr_reg, 0); + push(); + if (i == 0) { + gen_move(s, cursp(), idx_reg, 0); + } + else { + gen_move(s, cursp(), idx_reg, 0); + gen_int(s, cursp() + 1, (int)i); + genop_1(s, OP_ADD, cursp()); + } + push_n(2); pop_n(2); /* space for the index and the ADD operand */ + genop_1(s, OP_GETIDX, cursp() - 1); + int elem_reg = cursp() - 1; + codegen_pattern(s, pat_find->requireds.nodes[i], elem_reg, &match_fail, -1, 0); + pop(); + } + + /* All elements matched - bind pre and post if named */ + if (pat_find->left && nint((mrc_node *)pat_find->left) == PM_SPLAT_NODE) { + pm_splat_node_t *pre_splat = (pm_splat_node_t *)pat_find->left; + if (pre_splat->expression && + nint(pre_splat->expression) == PM_LOCAL_VARIABLE_TARGET_NODE) { + pm_local_variable_target_node_t *pre_var = + (pm_local_variable_target_node_t *)pre_splat->expression; + { + /* pre = arr[0...idx] */ + gen_move(s, cursp(), arr_reg, 0); + push(); + gen_int(s, cursp(), 0); + push(); + gen_move(s, cursp(), idx_reg, 0); + push(); pop(); /* space for the range end, which is also the block slot */ + genop_1(s, OP_RANGE_EXC, cursp() - 1); + pop(); pop(); + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(aref)), 1); + gen_pattern_bind(s, pre_var, cursp()); + } + } + } + + if (pat_find->right && nint((mrc_node *)pat_find->right) == PM_SPLAT_NODE) { + pm_splat_node_t *post_splat = (pm_splat_node_t *)pat_find->right; + if (post_splat->expression && + nint(post_splat->expression) == PM_LOCAL_VARIABLE_TARGET_NODE) { + pm_local_variable_target_node_t *post_var = + (pm_local_variable_target_node_t *)post_splat->expression; + { + /* post = arr[(idx+elems_len)..-1] */ + gen_move(s, cursp(), arr_reg, 0); + push(); + gen_move(s, cursp(), idx_reg, 0); + gen_int(s, cursp() + 1, elems_len); + genop_1(s, OP_ADD, cursp()); + push(); + gen_int(s, cursp(), -1); + push(); pop(); /* space for the range end, which is also the block slot */ + genop_1(s, OP_RANGE_INC, cursp() - 1); + pop(); pop(); + genop_3(s, OP_SEND, cursp(), new_sym(s, MRC_OPSYM_2(aref)), 1); + gen_pattern_bind(s, post_var, cursp()); + } + } + } + + /* Match succeeded - jump to end */ + tmp = genjmp(s, OP_JMP, JMPLINK_START); + loop_end = tmp; + + /* Match failed at this position - try next index */ + if (match_fail != JMPLINK_START) { + dispatch_linked(s, match_fail); + } + + /* idx++ */ + genop_2(s, OP_ADDI, idx_reg, 1); + + /* Jump back to loop start */ + genjmp(s, OP_JMP, loop_start); + + /* Dispatch loop_end jump */ + dispatch(s, loop_end); + + /* Clean up stack */ + pop(); /* idx_reg */ + pop(); /* arr_reg */ + } + break; + + default: + /* Unimplemented pattern type - for now, always fail */ + tmp = genjmp(s, OP_JMP, *fail_pos); + *fail_pos = tmp; + break; + } +} + +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, NULL); + s->for_depth = prev->for_depth + 1; + + 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); + genop_0(s, OP_NOP); /* for redo */ + + /* 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; + case PM_IT_PARAMETERS_NODE: + parameters = NULL; + na = 1; + 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)); + /* Use a non-NULL zero-length pointer: pm_constant_pool_insert feeds it to + memcmp/memcpy, whose nonnull attribute makes a NULL argument undefined + behavior that clang miscompiles (the constant pool then misbehaves and + codegen reports a spurious "Can't find local variables"). */ + pm_constant_id_t null_mark = pm_constant_pool_insert_constant(&s->c->p->constant_pool, (const uint8_t *)"", 0); + + // Create lv regs from Prism's locals + if (parameters == NULL) { + mrc_constant_id_list_init_capacity(s, lv, locals->size + 1); + if (!tree || nint(tree) != PM_NUMBERED_PARAMETERS_NODE) { + /* empty block or `it`: insert null_mark as a placeholder for slot 1 */ + mrc_constant_id_list_append(s, lv, null_mark); + for (i = 0; i < locals->size; i++) { + mrc_constant_id_list_append(s, lv, locals->ids[i]); + } + } + else { + /* Numbered parameters must own the first na slots so the arguments are + stored into them, but Prism lists block locals in order of first + appearance -- `{ tmp = _1 }` yields [tmp, _1]. Lay out _1.._na first + (interning returns the ids the parser already created) and the + remaining locals after them. */ + static const char *const num_names[9] = {"_1","_2","_3","_4","_5","_6","_7","_8","_9"}; + for (int k = 0; k < na; k++) { + pm_constant_id_t nid = pm_constant_pool_insert_constant(&s->c->p->constant_pool, + (const uint8_t*)num_names[k], 2); + mrc_constant_id_list_append(s, lv, nid); + } + for (i = 0; i < locals->size; i++) { + int k; + for (k = 0; k < na; k++) { + if (lv->ids[k] == locals->ids[i]) break; + } + if (k == na) { + 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 (nint(parameters->rest) == PM_REST_PARAMETER_NODE && + ((pm_rest_parameter_node_t *)parameters->rest)->name) { + mrc_constant_id_list_append(s, lv, ((pm_rest_parameter_node_t *)parameters->rest)->name); + } else { + /* anonymous rest (*) or implicit rest from a trailing comma (|a,|) */ + 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; + } else { + pm_constant_id_t dastr = MRC_OPSYM_2(pow); + mrc_constant_id_list_append(s, lv, dastr); + 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_sym = MRC_OPSYM_2(and); + mrc_constant_id_list_append(s, lv, and_sym); + 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_sym = MRC_OPSYM_2(and); + mrc_constant_id_list_append(s, lv, and_sym); + } + 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) { + /* `for_depth` compensates for the loop body scope that `for` needs but + Prism does not have, so a Prism depth of 0 there means the enclosing + scope rather than the loop body. A block is a scope Prism knows about, + so depth 0 means its own locals again and the compensation must not + carry into it: `gen_lvar()` reads depth only as local-or-upvar, and an + inherited depth turned every own local into an upvar that the enclosing + scopes do not hold ("Can't find local variables", #7012). */ + s->for_depth = 0; + 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; + s->aspec = MRC_ARGS_REQ(na); + } + 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); + s->aspec = a; + /* 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 { + mrc_assert(0 && "should not happen: keyword parameter must be local variable"); + } + } + 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 { + mrc_assert(0 && "should not happen: keyword parameter must be local variable"); + } + 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); + } + 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); + } +} + +/* `$&`, `` $` ``, `$'`, `$+` and `$1` onward are not globals of their own but + readings of `$~`, the way CRuby's `getspecial` derives each from the + backref when it is read, so they compile to a read of `$~` and, where it is + not nil, a send on it: `__group` with `n` for `$&` (0) and `$n`, + `__pre_match` for `` $` ``, `__post_match` for `$'` and `__last_group` + for `$+`. All five are private readings of the match rather than the + methods that read the same things in Ruby (`[]`, `pre_match` and + `post_match`), so that redefining those moves `$~[n]`, `$~.pre_match` + and `$~.post_match` and leaves these names alone, the way CRuby's + `rb_reg_nth_match` and the like are past reach. Where `$~` is nil, + which it is without mruby-regexp, the name reads as nil, as an unset + global does. A negative `n` is no argument. */ +static void +gen_match_ref(mrc_codegen_scope *s, mrc_sym meth, mrc_int n) +{ + uint32_t skip; + + genop_2(s, OP_GETGV, cursp(), new_sym(s, MRC_SYM_2(last_match))); + skip = genjmp2_0(s, OP_JMPNIL, cursp(), VAL); + push(); /* $~ is the receiver */ + if (n >= 0) { + gen_int(s, cursp(), n); + push(); + } + push(); pop(); /* space for a block */ + pop_n(n >= 0 ? 2 : 1); + if (n >= 0) { + genop_3(s, OP_SEND, cursp(), new_sym(s, meth), 1); + } + else { + genop_2(s, OP_SEND0, cursp(), new_sym(s, meth)); + } + dispatch(s, skip); + push(); +} + +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, int errsave, int landing) +{ + 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(); + /* This clause is the one that runs, so `$!` names its exception from here. + Set after the class match rather than at OP_EXCEPT, so that a clause that + does not match leaves the name alone. */ + genop_2(s, OP_SETGV, *exc, new_sym(s, MRC_SYM_2(errinfo))); + /* 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(); + /* Leaving the clause normally puts back what `$!` held before the begin, + so that the name does not outlive the clause that set it. */ + genop_2(s, OP_SETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + /* The saved slot is read by the ensure below as well, so the value cannot + land on it; it lands on the register the begin would have used without a + saved slot, which is where a body that raised nothing leaves its own. */ + if (val) gen_move(s, landing, cursp(), 0); + 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, errsave, landing); + } +} + +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, errsave, body_catch; + uint32_t skip, restored, body_begin, body_end; + push(); + ensure_end = ensure_target = s->pc; + push(); + idx = cursp(); + genop_1(s, OP_EXCEPT, idx); + push(); + /* An exception unwinding through the ensure is what `$!` names while it + runs, and what `$!` held before is put back on the way out. A normal + entry and a `break` or `return` passing through leave the name alone: + the register then holds `nil` or a break, neither an exception. */ + errsave = cursp(); + genop_2(s, OP_GETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + push(); + /* `::Exception` rather than the lexical name, which a library that keeps + an `Exception` of its own would shadow. */ + genop_1(s, OP_OCLASS, cursp()); + genop_2(s, OP_GETMCNST, cursp(), new_sym(s, MRC_SYM_1(Exception))); + push(); + pop(); + genop_2(s, OP_RESCUE, idx, cursp()); + skip = genjmp2_0(s, OP_JMPNOT, cursp(), NOVAL); + genop_2(s, OP_SETGV, idx, new_sym(s, MRC_SYM_2(errinfo))); + dispatch(s, skip); + body_catch = catch_handler_new(s); + body_begin = s->pc; + codegen(s, (mrc_node *)ensure->statements, NOVAL); + genop_2(s, OP_SETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + restored = genjmp_0(s, OP_JMP); + /* A body left by `return`, `break` or a raise of its own passes the + restore above by, so the restore is also an ensure over the body. */ + body_end = s->pc; + genop_1(s, OP_EXCEPT, cursp()); + genop_2(s, OP_SETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + genop_1(s, OP_RAISEIF, cursp()); + catch_handler_set(s, body_catch, MRC_CATCH_ENSURE, body_begin, body_end, body_end); + dispatch(s, restored); + pop(); + pop(); + genop_1(s, OP_RAISEIF, idx); + pop(); + catch_handler_set(s, catch_entry, MRC_CATCH_ENSURE, begin, ensure_end, ensure_target); +} + +/* Load the integer described by a pm_integer_t into the current stack slot, + handling the small (uint32), 64-bit, and bignum-literal cases. Shared by + PM_INTEGER_NODE and the rational literal codegen. */ +static void +gen_pm_integer(mrc_codegen_scope *s, const pm_integer_t *iv) +{ + mrc_uint value; + mrc_bool fits = FALSE; + + if (iv->length == 0) { + /* iv->value is a uint32_t, which is wider than mrc_int under MRC_INT32, + so the magnitude still has to be range-checked below. */ + value = iv->value; + fits = TRUE; + } +#ifdef MRC_INT64 + else if (iv->length == 2) { + value = ((mrc_uint)iv->values[0])|((mrc_uint)iv->values[1] << 32); + fits = TRUE; + } +#endif + if (fits) { + if (!iv->negative && MRC_INT_MAX < value) fits = FALSE; + if (iv->negative) { + if (value > (mrc_uint)MRC_INT_MIN) fits = FALSE; + else value *= -1; + } + } + if (fits) { + gen_int(s, cursp(), (mrc_int)value); + return; + } + { + pm_buffer_t buf = {0}; + pm_integer_string(&buf, iv); + /* pm_integer_string writes exactly buf.length bytes and reserves no room + for a terminator; append the NUL through the buffer API so it grows the + allocation instead of writing one byte past it. */ + pm_buffer_append_byte(&buf, '\0'); + const char *digits = buf.value; + if (iv->negative) { + digits++; /* skip the leading '-'; new_litbint takes the sign separately */ + } + int off = new_litbint(s, digits, 10, iv->negative); + genop_2(s, OP_LOADL, cursp(), off); + pm_buffer_free(&buf); + } +} + +/* The answer `defined?` gives for one operand, as far as the operand's node + type decides it: a literal string, or a runtime helper to ask, or neither, + which is the nil answer. */ +struct defined_answer { + const char *type; /* literal answer, NULL for none */ + int helper; /* helper symbol, 0 for none */ + pm_constant_id_t arg; /* symbol operand, 0 for none */ + pm_constant_id_t path[DEFINED_PATH_MAX]; /* a constant path, root first */ + int path_len; /* names in `path`, 0 for none */ + mrc_bool path_toplevel; /* the path is rooted at Object */ + mrc_node *recv; /* recv.meth, NULL for none */ + const char *unless_nil; /* answer where the operand reads + other than nil, NULL for none */ +}; + +/* Whether a body holds no statement at all: `()` and `begin; end` are the + nil they evaluate to, not an expression. */ +static mrc_bool +defined_body_empty_p(mrc_node *body) +{ + if (body == NULL) return TRUE; + return nint(body) == PM_STATEMENTS_NODE && + ((pm_statements_node_t *)body)->body.size == 0; +} + +/* Whether a `begin` carries no rescue, else or ensure clause, and so is + only its body. */ +static mrc_bool +defined_bare_begin_p(mrc_node *value) +{ + pm_begin_node_t *b = (pm_begin_node_t *)value; + return b->rescue_clause == NULL && b->else_clause == NULL && + b->ensure_clause == NULL; +} + +/* Parentheses around a single expression are transparent here, so + `defined?((x))` answers what `defined?(x)` does, and so is a bare `begin` + around one; either holding no statement or several stays, and answers for + itself. The value a pair leaves implicit, as in `{x:}`, is the `x` it + stands for. */ +static mrc_node * +defined_operand(mrc_node *value) +{ + for (;;) { + mrc_node *body; + + if (nint(value) == PM_IMPLICIT_NODE) { + value = (mrc_node *)((pm_implicit_node_t *)value)->value; + continue; + } + else if (nint(value) == PM_PARENTHESES_NODE) { + body = (mrc_node *)((pm_parentheses_node_t *)value)->body; + } + else if (nint(value) == PM_BEGIN_NODE && defined_bare_begin_p(value)) { + body = (mrc_node *)((pm_begin_node_t *)value)->statements; + } + else { + break; + } + if (body == NULL || nint(body) != PM_STATEMENTS_NODE) break; + pm_node_list_t *stmts = &((pm_statements_node_t *)body)->body; + if (stmts->size != 1) break; + value = (mrc_node *)stmts->nodes[0]; + } + return value; +} + +static void +defined_answer_for(mrc_node *value, struct defined_answer *a) +{ + memset(a, 0, sizeof(*a)); + switch (nint(value)) { + case PM_INTEGER_NODE: case PM_FLOAT_NODE: + case PM_RATIONAL_NODE: case PM_IMAGINARY_NODE: + case PM_STRING_NODE: case PM_INTERPOLATED_STRING_NODE: + case PM_X_STRING_NODE: case PM_INTERPOLATED_X_STRING_NODE: + case PM_SYMBOL_NODE: case PM_INTERPOLATED_SYMBOL_NODE: + case PM_REGULAR_EXPRESSION_NODE: case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + case PM_ARRAY_NODE: case PM_HASH_NODE: case PM_KEYWORD_HASH_NODE: + case PM_RANGE_NODE: case PM_LAMBDA_NODE: case PM_DEFINED_NODE: + case PM_SOURCE_FILE_NODE: case PM_SOURCE_LINE_NODE: case PM_SOURCE_ENCODING_NODE: + /* control flow, jumps and definitions: CRuby answers "expression" for + every one of these without looking inside them */ + case PM_AND_NODE: case PM_OR_NODE: + case PM_IF_NODE: case PM_UNLESS_NODE: + case PM_CASE_NODE: case PM_CASE_MATCH_NODE: + case PM_WHILE_NODE: case PM_UNTIL_NODE: case PM_FOR_NODE: + case PM_RETURN_NODE: case PM_BREAK_NODE: case PM_NEXT_NODE: + case PM_REDO_NODE: case PM_RETRY_NODE: + case PM_DEF_NODE: case PM_CLASS_NODE: case PM_MODULE_NODE: + case PM_SINGLETON_CLASS_NODE: + case PM_MATCH_PREDICATE_NODE: case PM_MATCH_REQUIRED_NODE: + case PM_RESCUE_MODIFIER_NODE: case PM_MATCH_WRITE_NODE: + case PM_ALIAS_METHOD_NODE: case PM_UNDEF_NODE: case PM_POST_EXECUTION_NODE: + a->type = "expression"; + break; + /* the literals CRuby names rather than calling expressions */ + case PM_NIL_NODE: + a->type = "nil"; + break; + case PM_TRUE_NODE: + a->type = "true"; + break; + case PM_FALSE_NODE: + a->type = "false"; + break; + case PM_PARENTHESES_NODE: + /* `()` is the nil it evaluates to; parentheses holding several + statements are an expression of their own */ + a->type = defined_body_empty_p((mrc_node *)((pm_parentheses_node_t *)value)->body) + ? "nil" : "expression"; + break; + case PM_BEGIN_NODE: + /* likewise for a bare `begin`; one with a rescue, else or ensure + clause is an expression whatever it holds */ + a->type = (defined_bare_begin_p(value) && + defined_body_empty_p((mrc_node *)((pm_begin_node_t *)value)->statements)) + ? "nil" : "expression"; + break; + case PM_SELF_NODE: + a->type = "self"; + break; + case PM_LOCAL_VARIABLE_READ_NODE: case PM_IT_LOCAL_VARIABLE_READ_NODE: + a->type = "local-variable"; + break; + case PM_LOCAL_VARIABLE_WRITE_NODE: case PM_INSTANCE_VARIABLE_WRITE_NODE: + case PM_GLOBAL_VARIABLE_WRITE_NODE: case PM_CLASS_VARIABLE_WRITE_NODE: + case PM_CONSTANT_WRITE_NODE: case PM_CONSTANT_PATH_WRITE_NODE: + case PM_MULTI_WRITE_NODE: + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: case PM_LOCAL_VARIABLE_AND_WRITE_NODE: + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: + case PM_CLASS_VARIABLE_OR_WRITE_NODE: case PM_CLASS_VARIABLE_AND_WRITE_NODE: + case PM_CONSTANT_OPERATOR_WRITE_NODE: + case PM_CONSTANT_OR_WRITE_NODE: case PM_CONSTANT_AND_WRITE_NODE: + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: + case PM_CONSTANT_PATH_OR_WRITE_NODE: case PM_CONSTANT_PATH_AND_WRITE_NODE: + case PM_INDEX_OPERATOR_WRITE_NODE: + case PM_INDEX_OR_WRITE_NODE: case PM_INDEX_AND_WRITE_NODE: + case PM_CALL_OPERATOR_WRITE_NODE: + case PM_CALL_OR_WRITE_NODE: case PM_CALL_AND_WRITE_NODE: + a->type = "assignment"; + break; + case PM_INSTANCE_VARIABLE_READ_NODE: + a->helper = MRC_SYM_2(defined_ivar_q); + a->arg = ((pm_instance_variable_read_node_t *)value)->name; + break; + case PM_CONSTANT_READ_NODE: + a->helper = MRC_SYM_2(defined_const_q); + a->arg = ((pm_constant_read_node_t *)value)->name; + break; + case PM_CONSTANT_PATH_NODE: + /* Walk the path to its root, collecting the names leaf first. The + root is either a plain constant, so the first name resolves in the + lexical scope, or nothing at all, so `::A` starts at Object, or any + other expression, which is evaluated the way a receiver is and has + the names looked up from its value. */ + { + mrc_node *seg = value; + pm_constant_id_t names[DEFINED_PATH_MAX]; + int n = 0; + mrc_bool rooted = FALSE; + + while (nint(seg) == PM_CONSTANT_PATH_NODE && n < DEFINED_PATH_MAX) { + names[n++] = ((pm_constant_path_node_t *)seg)->name; + seg = (mrc_node *)((pm_constant_path_node_t *)seg)->parent; + if (seg == NULL) { /* ::A, rooted at Object */ + a->path_toplevel = TRUE; + rooted = TRUE; + break; + } + } + if (!rooted && seg != NULL && nint(seg) == PM_CONSTANT_READ_NODE && + n < DEFINED_PATH_MAX) { + names[n++] = ((pm_constant_read_node_t *)seg)->name; + rooted = TRUE; + } + else if (!rooted && seg != NULL && nint(seg) != PM_CONSTANT_PATH_NODE && + nint(seg) != PM_CONSTANT_READ_NODE) { + a->recv = seg; + rooted = TRUE; + } + if (rooted) { + a->helper = MRC_SYM_2(defined_const_path_q); + a->path_len = n; + for (int i = 0; i < n; i++) a->path[i] = names[n - 1 - i]; + } + } + break; + case PM_GLOBAL_VARIABLE_READ_NODE: + a->helper = MRC_SYM_2(defined_gvar_q); + a->arg = ((pm_global_variable_read_node_t *)value)->name; + break; + /* `$&`, `` $` ``, `$'`, `$+` and `$1` onward are readings of `$~`, not + globals of their own, and are defined where they read other than nil, + the way CRuby decides them by what `getspecial` yields */ + case PM_BACK_REFERENCE_READ_NODE: case PM_NUMBERED_REFERENCE_READ_NODE: + a->unless_nil = "global-variable"; + break; + case PM_CLASS_VARIABLE_READ_NODE: + a->helper = MRC_SYM_2(defined_cvar_q); + a->arg = ((pm_class_variable_read_node_t *)value)->name; + break; + case PM_YIELD_NODE: + a->helper = MRC_SYM_2(defined_yield_q); + break; + case PM_SUPER_NODE: case PM_FORWARDING_SUPER_NODE: + a->helper = MRC_SYM_2(defined_super_q); + break; + case PM_CALL_NODE: + { + pm_call_node_t *call = (pm_call_node_t *)value; + /* CRuby answers "expression" for a call carrying a literal block, + without asking whether the method is there; a block passed as + `&arg` keeps the call an ordinary one */ + if (call->block != NULL && nint(call->block) == PM_BLOCK_NODE) { + a->type = "expression"; + } + /* a bare method call on self, which has no operand to evaluate */ + else if (call->receiver == NULL) { + a->helper = MRC_SYM_2(defined_method_q); + a->arg = call->name; + } + /* a call through a receiver: the receiver has to be defined, and then + evaluated, before the method can be looked for on it */ + else { + a->helper = MRC_SYM_2(defined_method_on_q); + a->arg = call->name; + a->recv = (mrc_node *)call->receiver; + } + break; + } + default: + break; + } +} + +static void codegen_defined(mrc_codegen_scope *s, mrc_node *value, int val); + +/* Leave the names of a constant path, root first, as an array at cursp(). */ +static void +gen_defined_path(mrc_codegen_scope *s, struct defined_answer *a) +{ + for (int i = 0; i < a->path_len; i++) { + genop_2(s, OP_LOADSYM, cursp(), new_sym(s, a->path[i])); + push(); + } + pop_n(a->path_len); + genop_2(s, OP_ARRAY, cursp(), a->path_len); + push(); +} + +/* Leave an answer at cursp() as a frozen string, which is what CRuby + answers with. A literal is a fresh string each time, so it is frozen by + the same send `freeze` compiles to; an answer a helper gives comes back + frozen already. */ +static void +gen_defined_literal(mrc_codegen_scope *s, const char *answer) +{ + genop_2(s, OP_STRING, cursp(), new_lit_cstr(s, answer)); + push(); /* the string is the receiver */ + push(); pop(); /* space for a block */ + pop(); + genop_2(s, OP_SEND0, cursp(), new_sym(s, MRC_SYM_1(freeze))); + push(); +} + +/* Emit the answer an operand's node type alone decides: a literal string, or + the helper call that resolves it at run time. */ +static void +gen_defined_answer(mrc_codegen_scope *s, struct defined_answer *a) +{ + if (a->type) { + gen_defined_literal(s, a->type); + return; + } + genop_1(s, OP_LOADSELF, cursp()); /* receiver slot for the SSEND */ + if (a->path_len > 0) { /* A::B::C: where to start, then the names */ + push(); + if (a->path_toplevel) genop_1(s, OP_OCLASS, cursp()); + else genop_1(s, OP_LOADNIL, cursp()); + push(); + gen_defined_path(s, a); + push(); /* reserve the block slot (nregs) */ + pop_n(4); + genop_3(s, OP_SSEND, cursp(), new_sym(s, a->helper), 2); + } + else if (a->arg == 0) { /* yield/super: no symbol operand */ + push(); push(); /* reserve arg + block slots (nregs) */ + pop_n(2); + genop_2(s, OP_SSEND0, cursp(), new_sym(s, a->helper)); + } + else { + push(); + genop_2(s, OP_LOADSYM, cursp(), new_sym(s, a->arg)); + push(); push(); /* reserve value + block slots (nregs) */ + pop_n(3); + genop_3(s, OP_SSEND, cursp(), new_sym(s, a->helper), 1); + } + push(); +} + +static void gen_defined_parts(mrc_codegen_scope *s, mrc_node *value, uint32_t *nil_jmps); +static void gen_defined_part(mrc_codegen_scope *s, mrc_node *part, uint32_t *nil_jmps); + +/* Ask `__defined_method_on?` about the receiver evaluated at cursp()-1, or + `__defined_const_path?` about the path rooted there. With `keep` the + receiver stays where it is and the answer lands above it, for a link of a + chain whose value the next link is called on; without it the answer lands + in the receiver's slot. */ +static void +gen_defined_ask_method_on(mrc_codegen_scope *s, struct defined_answer *a, int keep) +{ + int recv = cursp() - 1; + + if (keep) { + genop_1(s, OP_LOADSELF, cursp()); /* receiver slot for the SSEND */ + push(); + gen_move(s, cursp(), recv, 1); + } + else { + gen_move(s, cursp(), recv, 1); + genop_1(s, OP_LOADSELF, recv); /* receiver slot for the SSEND */ + } + push(); + if (a->path_len > 0) { + gen_defined_path(s, a); + } + else { + genop_2(s, OP_LOADSYM, cursp(), new_sym(s, a->arg)); + push(); + } + push(); /* reserve the block slot (nregs) */ + pop_n(4); + genop_3(s, OP_SSEND, cursp(), new_sym(s, a->helper), 2); + push(); +} + +/* Leave a receiver's value at cursp()-1, or jump to the nil answer. A + receiver that is itself a call through a receiver, or a constant path + from an evaluated root, is a chain, checked link by link from the inside + out, and each link is evaluated once: the value its method or constant + was looked for on is the one it is then read from, the way CRuby keeps + the result of its `defined` instruction. A link's arguments are weighed + before its receiver, as an operand's are. */ +static void +gen_defined_recv(mrc_codegen_scope *s, mrc_node *value, uint32_t *nil_jmps) +{ + struct defined_answer a; + int rlev = s->rlev; + + value = defined_operand(value); + defined_answer_for(value, &a); + if (a.recv == NULL) { + gen_defined_part(s, value, nil_jmps); + codegen(s, value, VAL); + return; + } + s->rlev++; + if (s->rlev > MRC_CODEGEN_LEVEL_MAX) { + codegen_error(s, "too complex expression"); + } + gen_defined_parts(s, value, nil_jmps); + gen_defined_recv(s, a.recv, nil_jmps); + gen_defined_ask_method_on(s, &a, 1); + pop(); + *nil_jmps = genjmp2(s, OP_JMPNOT, cursp(), *nil_jmps, NOVAL); + if (a.path_len > 0) { + for (int i = 0; i < a.path_len; i++) { + genop_2(s, OP_GETMCNST, cursp() - 1, new_sym(s, a.path[i])); + } + } + else { + gen_call(s, value, VAL, + (((pm_call_node_t *)value)->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) ? 1 : 0, + 1); + } + s->rlev = rlev; +} + +/* `defined?(recv.meth)`, and `defined?(expr::NAME)` the same way. The + receiver must itself be defined, and must then be evaluated before the + method can be looked for on it. That evaluation is the one place + `defined?` runs code the operand names, and CRuby answers nil rather than + letting what it raises out, so it sits under a catch handler whose landing + discards the exception. Both ways of not answering join the caller's nil + exit. */ +static void +gen_defined_method_on(mrc_codegen_scope *s, struct defined_answer *a, + uint32_t *nil_jmps) +{ + int sp = cursp(); + int catch_entry = catch_handler_new(s); + uint32_t begin = s->pc, end, ok; + + gen_defined_recv(s, a->recv, nil_jmps); + gen_defined_ask_method_on(s, a, 0); + end = s->pc; + ok = genjmp_0(s, OP_JMP); + catch_handler_set(s, catch_entry, MRC_CATCH_RESCUE, begin, end, s->pc); + genop_1(s, OP_EXCEPT, sp); /* discarded: what it raises is nil */ + *nil_jmps = genjmp(s, OP_JMP, *nil_jmps); + dispatch(s, ok); +} + +/* The parts of an operand whose own answers `defined?` weighs: a call's + arguments, and the elements of an array or a hash literal. The ends of a + range are not among them, and neither are the arguments of a call that + already answers "expression" for carrying a block. */ +static pm_node_list_t * +defined_parts_of(mrc_node *value) +{ + pm_node_list_t *list = NULL; + + switch (nint(value)) { + case PM_ARRAY_NODE: + list = &((pm_array_node_t *)value)->elements; + break; + case PM_HASH_NODE: + list = &((pm_hash_node_t *)value)->elements; + break; + case PM_KEYWORD_HASH_NODE: + list = &((pm_keyword_hash_node_t *)value)->elements; + break; + case PM_CALL_NODE: + { + pm_call_node_t *call = (pm_call_node_t *)value; + if (call->block != NULL && nint(call->block) == PM_BLOCK_NODE) break; + if (call->arguments) list = &call->arguments->arguments; + break; + } + default: + break; + } + return (list != NULL && list->size > 0) ? list : NULL; +} + +/* Weigh the parts of an operand, jumping to the nil answer where one of them + is not defined. CRuby weighs them before the receiver, so an argument that + is missing answers nil with the receiver left unevaluated. */ +static void +gen_defined_parts(mrc_codegen_scope *s, mrc_node *value, uint32_t *nil_jmps) +{ + pm_node_list_t *list = defined_parts_of(value); + int rlev = s->rlev; + + if (list == NULL) return; + s->rlev++; + if (s->rlev > MRC_CODEGEN_LEVEL_MAX) { + codegen_error(s, "too complex expression"); + } + for (size_t i = 0; i < list->size; i++) { + gen_defined_part(s, (mrc_node *)list->nodes[i], nil_jmps); + } + s->rlev = rlev; +} + +/* One part of an operand. A splat or a pair is weighed by what it holds, + and an anonymous `*` or `**` holds nothing; a block argument and forwarded + arguments are what CRuby does not look into. A part whose own node type + settles a non-nil answer emits no check, since the branch could not be + taken, and its parts, if it has any, are weighed in its place. */ +static void +gen_defined_part(mrc_codegen_scope *s, mrc_node *part, uint32_t *nil_jmps) +{ + struct defined_answer a; + + switch (nint(part)) { + case PM_BLOCK_ARGUMENT_NODE: case PM_FORWARDING_ARGUMENTS_NODE: + return; + case PM_SPLAT_NODE: + part = (mrc_node *)((pm_splat_node_t *)part)->expression; + break; + case PM_ASSOC_SPLAT_NODE: + part = (mrc_node *)((pm_assoc_splat_node_t *)part)->value; + break; + case PM_ASSOC_NODE: + gen_defined_part(s, (mrc_node *)((pm_assoc_node_t *)part)->key, nil_jmps); + part = (mrc_node *)((pm_assoc_node_t *)part)->value; + break; + default: + break; + } + if (part == NULL) return; + part = defined_operand(part); + defined_answer_for(part, &a); + if (a.type != NULL) { + gen_defined_parts(s, part, nil_jmps); + return; + } + codegen_defined(s, part, VAL); + pop(); + *nil_jmps = genjmp2(s, OP_JMPNOT, cursp(), *nil_jmps, NOVAL); +} + +/* `defined?` must not evaluate its operand, with the one exception a receiver + makes above. Cases decidable from the operand's node type alone yield a + literal string; ivar/const/method/yield existence is resolved at run time + by a private helper (the helper reads the caller's frame for const lexical + scope and for the block). */ +static void +codegen_defined(mrc_codegen_scope *s, mrc_node *value, int val) +{ + struct defined_answer a; + uint32_t nil_jmps = JMPLINK_START; + int rlev = s->rlev; + + value = defined_operand(value); + defined_answer_for(value, &a); + if (!val) return; + s->rlev++; + if (s->rlev > MRC_CODEGEN_LEVEL_MAX) { + codegen_error(s, "too complex expression"); + } + if (a.type || a.helper) { + gen_defined_parts(s, value, &nil_jmps); + if (a.recv) gen_defined_method_on(s, &a, &nil_jmps); + else gen_defined_answer(s, &a); + } + else if (a.unless_nil) { + codegen(s, value, VAL); + pop(); + nil_jmps = genjmp2(s, OP_JMPNIL, cursp(), nil_jmps, NOVAL); + gen_defined_literal(s, a.unless_nil); + } + else { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + if (nil_jmps != JMPLINK_START) { + uint32_t done = genjmp_0(s, OP_JMP); + dispatch_linked(s, nil_jmps); + pop(); + genop_1(s, OP_LOADNIL, cursp()); + push(); + dispatch(s, done); + } + s->rlev = rlev; +} + +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) { + 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; + s->filename_index++; + s->filename = (const char *)s->c->filename_table[s->filename_index].filename; + } + } + + 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 + s->for_depth); + } + break; + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: + { + if (val) { + gen_move(s, cursp(), 1, 1); /* `it` is always at slot 1 */ + push(); + } + 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 { + int sp = cursp(); + genop_1(s, OP_LOADNIL, sp); + push(); + gen_assignment(s, cast->lefts.nodes[i], NULL, sp, NOVAL); + pop(); + } + } + } + post = cast->rights.size; + if (cast->rest && nint(cast->rest) != PM_IMPLICIT_REST_NODE) { + 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); + } + if (((pm_splat_node_t *)cast->rest)->expression) { + int sp = cursp(); + /* the array is above the values but not reserved; a target that is + a call or an index assigns through a send whose receiver is built + at cursp() and would overwrite it */ + push(); + gen_assignment(s, ((pm_splat_node_t *)cast->rest)->expression, NULL, sp, NOVAL); + pop(); + } + n += rn; + } + else if (cast->rest && nint(cast->rest) == PM_IMPLICIT_REST_NODE) { + /* trailing comma: just consume the rest without assignment */ + int rn; + if (len < post + n) { + rn = 0; + } + else { + rn = len - post - n; + } + 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); + n++; + } + else { + int sp = cursp(); + genop_1(s, OP_LOADNIL, sp); + push(); + gen_assignment(s, cast->rights.nodes[i], NULL, sp, NOVAL); + pop(); + 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 + s->for_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; \ + safe = (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) ? 1 : 0; + mrc_node *receiver = NULL, *value = NULL; + mrc_sym read_name = -1, write_name = -1, binary_operator = -1, op_jmp = -1; + uint32_t pos = -1; + int safe = 0, skip = 0; + 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; + /* a written `self` is a call on self for both the read and the + write, so a private accessor is reachable as in CRuby; the + receiver is still loaded for the `&.` nil check and the copy */ + int op_send = (nint(receiver) == PM_SELF_NODE) ? OP_SSEND : OP_SEND; + if (val) { + vsp = cursp(); + push(); + } + codegen(s, receiver, VAL); + if (safe) { + /* nil&.x op= v short-circuits to nil without the read/write */ + int recv = cursp()-1; + gen_move(s, cursp(), recv, 1); + skip = genjmp2_0(s, OP_JMPNIL, cursp(), 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) { + /* nopeep: a real MOVE, so peephole cannot hoist a loaded RHS out of + the argument register (cursp()) that the write SEND reads below */ + gen_move(s, vsp, cursp(), TRUE); + } + pop(); + idx = new_sym(s, write_name); + genop_3(s, op_send, cursp(), idx, 1); + if (0 < pos) { dispatch(s, pos); } + if (safe) { dispatch(s, skip); } + 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; + /* a written `self` is a call on self for both the read and the + write, so a private `[]` or `[]=` is reachable as in CRuby; the + receiver is still loaded for the copy */ + int op_send = (nint(receiver) == PM_SELF_NODE) ? OP_SSEND : OP_SEND; + 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 + s->for_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_LOADFALSE, 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() only moves, so the result has to be pushed + here the way the other branches do below. Without it the + expression yields nothing and every later register is off by + one, which shows up as `bidx < irep->nregs` in the VM. */ + gen_assignment_lvar(s, cursp(), name, depth, val); + if (val) push(); + 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); + if (val) push(); + break; + default: codegen_error(s, "Not implemented (or_write)"); + } + dispatch(s, pos); + break; + } + case PM_INTEGER_NODE: + { + if (val) { + CAST(integer); + gen_pm_integer(s, &cast->value); + push(); + } + break; + } + case PM_RATIONAL_NODE: + { + /* `Nr` literal -> Rational(numerator, denominator). Prism normalizes + even float forms (1.5r -> 3/2), so both parts are integers. */ + if (val) { + CAST(rational); + int recv = cursp(); + push(); /* receiver (self) slot */ + gen_pm_integer(s, &cast->numerator); push(); + gen_pm_integer(s, &cast->denominator); push(); + push(); pop(); /* reserve OP_SSEND block slot */ + pop_n(3); + genop_3(s, OP_SSEND, recv, + new_sym(s, nsym(s->c->p, (const uint8_t*)"Rational", 8)), 2); + push(); + } + break; + } + case PM_IMAGINARY_NODE: + { + /* `Ni` literal -> Complex(0, numeric). */ + if (val) { + CAST(imaginary); + int recv = cursp(); + push(); /* receiver (self) slot */ + gen_int(s, cursp(), 0); push(); /* real part */ + codegen(s, (mrc_node*)cast->numeric, VAL); /* imaginary part */ + push(); pop(); /* reserve OP_SSEND block slot */ + pop_n(3); + genop_3(s, OP_SSEND, recv, + new_sym(s, nsym(s->c->p, (const uint8_t*)"Complex", 7)), 2); + push(); + } + break; + } + case PM_FLOAT_NODE: + { +#ifndef MRC_NO_FLOAT + if (val) { + CAST(float); + int off = new_lit_float(s, (mrc_float)cast->value); + genop_2(s, OP_LOADL, cursp(), off); + push(); + } +#else + /* A build without Float still has to compile source that spells a + float literal, so the literal is warned about and read as Integer 0, + as the lrama parser did under MRB_NO_FLOAT. */ + mrc_diagnostic_list_append(s->c, tree->location.start, + "floating-point numbers are not supported", + MRC_GENERATOR_WARNING); + if (val) { + gen_int(s, cursp(), 0); + push(); + } +#endif + break; + } + case PM_CALL_NODE: + { + CAST(call); + gen_call(s, tree, val, (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) ? 1 : 0, 0); + break; + } + case PM_ARRAY_NODE: + case PM_ARGUMENTS_NODE: + { + 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_IMPLICIT_NODE: + { + CAST(implicit); + codegen(s, (mrc_node *)cast->value, val); + break; + } + case PM_SPLAT_NODE: + { + CAST(splat); + if (cast->expression) { + codegen(s, (mrc_node *)cast->expression, val); + } else if (val) { + /* anonymous splat: load local variable '*' */ + pm_constant_id_t astr = MRC_OPSYM_2(mul); + gen_lvar(s, astr, 0); + } + 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); + /* SSEND: backtick is a private Kernel method, call it on self */ + genop_3(s, OP_SSEND, 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) { + CAST(back_reference_read); + pm_constant_t *c = pm_constant_pool_id_to_constant(&s->c->p->constant_pool, cast->name); + /* `$&`, `` $` ``, `$'` and `$+`; the parser admits no other name here */ + switch (c->start[1]) { + case '&': gen_match_ref(s, MRC_SYM_1(__group), 0); break; + case '`': gen_match_ref(s, MRC_SYM_1(__pre_match), -1); break; + case '\'': gen_match_ref(s, MRC_SYM_1(__post_match), -1); break; + default: gen_match_ref(s, MRC_SYM_1(__last_group), -1); break; + } + } + break; + } + case PM_NUMBERED_REFERENCE_READ_NODE: + { + if (val) { + CAST(numbered_reference_read); + /* The parser hands a number too large for its field over as 0, and + no match has a group that large either way, so the name reads as + nil without asking; CRuby warns and answers nil. */ + if (cast->number == 0 || cast->number > INT32_MAX) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + else { + gen_match_ref(s, MRC_SYM_1(__group), (mrc_int)cast->number); + } + } + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: + { + CAST(embedded_statements); + codegen(s, (mrc_node *)cast->statements, val); + break; + } + case PM_EMBEDDED_VARIABLE_NODE: + { + /* `"#@iv"`, the brace-less form of `"#{@iv}"`. The variable read is an + ordinary expression and OP_STRCAT converts it, so there is nothing to + do beyond generating it. */ + CAST(embedded_variable); + codegen(s, (mrc_node *)cast->variable, 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) { + /* A NOVAL codegen leaves nothing on the stack, so there is nothing + to pop: popping here took the register pointer down one for each + part, and what the parts were then compiled over was whatever + the frame already held below it. */ + codegen(s, nodes[i], NOVAL); + } + } + } + if (nt == PM_INTERPOLATED_SYMBOL_NODE) { + if (val) { + pop(); + if (!no_peephole(s)) { + struct mrc_insn_data data = mrc_last_insn(s); + if (data.insn == OP_STRING && data.a == cursp()) { + rewind_pc(s); + genop_2(s, OP_SYMBOL, data.a, data.b); + push(); + break; + } + } + 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(); + /* + * Use the same pattern as PM_INTERPOLATED_STRING_NODE to avoid + * mutating shared string references via OP_STRCAT. + * When the first part is not a string literal (e.g. a variable), + * prepend an empty string as a safe base for concatenation. + */ + 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 (i = 0; i < cast->parts.size; i++) { + codegen(s, (mrc_node *)cast->parts.nodes[i], VAL); + pop(); + if (str_begin || 0 < i) { + pop(); + genop_1(s, OP_STRCAT, cursp()); + } + push(); + } + push(); + pop_n(3); + sym = new_sym(s, MRC_OPSYM_2(tick)); + /* SSEND: backtick is a private Kernel method, call it on self */ + genop_3(s, OP_SSEND, 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) { + if (idx <= 0xff) { + /* TDEF fusion: TCLASS + METHOD + DEF -> TDEF */ + genop_3(s, OP_TDEF, cursp(), sym, idx); + } + else { + genop_1(s, OP_TCLASS, cursp()); + push(); + genop_2(s, OP_METHOD, cursp(), idx); + push(); pop(); + pop(); + genop_2(s, OP_DEF, cursp(), sym); + } + } + else { + codegen(s, cast->receiver, VAL); + pop(); + if (idx <= 0xff) { + /* SDEF fusion: SCLASS + METHOD + DEF -> SDEF */ + genop_3(s, OP_SDEF, cursp(), sym, idx); + } + else { + 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; + if (n->receiver) { + codegen(s, (mrc_node *)n->receiver, VAL); + } + else { + /* implicit receiver: bare `nil?` means `self.nil?` (#6874) */ + genop_1(s, OP_LOADSELF, cursp()); + push(); + } + } + } + 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 (cast->base.flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) { + /* do-while: `begin ... end while/until cond` runs the body once + before testing the condition (mirrors codegen_loop_mod). */ + mrc_bool is_until = (nt == PM_UNTIL_NODE); + if (is_until ? true_always(cast->predicate) : false_always(cast->predicate)) { + /* execute body once then exit */ + codegen(s, (mrc_node *)cast->statements, val); + if (val) push(); + goto exit; + } + if (is_until ? false_always(cast->predicate) : true_always(cast->predicate)) { + /* infinite loop after first execution */ + struct loopinfo *lp = loop_push(s, LOOP_NORMAL); + if (!val) lp->reg = -1; + uint32_t pos0 = genjmp_0(s, OP_JMP); + lp->pc0 = new_label(s); + lp->pc1 = new_label(s); + genop_0(s, OP_NOP); /* for redo */ + dispatch(s, pos0); + codegen(s, (mrc_node *)cast->statements, NOVAL); + genjmp(s, OP_JMP, lp->pc0); + loop_pop(s, val); + break; + } + struct loopinfo *lp = loop_push(s, LOOP_NORMAL); + if (!val) lp->reg = -1; + uint32_t pos0 = genjmp_0(s, OP_JMP); + lp->pc0 = new_label(s); + codegen(s, cast->predicate, VAL); + pop(); + uint32_t pos = genjmp2_0(s, is_until ? OP_JMPIF : OP_JMPNOT, cursp(), NOVAL); + lp->pc1 = new_label(s); + genop_0(s, OP_NOP); /* for redo */ + dispatch(s, pos0); + codegen(s, (mrc_node *)cast->statements, NOVAL); + genjmp(s, OP_JMP, lp->pc0); + dispatch(s, pos); + loop_pop(s, val); + break; + } + 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); + genop_0(s, OP_NOP); /* for redo */ + 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_MATCH_WRITE_NODE: + { + /* `regexp =~ string` whose regexp has named captures. mruby does not + bind the named captures to local variables (the bison compiler does + not either), so just emit the underlying =~ call and run the match. */ + CAST(match_write); + codegen(s, (mrc_node *)cast->call, val); + break; + } + case PM_MATCH_PREDICATE_NODE: + { + /* one-line `expr in pattern` -> true / false */ + CAST(match_predicate); + int head = cursp(); + codegen(s, (mrc_node *)cast->value, VAL); + uint32_t fail_pos = JMPLINK_START; + codegen_pattern(s, (mrc_node *)cast->pattern, head, &fail_pos, -1, 0); + genop_1(s, OP_LOADTRUE, head); + uint32_t done = genjmp(s, OP_JMP, JMPLINK_START); + if (fail_pos != JMPLINK_START) dispatch_linked(s, fail_pos); + genop_1(s, OP_LOADFALSE, head); + dispatch(s, done); + if (!val) pop(); + break; + } + case PM_MATCH_REQUIRED_NODE: + { + /* one-line `expr => pattern`: binds on match, raises + NoMatchingPatternError otherwise; evaluates to nil. */ + CAST(match_required); + int head = cursp(); + codegen(s, (mrc_node *)cast->value, VAL); + uint32_t fail_pos = JMPLINK_START; + codegen_pattern(s, (mrc_node *)cast->pattern, head, &fail_pos, -1, 0); + uint32_t ok = genjmp(s, OP_JMP, JMPLINK_START); + if (fail_pos != JMPLINK_START) dispatch_linked(s, fail_pos); + genop_1(s, OP_LOADFALSE, cursp()); + genop_1(s, OP_MATCHERR, cursp()); + dispatch(s, ok); + pop(); + if (val) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + break; + } + case PM_CASE_MATCH_NODE: + { + CAST(case_match); + int head = 0; + uint32_t case_end_jumps = JMPLINK_START; + uint32_t tmp; + + /* Optimization: detect if predicate is array literal */ + int known_array_len = -1; + if (cast->predicate && nint(cast->predicate) == PM_ARRAY_NODE) { + pm_array_node_t *arr = (pm_array_node_t *)cast->predicate; + int has_splat = 0; + for (size_t i = 0; i < arr->elements.size; i++) { + if (nint(arr->elements.nodes[i]) == PM_SPLAT_NODE) { + has_splat = 1; + break; + } + } + if (!has_splat) { + known_array_len = arr->elements.size; + } + } + + /* Generate code for the case value */ + int cache = 0; + if (cast->predicate) { + head = cursp(); + codegen(s, (mrc_node *)cast->predicate, VAL); + + /* A register that keeps what `deconstruct` answered across the + clauses, when more than one of them could ask (an array literal + subject is never asked). */ + if (known_array_len < 0 && cast->conditions.size > 1) { + for (size_t i = 0; i < cast->conditions.size; i++) { + pm_in_node_t *in_n = (pm_in_node_t *)cast->conditions.nodes[i]; + if (in_n->pattern && pattern_deconstructs((mrc_node *)in_n->pattern)) { + cache = cursp(); + genop_1(s, OP_LOADNIL, cache); + push(); + break; + } + } + } + } + + /* Iterate through in clauses */ + for (size_t i = 0; i < cast->conditions.size; i++) { + pm_in_node_t *in_n = (pm_in_node_t *)cast->conditions.nodes[i]; + uint32_t fail_pos = JMPLINK_START; + + /* Generate pattern matching code */ + if (in_n->pattern) { + codegen_pattern(s, (mrc_node *)in_n->pattern, head, &fail_pos, known_array_len, cache); + } + + /* Guard clauses on patterns are handled inside codegen_pattern (via PM_IF_NODE/PM_UNLESS_NODE wrappers) */ + + /* Generate in-clause body */ + codegen(s, (mrc_node *)in_n->statements, val); + if (val) pop(); + + /* Jump to end of case/in */ + tmp = genjmp(s, OP_JMP, case_end_jumps); + case_end_jumps = tmp; + + /* Dispatch fail jumps to next in-clause */ + if (fail_pos != JMPLINK_START) { + dispatch_linked(s, fail_pos); + } + } + + /* Handle else clause */ + if (cast->else_clause) { + codegen(s, (mrc_node *)cast->else_clause, val); + if (val) pop(); + } + else { + /* No pattern matched: raise NoMatchingPatternError */ + genop_1(s, OP_LOADFALSE, cursp()); + genop_1(s, OP_MATCHERR, cursp()); + } + + /* Dispatch all end jumps */ + if (case_end_jumps != JMPLINK_START) { + dispatch_linked(s, case_end_jumps); + } + + if (val) { + /* Move result to original case value position */ + if (head) { + gen_move(s, head, cursp(), 0); + pop_n(cache ? 2 : 1); + } + push(); + } + else { + if (head) pop_n(cache ? 2 : 1); + } + 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_LOADTRUE, cursp()); + push(); + } + break; + } + case PM_FALSE_NODE: + { + if (val) { + genop_1(s, OP_LOADFALSE, 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; + if (parent) { + codegen(s, parent, VAL); + } + else { /* ::ClassName - root namespace */ + genop_1(s, OP_OCLASS, cursp()); + push(); + } + 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); + if (cpath->parent) { + codegen(s, cpath->parent, VAL); + } + else { /* ::ModuleName - root namespace */ + genop_1(s, OP_OCLASS, cursp()); + push(); + } + break; + } + case PM_CONSTANT_READ_NODE: + 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); + int a = alias_sym(s, (mrc_node *)cast->new_name); + int b = alias_sym(s, (mrc_node *)cast->old_name); + 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++) { + genop_1(s, OP_UNDEF, alias_sym(s, (mrc_node *)cast->names.nodes[i])); + } + if (val) { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + break; + } + case PM_SUPER_NODE: + { + CAST(super); + struct mscope m; + int n = 0, nk = 0, st = 0; + + search_mscope(s, &m); + push(); + 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 (m.ainfo >= 0) gen_blkmove(s, &m); + else { + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + } + else { /* `super()` parentheses without argument */ + /* block argument */ + if (cast->block) { + codegen(s, (mrc_node *)cast->block, VAL); + } + else if (m.ainfo >= 0) gen_blkmove(s, &m); + 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); + struct mscope m; + int n = CALL_MAXARGS; + int sp = cursp(); + + search_mscope(s, &m); + push(); /* room for receiver */ + if (m.ainfo > 0) { + mrc_bool blk_lost = FALSE; + + genop_2S(s, OP_ARGARY, cursp(), mscope_operand(s, &m)); + push(); push(); push(); /* ARGARY pushes 3 values at most */ + pop(); pop(); pop(); + /* keyword arguments */ + if (m.ainfo & 0x1) { + n |= CALL_MAXARGS<<4; + push(); + blk_lost = gen_zsuper_kwargs(s, &m); + } + /* block argument */ + if (cast->block) { + push(); + codegen(s, (mrc_node *)cast->block, VAL); + } + else if (blk_lost) { + push(); + gen_blkmove(s, &m); + } + } + else { + /* block argument */ + if (cast->block) { + codegen(s, (mrc_node *)cast->block, VAL); + } + else if (m.ainfo >= 0) { + gen_blkmove(s, &m); + } + else { + /* There is no method scope to belong to, so there is no block to + forward: a `super` here raises rather than call anything. + PM_SUPER_NODE says the same. */ + genop_1(s, OP_LOADNIL, cursp()); + push(); + } + 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 || return_leaves_upper_p(s)) { + gen_return(s, OP_RETURN_BLK, cursp()); + } + else { + gen_return(s, OP_RETURN, cursp()); + } + if (val) push(); + break; + } + case PM_YIELD_NODE: + { + CAST(yield); + struct mscope m; + int n = 0, nk = 0, st = 0; + + search_mscope(s, &m); + if (m.ainfo < 0) codegen_error(s, "invalid yield (SyntaxError)"); + push(); + CAST3(arguments, cast->arguments, arguments); + if (arguments) { + st = n = gen_values(s, (mrc_node *)cast->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; + } + } + } + push(); pop(); /* space for a block */ + pop_n(st+1); + genop_2S(s, OP_BLKPUSH, cursp(), mscope_operand(s, &m)); + if (nk == 0 && n < 15) { + /* fast path: direct block call without method dispatch */ + genop_2(s, OP_BLKCALL, cursp(), n); + } + else { + /* SEND carries the keyword count / splat array to Proc#call */ + 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); + /* next targets the enclosing loop or block, not the exception + frames of surrounding begin/rescue, just like break and redo */ + struct loopinfo *lp = s->loop; + while (lp && (lp->type == LOOP_BEGIN || lp->type == LOOP_RESCUE)) { + lp = lp->prev; + } + if (!lp) { + raise_error(s, "unexpected next"); + } + else if (lp->type == LOOP_NORMAL) { + codegen(s, (mrc_node *)cast->arguments, NOVAL); + genjmp(s, OP_JMPUW, lp->pc0); + } + else { + if ((mrc_node *)cast->arguments) { + /* next v returns v itself; gen_retval unwraps a single argument + instead of wrapping it in an array (next 1, 2 stays [1, 2]) */ + gen_retval(s, (mrc_node *)cast->arguments); + } + else { + genop_1(s, OP_LOADNIL, cursp()); + } + gen_return(s, OP_RETURN, cursp()); + } + if (val) push(); + break; + } + case PM_REDO_NODE: + { + struct loopinfo *lp = s->loop; + while (lp && (lp->type == LOOP_BEGIN || lp->type == LOOP_RESCUE)) { + lp = lp->prev; + } + if (!lp) { + raise_error(s, "unexpected redo"); + } + else { + genjmp(s, OP_JMPUW, lp->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); + if (cast->rescue_clause == NULL && cast->else_clause == NULL && + cast->ensure_clause == NULL) { + /* plain begin/end: no exception machinery, just the body. Emitting + the rescue scaffold here left a dangling jump and an unbalanced + loop entry that corrupted following code. */ + gen_begin(s, (mrc_node *)cast, val); + break; + } + 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 errsave, exc, landing; + int err_catch; + uint32_t err_begin, err_end; + /* Where the begin leaves its value: the register it would use with no + saved slot at all, so that a clause and a body that raised nothing + agree on it. */ + landing = cursp(); + push(); + /* What `$!` held before this begin, kept below the exception register + so that a clause body cannot reuse it, and read on the way in so + that leaving a clause can put it back. Only an exception reaches + here, so a begin whose body raises nothing never touches the name. */ + errsave = cursp(); + genop_2(s, OP_GETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + push(); + exc = cursp(); + genop_1(s, OP_EXCEPT, exc); + push(); + err_catch = catch_handler_new(s); + err_begin = s->pc; + /* rescue */ + gen_rescue(s, (mrc_node *)cast->rescue_clause, &pos1, &exc, &exend, val, errsave, landing); + if (pos1 != JMPLINK_START) { + dispatch(s, pos1); + /* No clause matched, so this begin never named the exception and + what it saved goes back before the raise carries on outward. */ + genop_2(s, OP_SETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + genop_1(s, OP_RAISEIF, exc); + } + pop(); + /* A clause left by `return`, `break` or a raise of its own passes none + of the restores above, so the same restore is also an ensure over + the clauses: the only way out that skips it is the VM tearing the + frame down, where the name is going away regardless. Normal exits + reach here already restored and jump past it. */ + { + int idx; + push(); + err_end = s->pc; + push(); + idx = cursp(); + genop_1(s, OP_EXCEPT, idx); + genop_2(s, OP_SETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + genop_1(s, OP_RAISEIF, idx); + pop(); + pop(); + catch_handler_set(s, err_catch, MRC_CATCH_ENSURE, err_begin, err_end, err_end); + } + pop(); + } + 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) { + /* When rescue is present, cursp is 1 higher than the no-rescue case + * regardless of val. Normalize before gen_ensure so that the node + * stays register-balanced; otherwise a NOVAL begin/rescue/ensure + * (e.g. as a loop body) leaks one register and `break value` lands + * in a different register than the loop exit reads. */ + if (cast->rescue_clause) pop(); + 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 - only catches StandardError */ + /* The same layout as a begin with a rescue clause: the value lands + where the expression left its own, `$!` is saved below the exception + register on the way in and put back on the way out. */ + int landing = cursp(); + push(); + int errsave = cursp(); + genop_2(s, OP_GETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + push(); + int exc = cursp(); + genop_1(s, OP_EXCEPT, exc); + push(); + /* check if exception is StandardError */ + genop_2(s, OP_GETCONST, cursp(), new_sym(s, MRC_SYM_1(StandardError))); + push(); + pop(); + genop_2(s, OP_RESCUE, exc, cursp()); + int rescue_jmp = genjmp2_0(s, OP_JMPIF, cursp(), val); + /* not StandardError - re-raise */ + genop_1(s, OP_RAISEIF, exc); + /* StandardError - execute rescue expression */ + dispatch(s, rescue_jmp); + pop(); + genop_2(s, OP_SETGV, exc, new_sym(s, MRC_SYM_2(errinfo))); + int err_catch = catch_handler_new(s); + uint32_t err_begin = s->pc; + codegen(s, cast->rescue_expression, val); + if (val) pop(); + genop_2(s, OP_SETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + if (val) gen_move(s, landing, cursp(), 0); + int restored = genjmp_0(s, OP_JMP); + /* A rescue expression left by `return`, `break` or a raise of its own + passes the restore above by, so the restore is also an ensure over + it. */ + { + uint32_t err_end = s->pc; + push(); + int idx = cursp(); + genop_1(s, OP_EXCEPT, idx); + genop_2(s, OP_SETGV, errsave, new_sym(s, MRC_SYM_2(errinfo))); + genop_1(s, OP_RAISEIF, idx); + pop(); + catch_handler_set(s, err_catch, MRC_CATCH_ENSURE, err_begin, err_end, err_end); + } + pop(); + pop(); + dispatch(s, restored); + + 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_sym = MRC_OPSYM_2(and); + int idx = lv_idx(s, and_sym); + if (idx == 0) { + gen_getupvar(s, cursp(), and_sym); + } + 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_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_LINE_NODE: + { + if (val) { + int line = node_lineno(s->c, tree); + gen_int(s, cursp(), (mrc_int)line); + 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: + { + 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); + codegen_defined(s, cast->value, val); + 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/codegen_prism.inc b/vendor/mruby-compiler2/src/codegen_prism.inc deleted file mode 100644 index 094e56f..0000000 --- a/vendor/mruby-compiler2/src/codegen_prism.inc +++ /dev/null @@ -1,2957 +0,0 @@ -#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 index 9a87ecb..0284207 100644 --- a/vendor/mruby-compiler2/src/compile.c +++ b/vendor/mruby-compiler2/src/compile.c @@ -8,7 +8,7 @@ #include "../include/mrc_presym.h" #include "../include/mrc_diagnostic.h" -#if defined(PICORB_VM_MRUBY) +#if defined(MRC_TARGET_MRUBY) #include "../include/mrc_proc.h" #endif @@ -34,10 +34,13 @@ mrc_load_exec(mrc_ccontext *c, mrc_node *ast) } } #if defined(MRC_DUMP_PRETTY) && !defined(MRC_NO_STDIO) - if (c->dump_result) { + if (c->dump_ast) { pm_buffer_t buffer = { 0 }; pm_prettyprint(&buffer, c->p, ast); - fprintf(stderr, "%s\n", buffer.value); + /* stdout, like the irep dump from mrc_codedump_all(). The buffer is not + NUL terminated, so it must be written by length. */ + fwrite(pm_buffer_value(&buffer), 1, pm_buffer_length(&buffer), stdout); + putchar('\n'); pm_buffer_free(&buffer); } #endif @@ -52,10 +55,53 @@ mrc_load_exec(mrc_ccontext *c, mrc_node *ast) return irep; } +/* Refuse a nesting deeper than Prism means to parse. + * + * Prism counts how deep it is and refuses to go past PRISM_DEPTH_MAXIMUM, + * but only where it parses an expression: the walk over a pattern carries + * the count and never reads it, so a pattern nested as deep as it is + * written recurses until the C stack runs out. The count kept here is of + * the brackets the lexer has opened, which is what such a nesting is made + * of, and the token that would open one past the limit is handed to the + * parser as the end of the input instead, which every part of Prism is + * written to stop at. + * + * The limit is Prism's own, so a program it would have parsed is parsed + * still: a nesting it accepts never reaches this, and one it refuses was + * refused before, only now before the recursion rather than during it. + */ +static void +lex_nesting_check(mrc_ccontext *c, pm_token_t *token) +{ + switch (token->type) { + case PM_TOKEN_BRACKET_LEFT: case PM_TOKEN_BRACKET_LEFT_ARRAY: + case PM_TOKEN_BRACE_LEFT: case PM_TOKEN_PARENTHESIS_LEFT: + case PM_TOKEN_EMBEXPR_BEGIN: + if (c->nesting > PRISM_DEPTH_MAXIMUM) { + /* The parser stops at the end of the input wherever it stands, and + reports what it was waiting for; the tree it built so far goes back + with the arena. */ + token->type = PM_TOKEN_EOF; + token->end = token->start; + return; + } + c->nesting++; + break; + case PM_TOKEN_BRACKET_RIGHT: case PM_TOKEN_BRACE_RIGHT: + case PM_TOKEN_PARENTHESIS_RIGHT: case PM_TOKEN_EMBEXPR_END: + if (c->nesting > 0) c->nesting--; + break; + default: + break; + } +} + static void partial_hook(void *data, pm_parser_t *p, pm_token_t *token) { mrc_ccontext *c = (mrc_ccontext *)data; + + lex_nesting_check(c, token); if (c->current_filename_index + 1 == c->filename_table_length) { return; } @@ -74,7 +120,49 @@ partial_hook(void *data, pm_parser_t *p, pm_token_t *token) } } -#if defined(PICORB_VM_MRUBY) +#if defined(MRC_TARGET_MRUBY) +static mrc_bool +mrc_mruby_lvspace_proc_p(const struct RProc *proc) +{ + const struct mrc_irep *irep; + + if (proc == NULL || MRC_PROC_CFUNC_P(proc) || proc->upper == NULL) { + return FALSE; + } + irep = (const struct mrc_irep *)proc->body.irep; + return irep && irep->lv == NULL && irep->nlocals == 1; +} + +static size_t +mrc_mruby_irep_local_count(mrb_state *mrb, const struct mrc_irep *irep) +{ + size_t count = 0; + + if (irep && irep->lv) { + size_t lv_count = irep->nlocals > 0 ? irep->nlocals - 1 : 0; + for (size_t i = 0; i < lv_count; i++) { + if (mrb_sym_name(mrb, irep->lv[i])) { + count++; + } + } + } + return count; +} + +static void +mrc_mruby_options_scope_local_init(mrc_ccontext *cc, pm_string_t *local, mrc_sym sym) +{ + const char *name = mrb_sym_name(cc->mrb, sym); + size_t length; + uint8_t *copy; + + if (!name) return; + length = strlen(name); + copy = (uint8_t *)mrc_malloc(cc, length); + memcpy(copy, name, length); + pm_string_constant_init(local, (const char *)copy, length); +} + static void mrc_pm_options_init(mrc_ccontext *cc) { @@ -87,34 +175,48 @@ mrc_pm_options_init(mrc_ccontext *cc) 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; + /* The scopes the string is compiled against are the ones codegen can reach + from here, so this walk ends where the chain of local variables does + (MRB_PROC_LVAR_BOUNDARY_P() in mruby/proc.h). A scope past that end + resolves a name in the parser that codegen would then have nowhere to + put. Both loops below ask the question of every proc, including one whose + scope is left out: an empty scope is indistinguishable from a binding's + local-variable space, and the two have to end on the same proc for the + index the second one counts down to hold. */ + size_t scopes_count = 0; + for (u = (struct RProc *)cc->upper; u && !MRC_PROC_CFUNC_P(u); u = (struct RProc *)u->upper) { + if (!mrc_mruby_lvspace_proc_p(u)) { + scopes_count++; + } + if (MRC_PROC_LVAR_BOUNDARY_P(u)) break; } 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)); + size_t scope_index = scopes_count; + for (; u && !MRC_PROC_CFUNC_P(u); u = (struct RProc *)u->upper) { + if (!mrc_mruby_lvspace_proc_p(u)) { + const struct mrc_irep *ir = (const struct mrc_irep *)u->body.irep; + size_t lv_count = ir->nlocals > 0 ? ir->nlocals - 1 : 0; + const mrc_sym *v = ir->lv; + size_t locals_count = mrc_mruby_irep_local_count(cc->mrb, ir); + + scope = &options->scopes[--scope_index]; + pm_options_scope_init(scope, locals_count); + if (v) { + const char *name; + size_t local_index = 0; + for (size_t j = 0; j < lv_count; j++) { + name = mrb_sym_name(cc->mrb, v[j]); + if (name) { + mrc_mruby_options_scope_local_init(cc, &scope->locals[local_index++], v[j]); + } } } } - u = (struct RProc *)u->upper; + if (MRC_PROC_LVAR_BOUNDARY_P(u)) break; } cc->options = options; @@ -127,9 +229,10 @@ mrc_pm_parser_init(mrc_parser_state *p, uint8_t **source, size_t size, mrc_ccont 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) +#if defined(MRC_TARGET_MRUBY) mrc_pm_options_init(cc); #endif + cc->nesting = 0; pm_parser_init(p, *source, size, cc->options); p->lex_callback = cb; mrc_init_presym(&p->constant_pool); @@ -144,13 +247,13 @@ mrc_pm_parser_init(mrc_parser_state *p, uint8_t **source, size_t size, mrc_ccont #ifndef MRC_NO_STDIO #define INITIAL_BUF_SIZE 1024 -static ssize_t +static intptr_t append_from_stdin(mrc_ccontext *c, uint8_t **source, size_t source_length) { - uint8_t *buffer = mrc_malloc(c, INITIAL_BUF_SIZE); + uint8_t *buffer = (uint8_t *)mrc_malloc(c, INITIAL_BUF_SIZE); if (buffer == NULL) return -1; - int capacity = INITIAL_BUF_SIZE; + size_t capacity = INITIAL_BUF_SIZE; size_t length = 0; while (1) { @@ -170,7 +273,7 @@ append_from_stdin(mrc_ccontext *c, uint8_t **source, size_t source_length) if (capacity <= length) { capacity *= 2; - uint8_t *new_buffer = mrc_realloc(c, buffer, capacity); + uint8_t *new_buffer = (uint8_t *)mrc_realloc(c, buffer, capacity); if (new_buffer == NULL) { mrc_free(c, buffer); return -1; @@ -180,16 +283,52 @@ append_from_stdin(mrc_ccontext *c, uint8_t **source, size_t source_length) } } -static ssize_t +/* A directory opens for reading on POSIX systems and then fails every read + with EISDIR, so a stream that opened says nothing about whether it can be + read. One byte tells the two apart without asking the platform what kind + of file this is: an empty file reports end-of-file and no error, while a + directory raises the error indicator. The byte is pushed back, so the + stream is left where it was found. + + The same probe is exported as mrb_stream_is_unreadable() for callers that + have mruby.h. This file does not: it is the portable mrc layer, built for + targets with no mruby core, so it keeps its own copy rather than reach for + one. */ +static int +stream_is_unreadable(FILE *file) +{ + int c = getc(file); + if (c == EOF) return ferror(file) != 0; + ungetc(c, file); + return 0; +} + +static intptr_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; + intptr_t length = 0; + intptr_t each_size; FILE *file; const char *filename = filenames[0]; while (filename) { + if (i > 0) { + /* Separate files with a newline so that a file without a trailing + newline does not merge its last token with the first token of the + next file (e.g. `end` + `module` becoming `endmodule`). The separator + precedes the file content, so filename_table[i].start still points at + the content and the filename/line mapping is unaffected. See #6907. */ + length += 1; + if (*source == NULL) { + *source = (uint8_t *)mrc_malloc(c, length + 1); + } + else { + *source = (uint8_t *)mrc_realloc(c, *source, length + 1); + } + (*source)[pos++] = '\n'; + (*source)[length] = '\0'; + } filename_table[i].filename = filenames[i]; filename_table[i].start = pos; if (filename[0] == '-' && filename[1] == '\0') { @@ -210,6 +349,25 @@ read_input_files(mrc_ccontext *c, const char **filenames, uint8_t **source, mrc_ fseek(file, 0, SEEK_END); each_size = ftell(file); fseek(file, 0, SEEK_SET); + if (each_size < 0) { + /* Not a seekable file (a pipe, FIFO or terminal); its size cannot be + determined up front, so the read-it-all-at-once path below does not + apply. */ + fprintf(stderr, "compile.c: cannot get size of program file. (%s)\n", filename); + fclose(file); + return -1; + } + if (stream_is_unreadable(file)) { + /* The size above is not trustworthy for a stream that cannot be read: + a directory answers LONG_MAX to ftell() on ext4 and 0 on tmpfs, so + it either overflows the length arithmetic below before the + allocation is attempted, or compiles as an empty program. The + wording differs from the fread() failure below so that the two + cannot be mistaken for each other. */ + fprintf(stderr, "compile.c: cannot read from program file. (%s)\n", filename); + fclose(file); + return -1; + } length += each_size; if (*source == NULL) { *source = (uint8_t *)mrc_malloc(c, length + 1); @@ -217,7 +375,7 @@ read_input_files(mrc_ccontext *c, const char **filenames, uint8_t **source, mrc_ else { *source = (uint8_t *)mrc_realloc(c, *source, length + 1); } - if (fread(*source + pos, sizeof(char), each_size, file) != each_size) { + if (fread(*source + pos, sizeof(char), (size_t)each_size, file) != (size_t)each_size) { fprintf(stderr, "compile.c: cannot read program file. (%s)\n", filename); fclose(file); return -1; @@ -282,7 +440,7 @@ mrc_parse_file_cxt(mrc_ccontext *c, const char **filenames, uint8_t **source) 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); + intptr_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++) { @@ -295,6 +453,21 @@ mrc_parse_file_cxt(mrc_ccontext *c, const char **filenames, uint8_t **source) return mrc_pm_parse(c); } +/* Give back the tree. Where the arena is in use nothing is done here: the + parser is still holding what it allocated from the same arena, and the + whole of it is given back at mrc_ccontext_free() instead. Where the arena + is not in use, which is a build with its own allocator, the tree is walked + as prism walks it. */ +static void +mrc_prism_release_tree(mrc_ccontext *c, mrc_node *root) +{ +#if defined(MRC_TARGET_MRUBY) && defined(MRC_PRISM_ARENA) + (void)c; (void)root; +#else + if (root) pm_node_destroy(c->p, root); +#endif +} + MRC_API mrc_irep * mrc_load_file_cxt(mrc_ccontext *c, const char **filenames, uint8_t **source) { @@ -303,7 +476,10 @@ mrc_load_file_cxt(mrc_ccontext *c, const char **filenames, uint8_t **source) return NULL; } mrc_irep *irep = mrc_load_exec(c, root); - pm_node_destroy(c->p, root); + /* The tree is given back with the arena it was parsed into rather than + walked: see prism_xallocator.h. Everything prism allocated for this + parse goes with it, so nothing is left behind. */ + mrc_prism_release_tree(c, root); return irep; } #endif @@ -312,7 +488,7 @@ 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].filename = c->filename ? c->filename : "-e"; c->filename_table[0].start = 0; c->filename_table_length = 1; c->current_filename_index = 0; @@ -325,6 +501,10 @@ 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); + /* The tree is given back with the arena it was parsed into rather than + walked: see prism_xallocator.h. Everything prism allocated for this + parse goes with it, so nothing is left behind. */ + mrc_prism_release_tree(c, root); return irep; } @@ -340,4 +520,14 @@ mrb_mruby_compiler2_gem_final(mrb_state *mrb) { } +MRC_API void +mrb_mruby_compiler_gem_init(mrb_state *mrb) +{ +} + +MRC_API void +mrb_mruby_compiler_gem_final(mrb_state *mrb) +{ +} + #endif diff --git a/vendor/mruby-compiler2/src/debug.c b/vendor/mruby-compiler2/src/debug.c index e8574ed..8486780 100644 --- a/vendor/mruby-compiler2/src/debug.c +++ b/vendor/mruby-compiler2/src/debug.c @@ -153,9 +153,6 @@ mrc_debug_info_append_file(mrc_ccontext *c, mrc_irep_debug_info *d, 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 && @@ -174,7 +171,6 @@ mrc_debug_info_append_file(mrc_ccontext *c, mrc_irep_debug_info *d, 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; diff --git a/vendor/mruby-compiler2/src/diagnostic.c b/vendor/mruby-compiler2/src/diagnostic.c index ce1da9c..c376643 100644 --- a/vendor/mruby-compiler2/src/diagnostic.c +++ b/vendor/mruby-compiler2/src/diagnostic.c @@ -61,15 +61,28 @@ mrc_diagnostic_code_to_string(mrc_diagnostic_code code) 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); + mrc_diagnostic_list *list = (mrc_diagnostic_list *)mrc_calloc(c, 1, sizeof(mrc_diagnostic_list)); + const uint8_t *file_start = c->p->start; + list->filename = NULL; +#ifndef MRC_NO_STDIO + if (c->filename_table && 0 < c->filename_table_length && location_start) { + uint32_t offset = (uint32_t)(location_start - c->p->start); + int file_idx = 0; + for (int i = 1; i < c->filename_table_length; i++) { + if (offset < c->filename_table[i].start) break; + file_idx = i; + } + list->filename = c->filename_table[file_idx].filename; + file_start = c->p->start + c->filename_table[file_idx].start; + } +#endif + line_and_column_by_start_and_offset(file_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'; + memcpy(list->message, buf, len + 1); list->code = code; if (c->diagnostic_list == NULL) { @@ -99,4 +112,3 @@ mrc_diagnostic_list_free(mrc_ccontext *c) } c->diagnostic_list = NULL; } - diff --git a/vendor/mruby-compiler2/src/dump.c b/vendor/mruby-compiler2/src/dump.c index a1da7da..29b0063 100644 --- a/vendor/mruby-compiler2/src/dump.c +++ b/vendor/mruby-compiler2/src/dump.c @@ -147,7 +147,9 @@ get_pool_block_size(mrc_ccontext *c, const mrc_irep *irep) case IREP_TT_BIGINT: { - mrc_int len = irep->pool[pool_no].u.str[0]; + /* str[0] is an unsigned length byte (0-255); reading it through a + signed char would make a >=128 byte bignum literal negative. */ + mrc_int len = (unsigned char)irep->pool[pool_no].u.str[0]; mrc_assert_int_fit(mrc_int, len, size_t, SIZE_MAX); size += (size_t)len+2; } @@ -213,7 +215,7 @@ write_pool_block(mrc_ccontext *c, const mrc_irep *irep, uint8_t *buf) case IREP_TT_BIGINT: cur += mrc_uint8_to_bin(IREP_TT_BIGINT, cur); /* data type */ - len = irep->pool[pool_no].u.str[0]; + len = (unsigned char)irep->pool[pool_no].u.str[0]; memcpy(cur, irep->pool[pool_no].u.str, (size_t)len+2); cur += len+2; break; @@ -597,7 +599,10 @@ write_section_debug(mrc_ccontext *c, const mrc_irep *irep, uint8_t *cur, mrc_sym 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); + /* Match the non-NULL zero-length marker inserted by the code generator; + a NULL argument here is undefined behavior (memcmp nonnull) that clang + miscompiles. */ + pm_constant_id_t null_mark = pm_constant_pool_find(&c->p->constant_pool, (const uint8_t *)"", 0); if (*syms == NULL) { *syms = (mrc_sym*)mrc_malloc(c, sizeof(mrc_sym) * 1); @@ -605,7 +610,7 @@ create_lv_sym_table(mrc_ccontext *c, const mrc_irep *irep, mrc_sym **syms, uint3 for (int i = 0; i + 1 < irep->nlocals; i++) { mrc_sym const name = irep->lv[i]; - if (name == null_mark) continue; + if (name == 0 || name == null_mark) continue; if (find_filename_index(*syms, *syms_len, name) != -1) continue; ++(*syms_len); @@ -644,10 +649,13 @@ write_lv_record(mrc_ccontext *c, const mrc_irep *irep, uint8_t **start, mrc_sym { uint8_t *cur = *start; - pm_constant_id_t null_mark = pm_constant_pool_find(&c->p->constant_pool, NULL, 0); + /* Match the non-NULL zero-length marker inserted by the code generator; + a NULL argument here is undefined behavior (memcmp nonnull) that clang + miscompiles. */ + pm_constant_id_t null_mark = pm_constant_pool_find(&c->p->constant_pool, (const uint8_t *)"", 0); for (int i = 0; i + 1 < irep->nlocals; i++) { - if (irep->lv[i] == null_mark) { + if (irep->lv[i] == 0 || irep->lv[i] == null_mark) { cur += mrc_uint16_to_bin(RITE_LV_NULL_MARK, cur); } else { @@ -759,7 +767,11 @@ debug_info_defined_p(const mrc_irep *irep) static mrc_bool lv_defined_p(const mrc_irep *irep) { - if (irep->lv && 0 < ((pm_constant_id_list_t *)irep->lv)->size) { return TRUE; } + /* irep->lv is an mrc_sym array (NULL when the scope has no named locals); + the old `((pm_constant_id_list_t *)irep->lv)->size` reinterpreted it as a + different struct, an out-of-bounds read and a strict-aliasing violation + that clang miscompiles. A plain NULL check matches the bytecode dumper. */ + if (irep->lv) { return TRUE; } for (int i = 0; i < irep->rlen; i++) { if (lv_defined_p(irep->reps[i])) { return TRUE; } } diff --git a/vendor/mruby-compiler2/src/irep.c b/vendor/mruby-compiler2/src/irep.c index 92108fe..c5ffd62 100644 --- a/vendor/mruby-compiler2/src/irep.c +++ b/vendor/mruby-compiler2/src/irep.c @@ -38,8 +38,6 @@ mrc_irep_free(mrc_ccontext *c, mrc_irep *irep) 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); diff --git a/vendor/mruby-compiler2/src/mrc_presym.c b/vendor/mruby-compiler2/src/mrc_presym.c index c6c4ac8..994f413 100644 --- a/vendor/mruby-compiler2/src/mrc_presym.c +++ b/vendor/mruby-compiler2/src/mrc_presym.c @@ -7,6 +7,11 @@ typedef struct { } mrc_sym_entry; static mrc_sym_entry symTable[] = { +/* mrc_presym.h defines these as lookup macros; drop them first in case it + is visible in this translation unit (e.g. the amalgamated build) */ +#undef MRC_OPSYM_2 +#undef MRC_SYM_1 +#undef MRC_SYM_2 #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}, @@ -17,24 +22,30 @@ static mrc_sym_entry symTable[] = { {0, NULL} // sentinel }; -static uint32_t offset = 0; +/* mrc_presym.inc numbers its literals from 1 in table order, which is what + lets the enum in mrc_presym.h index this array; slot 0 stays unused. */ +static mrc_sym presym_ids[sizeof(symTable) / sizeof(symTable[0])]; -mrc_sym mrc_sym_offset(mrc_sym sym) +mrc_sym +mrc_presym_id(mrc_sym sym) { - return sym + offset; + mrc_assert(0 < sym && sym < sizeof(presym_ids) / sizeof(presym_ids[0])); + return presym_ids[sym]; } void mrc_init_presym(pm_constant_pool_t *pool) { - offset = pool->size; + /* The pool is not necessarily empty here: when the compile context carries + enclosing scopes (an eval or a binding), the parser has already interned + those scopes' local names, and a local can be named after a presym + literal; an anonymous rest parameter is stored under the name `*`. The + insert then hands back the id that name already has, so the presym ids + are not contiguous and have to be recorded one by one. */ 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 + mrc_assert(symTable[i].index == i + 1); + presym_ids[symTable[i].index] = + (mrc_sym)pm_constant_pool_insert_constant(pool, (const uint8_t *)symTable[i].lit, strlen(symTable[i].lit)); } } diff --git a/vendor/mruby-compiler2/src/mruby_compat.c b/vendor/mruby-compiler2/src/mruby_compat.c new file mode 100644 index 0000000..0f2303a --- /dev/null +++ b/vendor/mruby-compiler2/src/mruby_compat.c @@ -0,0 +1,702 @@ +#include "../include/mrc_common.h" + +#if defined(MRC_TARGET_MRUBY) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../include/mrc_ccontext.h" +#include "../include/mrc_compile.h" +#include "../include/mrc_diagnostic.h" +#include "../include/mrc_irep.h" +#include "../include/mrc_parser_util.h" +#include "../include/mrc_pool.h" + +#define MRC_COMPAT_DUMP_OK 0 +#define MRC_COMPAT_DUMP_DEBUG_INFO 1 + +int mrc_dump_irep(mrc_ccontext *c, const mrc_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size); + +static void +copy_context_to_mrc(mrc_ccontext *dst, const mrb_ccontext *src) +{ + if (!src) return; + + dst->slen = src->slen; + dst->lineno = src->lineno; + dst->target_class = src->target_class; + dst->capture_errors = FALSE; + /* `capture_errors` is the compiler's own "an error was recorded" flag, so it + starts clear; the caller's intent to report errors itself travels in + `quiet_errors` and keeps codegen_error() off stderr, the way parse errors + are gated below. */ + dst->quiet_errors = src->capture_errors; + /* Only the AST dump is delegated to the compiler. `dump_result` stays off + here because mrb_load_exec() dumps the generated proc through + mrb_codedump_all(); letting mrc_load_exec() dump too would print the + same irep twice. */ + dst->dump_ast = src->dump_result; + dst->no_exec = src->no_exec; + dst->keep_lv = src->keep_lv; + dst->no_optimize = src->no_optimize; + dst->no_ext_ops = src->no_ext_ops; + dst->upper = src->upper; + if (src->filename) { + mrc_ccontext_filename(dst, src->filename); + } + if (src->syms && src->slen > 0) { + pm_options_t *options = (pm_options_t*)mrc_calloc(dst, 1, sizeof(pm_options_t)); + pm_options_scope_t *scope; + + pm_string_constant_init(&options->encoding, "UTF-8", 5); + pm_options_scopes_init(options, 1); + scope = &options->scopes[0]; + pm_options_scope_init(scope, (size_t)src->slen); + for (int i = 0; i < src->slen; i++) { + const char *name = mrb_sym_name(dst->mrb, src->syms[i]); + if (name) { + size_t len = strlen(name); + char *copy = (char*)mrc_malloc(dst, len); + memcpy(copy, name, len); + pm_string_constant_init(&scope->locals[i], copy, len); + } + } + dst->options = options; + } +} + +static void +update_context_locals_from_irep(mrb_state *mrb, mrb_ccontext *c, mrc_ccontext *mc, const mrc_irep *irep) +{ + mrb_sym *syms; + uint16_t i; + int count = 0; + + if (!c || !irep || !irep->lv) return; + for (i = 0; i + 1 < irep->nlocals; i++) { + mrc_int len = 0; + const char *name = mrc_sym_name_len(mc, irep->lv[i], &len); + if (name && len > 0) count++; + } + if (count == 0) return; + + syms = (mrb_sym*)mrb_realloc(mrb, c->syms, sizeof(mrb_sym) * count); + c->syms = syms; + c->slen = count; + count = 0; + for (i = 0; i + 1 < irep->nlocals; i++) { + mrc_int len = 0; + const char *name = mrc_sym_name_len(mc, irep->lv[i], &len); + if (name && len > 0) { + c->syms[count++] = mrb_intern(mrb, name, (size_t)len); + } + } +} + +static void +free_parser_messages(mrb_state *mrb, struct mrb_parser_message *messages, size_t count) +{ + size_t i; + + for (i = 0; i < count; i++) { + if (messages[i].message) { + mrb_free(mrb, messages[i].message); + messages[i].message = NULL; + } + } +} + +static void +copy_parser_message(mrb_state *mrb, struct mrb_parser_message *dst, const mrc_diagnostic_list *src) +{ + size_t len; + + dst->lineno = src->line > UINT16_MAX ? UINT16_MAX : (uint16_t)src->line; + dst->column = (int)src->column; + len = strlen(src->message); + dst->message = (char*)mrb_malloc(mrb, len + 1); + memcpy(dst->message, src->message, len + 1); +} + +static void +copy_diagnostics_to_parser(mrb_state *mrb, struct mrb_parser_state *p, const mrc_ccontext *c) +{ + const mrc_diagnostic_list *d; + + for (d = c->diagnostic_list; d; d = d->next) { + if (d->code == MRC_PARSER_WARNING || d->code == MRC_GENERATOR_WARNING) { + if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) { + copy_parser_message(mrb, &p->warn_buffer[p->nwarn], d); + } + p->nwarn++; + } + else { + if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) { + copy_parser_message(mrb, &p->error_buffer[p->nerr], d); + } + p->nerr++; + } + } + if (p->nerr == 0 && p->tree == NULL) { + p->error_buffer[0].lineno = 0; + p->error_buffer[0].column = 0; + p->error_buffer[0].message = (char*)mrb_malloc(mrb, sizeof("compile error")); + memcpy(p->error_buffer[0].message, "compile error", sizeof("compile error")); + p->nerr = 1; + } +} + +static struct mrb_parser_state* +parser_alloc(mrb_state *mrb, mrb_ccontext *c) +{ + struct mrb_parser_state *p; + + p = (struct mrb_parser_state*)mrb_calloc(mrb, 1, sizeof(struct mrb_parser_state)); + p->mrb = mrb; + p->cxt = c; + p->capture_errors = c ? c->capture_errors : FALSE; + p->no_ext_ops = c ? c->no_ext_ops : FALSE; + p->no_return_value = c ? c->no_return_value : FALSE; + p->upper = c ? c->upper : NULL; + if (c && c->filename) { + p->filename_sym = mrb_intern_cstr(mrb, c->filename); + } + return p; +} + +static struct mrb_parser_state* +parse_source(mrb_state *mrb, const char *s, size_t len, mrb_ccontext *c) +{ + struct mrb_parser_state *p; + mrc_ccontext *mc; + uint8_t *source; + const uint8_t *parse_source; + mrc_irep *irep; + + p = parser_alloc(mrb, c); + source = (uint8_t*)mrb_malloc(mrb, len + 1); + memcpy(source, s, len); + source[len] = '\0'; + p->s = (const char*)source; + p->send = (const char*)source + len; + + mc = mrc_ccontext_new(mrb); + copy_context_to_mrc(mc, c); + p->ylval = mc; + + parse_source = source; + irep = mrc_load_string_cxt(mc, &parse_source, len); + update_context_locals_from_irep(mrb, c, mc, irep); + p->tree = (mrb_ast_node*)irep; + copy_diagnostics_to_parser(mrb, p, mc); + if (c) { + c->parser_nerr = p->nerr; + } +#ifndef MRB_NO_STDIO + /* Unless the caller captures errors (e.g. eval), report parse errors to + stderr like the bison parser does, so a syntax error is not a silent + failure. Codegen errors take the same gate inside codegen_error(), + through `quiet_errors`. */ + if (!c || !c->capture_errors) { + const char *fn = (c && c->filename) ? c->filename : "(string)"; + const mrc_diagnostic_list *d; + for (d = mc->diagnostic_list; d; d = d->next) { + if (d->code == MRC_PARSER_ERROR && d->message) { + fprintf(stderr, "%s:%d:%d: %s\n", fn, (int)d->line, (int)d->column, + d->message); + } + } + } +#endif + return p; +} + +#ifndef MRB_NO_STDIO +static char* +read_file_content(mrb_state *mrb, FILE *f, size_t *lenp) +{ + size_t capa = 4096; + size_t len = 0; + char *buf = (char*)mrb_malloc(mrb, capa + 1); + + while (!feof(f)) { + size_t n; + if (len == capa) { + capa *= 2; + buf = (char*)mrb_realloc(mrb, buf, capa + 1); + } + n = fread(buf + len, 1, capa - len, f); + len += n; + if (ferror(f)) { + mrb_free(mrb, buf); + return NULL; + } + } + buf[len] = '\0'; + *lenp = len; + return buf; +} + +/* A stream that opened can still fail every read: a directory does exactly + that on POSIX, failing with EISDIR rather than reporting end of input. + read_file_content() answers NULL for that, and without an exception here + the failure is indistinguishable from an empty file one layer up + (mrb_parse_file() passes the NULL on and mrb_load_exec() answers an + undefined value), so the caller ran nothing and was told nothing. The + binary loader already raises for its own read failures (`irep load error` + in load.c); this is the source loader's counterpart. */ +static void +set_read_error(mrb_state *mrb, mrb_ccontext *c) +{ + mrb_value mesg; + + if (mrb->exc) return; + if (c && c->filename) { + mesg = mrb_format(mrb, "cannot read file: %s", c->filename); + } + else { + mesg = mrb_str_new_lit(mrb, "cannot read file"); + } + mrb->exc = mrb_obj_ptr(mrb_exc_new_str(mrb, E_SCRIPT_ERROR, mesg)); +} +#endif + +MRB_API mrb_ccontext* +mrb_ccontext_new(mrb_state *mrb) +{ + return (mrb_ccontext*)mrb_calloc(mrb, 1, sizeof(mrb_ccontext)); +} + +MRB_API void +mrb_ccontext_free(mrb_state *mrb, mrb_ccontext *cxt) +{ + if (!cxt) return; + if (cxt->syms) mrb_free(mrb, cxt->syms); + if (cxt->filename) mrb_free(mrb, cxt->filename); + mrb_free(mrb, cxt); +} + +MRB_API const char* +mrb_ccontext_filename(mrb_state *mrb, mrb_ccontext *c, const char *s) +{ + size_t len; + char *filename; + + if (!c) return NULL; + if (!s) return c->filename; + len = strlen(s); + filename = (char*)mrb_malloc(mrb, len + 1); + memcpy(filename, s, len + 1); + if (c->filename) mrb_free(mrb, c->filename); + c->filename = filename; + return c->filename; +} + +MRB_API void +mrb_ccontext_partial_hook(mrb_ccontext *c, int (*partial_hook)(struct mrb_parser_state*), void *data) +{ + if (!c) return; + c->partial_hook = partial_hook; + c->partial_data = data; +} + +MRB_API void +mrb_ccontext_cleanup_local_variables(mrb_ccontext *c) +{ + if (!c) return; + /* Forget the locals accumulated so far so they do not leak into the next + compilation unit (e.g. a script run after a -r required file). The syms + buffer itself is kept; it is reused by the next compile's realloc or + freed by mrb_ccontext_free(). copy_context_to_mrc() only propagates + locals when slen > 0, so clearing slen is enough to stop the leak. */ + c->slen = 0; + c->keep_lv = FALSE; +} + +MRB_API struct mrb_parser_state* +mrb_parser_new(mrb_state *mrb) +{ + return parser_alloc(mrb, NULL); +} + +MRB_API void +mrb_parser_free(struct mrb_parser_state *p) +{ + mrb_state *mrb; + mrc_ccontext *mc; + + if (!p) return; + mrb = p->mrb; + mc = (mrc_ccontext*)p->ylval; + if (mc && p->tree) { + mrc_irep_free(mc, (mrc_irep*)p->tree); + } + if (mc) { + mrc_ccontext_free(mc); + } + free_parser_messages(mrb, p->error_buffer, sizeof(p->error_buffer) / sizeof(p->error_buffer[0])); + free_parser_messages(mrb, p->warn_buffer, sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])); + /* Free the source copy that parse_source() allocated and stored in p->s. + The irep keeps pointers into it (symbol names, debug info) until codegen + and the irep dump finish, so it must live until the parser state is freed. + mrb_parser_parse() transfers this owned copy into p->s, replacing the + caller's borrowed buffer, so freeing p->s here always frees the copy. */ + if (p->s) mrb_free(mrb, (void*)p->s); + mrb_free(mrb, p); +} + +MRB_API void +mrb_parser_parse(struct mrb_parser_state *p, mrb_ccontext *c) +{ + struct mrb_parser_state *parsed; + size_t len; + + if (!p || !p->s || p->tree || p->nerr) return; + len = (size_t)(p->send - p->s); + parsed = parse_source(p->mrb, p->s, len, c); + p->tree = parsed->tree; + p->ylval = parsed->ylval; + p->nerr = parsed->nerr; + p->nwarn = parsed->nwarn; + memcpy(p->error_buffer, parsed->error_buffer, sizeof(p->error_buffer)); + memcpy(p->warn_buffer, parsed->warn_buffer, sizeof(p->warn_buffer)); + /* Transfer ownership of the source copy parse_source() allocated from the + inner state to p. The irep (now in p->tree) keeps pointers into it until + codegen finishes, so the copy must outlive `parsed`. p->s previously held + the caller's borrowed buffer, which the caller frees itself; overwriting it + here is safe because no one reads p->s after parsing. */ + p->s = parsed->s; + p->send = parsed->send; + parsed->s = parsed->send = NULL; + parsed->tree = NULL; + parsed->ylval = NULL; + memset(parsed->error_buffer, 0, sizeof(parsed->error_buffer)); + memset(parsed->warn_buffer, 0, sizeof(parsed->warn_buffer)); + mrb_parser_free(parsed); +} + +MRB_API void +mrb_parser_set_filename(struct mrb_parser_state *p, char const *filename) +{ + if (!p || !filename) return; + p->filename_sym = mrb_intern_cstr(p->mrb, filename); + if (p->cxt) { + mrb_ccontext_filename(p->mrb, p->cxt, filename); + } +} + +MRB_API mrb_sym +mrb_parser_get_filename(struct mrb_parser_state *p, uint16_t idx) +{ + (void)idx; + return p ? p->filename_sym : 0; +} + +#ifndef MRB_NO_STDIO +MRB_API struct mrb_parser_state* +mrb_parse_file(mrb_state *mrb, FILE *f, mrb_ccontext *c) +{ + char *buf; + size_t len = 0; + struct mrb_parser_state *p; + + if (!f) return NULL; + buf = read_file_content(mrb, f, &len); + if (!buf) { + set_read_error(mrb, c); + return NULL; + } + p = parse_source(mrb, buf, len, c); + mrb_free(mrb, buf); + return p; +} +#endif + +MRB_API struct mrb_parser_state* +mrb_parse_nstring(mrb_state *mrb, const char *s, size_t len, mrb_ccontext *c) +{ + if (!s) return NULL; + return parse_source(mrb, s, len, c); +} + +MRB_API struct mrb_parser_state* +mrb_parse_string(mrb_state *mrb, const char *s, mrb_ccontext *c) +{ + return mrb_parse_nstring(mrb, s, strlen(s), c); +} + +/* The dump and reload below is the one compiler-side failure codegen_error() + does not cover, and it leaves nothing on stderr on its own. Report it + through the same `quiet_errors` gate, so a caller that captures errors + (eval) still sees only the exception mrb_load_exec() raises. */ +static void +report_roundtrip_error(mrc_ccontext *mc, const char *message) +{ +#ifndef MRB_NO_STDIO + if (mc->quiet_errors) return; + if (mc->filename) { + fprintf(stderr, "%s: %s\n", mc->filename, message); + } + else { + fprintf(stderr, "%s\n", message); + } +#endif +} + +MRB_API struct RProc* +mrb_generate_code(mrb_state *mrb, struct mrb_parser_state *p) +{ + mrc_ccontext *mc; + mrc_irep *irep; + mrb_irep *mir; + struct RProc *proc; + uint8_t *bin = NULL; + size_t bin_size = 0; + /* Always carry debug info across the dump/reload that turns the mrc_irep + into an mrb_irep: without it runtime backtraces lose the file name and + line number, and mruby reports those even when compiled without -g. */ + uint8_t flags = MRC_COMPAT_DUMP_DEBUG_INFO; + + if (!p || !p->tree || p->nerr) return NULL; + mc = (mrc_ccontext*)p->ylval; + irep = (mrc_irep*)p->tree; + if (mrc_dump_irep(mc, irep, flags, &bin, &bin_size) != MRC_COMPAT_DUMP_OK) { + report_roundtrip_error(mc, "irep dump error"); + return NULL; + } + mir = mrb_read_irep_buf(mrb, bin, bin_size); + mrc_free(mc, bin); + if (!mir) { + report_roundtrip_error(mc, "irep load error"); + return NULL; + } + proc = mrb_proc_new(mrb, mir); + mrb_irep_decref(mrb, mir); + proc->c = NULL; + proc->upper = p->upper; + mrc_irep_free(mc, irep); + p->tree = NULL; + return proc; +} + +MRB_API mrb_value +mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrb_ccontext *c) +{ + struct RClass *target = mrb->object_class; + struct RProc *proc; + mrb_int keep = 0; + + if (!p) { + /* No parser state at all: the source could not be obtained. The reader + that knows why (set_read_error() above) has already said so; anything + else reaching here (a NULL stream, a NULL string) gets a message + rather than the silent undefined value this used to answer. */ + if (mrb->exc == NULL) { + mrb->exc = mrb_obj_ptr(mrb_exc_new_lit(mrb, E_SCRIPT_ERROR, "cannot load source")); + } + return mrb_undef_value(); + } + if (!p->tree || p->nerr) { + if (c) c->parser_nerr = p->nerr; + if (mrb->exc == NULL) { + const char *message = "syntax error"; + if (p->error_buffer[0].message) { + message = p->error_buffer[0].message; + } + mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, message, strlen(message))); + } + mrb_parser_free(p); + return mrb_undef_value(); + } + + proc = mrb_generate_code(mrb, p); + mrb_parser_free(p); + if (!proc) { + if (mrb->exc == NULL) { + mrb->exc = mrb_obj_ptr(mrb_exc_new_lit(mrb, E_SCRIPT_ERROR, "codegen error")); + } + return mrb_undef_value(); + } + + if (c) { + if (c->dump_result) mrb_codedump_all(mrb, proc); + if (c->no_exec) return mrb_obj_value(proc); + if (c->target_class) target = c->target_class; + if (c->keep_lv) { + keep = c->slen + 1; + } + else { + c->keep_lv = TRUE; + } + } + MRB_PROC_SET_TARGET_CLASS(proc, target); + proc->flags |= MRB_PROC_CREF; + if (mrb->c->ci) { + mrb_vm_ci_target_class_set(mrb->c->ci, target); + } + return mrb_top_run(mrb, proc, mrb_top_self(mrb), keep); +} + +#ifndef MRB_NO_STDIO +MRB_API mrb_value +mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrb_ccontext *c) +{ + return mrb_load_exec(mrb, mrb_parse_file(mrb, f, c), c); +} + +MRB_API mrb_value +mrb_load_file(mrb_state *mrb, FILE *f) +{ + return mrb_load_file_cxt(mrb, f, NULL); +} + +MRB_API mrb_value +mrb_load_detect_file_cxt(mrb_state *mrb, FILE *fp, mrb_ccontext *c) +{ + char *buf; + size_t len = 0; + mrb_value result; + + if (!fp) return mrb_nil_value(); + buf = read_file_content(mrb, fp, &len); + if (!buf) { + set_read_error(mrb, c); + return mrb_nil_value(); + } + if (len >= 4 && memcmp(buf, "RITE", 4) == 0) { + result = mrb_load_irep_buf_cxt(mrb, buf, len, c); + } + else { + result = mrb_load_nstring_cxt(mrb, buf, len, c); + } + mrb_free(mrb, buf); + return result; +} + +/* One byte tells a stream that cannot be read from one that is merely empty, + without asking the platform what kind of file it has: an empty file reports + end-of-file and no error, while a directory raises the error indicator. The + byte is pushed back, so the stream is left where it was found. + + This lives beside the loaders because it is their failure mode it detects, + and the command line tools each opened their own streams and needed their + own copy of it before it was exported. `mruby-compiler`'s own + read_input_files() keeps a private copy: compile.c is the portable mrc + layer, built for targets that have no mruby.h to declare this. */ +MRB_API mrb_bool +mrb_stream_is_unreadable(FILE *file) +{ + int c = getc(file); + if (c == EOF) return ferror(file) != 0; + ungetc(c, file); + return FALSE; +} +#endif + +MRB_API mrb_value +mrb_load_nstring_cxt(mrb_state *mrb, const char *s, size_t len, mrb_ccontext *c) +{ + return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); +} + +MRB_API mrb_value +mrb_load_nstring(mrb_state *mrb, const char *s, size_t len) +{ + return mrb_load_nstring_cxt(mrb, s, len, NULL); +} + +MRB_API mrb_value +mrb_load_string_cxt(mrb_state *mrb, const char *s, mrb_ccontext *c) +{ + return mrb_load_nstring_cxt(mrb, s, strlen(s), c); +} + +MRB_API mrb_value +mrb_load_string(mrb_state *mrb, const char *s) +{ + return mrb_load_string_cxt(mrb, s, NULL); +} + +void +mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user) +{ + mrc_ccontext *mc; + mrc_irep *irep; + uint16_t i; + + if (!p || !p->tree || !func) return; + mc = (mrc_ccontext*)p->ylval; + irep = (mrc_irep*)p->tree; + if (!mc || !irep || !irep->lv) return; + for (i = 0; i + 1 < irep->nlocals; i++) { + mrc_int len = 0; + const char *name = mrc_sym_name_len(mc, irep->lv[i], &len); + if (name && len > 0) { + mrb_sym sym = mrb_intern(mrb, name, (size_t)len); + if (!func(mrb, sym, user)) return; + } + } +} + +/* Decode a single bytecode instruction. Declared in mruby/internal.h and + used by tools such as mrdb; provided here so the Prism compiler library + exposes the same symbol the legacy compiler did. */ +struct mrb_insn_data +mrb_decode_insn(const mrb_code *pc) +{ + struct mrb_insn_data data = { 0 }; + if (pc == 0) return data; + data.addr = pc; + mrb_code insn = READ_B(); + uint16_t a = 0; + uint16_t b = 0; + uint16_t c = 0; + + switch (insn) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x (); break; +#include +#undef OPCODE + } + switch (insn) { + case OP_EXT1: + insn = READ_B(); + switch (insn) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); break; +#include +#undef OPCODE + } + break; + case OP_EXT2: + insn = READ_B(); + switch (insn) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); break; +#include +#undef OPCODE + } + break; + case OP_EXT3: + insn = READ_B(); + switch (insn) { +#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); break; +#include +#undef OPCODE + } + break; + default: + break; + } + data.insn = insn; + data.a = a; + data.b = b; + data.c = c; + return data; +} + +#endif diff --git a/vendor/mruby-compiler2/src/parser_util.c b/vendor/mruby-compiler2/src/parser_util.c index 72b4ff9..2645c34 100644 --- a/vendor/mruby-compiler2/src/parser_util.c +++ b/vendor/mruby-compiler2/src/parser_util.c @@ -9,5 +9,6 @@ mrc_sym_name_len(mrc_ccontext *c, mrc_sym sym, mrc_int *lenp) *lenp = constant->length; return (const char*)constant->start; } + *lenp = 0; return NULL; } diff --git a/vendor/mruby-compiler2/src/pool.c b/vendor/mruby-compiler2/src/pool.c index 7675297..4cd2974 100644 --- a/vendor/mruby-compiler2/src/pool.c +++ b/vendor/mruby-compiler2/src/pool.c @@ -149,4 +149,3 @@ mrc_pool_realloc(mrc_pool *pool, void *p, size_t oldlen, size_t newlen) memcpy(np, p, oldlen); return np; } - From 01b77b7f2d1718244f718f93925068cced8bbf81 Mon Sep 17 00:00:00 2001 From: bash0C7 Date: Thu, 10 Sep 2026 16:17:32 +0000 Subject: [PATCH 2/2] Compile def m(&nil) into ENTER's noblock flag mruby 4.0 accepts `&nil` in formal parameters to refuse a block (NEWS 4.0.0, b07518e) and codegen.c sets bit 23 of the ENTER operand for it. Prism has no such syntax, so the parser now takes `nil` after `&` and marks the block parameter, and codegen turns that mark into MRC_ARGS_NOBLOCK(). mrc_irep.h already reads the bit as MRC_ASPEC_NOBLOCK. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FfoC7uMVr3d3VaxWFnaPFT --- .../lib/prism/include/prism/ast.h | 11 +++++ vendor/mruby-compiler2/lib/prism/src/node.c | 40 +++++++++++++++++++ .../lib/prism/src/prettyprint.c | 40 +++++++++++++++++++ vendor/mruby-compiler2/lib/prism/src/prism.c | 8 ++++ vendor/mruby-compiler2/src/codegen.c | 13 +++++- 5 files changed, 110 insertions(+), 2 deletions(-) diff --git a/vendor/mruby-compiler2/lib/prism/include/prism/ast.h b/vendor/mruby-compiler2/lib/prism/include/prism/ast.h index 6911006..31e9250 100644 --- a/vendor/mruby-compiler2/lib/prism/include/prism/ast.h +++ b/vendor/mruby-compiler2/lib/prism/include/prism/ast.h @@ -1753,6 +1753,7 @@ typedef struct pm_block_argument_node { * Flags (#pm_parameter_flags): * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * * ::PM_PARAMETER_FLAGS_NIL_BLOCK * * @extends pm_node_t */ @@ -1857,6 +1858,7 @@ typedef struct pm_block_node { * Flags (#pm_parameter_flags): * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * * ::PM_PARAMETER_FLAGS_NIL_BLOCK * * @extends pm_node_t */ @@ -5527,6 +5529,7 @@ typedef struct pm_keyword_hash_node { * Flags (#pm_parameter_flags): * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * * ::PM_PARAMETER_FLAGS_NIL_BLOCK * * @extends pm_node_t */ @@ -6484,6 +6487,7 @@ typedef struct pm_numbered_reference_read_node { * Flags (#pm_parameter_flags): * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * * ::PM_PARAMETER_FLAGS_NIL_BLOCK * * @extends pm_node_t */ @@ -6521,6 +6525,7 @@ typedef struct pm_optional_keyword_parameter_node { * Flags (#pm_parameter_flags): * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * * ::PM_PARAMETER_FLAGS_NIL_BLOCK * * @extends pm_node_t */ @@ -7073,6 +7078,7 @@ typedef struct pm_regular_expression_node { * Flags (#pm_parameter_flags): * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * * ::PM_PARAMETER_FLAGS_NIL_BLOCK * * @extends pm_node_t */ @@ -7105,6 +7111,7 @@ typedef struct pm_required_keyword_parameter_node { * Flags (#pm_parameter_flags): * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * * ::PM_PARAMETER_FLAGS_NIL_BLOCK * * @extends pm_node_t */ @@ -7223,6 +7230,7 @@ typedef struct pm_rescue_node { * Flags (#pm_parameter_flags): * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * * ::PM_PARAMETER_FLAGS_NIL_BLOCK * * @extends pm_node_t */ @@ -8129,6 +8137,9 @@ typedef enum pm_parameter_flags { /** a parameter name that has been repeated in the method signature */ PM_PARAMETER_FLAGS_REPEATED_PARAMETER = 4, + /** a block parameter written as `&nil`, which accepts no block (mruby) */ + PM_PARAMETER_FLAGS_NIL_BLOCK = 8, + PM_PARAMETER_FLAGS_LAST, } pm_parameter_flags_t; diff --git a/vendor/mruby-compiler2/lib/prism/src/node.c b/vendor/mruby-compiler2/lib/prism/src/node.c index e60ea19..79b7e62 100644 --- a/vendor/mruby-compiler2/lib/prism/src/node.c +++ b/vendor/mruby-compiler2/lib/prism/src/node.c @@ -3456,6 +3456,11 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); flags++; } + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_NIL_BLOCK)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"NIL_BLOCK\"", 11); + flags++; + } pm_buffer_append_byte(buffer, ']'); // Dump the name field @@ -3531,6 +3536,11 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); flags++; } + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_NIL_BLOCK)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"NIL_BLOCK\"", 11); + flags++; + } pm_buffer_append_byte(buffer, ']'); // Dump the name field @@ -6470,6 +6480,11 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); flags++; } + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_NIL_BLOCK)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"NIL_BLOCK\"", 11); + flags++; + } pm_buffer_append_byte(buffer, ']'); // Dump the name field @@ -7183,6 +7198,11 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); flags++; } + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_NIL_BLOCK)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"NIL_BLOCK\"", 11); + flags++; + } pm_buffer_append_byte(buffer, ']'); // Dump the name field @@ -7219,6 +7239,11 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); flags++; } + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_NIL_BLOCK)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"NIL_BLOCK\"", 11); + flags++; + } pm_buffer_append_byte(buffer, ']'); // Dump the name field @@ -7741,6 +7766,11 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); flags++; } + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_NIL_BLOCK)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"NIL_BLOCK\"", 11); + flags++; + } pm_buffer_append_byte(buffer, ']'); // Dump the name field @@ -7772,6 +7802,11 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); flags++; } + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_NIL_BLOCK)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"NIL_BLOCK\"", 11); + flags++; + } pm_buffer_append_byte(buffer, ']'); // Dump the name field @@ -7893,6 +7928,11 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); flags++; } + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_NIL_BLOCK)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"NIL_BLOCK\"", 11); + flags++; + } pm_buffer_append_byte(buffer, ']'); // Dump the name field diff --git a/vendor/mruby-compiler2/lib/prism/src/prettyprint.c b/vendor/mruby-compiler2/lib/prism/src/prettyprint.c index ad25c0a..420dfa8 100644 --- a/vendor/mruby-compiler2/lib/prism/src/prettyprint.c +++ b/vendor/mruby-compiler2/lib/prism/src/prettyprint.c @@ -729,6 +729,11 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm pm_buffer_append_string(output_buffer, " repeated_parameter", 19); found = true; } + if (cast->base.flags & PM_PARAMETER_FLAGS_NIL_BLOCK) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " nil_block", 10); + found = true; + } if (!found) pm_buffer_append_string(output_buffer, " nil", 4); pm_buffer_append_byte(output_buffer, '\n'); } @@ -838,6 +843,11 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm pm_buffer_append_string(output_buffer, " repeated_parameter", 19); found = true; } + if (cast->base.flags & PM_PARAMETER_FLAGS_NIL_BLOCK) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " nil_block", 10); + found = true; + } if (!found) pm_buffer_append_string(output_buffer, " nil", 4); pm_buffer_append_byte(output_buffer, '\n'); } @@ -5568,6 +5578,11 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm pm_buffer_append_string(output_buffer, " repeated_parameter", 19); found = true; } + if (cast->base.flags & PM_PARAMETER_FLAGS_NIL_BLOCK) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " nil_block", 10); + found = true; + } if (!found) pm_buffer_append_string(output_buffer, " nil", 4); pm_buffer_append_byte(output_buffer, '\n'); } @@ -6677,6 +6692,11 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm pm_buffer_append_string(output_buffer, " repeated_parameter", 19); found = true; } + if (cast->base.flags & PM_PARAMETER_FLAGS_NIL_BLOCK) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " nil_block", 10); + found = true; + } if (!found) pm_buffer_append_string(output_buffer, " nil", 4); pm_buffer_append_byte(output_buffer, '\n'); } @@ -6733,6 +6753,11 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm pm_buffer_append_string(output_buffer, " repeated_parameter", 19); found = true; } + if (cast->base.flags & PM_PARAMETER_FLAGS_NIL_BLOCK) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " nil_block", 10); + found = true; + } if (!found) pm_buffer_append_string(output_buffer, " nil", 4); pm_buffer_append_byte(output_buffer, '\n'); } @@ -7535,6 +7560,11 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm pm_buffer_append_string(output_buffer, " repeated_parameter", 19); found = true; } + if (cast->base.flags & PM_PARAMETER_FLAGS_NIL_BLOCK) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " nil_block", 10); + found = true; + } if (!found) pm_buffer_append_string(output_buffer, " nil", 4); pm_buffer_append_byte(output_buffer, '\n'); } @@ -7578,6 +7608,11 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm pm_buffer_append_string(output_buffer, " repeated_parameter", 19); found = true; } + if (cast->base.flags & PM_PARAMETER_FLAGS_NIL_BLOCK) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " nil_block", 10); + found = true; + } if (!found) pm_buffer_append_string(output_buffer, " nil", 4); pm_buffer_append_byte(output_buffer, '\n'); } @@ -7776,6 +7811,11 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm pm_buffer_append_string(output_buffer, " repeated_parameter", 19); found = true; } + if (cast->base.flags & PM_PARAMETER_FLAGS_NIL_BLOCK) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " nil_block", 10); + found = true; + } if (!found) pm_buffer_append_string(output_buffer, " nil", 4); pm_buffer_append_byte(output_buffer, '\n'); } diff --git a/vendor/mruby-compiler2/lib/prism/src/prism.c b/vendor/mruby-compiler2/lib/prism/src/prism.c index b158e50..8c5c208 100644 --- a/vendor/mruby-compiler2/lib/prism/src/prism.c +++ b/vendor/mruby-compiler2/lib/prism/src/prism.c @@ -13890,10 +13890,15 @@ parse_parameters( pm_token_t name; bool repeated = false; + bool nil_block = 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 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) { + /* `&nil` declares that the method accepts no block (mruby). */ + name = not_provided(parser); + nil_block = true; } else { name = not_provided(parser); parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK; @@ -13903,6 +13908,9 @@ parse_parameters( if (repeated) { pm_node_flag_set_repeated_parameter(UP(param)); } + if (nil_block) { + pm_node_flag_set(UP(param), PM_PARAMETER_FLAGS_NIL_BLOCK); + } if (params->block == NULL) { pm_parameters_node_block_set(params, param); } else { diff --git a/vendor/mruby-compiler2/src/codegen.c b/vendor/mruby-compiler2/src/codegen.c index b317232..31f87e2 100644 --- a/vendor/mruby-compiler2/src/codegen.c +++ b/vendor/mruby-compiler2/src/codegen.c @@ -80,6 +80,11 @@ */ #define MRC_ARGS_BLOCK() ((mrc_aspec)1) +/** + * Function accepts no block argument (`&nil`) + */ +#define MRC_ARGS_NOBLOCK() ((mrc_aspec)1 << 23) + /** * Function accepts any number of arguments */ @@ -3751,6 +3756,7 @@ lambda_body(mrc_codegen_scope *s, mrc_node *tree, mrc_node *body, pm_constant_id size_t i, ma, mma, oa, ra, pa, ppa, ka, kd, ba, forwarding;; + mrc_bool noblock = FALSE; 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)); @@ -3817,7 +3823,9 @@ lambda_body(mrc_codegen_scope *s, mrc_node *tree, mrc_node *body, pm_constant_id } ka = parameters->keywords.size; kd = parameters->keyword_rest ? 1 : 0; - ba = parameters->block ? 1 : 0; + noblock = parameters->block + && PM_NODE_FLAG_P(parameters->block, PM_PARAMETER_FLAGS_NIL_BLOCK); + ba = (parameters->block && !noblock) ? 1 : 0; nregs = ma + mma + oa + ra + pa + ppa + ka + kd + ba; mrc_constant_id_list_init_capacity(s, lv, nregs); // mandatory @@ -3981,7 +3989,8 @@ lambda_body(mrc_codegen_scope *s, mrc_node *tree, mrc_node *body, pm_constant_id /* (23bits = 5:5:1:5:5:1:1) */ ra = ra|forwarding; ba = ba|forwarding; - a = MRC_ARGS_REQ(ma) + a = (noblock? MRC_ARGS_NOBLOCK() : 0) + | MRC_ARGS_REQ(ma) | MRC_ARGS_OPT(oa) | (ra? MRC_ARGS_REST() : 0) | MRC_ARGS_POST(pa)