From ac39b7d8addfed9acc04200650cf1f9461417799 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 3 Dec 2025 18:54:29 +0900 Subject: [PATCH] mruby-bin-mirb: enhanced syntax error display syntax errors now show: - line:column format with relative line numbers (matching prompt) - source line from user input - caret indicator pointing to error position example: 1> x = @@@ line 1:6: syntax error, unexpected invalid token x = @@@ ^ multi-line example: 1> class Foo 2* def bar 3* x = @@@ line 3:6: syntax error, unexpected invalid token x = @@@ ^ Co-authored-by: Claude --- mrbgems/mruby-bin-mirb/tools/mirb/mirb.c | 65 +++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index cbf9354ae..17c1fd76a 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -137,6 +137,19 @@ p_error(mrb_state *mrb, struct RObject* exc) if (!mrb_string_p(val)) { val = mrb_obj_as_string(mrb, val); } + + /* get first line of backtrace for location info */ + mrb_value bt = mrb_exc_backtrace(mrb, mrb_obj_value(exc)); + if (mrb_array_p(bt) && RARRAY_LEN(bt) > 0) { + mrb_value location = RARRAY_PTR(bt)[0]; + if (mrb_string_p(location)) { + char* loc_msg = mrb_locale_from_utf8(RSTRING_PTR(location), (int)RSTRING_LEN(location)); + fputs(loc_msg, stdout); + fputs(": ", stdout); + mrb_locale_free(loc_msg); + } + } + char* msg = mrb_locale_from_utf8(RSTRING_PTR(val), (int)RSTRING_LEN(val)); fwrite(msg, strlen(msg), 1, stdout); mrb_locale_free(msg); @@ -374,6 +387,33 @@ print_hint(void) printf("mirb - Embeddable Interactive Ruby Shell\n\n"); } +/* Extract a specific line from source code */ +static const char* +extract_line(const char *str, int target_line, size_t *line_len) +{ + const char *line_start = str; + const char *p = str; + int current_line = 1; + + /* skip to target line */ + while (current_line < target_line && *p) { + if (*p == '\n') { + current_line++; + line_start = p + 1; + } + p++; + } + + /* find line end */ + const char *line_end = line_start; + while (*line_end && *line_end != '\n') { + line_end++; + } + + *line_len = line_end - line_start; + return line_start; +} + #ifndef MRB_USE_READLINE /* Print the command line prompt of the REPL */ static void @@ -654,8 +694,31 @@ main(int argc, char **argv) } if (0 < parser->nerr) { /* syntax error */ + int err_line = parser->error_buffer[0].lineno; + int err_col = parser->error_buffer[0].column; char* msg = mrb_locale_from_utf8(parser->error_buffer[0].message, -1); - printf("line %d: %s\n", parser->error_buffer[0].lineno, msg); + + /* convert absolute line number to relative line within ruby_code */ + int relative_line = err_line - cxt->lineno + 1; + + /* show error with line:column (using relative line number) */ + printf("line %d:%d: %s\n", relative_line, err_col, msg); + + /* show source line and caret if available */ + if (ruby_code[0] != '\0') { + size_t line_len; + const char *line_start = extract_line(ruby_code, relative_line, &line_len); + + if (line_len > 0) { + printf(" %.*s\n", (int)line_len, line_start); + printf(" "); + for (int i = 0; i < err_col; i++) { + printf(" "); + } + printf("^\n"); + } + } + mrb_locale_free(msg); line_num = 1; }