18905 Commits

Author SHA1 Message Date
Yukihiro "Matz" Matsumoto 3af15e0012 mruby-compiler: optimize simple variable pattern to match assignment
For `1 => a`, generate the same bytecode as `a = 1` by leveraging
gen_move's peephole optimization. The peephole optimizer rewrites
LOADI+MOVE into a single LOADI to the target register.

Before: LOADI_1 R2; MOVE R1 R2 (6 bytes)
After:  LOADI_1 R1 (3 bytes)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 15:28:15 +09:00
Yukihiro "Matz" Matsumoto 9d4ad58adc mruby-compiler: optimize pattern matching for always-matching patterns
Skip generating failure handling code (JMP, GETCONST, STRING, SEND,
RAISEIF) when the pattern always succeeds (e.g., simple variable
binding like `1 => a`).

Before: 27 bytes for `1 => a; p a`
After: 13 bytes (52% reduction)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 15:19:21 +09:00
Yukihiro "Matz" Matsumoto 3a012a7b45 TODO.md: add amalgamation
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 13:57:34 +09:00
Yukihiro "Matz" Matsumoto e06ec699a4 mruby-compiler: rename get_node_type to node_type
Resolve static function name collision between parse.y and codegen.c
for amalgamation support.

- parse.y: rename get_node_type() to node_type() (keeps validation)
- codegen.c: replace with node_type() macro (NULL-safe via NODE_TYPE)
- node.h: rename VAR_NODE_TYPE() to NODE_TYPE()

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 12:27:12 +09:00
Yukihiro "Matz" Matsumoto acc82da637 codegen.c: rename new_sym to sym_idx
The function registers a symbol in the IREP symbol table and returns
its index. The new name better reflects this behavior and avoids
collision with parse.y's new_sym (which creates AST nodes).

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 11:44:22 +09:00
Yukihiro "Matz" Matsumoto e369bb3475 mruby-sprintf: rename get_format_info to get_fmt_spec
Avoid static function name collision with mruby-pack for future
amalgamation support.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 11:27:47 +09:00
Yukihiro "Matz" Matsumoto 587561100e mruby-pack: rename get_format_info to pack_format_info
Avoid static function name collision with mruby-sprintf for future
amalgamation support.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 11:02:34 +09:00
Yukihiro "Matz" Matsumoto 768a1f7752 string.c: add mrb_strcasecmp_p for case-insensitive comparison
Move casecmp_p from mruby-string-ext and mruby-encoding to core as
mrb_strcasecmp_p (predicate function returning mrb_bool). Add
MRB_STR_CASECMP_P macro to internal.h for comparing mrb_value strings
with literal strings.

This eliminates code duplication and avoids static function name
collision for future amalgamation support.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 10:41:21 +09:00
Yukihiro "Matz" Matsumoto ce570c28bb mruby-rational: inline int_lshift into rational_new_f
Remove the static int_lshift function and directly call mrb_bint_lshift
at the only call site. This simplifies the code and avoids static
function name collision with src/numeric.c for future amalgamation
support.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 08:50:30 +09:00
Yukihiro "Matz" Matsumoto 3d0b5c848a mruby-eval: rename exec_irep to eval_irep
Avoid static function name collision with src/vm.c for future
amalgamation support.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 08:34:52 +09:00
Yukihiro "Matz" Matsumoto d5c93fc724 class.c, variable.c: rename bsearch_idx to avoid name collision
Rename static bsearch_idx functions to disambiguate:
- class.c: mt_bsearch_idx (method table)
- variable.c: iv_bsearch_idx (instance variable table)

This prepares for future amalgamation support.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 08:32:23 +09:00
Yukihiro "Matz" Matsumoto ca364e3f3e emscripten.rake: use native WASM exception handling
Enable -fwasm-exceptions and -sSUPPORT_LONGJMP=wasm for the Emscripten
toolchain. This implements setjmp/longjmp using native WebAssembly
exception handling instructions instead of Asyncify-based emulation.

Benefits:
- Minimal memory overhead (no shadow stack buffer needed)
- No code size penalty
- Works with both C and C++ code

WASM exception handling is supported by all major browsers since 2021-2022
(Chrome 95+, Firefox 100+, Safari 15.2+) and standalone runtimes
(Node.js 17+, Wasmtime, Wasmer).

