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 <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-12-03 18:54:29 +09:00
parent 5a3f0e2e0b
commit ac39b7d8ad
+64 -1
View File
@@ -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;
}