Extract duplicated UTF-8 codepoint-to-bytes encoding into a shared
function in src/string.c. Update all gems to use it:
- mruby-sprintf: %c specifier
- mruby-io: putc
- mruby-string-ext: Integer#chr
- mruby-pack: pack("U")
- mruby-compiler: Unicode escapes in parser
Also use existing mrb_utf8len() in io.c for character length detection.
Co-authored-by: Claude <noreply@anthropic.com>
add support for find patterns in case/in expressions:
- [*pre, elem, *post] - find elem anywhere in array
- [*, elem, *] - anonymous rest (discarded)
- [*pre, a, b, *post] - multiple middle elements
implementation includes:
- grammar rules for find patterns with p_args, p_rest in parse.y
- NODE_PAT_FIND codegen with iterative search loop
- pre/post variable binding via range slicing
- p_const rule to prevent conflict with array literals
Co-authored-by: Claude <noreply@anthropic.com>
add support for one-line pattern matching syntax:
- 'expr in pattern' returns true/false
- 'expr => pattern' raises NoMatchingPatternError on mismatch
add NODE_MATCH_PAT node type for both forms, distinguished by
raise_on_fail flag. grammar rules placed at expr level to avoid
conflict with rescue clause's exception variable syntax.
Co-authored-by: Claude <noreply@anthropic.com>
Add support for brace-less hash patterns at top level of case/in.
`in a: x, b: y` is now equivalent to `in {a: x, b: y}`.
`in a:, b:` shorthand now works with newlines (CRuby compatible).
Changes:
- Add EXPR_VALUE to IS_LABEL_POSSIBLE() to recognize labels after `in`
- Add brace-less hash pattern rules to p_expr
- Change p_hash_elem to use p_as instead of p_expr to avoid recursion
- Add in_kwarg flag to parser state for pattern matching context
- Set in_kwarg in lexer when keyword_in is returned
- Use EXPR_ARG after tLABEL_TAG when in_kwarg is set (makes newlines significant)
Co-authored-by: Claude <noreply@anthropic.com>
Add pin operator `^var` that matches against existing variable values
instead of creating new bindings. Also add bracket-less array pattern
syntax at top level: `in 1, 2, x` is equivalent to `in [1, 2, x]`.
Co-authored-by: Claude <noreply@anthropic.com>
Add support for hash patterns in pattern matching expressions:
- {key:} shorthand binds to variable with same name
- {key: pattern} matches key against pattern
- {**rest} captures remaining keys
- {**nil} requires exact match (no extra keys)
- {**} ignores extra keys without capturing
Parser adds new grammar rules (p_hash, p_hash_body, p_hash_elems,
p_hash_elem, p_kwrest) and new_pat_hash() constructor.
Codegen generates code to call deconstruct_keys on the target hash,
then iterates through key-pattern pairs to match each key's value.
Adds Hash#deconstruct_keys method that returns self for pattern matching.
Co-authored-by: Claude <noreply@anthropic.com>
Add support for if/unless guards in case/in pattern matching:
case value
in x if x > 0 then :positive
in x unless x == 0 then :non_zero
end
Uses modifier_if/modifier_unless tokens since guards appear after
an expression. Disable peephole optimization for pattern variable
binding to prevent gen_move() from being optimized away when failed
guard jumps target the binding instruction.
Co-authored-by: Claude <noreply@anthropic.com>
Implement Phase 1 of Ruby pattern matching:
- value patterns (literals, constants, nil/true/false)
- variable patterns (binds matched value)
- alternative patterns (pat1 | pat2)
- as patterns (pattern => var)
Pattern matching uses === operator for value comparison,
allowing type checking with class patterns (e.g., in Integer).
Co-authored-by: Claude <noreply@anthropic.com>
restore correct argument passing for Regexp.compile when encoding is
present but flags are not. regexp literals like /a/n should compile to
Regexp.compile("a", nil, "n") with 3 arguments, not
Regexp.compile("a", "n") with 2 arguments.
the bug was introduced during refactoring when the nil-insertion logic
for the options parameter was accidentally omitted. now properly inserts
OP_LOADNIL when flags are absent but encoding is present.
Co-authored-by: Claude <noreply@anthropic.com>
allocate ** keyword dictionary register when methods have keyword
arguments (parse.y new_args_tail), broken in commit 26ea71260 during
cons-list to struct migration. reconstruct keyword hash after KEYEND
from extracted keyword local variables so super can access keyword
values. encode block parameter flag in ainfo bit 13 and generate
LOADNIL for block register in codegen_zsuper when parent has keywords
but no block parameter.
Co-authored-by: Claude <noreply@anthropic.com>
Restore exception handling for `||=` operator on class variables and constants
that was inadvertently removed in commit 0ca48e24f. When reading an undefined
class variable with GETCV opcode raises NameError, the exception handler
catches it and loads false, allowing the assignment to proceed.
Co-authored-by: Claude <noreply@anthropic.com>
pr #6643 fixed node type check but introduced a bug by accessing t->cdr
on a NODE_ARRAY structure. NODE_ARRAY nodes use the elements field, not
cdr. additionally, the fixed rhs path needs to update rhs_reg to point
to where values are actually pushed on the stack.
Co-authored-by: Claude <noreply@anthropic.com>
added stmts_push(p, stmts, stmt) helper function to properly push
statements to NODE_STMTS nodes by accessing the internal stmts field
(a cons list). this avoids ugly casts and prevents bugs.
fixed incorrect usage in:
- top_stmts rule (line 2081): was calling push($1, ...) directly on
NODE_STMTS instead of pushing to $1->stmts
- bodystmt rule (line 2114): same issue when handling else without
rescue
- stmts rule (line 2146): simplified to use new helper for consistency
the push macro works on cons lists, not NODE_STMTS variable nodes.
the new helper encapsulates the cast and provides type-safe access.
Co-authored-by: Claude <noreply@anthropic.com>
segment nodes allocated with mrbc_malloc were leaked if gen_string
raised an exception via longjmp. fix by avoiding allocation entirely:
temporarily modify tree structure by saving and clearing cdr pointer,
call gen_string, then restore cdr. no memory is allocated so nothing
leaks even on longjmp.
Co-authored-by: Claude <noreply@anthropic.com>
The bug was in codegen_colon3 which used genop_2(OP_OCLASS, sym)
treating OCLASS as BB format, but OCLASS is B format that only
loads ::Object without a symbol parameter. The fix uses the correct
two-instruction pattern: OCLASS to load Object class, then GETMCNST
to retrieve the constant from it.
Co-authored-by: Claude <noreply@anthropic.com>
cast uint8_t node_type field to enum node_type to satisfy c++ stricter
type checking while maintaining memory efficiency of 1-byte storage.
Co-authored-by: Claude <noreply@anthropic.com>
replace obsolete cons-style comments like /* (:begin prog...) */ with
modern struct-style comments like /* struct: begin_node(body) */ to
reflect current variable-sized node implementation.
Co-authored-by: Claude <noreply@anthropic.com>
add braces around node_hash case in dump_node() to fix variable
initialization crossing case labels error when compiling with c++.
Co-authored-by: Claude <noreply@anthropic.com>
introduce new_node() helper and NEW_NODE() macro to eliminate repetitive
allocation and header initialization pattern across 64 new_* functions.
before: each function required 2-3 lines for allocation:
struct mrb_ast_xxx_node *n = (...)parser_palloc(p, sizeof(...));
init_var_header(&n->header, p, NODE_XXX);
after: single line with type-safe macro:
struct mrb_ast_xxx_node *n = NEW_NODE(xxx, NODE_XXX);
saves approximately 128 lines while maintaining readability and providing
central point for future allocation logic changes.
Co-authored-by: Claude <noreply@anthropic.com>
Remove migration-stage "Phase" and "Group" references from comments,
replacing them with descriptions of actual code organization.
Co-authored-by: Claude <noreply@anthropic.com>
replaced migration-related comments (Phase 1/2/3, Group 8-16) with
descriptive comments that explain the current structure organization.
these phase/group comments were artifacts from incremental development
and no longer serve a meaningful purpose in the production codebase.
updated comments to describe what each section contains:
- "Literal value nodes" instead of "Phase 1 Variable Node Structures"
- "Expression and operation nodes" instead of "Phase 2..."
- "Control flow and definition nodes" instead of "Phase 3..."
- removed "Group N:" prefixes and replaced with descriptive headers
Co-authored-by: Claude <noreply@anthropic.com>
removed struct mrb_ast_when_node and when_node() casting macro which
were never actually used. NODE_CASE uses cons lists to represent
when clauses, not dedicated when_node structures. the structure
definition and macro were dead code left over from earlier design.
case/when implementation uses: cons(cons(conditions, body), next_when)
where each when clause is a cons cell in a list, not a typed node.
Co-authored-by: Claude <noreply@anthropic.com>
replaced all *_NODE_* accessor macros (e.g., SYM_NODE_VALUE,
INT_NODE_VALUE, CALL_NODE_METHOD) with direct member access using
casting macros (e.g., sym_node(n)->symbol, int_node(n)->value,
call_node(n)->method_name). this eliminates an unnecessary abstraction
layer and improves code readability by making field access explicit.
the accessor macros simply wrapped cast_func(n)->field, providing no
real benefit. direct member access makes it clear what field is being
accessed and reduces macro indirection.
affected files:
- node.h: removed ~100 accessor macro definitions
- codegen.c: replaced 19 macro uses with direct access
- parse.y: replaced 152 macro uses with direct access
Co-authored-by: Claude <noreply@anthropic.com>
replaced unnecessary macro usage with direct struct member access when
struct pointers are already available:
- return_n->args instead of RETURN_NODE_ARGS(return_n)
- yield_n->args instead of YIELD_NODE_ARGS(yield_n)
- for_n->var/iterable/body instead of FOR_NODE_VAR/ITERABLE/BODY(for_n)
- class_n->name/superclass/body instead of CLASS_NODE_* macros
- module_n->name/body instead of MODULE_NODE_NAME/BODY(module_n)
- sclass_n->obj/body instead of SCLASS_NODE_OBJ/BODY(sclass_n)
- hash->pairs instead of HASH_NODE_PAIRS(hash)
- call->method_name/safe_call instead of CALL_NODE_METHOD/SAFE(call)
- array->elements and an->elements instead of ARRAY_NODE_ELEMENTS macro
- splat->value instead of SPLAT_NODE_VALUE macro
improves code readability by removing unnecessary indirection.
Co-authored-by: Claude <noreply@anthropic.com>
changed NODE_YIELD dump from dump_recur to dump_callargs for consistent
argument display format. added null check to handle yield without args.
Co-authored-by: Claude <noreply@anthropic.com>
Removed STR_INLINE_THRESHOLD and STR_SMALL_THRESHOLD macros from node.h
as they are no longer referenced anywhere in the codebase. These appear
to be remnants from a previous string storage optimization strategy.
Co-authored-by: Claude <noreply@anthropic.com>
Refactored NODE_DSYM to use unified structure directly instead of wrapping
NODE_STR. This eliminates unnecessary allocation and simplifies the AST.
Changes:
- new_dsym() now creates NODE_DSYM directly with mrb_ast_str_node structure
- Parser calls new_dsym(p, n) instead of new_dsym(p, new_str(p, n))
- codegen_dsym() uses gen_string() for proper string generation
- NODE_DSYM dump uses dump_str() for consistent string list handling
- Removed redundant mrb_ast_dsym_node struct definition
This maintains identical functionality while reducing memory overhead
and architectural complexity, with proper string handling to prevent
mrbtest crashes.
Co-authored-by: Claude <noreply@anthropic.com>
Consolidated NODE_WHILE, NODE_UNTIL, NODE_WHILE_MOD, and NODE_UNTIL_MOD
dump cases using a shared dump_loop_node label. All four loop constructs
have identical structure (condition + body) and only differ in their
node type names.
Uses fall-through for the last case (NODE_UNTIL_MOD) to avoid unnecessary
goto. This eliminates code duplication (28 lines -> 12 lines) while
maintaining the same clear output format for each loop type.
Co-authored-by: Claude <noreply@anthropic.com>
Enhanced NODE_DSYM dump to use dump_node() instead of dump_str() for
the symbol's content list. Dynamic symbols (:"#{expr}") contain node
lists that may include complex interpolated expressions, not just simple
strings, so they need full node dumping to properly display their structure.
This provides much better visibility into interpolated symbol content
and makes debugging dynamic symbols more effective.
Co-authored-by: Claude <noreply@anthropic.com>
Enhanced NODE_HASH dump to detect and display the double-splat operator
(**) in a readable format. When a hash contains **other_hash syntax,
the parser represents ** as MRB_OPSYM(pow). Instead of dumping this
complex operator node, now displays a clean "**" for better readability.
This makes hash dumps with splat operations much easier to understand
and debug.
Co-authored-by: Claude <noreply@anthropic.com>
Enhanced NODE_FOR dump to properly handle the cons-list structure of
FOR_NODE_VAR with clear section labels. The structure contains:
- car: cons-list of pre-splat variables
- cdr->car: splat varnode (not a cons-list)
- cdr->cdr->car: cons-list of post-splat variables
Added "splat var:" and "post var:" labels to distinguish sections
and simplified the dump logic for better readability.
Co-authored-by: Claude <noreply@anthropic.com>
Implement NODE_MARG as a dedicated node type for parameter destructuring
to separate it architecturally from general multiple assignment (NODE_MASGN).
This resolves crashes when dumping parameter destructuring nodes and
improves code organization.
Key changes:
- Add NODE_MARG to node type enum
- Create new_marg() function for parameter destructuring
- Consolidate new_masgn() and new_marg() using shared helper
- Fix parameter context checks in lambda_body() to use NODE_MARG only
- Enable shared dumping logic for both NODE_MASGN and NODE_MARG
- Optimize memory management with immediate RHS cleanup
- Combine gen_assignment() cases for code deduplication
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
The NODE_CASE dump was treating the case body as a single varnode,
but it's actually a cons-list structure containing when clauses.
Changed to iterate through the cons-list similar to rescue clauses,
allowing proper display of when conditions and bodies.
Co-authored-by: Claude <noreply@anthropic.com>
Refactored NODE_MASGN from single lhs field to separate pre/rest/post
fields for cleaner multiple assignment handling. Fixed segfault when
compiling methods with destructured parameters by properly handling
parameter destructuring in lambda_body function.
Co-authored-by: Claude <noreply@anthropic.com>
Fixed copy-paste error where NODE_SUPER and NODE_ZSUPER cases in
dump_node incorrectly used CALL_NODE_ARGS macro instead of
SUPER_NODE_ARGS, causing segmentation faults when parser dump
tried to access invalid memory addresses.
Co-authored-by: Claude <noreply@anthropic.com>
Replace direct cons-list access (tree->car, tree->cdr->cdr) with
proper accessor macros (ENSURE_NODE_BODY, ENSURE_NODE_ENSURE_CLAUSE)
to support variable-sized node structures. Adds null checks for
improved safety and follows the same pattern as other migrated nodes.
Co-authored-by: Claude <noreply@anthropic.com>
Add support for dumping NODE_NVAR nodes in dump_node function.
NODE_NVAR represents numbered variables and displays the variable
number for debugging AST structures.
Co-authored-by: Claude <noreply@anthropic.com>
- Skip no-op splats of empty array literals (`*[]` / zarray) in
call argument generation and array literal codegen.
- Inline non-empty literal splat arrays without inner splats
(e.g. `*[a,b]`) as regular positional args/elements, avoiding
building a temporary array and ARYCAT.
This removes unnecessary `LOADNIL` + `ARRAY 0` + `ARYCAT` sequences
(e.g. `mruby -ve 'p *[]'`) and reduces temporary allocations while
preserving semantics and evaluation order. Falls back to the generic
path when nested splats are present or counts exceed fixed-arity.
No behavior change intended; only codegen improvements.
Co-authored-by: Codex <codex@openai.com>
Replace dump_recur() with dump_str() in NODE_HEREDOC case to properly
handle cons-lists of string representations instead of AST nodes.
This fixes segmentation faults when dumping heredoc AST nodes.
Co-authored-by: Claude <noreply@anthropic.com>
Now that all cons-list based codegen_* functions have been removed,
rename the gen_*_var functions to use the consistent codegen_* prefix.
This affects 70 functions and improves code clarity by establishing
a single naming convention for all code generation functions.
- gen_scope_var renamed to codegen_scope_node to avoid conflict with codegen_scope type
- All other gen_*_var functions renamed to codegen_* (removing _var suffix)
- Updated all function calls throughout codegen.c
Co-authored-by: Claude <noreply@anthropic.com>
Renamed the internal implementation from mrb_parser_dump() to dump_node()
to follow the naming convention of other dump functions (dump_prefix,
dump_str, dump_recur). Added a public wrapper mrb_parser_dump() that
calls dump_node() to maintain API compatibility.
Co-authored-by: Claude <noreply@anthropic.com>
Move str_dump function from commented section to active code and update
dump_str to use proper string dumping with escape sequence handling.
Remove obsolete commented str_dump implementation.
Co-authored-by: Claude <noreply@anthropic.com>
Add proper traversal of cons list structure with (0 . 0) separators
for word arrays (%w[]) and symbol arrays (%i[]). Includes safety
checks for pointer validation and length bounds.
Note: Crashes still occur during testing, indicating the issue may
be in accessor macros or data structure alignment.
Co-authored-by: Claude <noreply@anthropic.com>
Remove mrb_ast_method_node structure definition, accessor macro,
and field accessor macro. This structure had no corresponding
node type enum and was never used in the parser or codegen.
Co-authored-by: Claude <noreply@anthropic.com>