For older runtimes, users can override with CFLAGS/LDFLAGS environment
variables.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 18:02:33 +09:00
Yukihiro "Matz" Matsumoto 98c33acc75 mruby.h: fix build with MRB_NO_METHOD_CACHE
mrb_method_cache_clear() was called unconditionally from class.c and
state.c, but the function definition was guarded by MRB_NO_METHOD_CACHE.
This caused linker errors when building with MRB_NO_METHOD_CACHE defined.

Add empty macro definition when MRB_NO_METHOD_CACHE is defined, matching
the existing pattern used for mrb_mc_clear_by_class().

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 13:55:42 +09:00
Yukihiro "Matz" Matsumoto 58604ba1d5 mruby-bin-mirb: extract is_line_blank() helper function
Consolidate duplicated blank line check logic from two places in the
ENTER key handler into a single helper function.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 11:24:12 +09:00
Yukihiro "Matz" Matsumoto c777762c9a mruby-bin-mirb: remove unused cursor save/restore functions
mirb_term_save_cursor() and mirb_term_restore_cursor() were never
called anywhere in the codebase.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 11:22:22 +09:00
Yukihiro "Matz" Matsumoto c418fc90a8 mruby-bin-mirb: add mirb_buffer_to_string_upto_line()
move buffer_to_string_upto_line() from mirb_editor.c to mirb_buffer.c
as a public API. mirb_buffer_to_string() now delegates to this function.
this eliminates code duplication and provides proper module encapsulation.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 10:28:03 +09:00
Yukihiro "Matz" Matsumoto c09fbbd262 mirb_editor.c: extract is_dedent_keyword() helper
consolidate duplicated dedenting keyword detection logic that was
repeated in reindent_line(), handle_tab_indent(), and handle_key().
the helper checks for end, else, elsif, when, in, rescue, ensure, and }.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 09:13:51 +09:00
Yukihiro "Matz" Matsumoto e4b8ae5156 mirb_completion.h: fix double semicolon typo
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-21 17:43:51 +09:00
Yukihiro "Matz" Matsumoto 613b03ac18 mruby-compiler: add no_return_value context flag for script optimization
when running scripts via mruby -e or file, return values are unused.
this adds a no_return_value flag to skip generating unnecessary code.

for parallel assignment like a,b = 1,2:
- before: 18 bytes, 5 registers, creates temporary array
- after: 5 bytes, 3 registers, direct register assignment, no RETURN

the flag is set only for the main program, not for libraries loaded
with -r option. eval() and mirb continue returning values correctly.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-21 17:26:49 +09:00
Yukihiro "Matz" Matsumoto fb5d966a45 mruby-compiler: optimize masgn to generate literals directly into target registers
When all lhs are local variables and all rhs are simple literals
(integers, nil, true, false), generate values directly into target
registers instead of using temporaries and MOVE instructions.

For example, `a,b = 1,2` now generates:
  LOADI_1 R1
  LOADI_2 R2
instead of:
  LOADI_1 R3
  LOADI_2 R4
  MOVE R1 R3
  MOVE R2 R4

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-21 17:26:49 +09:00
Yukihiro "Matz" Matsumoto e84f7bb3b0 mruby-compiler: remove unnecessary MOVE/APOST in simple masgn
For simple parallel assignment like `a,b = 1,2` without rest or post
variables, skip generating APOST instruction and unnecessary MOVE.
The values are already extracted via AREF.

Before (27 bytes):
    LOADI_1 R3
    LOADI_2 R4
    ARRAY   R3 2
    AREF    R1 R3 0
    AREF    R2 R3 1
    MOVE    R4 R3
    APOST   R4 2 0
    MOVE    R4 R3
    RETURN  R3

After (18 bytes):
    LOADI_1 R3
    LOADI_2 R4
    ARRAY   R3 2
    AREF    R1 R3 0
    AREF    R2 R3 1
    RETURN  R3

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-21 17:26:42 +09:00
Yukihiro "Matz" Matsumoto 7e28e68dca string.c: add mrb_utf8_to_buf() to consolidate UTF-8 encoding
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>
2025-12-18 16:30:03 +09:00
Yukihiro "Matz" Matsumoto 53fce124e6 mruby-sprintf: optimize %c to avoid temporary string allocation
For integer arguments, encode UTF-8 directly into a stack buffer
instead of creating a temporary mrb_value string via mrb_str_new()
or calling Integer#chr.

- ~5% faster for single %c
- ~15% faster for multiple %c in one format string
- fixes UTF-8 characters (>= 0x80) which previously raised RangeError

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:27 +09:00
Yukihiro "Matz" Matsumoto baff6e630a mruby-io: add IO#putc and Kernel#putc for efficient character output
IO#putc writes a single character without intermediate string allocation.
- Integer argument: writes byte value (mod 256)
- String argument: writes first character (UTF-8 aware when MRB_UTF8_STRING)
- Returns the argument (IO#putc) or nil (Kernel#putc, matching CRuby)

This provides ~44% memory reduction for character-by-character output
compared to printf "%c" or print ch.chr approaches.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:27 +09:00
Yukihiro "Matz" Matsumoto 814b0cccd9 mruby-bin-mirb: add auto-dedent for 'in' keyword
add support for automatic dedentation when typing 'in' at the
beginning of a line, matching the behavior of 'when' for pattern
matching case/in expressions.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:27 +09:00
Yukihiro "Matz" Matsumoto fb52c1eea2 TODO.md: remove pattern matching (implemented)
pattern matching is now implemented with support for:
- case/in syntax with multiple in-clauses
- array patterns with rest (*) and post-rest elements
- hash patterns with shorthand and rest (**)
- guard clauses (if/unless)
- alternative patterns (|)
- pin operator (^)
- as pattern (=>)
- one-line pattern matching (expr in pat, expr => pat)
- NoMatchingPatternError exception

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:27 +09:00
Yukihiro "Matz" Matsumoto 56d4dab6ed test/syntax.rb: add find pattern tests
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:27 +09:00
Yukihiro "Matz" Matsumoto 6c4d98be8b mruby-compiler: implement find pattern matching
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>
2025-12-18 16:27:26 +09:00
Yukihiro "Matz" Matsumoto 42f5215466 test/syntax.rb: add pattern matching test cases
add comprehensive tests for pattern matching features:
- basic case/in with literals and variables
- array patterns with rest and nested structures
- hash patterns with shorthand and rest
- guard clauses (if/unless)
- alternative patterns (|)
- pin operator (^)
- as pattern (=>)
- one-line pattern matching (in and =>)
- NoMatchingPatternError handling

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:26 +09:00
Yukihiro "Matz" Matsumoto e76ce24860 mruby-compiler: add one-line pattern matching
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>
2025-12-18 16:27:26 +09:00
Yukihiro "Matz" Matsumoto e8096bf745 mruby-compiler: add brace-less hash pattern support
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>
2025-12-18 16:27:26 +09:00
Yukihiro "Matz" Matsumoto 1de6340f1b mruby-compiler: add pin operator to pattern matching
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>
2025-12-18 16:27:26 +09:00
Yukihiro "Matz" Matsumoto 21472638b9 mruby-compiler: implement hash pattern matching
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>
2025-12-18 16:27:26 +09:00
Yukihiro "Matz" Matsumoto ec67fd9587 mruby-compiler: implement array pattern matching
add support for array destructuring patterns in case/in expressions:
- [a, b, c] - fixed length match
- [first, *rest] - head + rest
- [*init, last] - init + tail
- [first, *middle, last] - head + middle + tail
- [1, x, 3] - mixed value and variable patterns
- [first, *, last] - anonymous rest (discarded)

implementation includes:
- grammar rules for p_array, p_array_body, p_rest in parse.y
- NODE_PAT_ARRAY codegen with deconstruct call and length checks
- rest variable binding via range slicing (arr[pre..-(post+1)])
- Array#deconstruct method (returns self)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:26 +09:00
Yukihiro "Matz" Matsumoto e61d71aa29 mruby-compiler: add dump_node() support for pattern matching nodes
Add cases to dump_node() for all pattern matching AST node types:
NODE_CASE_MATCH, NODE_IN, NODE_PAT_VALUE, NODE_PAT_VAR, NODE_PAT_PIN,
NODE_PAT_AS, NODE_PAT_ALT, NODE_PAT_ARRAY, NODE_PAT_HASH.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:25 +09:00
Yukihiro "Matz" Matsumoto 07ac110ddd mruby-compiler: add guard clauses to pattern matching
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>
2025-12-18 16:27:25 +09:00
Yukihiro "Matz" Matsumoto dadfac678d mruby-compiler: add pattern matching (case/in) support
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>
2025-12-18 16:27:25 +09:00
Yukihiro "Matz" Matsumoto 1ae4b7742e TODO.md: update mirb features status
Remove multi-line editing and completion from TODO as they are now
implemented.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:25 +09:00
Yukihiro "Matz" Matsumoto d8045dc5bc mruby-bin-mirb: re-indent current line on Enter
Before inserting a newline, re-indent the current line to match
the expected indent level. This fixes cases where the user typed
with incorrect indentation.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:25 +09:00
Yukihiro "Matz" Matsumoto ac306ae3e5 mruby-bin-mirb: preserve cursor position during TAB auto-indent
When TAB triggers auto-indentation, preserve the cursor's relative
position within the line instead of moving it to the indent boundary.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:25 +09:00
Yukihiro "Matz" Matsumoto f585cc2eff mruby-bin-mirb: align dedent to proper indent level
Instead of just removing 2 spaces, perform_dedent() now calculates
the expected indent level from previous lines and aligns to that.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:24 +09:00
Yukihiro "Matz" Matsumoto e901b6d1a3 mruby-bin-mirb: auto-dedent for else, elsif, when, rescue, ensure
Extend auto-dedent to trigger when typing dedent keywords, not just
end and }. Now dedent occurs when completing: else, elsif, when,
rescue, ensure.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:24 +09:00
Yukihiro "Matz" Matsumoto 1c07ac4892 mruby-bin-mirb: fix indentation when splitting line at dedent keyword
When splitting a line with Enter, check if the new line starts with
a dedenting keyword (end, else, elsif, when, rescue, ensure, }) and
reduce indentation by one level.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:24 +09:00
Yukihiro "Matz" Matsumoto fa74186b51 mruby-bin-mirb: add TAB auto-indentation
TAB now performs auto-indentation instead of completion when:
- cursor is at start of line
- cursor is at end of line
- character before cursor is whitespace

Auto-indent calculates expected indent level from previous lines
and adjusts current line. Dedenting keywords (end, else, elsif,
when, rescue, ensure, }) reduce indent by one level.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:24 +09:00
Yukihiro "Matz" Matsumoto cda57fcf03 mruby-bin-mirb: make Ctrl+K delete empty lines
when pressing Ctrl+K on an empty line, delete the entire line instead
of doing nothing. this makes it easier to clean up empty lines while
editing multi-line input.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:24 +09:00
Yukihiro "Matz" Matsumoto 53e78b8353 mruby-bin-mirb: only evaluate on Enter at end of last line
pressing Enter in the middle of multi-line input now always inserts
a new line instead of evaluating, even if the code is syntactically
complete. evaluation only occurs when cursor is at the end of the
last line.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:24 +09:00
Yukihiro "Matz" Matsumoto d3fcb176bf mruby-bin-mirb: restore tab completion for custom editor
- restore mirb_completion.c/h from before readline removal
- add editor adapter for tab completion (mirb_setup_editor_completion,
  mirb_get_completions, mirb_free_completions)
- add TAB key handling in mirb_editor.c
- fix string literal completion: properly detect when cursor is outside
  a string by scanning forward, allow string/array/hash literals as
  safe receivers for method completion

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:24 +09:00
Yukihiro "Matz" Matsumoto d47dedd17d mruby-bin-mirb: improve Enter key behavior and auto-indentation
- fix Enter in middle of line with trailing blank continuation line:
  now properly splits the line and removes redundant blank line
- fix auto-indentation when inserting in middle of existing code:
  calculate indent from lines up to cursor, not entire buffer
- add mirb_buffer_delete_line() for removing lines from buffer

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:23 +09:00
Yukihiro "Matz" Matsumoto 23c49ad026 mruby-bin-mirb: show incrementing line numbers in multi-line prompts
Previously, all continuation lines showed the same line number (e.g.,
"1*" for every line). Now each line shows its actual line number:

  1> class Foo
  2*   def bar
  3*   end
  4* end

Add mirb_editor_set_prompt_format() which accepts printf-style format
strings (e.g., "%d> ", "%d* ") and calculates the correct prompt length
for each line to ensure proper cursor positioning.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:23 +09:00
Yukihiro "Matz" Matsumoto 5f85c1beae mruby-bin-mirb: add command history with Up/Down navigation
Add in-memory command history for mirb sessions:
- Up arrow on first line: navigate to older history entries
- Down arrow on last line: navigate to newer history entries
- Current input is preserved when browsing and restored when
  navigating past the newest entry
- History uses a circular buffer (100 entries max)
- Duplicate consecutive entries are not added

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:22 +09:00