From 6396aac434f4ae0898efdeab123f5c2cffff8ef7 Mon Sep 17 00:00:00 2001 From: Mark Delk Date: Mon, 17 Mar 2025 10:47:33 -0500 Subject: [PATCH 01/68] fix a typo, update specs --- src/vm.c | 2 +- test/t/module.rb | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vm.c b/src/vm.c index 5bfae75c2..0ff1d6691 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1915,7 +1915,7 @@ RETRY_TRY_BLOCK: } else if ((m.flags & MRB_METHOD_PROTECTED_FL) && mrb_obj_is_kind_of(mrb, recv, ci->u.target_class)) { mrb_value args = (ci->n == 15) ? regs[1] : mrb_ary_new_from_values(mrb, ci->n, regs+1); - mrb_no_method_error(mrb, mid, args, "proteced method '%n' called for %T", mid, recv); + mrb_no_method_error(mrb, mid, args, "protected method '%n' called for %T", mid, recv); } } ci->cci = CINFO_NONE; diff --git a/test/t/module.rb b/test/t/module.rb index 7a6b895c8..dee47c814 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -802,14 +802,18 @@ assert('method visibility') do v = CallTypeTest.new - assert_raise(NoMethodError) { v.func { :test } } + assert_raise_with_message_pattern(NameError, "private method 'func' called for CallTypeTest") do + v.func { :test } + end assert_equal :test, v.test_private { :test } class CallTypeTest protected :func end - assert_raise(NoMethodError) { v.func { :test } } + assert_raise_with_message_pattern(NameError, "protected method 'func' called for CallTypeTest") do + v.func { :test } + end assert_equal :test, v.test_protected { :test } assert_equal :test, v.test_private { :test } From a55e3685a90e035019445fbce4c5ed5bf1d0b9ea Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 18 Mar 2025 10:40:30 +0900 Subject: [PATCH 02/68] string.c: rename local variable `indx` to `idx` `indx` seems not to be a common abbreviation. --- src/string.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/string.c b/src/string.c index 24e0959cc..abb8b4e36 100644 --- a/src/string.c +++ b/src/string.c @@ -1181,32 +1181,32 @@ enum str_convert_range { }; static enum str_convert_range -str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_int *beg, mrb_int *len) +str_convert_range(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen, mrb_int *beg, mrb_int *len) { if (!mrb_undef_p(alen)) { - *beg = mrb_as_int(mrb, indx); + *beg = mrb_as_int(mrb, idx); *len = mrb_as_int(mrb, alen); return STR_CHAR_RANGE; } else { - switch (mrb_type(indx)) { + switch (mrb_type(idx)) { default: - indx = mrb_ensure_int_type(mrb, indx); + idx = mrb_ensure_int_type(mrb, idx); /* fall through */ case MRB_TT_INTEGER: - *beg = mrb_integer(indx); + *beg = mrb_integer(idx); *len = 1; return STR_CHAR_RANGE; case MRB_TT_STRING: - *beg = str_index_str(mrb, str, indx, 0); + *beg = str_index_str(mrb, str, idx, 0); if (*beg < 0) { break; } - *len = RSTRING_LEN(indx); + *len = RSTRING_LEN(idx); return STR_BYTE_RANGE_CORRECTED; case MRB_TT_RANGE: *len = RSTRING_CHAR_LEN(str); - switch (mrb_range_beg_len(mrb, indx, beg, len, *len, TRUE)) { + switch (mrb_range_beg_len(mrb, idx, beg, len, *len, TRUE)) { case MRB_RANGE_OK: return STR_CHAR_RANGE_CORRECTED; case MRB_RANGE_OUT: @@ -1220,11 +1220,11 @@ str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, } mrb_value -mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen) +mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen) { mrb_int beg, len; - switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) { + switch (str_convert_range(mrb, str, idx, alen, &beg, &len)) { case STR_CHAR_RANGE_CORRECTED: return str_subseq(mrb, str, beg, len); case STR_CHAR_RANGE: @@ -1232,8 +1232,8 @@ mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen) if (mrb_undef_p(alen) && !mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); return str; case STR_BYTE_RANGE_CORRECTED: - if (mrb_string_p(indx)) { - return mrb_str_dup(mrb, indx); + if (mrb_string_p(idx)) { + return mrb_str_dup(mrb, idx); } else { return mrb_str_byte_subseq(mrb, str, beg, len); @@ -1417,12 +1417,12 @@ str_escape(mrb_state *mrb, mrb_value str, mrb_bool inspect) } static void -mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_value replace) +mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen, mrb_value replace) { mrb_int beg, len, charlen; mrb_ensure_string_type(mrb, replace); - switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) { + switch (str_convert_range(mrb, str, idx, alen, &beg, &len)) { case STR_OUT_OF_RANGE: default: mrb_raise(mrb, E_INDEX_ERROR, "string not matched"); @@ -1432,7 +1432,7 @@ mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_ } charlen = RSTRING_CHAR_LEN(str); if (beg < 0) { beg += charlen; } - if (beg < 0 || beg > charlen) { str_out_of_index(mrb, indx); } + if (beg < 0 || beg > charlen) { str_out_of_index(mrb, idx); } /* fall through */ case STR_CHAR_RANGE_CORRECTED: beg = chars2bytes(str, 0, beg); @@ -1460,9 +1460,9 @@ mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_ static mrb_value mrb_str_aset_m(mrb_state *mrb, mrb_value str) { - mrb_value indx, alen, replace; + mrb_value idx, alen, replace; - switch (mrb_get_args(mrb, "oo|S!", &indx, &alen, &replace)) { + switch (mrb_get_args(mrb, "oo|S!", &idx, &alen, &replace)) { case 2: replace = alen; alen = mrb_undef_value(); @@ -1470,7 +1470,7 @@ mrb_str_aset_m(mrb_state *mrb, mrb_value str) case 3: break; } - mrb_str_aset(mrb, str, indx, alen, replace); + mrb_str_aset(mrb, str, idx, alen, replace); return replace; } From 226acdc54031059b2321af2d1cb3426523f900da Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 19 Mar 2025 14:57:02 +0900 Subject: [PATCH 03/68] hash.c (mrb_hash_set_default_proc): avoid repeated calls for mrb_nil_p() --- src/hash.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hash.c b/src/hash.c index bafeff4a7..ffef172b5 100644 --- a/src/hash.c +++ b/src/hash.c @@ -1274,6 +1274,7 @@ mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), key); mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), val); } + static void hash_set_default_proc(mrb_state *mrb, mrb_value hash, mrb_value proc) { @@ -1493,11 +1494,12 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash) mrb_value ifnone = mrb_get_arg1(mrb); hash_modify(mrb, hash); - if (!mrb_nil_p(ifnone)) { + mrb_bool has_ifnone = !mrb_nil_p(ifnone); + if (has_ifnone) { mrb_check_type(mrb, ifnone, MRB_TT_PROC); } mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone); - if (!mrb_nil_p(ifnone)) { + if (has_ifnone) { hash_set_default_proc(mrb, hash, ifnone); } else { From 1ec84a2874b371fdd90ac325ccda75cba6cb816c Mon Sep 17 00:00:00 2001 From: HASUMI Hitoshi Date: Thu, 20 Mar 2025 10:15:20 +0900 Subject: [PATCH 04/68] Fix TYPO in memory.md No `alloc.c` in the source tree. It'd be `allocf.c` --- doc/guides/memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/guides/memory.md b/doc/guides/memory.md index adfa6c0fc..ac62a0482 100644 --- a/doc/guides/memory.md +++ b/doc/guides/memory.md @@ -16,7 +16,7 @@ First, `realloc(NULL, size)` behaves the same as malloc(size). Second, `free(NUL ## Redefine `mrb_default_allocf()` -The only function in mruby that uses the standard C library's memory allocation functions is `mrb_default_allocf()`, defined in `alloc.c`. By defining this function within your application, you can customize the memory management of your application. +The only function in mruby that uses the standard C library's memory allocation functions is `mrb_default_allocf()`, defined in `allocf.c`. By defining this function within your application, you can customize the memory management of your application. ## Specify a function with `mrb_open_allocf()` From dbafada7bed231914a9b6451c70d2fe83c5bfd25 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 20 Mar 2025 13:39:38 +0900 Subject: [PATCH 05/68] mruby-io (mrb_file_basename): use "z" specifier for mrb_get_args() --- mrbgems/mruby-io/src/file.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 269f9c7d5..3e2935e0e 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -246,10 +246,9 @@ mrb_file_basename(mrb_state *mrb, mrb_value klass) char bname[_MAX_DIR]; char extname[_MAX_EXT]; char buffer[_MAX_DIR + _MAX_EXT]; - mrb_value s; + char *path; - mrb_get_args(mrb, "S", &s); - char *path = mrb_str_to_cstr(mrb, s); + mrb_get_args(mrb, "z", &path); size_t ridx = strlen(path); if (ridx > 0) { ridx--; @@ -265,10 +264,9 @@ mrb_file_basename(mrb_state *mrb, mrb_value klass) snprintf(buffer, _MAX_DIR + _MAX_EXT, "%s%s", bname, extname); return mrb_str_new_cstr(mrb, buffer); #else - mrb_value s; - mrb_get_args(mrb, "S", &s); - char *path = mrb_str_to_cstr(mrb, s); - char *bname; + char *path, *bname; + + mrb_get_args(mrb, "z", &path); if ((bname = basename(path)) == NULL) { mrb_sys_fail(mrb, "basename"); } From 5bc391e170dcfd6b5e49b95bf6482cd7b0311866 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 20 Mar 2025 22:03:30 +0900 Subject: [PATCH 06/68] mruby-io (mrb_file_basename): use mrb_str_cat() instead of snprintf(3) --- mrbgems/mruby-io/src/file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 3e2935e0e..3fe31fc00 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -245,7 +245,6 @@ mrb_file_basename(mrb_state *mrb, mrb_value klass) #if defined(_WIN32) char bname[_MAX_DIR]; char extname[_MAX_EXT]; - char buffer[_MAX_DIR + _MAX_EXT]; char *path; mrb_get_args(mrb, "z", &path); @@ -261,8 +260,9 @@ mrb_file_basename(mrb_state *mrb, mrb_value klass) } } _splitpath((const char*)path, NULL, NULL, bname, extname); - snprintf(buffer, _MAX_DIR + _MAX_EXT, "%s%s", bname, extname); - return mrb_str_new_cstr(mrb, buffer); + mrb_value buffer = mrb_str_new_cstr(mrb, bname); + mrb_str_cat_cstr(mrb, buffer, extname); + return buffer; #else char *path, *bname; From 90d4805f5b9fe943f18d3d782ae340c64d604269 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 20 Mar 2025 22:04:49 +0900 Subject: [PATCH 07/68] mruby-io (mrb_file_realpath): use mrb_str_cat instead of mrb_str_append --- mrbgems/mruby-io/src/file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 3fe31fc00..cd82cab78 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -279,10 +279,10 @@ static mrb_value mrb_file_realpath(mrb_state *mrb, mrb_value klass) { mrb_value pathname, dir_string; - mrb_int argc = mrb_get_args(mrb, "S|S", &pathname, &dir_string); - if (argc == 2) { + + if (mrb_get_args(mrb, "S|S", &pathname, &dir_string) == 2) { mrb_value s = mrb_str_dup(mrb, dir_string); - s = mrb_str_append(mrb, s, mrb_str_new_cstr(mrb, FILE_SEPARATOR)); + s = mrb_str_cat_cstr(mrb, s, FILE_SEPARATOR); s = mrb_str_append(mrb, s, pathname); pathname = s; } From 9e315af2a54633c88b60786c3f06075e645d4fd3 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 21 Mar 2025 10:37:05 +0900 Subject: [PATCH 08/68] mruby-io (mrb_file_basename): fixed wrong strncmp usage --- mrbgems/mruby-io/src/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index cd82cab78..6b364d2fe 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -255,7 +255,7 @@ mrb_file_basename(mrb_state *mrb, mrb_value klass) path[ridx] = '\0'; ridx--; } - if (strncmp(path, "/", 2) == 0) { + if (ridx == 0 && path[0] == "/") { return mrb_str_new_cstr(mrb, path); } } @@ -270,7 +270,7 @@ mrb_file_basename(mrb_state *mrb, mrb_value klass) if ((bname = basename(path)) == NULL) { mrb_sys_fail(mrb, "basename"); } - if (strncmp(bname, "//", 3) == 0) bname[1] = '\0'; /* patch for Cygwin */ + if (strcmp(bname, "//") == 0) bname[1] = '\0'; /* patch for Cygwin */ return mrb_str_new_cstr(mrb, bname); #endif } From ca693dec30213976b22c838b5f1ab9e411ce78c5 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 21 Mar 2025 19:31:12 +0900 Subject: [PATCH 09/68] mruby-io (mrb_file_s_readlink): when rc == bufsize, rc never be -1 --- mrbgems/mruby-io/src/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 6b364d2fe..655d8651e 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -768,7 +768,7 @@ mrb_file_s_readlink(mrb_state *mrb, mrb_value klass) tmp = mrb_locale_from_utf8(path, -1); buf = (char*)mrb_malloc(mrb, bufsize); - while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize && rc != -1) { + while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize) { bufsize *= 2; buf = (char*)mrb_realloc(mrb, buf, bufsize); } From f3b79fcc23fd1b2c606f2148571f4c8f9ead2406 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 21 Mar 2025 19:34:05 +0900 Subject: [PATCH 10/68] mruby-io (mrb_file_s_readlink): increase buffer size linearly --- mrbgems/mruby-io/src/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 655d8651e..e9963173a 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -769,7 +769,7 @@ mrb_file_s_readlink(mrb_state *mrb, mrb_value klass) buf = (char*)mrb_malloc(mrb, bufsize); while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize) { - bufsize *= 2; + bufsize += 100; buf = (char*)mrb_realloc(mrb, buf, bufsize); } mrb_locale_free(tmp); From 4e7399d4f428c40df606eb2c21a6d341fbfd8c93 Mon Sep 17 00:00:00 2001 From: dearblue Date: Fri, 21 Mar 2025 21:56:42 +0900 Subject: [PATCH 11/68] Remove `MRB_ENDIAN_LOHI()` that is no longer in use --- include/mruby/value.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/mruby/value.h b/include/mruby/value.h index 6aa7c927e..057054463 100644 --- a/include/mruby/value.h +++ b/include/mruby/value.h @@ -94,12 +94,6 @@ struct mrb_state; # define MRB_PRIx PRIx32 #endif -#ifdef MRB_ENDIAN_BIG -# define MRB_ENDIAN_LOHI(a,b) a b -#else -# define MRB_ENDIAN_LOHI(a,b) b a -#endif - MRB_API mrb_bool mrb_read_int(const char *p, const char *e, char **endp, mrb_int *np); /* obsolete; do not use mrb_int_read() */ MRB_API mrb_int mrb_int_read(const char*, const char*, char**); From aa06aff50d9974e9128a4f6db77bec7fc7970de0 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 22 Mar 2025 11:02:05 +0900 Subject: [PATCH 12/68] mruby-io/file.c: use mrb_sys_fail instead of mrb_raise (w/ errno info) --- mrbgems/mruby-io/src/file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index e9963173a..3a79ff8d7 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -637,7 +637,7 @@ mrb_file_flock(mrb_state *mrb, mrb_value self) } /* FALLTHRU - should not happen */ default: - mrb_sys_fail(mrb, "flock failed"); + mrb_sys_fail(mrb, "flock"); break; } } @@ -651,7 +651,7 @@ mrb_file_size(mrb_state *mrb, mrb_value self) mrb_stat st; int fd = mrb_io_fileno(mrb, self); if (mrb_fstat(fd, &st) == -1) { - mrb_raise(mrb, E_RUNTIME_ERROR, "fstat failed"); + mrb_sys_fail(mrb, "fstat"); } if (st.st_size > MRB_INT_MAX) { @@ -700,7 +700,7 @@ mrb_file_truncate(mrb_state *mrb, mrb_value self) int fd = mrb_io_fileno(mrb, self); mrb_int length = mrb_as_int(mrb, lenv); if (mrb_ftruncate(fd, length) != 0) { - mrb_raise(mrb, E_IO_ERROR, "ftruncate failed"); + mrb_sys_fail(mrb, "ftruncate"); } return mrb_fixnum_value(0); From 11f6251d23107d89b53a9a2cab84ba8dad814910 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 22 Mar 2025 13:00:24 +0900 Subject: [PATCH 13/68] mruby-io (mrb_file_basename): fixed a silly mistake --- mrbgems/mruby-io/src/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 3a79ff8d7..3545ab1e7 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -255,7 +255,7 @@ mrb_file_basename(mrb_state *mrb, mrb_value klass) path[ridx] = '\0'; ridx--; } - if (ridx == 0 && path[0] == "/") { + if (ridx == 0 && path[0] == '/') { return mrb_str_new_cstr(mrb, path); } } From a33426da154eda2e43ad72742759db37babe40d0 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 23 Mar 2025 20:04:25 +0900 Subject: [PATCH 14/68] mruby-io (mrb_file_s_chmod): check string type before casting --- mrbgems/mruby-io/src/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 3545ab1e7..4b652cde2 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -738,6 +738,7 @@ mrb_file_s_chmod(mrb_state *mrb, mrb_value klass) mrb_get_args(mrb, "i*", &mode, &filenames, &argc); for (int i = 0; i < argc; i++) { + mrb_ensure_string_type(mrb, filenames[i]); const char *utf8_path = RSTRING_CSTR(mrb, filenames[i]); char *path = mrb_locale_from_utf8(utf8_path, -1); if (CHMOD(path, mode) == -1) { From 4aae7be949d63601e47bc1752987e379ae4a8c3c Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 24 Mar 2025 14:01:15 +0900 Subject: [PATCH 15/68] mruby-io (path_gethome): move local variable declarations --- mrbgems/mruby-io/src/file.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 4b652cde2..6956289c5 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -429,7 +429,7 @@ path_gethome(mrb_state *mrb, const char **pathp) } } else { - const char *uname = RSTRING_CSTR(mrb, mrb_str_new(mrb, username, len)); + const char *uname = RSTRING_CSTR(mrb, mrb_str_new(mrb, username, (mrb_int)len)); #if defined(_WIN32) || defined(MRB_IO_NO_PWNAM) mrb_raisef(mrb, E_ARGUMENT_ERROR, "user %s doesn't exist", uname); #else @@ -760,15 +760,14 @@ mrb_file_s_readlink(mrb_state *mrb, mrb_value klass) return mrb_nil_value(); // unreachable #else const char *path; - char *buf, *tmp; size_t bufsize = 100; - ssize_t rc; - mrb_value ret; mrb_get_args(mrb, "z", &path); - tmp = mrb_locale_from_utf8(path, -1); - buf = (char*)mrb_malloc(mrb, bufsize); + char *tmp = mrb_locale_from_utf8(path, -1); + char *buf = (char*)mrb_malloc(mrb, bufsize); + + ssize_t rc; while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize) { bufsize += 100; buf = (char*)mrb_realloc(mrb, buf, bufsize); @@ -779,7 +778,8 @@ mrb_file_s_readlink(mrb_state *mrb, mrb_value klass) mrb_sys_fail(mrb, path); } tmp = mrb_utf8_from_locale(buf, -1); - ret = mrb_str_new(mrb, tmp, rc); + + mrb_value ret = mrb_str_new(mrb, tmp, rc); mrb_utf8_free(tmp); mrb_free(mrb, buf); From dd2950211c2e9a635c43ee9dbb52d4d661ef115c Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 24 Mar 2025 15:19:54 +0900 Subject: [PATCH 16/68] mruby-io/mruby_io_test.c: use `-1` for length (means strlen(s)) --- mrbgems/mruby-io/test/mruby_io_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-io/test/mruby_io_test.c b/mrbgems/mruby-io/test/mruby_io_test.c index b21d8ccb4..2f298ceb2 100644 --- a/mrbgems/mruby-io/test/mruby_io_test.c +++ b/mrbgems/mruby-io/test/mruby_io_test.c @@ -276,7 +276,7 @@ mrb_mruby_io_gem_test(mrb_state* mrb) } #endif if (env_home) { - char *utf8 = mrb_utf8_from_locale(env_home, strlen(env_home)); + char *utf8 = mrb_utf8_from_locale(env_home, -1); mrb_value path = mrb_str_new_cstr(mrb, utf8); #ifdef _WIN32 char *pathp = RSTRING_PTR(path); From b8351d6191f623db60ca2f58e5f458a097464f33 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 24 Mar 2025 22:30:53 +0900 Subject: [PATCH 17/68] mruby-io/file.c: use ptrdiff_t for pointer difference --- mrbgems/mruby-io/src/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 6956289c5..8754e02da 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -361,7 +361,7 @@ path_parse(mrb_state *mrb, mrb_value ary, const char *path, int ai) SKIP_DIRSEP(path); const char *path0 = path; NEXT_DIRSEP(path); - int len = path - path0; + ptrdiff_t len = path - path0; if (len == 0) { break; } @@ -412,7 +412,7 @@ path_gethome(mrb_state *mrb, const char **pathp) const char *username = ++*pathp; NEXT_DIRSEP(*pathp); - int len = *pathp - username; + ptrdiff_t len = *pathp - username; if (len == 0) { home = getenv("HOME"); From 7e18b1dadbe8e405d5b7e68852c15dfb72453905 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 24 Mar 2025 22:31:27 +0900 Subject: [PATCH 18/68] mruby-fiber/fibertest.c: use ptrdiff_t for pointer difference --- mrbgems/mruby-fiber/test/fibertest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-fiber/test/fibertest.c b/mrbgems/mruby-fiber/test/fibertest.c index 69680aecb..16d791d0c 100644 --- a/mrbgems/mruby-fiber/test/fibertest.c +++ b/mrbgems/mruby-fiber/test/fibertest.c @@ -21,7 +21,7 @@ fiber_s_yield_by_c_method(mrb_state *mrb, mrb_value self) static mrb_value fiber_resume_by_c_func(mrb_state *mrb, mrb_value self) { - int ci_index = mrb->c->ci - mrb->c->cibase; + ptrdiff_t ci_index = mrb->c->ci - mrb->c->cibase; mrb_value ret = mrb_fiber_resume(mrb, self, 0, NULL); if (ci_index != mrb->c->ci - mrb->c->cibase) { mrb_raisef(mrb, E_EXCEPTION, @@ -34,7 +34,7 @@ fiber_resume_by_c_func(mrb_state *mrb, mrb_value self) static mrb_value fiber_resume_by_c_method(mrb_state *mrb, mrb_value self) { - int ci_index = mrb->c->ci - mrb->c->cibase; + ptrdiff_t ci_index = mrb->c->ci - mrb->c->cibase; mrb_value ret = mrb_funcall_argv(mrb, self, mrb_intern_lit(mrb, "resume"), 0, NULL); if (ci_index != mrb->c->ci - mrb->c->cibase) { mrb_raisef(mrb, E_EXCEPTION, From 4ca61298ac47e86a5422db403e57341c9fe326d9 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 25 Mar 2025 08:08:26 +0900 Subject: [PATCH 19/68] mruby-sprintf: add type cast to silence warning --- mrbgems/mruby-sprintf/src/sprintf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index 45f19a95c..5d6401e3a 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -608,7 +608,7 @@ retry: } mrb_value str = mrb_bint_to_s(mrb, val, base); s = RSTRING_PTR(str); - len = RSTRING_LEN(str); + len = (int)RSTRING_LEN(str); } goto str_skip; #endif From a574034cb2ea08fe478b260a5dabe5b060ff1c87 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 25 Mar 2025 08:09:21 +0900 Subject: [PATCH 20/68] hash.c (hash_set_default_proc): mrb_proc_arity() returns mrb_int --- src/hash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hash.c b/src/hash.c index ffef172b5..e03acbb19 100644 --- a/src/hash.c +++ b/src/hash.c @@ -1280,7 +1280,7 @@ hash_set_default_proc(mrb_state *mrb, mrb_value hash, mrb_value proc) { struct RProc *p = mrb_proc_ptr(proc); if (MRB_PROC_STRICT_P(p)) { - int n = mrb_proc_arity(p); + mrb_int n = mrb_proc_arity(p); if (n != 2 && (n >= 0 || n < -3)) { if (n < 0) n = -n-1; mrb_raisef(mrb, E_TYPE_ERROR, "default_proc takes two arguments (2 for %d)", n); From b446b0d547ce2982e4a7650f4dcb1f9c5afce5ed Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 25 Mar 2025 08:10:04 +0900 Subject: [PATCH 21/68] mruby-compiler: add type cast to silence warning --- mrbgems/mruby-compiler/core/codegen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 357440d20..be0bf875a 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -811,7 +811,7 @@ realloc_pool_str(codegen_scope *s, mrb_irep_pool *p, mrb_int len) str = (char*)p->u.str; str = (char*)codegen_realloc(s, str, len+1); } - p->tt = len<<2 | IREP_TT_STR; + p->tt = (uint32_t)(len<<2 | IREP_TT_STR); str[len] = '\0'; p->u.str = (const char*)str; } From d908daa8b96518b2e9dff53f5239e8d16c51dc96 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 25 Mar 2025 08:10:49 +0900 Subject: [PATCH 22/68] mruby-rational (rational_hash): hash value is uint32_t --- mrbgems/mruby-rational/src/rational.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-rational/src/rational.c b/mrbgems/mruby-rational/src/rational.c index 6e15aa265..d15af73ce 100644 --- a/mrbgems/mruby-rational/src/rational.c +++ b/mrbgems/mruby-rational/src/rational.c @@ -1028,9 +1028,9 @@ rational_hash(mrb_state *mrb, mrb_value rat) #ifdef RAT_BIGINT if (RAT_BIGINT_P(rat)) { mrb_value tmp = mrb_bint_hash(mrb, mrb_obj_value(r->b.num)); - hash = mrb_integer(tmp); + hash = (uint32_t)mrb_integer(tmp); tmp = mrb_bint_hash(mrb, mrb_obj_value(r->b.den)); - hash ^= mrb_integer(tmp); + hash ^= (uint32_t)mrb_integer(tmp); return mrb_int_value(mrb, hash); } #endif From 7351f1127b61c21a8d3a5011a497387712729d02 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 26 Mar 2025 16:18:43 +0900 Subject: [PATCH 23/68] vm.c: ci_bidx() returns mrb_int, that may be different in size from int --- src/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm.c b/src/vm.c index 0ff1d6691..c7e4cc8f2 100644 --- a/src/vm.c +++ b/src/vm.c @@ -845,7 +845,7 @@ mrb_value mrb_object_exec(mrb_state *mrb, mrb_value self, struct RClass *target_class) { mrb_callinfo *ci = mrb->c->ci; - int bidx = mrb_ci_bidx(ci); + mrb_int bidx = ci_bidx(ci); mrb_value blk = ci->stack[bidx]; if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); From 0090e2cd013ec8e44d48f0d26112c58551ad3b38 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 26 Mar 2025 16:22:37 +0900 Subject: [PATCH 24/68] github/workflows/coverage.yml: measure coverage on push only --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index ebee83f73..8d392fe17 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,6 +1,6 @@ name: Coverage -on: [push, pull_request] +on: [push] jobs: coverage: From 68a0ecc78281d4d7ef5820ed17886abca1dd4414 Mon Sep 17 00:00:00 2001 From: John Bampton Date: Fri, 28 Mar 2025 04:24:32 +1000 Subject: [PATCH 25/68] gha: update `build.yml` try `windows-2025` image https://github.com/actions/runner-images?tab=readme-ov-file#available-images https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d33dbb70..deac7ac12 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,8 @@ jobs: - {os: ubuntu-22.04, cc: clang, cxx: clang++} - {os: macos-15, cc: clang, cxx: clang++} - {os: macos-14, cc: clang, cxx: clang++} - - {os: windows-latest, cc: gcc, cxx: g++, altname: "mingw-gcc"} + - {os: windows-2025, cc: gcc, cxx: g++, altname: "mingw-gcc"} + - {os: windows-2022, cc: gcc, cxx: g++, altname: "mingw-gcc"} env: MRUBY_CONFIG: ci/gcc-clang CC: ${{ matrix.cc }} From 7538ee8f5d807e2857bb3c0aeb8d561a382dce21 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 27 Mar 2025 22:15:50 +0900 Subject: [PATCH 26/68] gperf: remove obsolete command line options -p and -g --- build_config/default.rb | 2 +- lib/mruby/build/command.rb | 2 +- tasks/toolchains/visualcpp.rake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build_config/default.rb b/build_config/default.rb index 69943a7d3..21795b453 100644 --- a/build_config/default.rb +++ b/build_config/default.rb @@ -60,7 +60,7 @@ MRuby::Build.new do |conf| # gperf settings # conf.gperf do |gperf| # gperf.command = 'gperf' - # gperf.compile_options = %q[-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"] + # gperf.compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"] # end # file extensions diff --git a/lib/mruby/build/command.rb b/lib/mruby/build/command.rb index 970264407..773166a4f 100644 --- a/lib/mruby/build/command.rb +++ b/lib/mruby/build/command.rb @@ -266,7 +266,7 @@ module MRuby def initialize(build) super @command = 'gperf' - @compile_options = %q[-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"] + @compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"] end def run(outfile, infile) diff --git a/tasks/toolchains/visualcpp.rake b/tasks/toolchains/visualcpp.rake index 53954e9a5..b23793f5a 100644 --- a/tasks/toolchains/visualcpp.rake +++ b/tasks/toolchains/visualcpp.rake @@ -35,7 +35,7 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params| conf.gperf do |gperf| gperf.command = 'gperf.exe' - gperf.compile_options = %q[-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"] + gperf.compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"] end conf.exts do |exts| From 1ae4f9484474210226322241b135c36faeba6582 Mon Sep 17 00:00:00 2001 From: John Bampton Date: Fri, 28 Mar 2025 07:39:59 +1000 Subject: [PATCH 27/68] Clean up and standardize the pre-commit config --- .pre-commit-config.yaml | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6ad1b203..3fededd26 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,9 +10,11 @@ repos: - repo: meta hooks: - id: identity - name: run identity check + name: run identity + description: check your identity - id: check-hooks-apply - name: check hooks apply to the repository + name: run check-hooks-apply + description: check hooks apply to the repository - repo: local hooks: - id: prettier @@ -26,12 +28,14 @@ repos: rev: v8.24.0 hooks: - id: gitleaks - name: detect hardcoded secrets + name: run gitleaks + description: detect hardcoded secrets with gitleaks - repo: https://github.com/shssoichiro/oxipng rev: v9.1.4 hooks: - id: oxipng name: run oxipng + description: use lossless compression to optimize PNG files args: ["-o", "4", "--strip", "safe", "--alpha"] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 @@ -64,10 +68,12 @@ repos: rev: v1.5.5 hooks: - id: forbid-tabs - name: no-tabs checker + name: run no-tabs checker + description: check the codebase for tabs exclude: Makefile$ - id: remove-tabs - name: tabs remover + name: run tabs remover + description: find and convert tabs to spaces args: [--whitespaces-count, "2"] exclude: Makefile$ - repo: https://github.com/rhysd/actionlint @@ -81,7 +87,7 @@ repos: hooks: - id: codespell name: run codespell - description: Check spelling with codespell + description: check spelling with codespell args: [--ignore-words=.github/linters/codespell.txt] - repo: https://github.com/jumanjihouse/pre-commit-hooks rev: 3.0.0 @@ -95,7 +101,7 @@ repos: hooks: - id: markdownlint name: run markdownlint - description: Checks the style of Markdown files + description: checks the style of Markdown files args: [--config=.github/linters/.markdown-lint.yml] types: [markdown] files: \.md$ @@ -104,7 +110,7 @@ repos: hooks: - id: markdown-link-check name: run markdown-link-check - description: Checks hyperlinks in Markdown files + description: checks hyperlinks in Markdown files args: [--config=.github/linters/mlc_config.json, -q] types: [markdown] files: \.md$ @@ -113,12 +119,13 @@ repos: hooks: - id: shellcheck name: run shellcheck + description: check shell scripts with a static analysis tool - repo: https://github.com/adrienverge/yamllint rev: v1.36.0 hooks: - id: yamllint name: run yamllint - description: Check YAML files with yamllint + description: check YAML files with yamllint args: [--strict, -c=.github/linters/.yaml-lint.yml] types: [yaml] files: \.ya?ml$ From c1a0a6193bf16a0a3875de593bc061773918f6ec Mon Sep 17 00:00:00 2001 From: John Bampton Date: Fri, 28 Mar 2025 08:04:21 +1000 Subject: [PATCH 28/68] pre-commit autoupdate --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3fededd26..16f692574 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: language: node additional_dependencies: ["prettier@3.3.3"] - repo: https://github.com/gitleaks/gitleaks - rev: v8.24.0 + rev: v8.24.2 hooks: - id: gitleaks name: run gitleaks @@ -121,7 +121,7 @@ repos: name: run shellcheck description: check shell scripts with a static analysis tool - repo: https://github.com/adrienverge/yamllint - rev: v1.36.0 + rev: v1.37.0 hooks: - id: yamllint name: run yamllint From 2ce0606f147fde7cab9d05edad77033dd81f916f Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 28 Mar 2025 08:20:44 +0900 Subject: [PATCH 29/68] guides/symbol.md: 'punctuation' is uncountable; close #6488 Issue #6488 is addressed by fixing spelling. --- doc/guides/symbol.md | 2 +- include/mruby/presym.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/guides/symbol.md b/doc/guides/symbol.md index dcdb58459..913cb495c 100644 --- a/doc/guides/symbol.md +++ b/doc/guides/symbol.md @@ -71,7 +71,7 @@ header. For `MRB_OPSYM()`, specify the names corresponding to operators (see `MRuby::Presym::OPERATORS` in `lib/mruby/presym.rb` for the names that can be specified for it). Other than that, describe only word characters -excluding leading and ending punctuations. +excluding leading and ending punctuation. These macros are converted to static symbol IDs at compile time, unless preallocate symbols are disabled by `conf.disable_presym`. In that case, diff --git a/include/mruby/presym.h b/include/mruby/presym.h index 7c6374ad1..cb6922218 100644 --- a/include/mruby/presym.h +++ b/include/mruby/presym.h @@ -30,7 +30,7 @@ * For `MRB_OPSYM`, specify the names corresponding to operators (see * `MRuby::Presym::OPERATORS` in `lib/mruby/presym.rb` for the names that * can be specified for it). Other than that, describe only word characters - * excluding leading and ending punctuations. + * excluding leading and ending punctuation. * * These macros are expanded to `mrb_intern_lit` if presym is disabled, * therefore the mruby state variable is required. The above macros can be From 14de6442349266b4eb9c7f08546c5c7fb53e3c6f Mon Sep 17 00:00:00 2001 From: John Bampton Date: Sat, 29 Mar 2025 07:32:47 +1000 Subject: [PATCH 30/68] Update pre-commit Node.js version to `v22.14.0 LTS` --- .pre-commit-config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 16f692574..cceb62c0c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,8 @@ default_stages: [pre-commit, pre-push] default_language_version: # force all unspecified Python hooks to run python3 python: python3 - node: 22.2.0 + # force all unspecified Node hooks to run Node.js v22.14.0 LTS + node: 22.14.0 minimum_pre_commit_version: "3.2.0" repos: - repo: meta From 14f2e6db5aa59aaeec2e58ab59c892fbff35b1a3 Mon Sep 17 00:00:00 2001 From: John Bampton Date: Sat, 29 Mar 2025 16:31:58 +1000 Subject: [PATCH 31/68] pre-commit: update prettier to the latest version https://www.npmjs.com/package/prettier --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cceb62c0c..63426089f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: entry: prettier --write . files: \.(json|md|ya?ml)$ language: node - additional_dependencies: ["prettier@3.3.3"] + additional_dependencies: ["prettier@3.5.3"] - repo: https://github.com/gitleaks/gitleaks rev: v8.24.2 hooks: From b56f984c2520689196bc5caf2be32db9b354353b Mon Sep 17 00:00:00 2001 From: John Bampton Date: Sun, 30 Mar 2025 14:04:34 +1000 Subject: [PATCH 32/68] misc: fix typos --- doc/mruby3.4.md | 2 +- src/array.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/mruby3.4.md b/doc/mruby3.4.md index f21f61dc9..ed623bc9b 100644 --- a/doc/mruby3.4.md +++ b/doc/mruby3.4.md @@ -4,7 +4,7 @@ # The language -- mruby now supports `private` and `protected` visibitily ([b0db0bd](https://github.com/mruby/mruby/commit/b0db0bd)) +- mruby now supports `private` and `protected` visibility ([b0db0bd](https://github.com/mruby/mruby/commit/b0db0bd)) - Maximum length of inlined symbols reduced from 5 to 4 characters to provide space for visibility flags ([6442a01](https://github.com/mruby/mruby/commit/6442a01)) - Many methods are made private according to CRuby visibility ([4a0e806](https://github.com/mruby/mruby/commit/4a0e806)) - Generate OP_SSEND for `self.method` type calls ([111fe4b](https://github.com/mruby/mruby/commit/111fe4b)) diff --git a/src/array.c b/src/array.c index 1d649c10e..f6051157c 100644 --- a/src/array.c +++ b/src/array.c @@ -1407,7 +1407,7 @@ mrb_ary_to_s(mrb_state *mrb, mrb_value self) return ret; } -/* check array equality: 1=equal,0=not_equal,-1=need_elments_check */ +/* check array equality: 1=equal,0=not_equal,-1=need_elements_check */ static mrb_int ary_eq(mrb_state *mrb, mrb_value ary1, mrb_value ary2) { From 206eccb828cde1bf4668cf3eff182114d5601984 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 30 Mar 2025 23:23:02 +0900 Subject: [PATCH 33/68] boxing_word.h: allow 4 byte alignment of object pointers Only if MRB_WORDBOX_NO_FLOAT_TRUNCATE or MRB_NO_FLOAT is defined. --- include/mruby/boxing_word.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/mruby/boxing_word.h b/include/mruby/boxing_word.h index 80736ad18..75b133c33 100644 --- a/include/mruby/boxing_word.h +++ b/include/mruby/boxing_word.h @@ -50,8 +50,9 @@ enum mrb_special_consts { #define WORDBOX_FIXNUM_FLAG (1 << (WORDBOX_FIXNUM_BIT_POS - 1)) #define WORDBOX_FIXNUM_MASK ((1 << WORDBOX_FIXNUM_BIT_POS) - 1) -#if defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) +#if defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) || defined(MRB_NO_FLOAT) /* floats are allocated in heaps */ +#define WORDBOX_IMMEDIATE_MASK 0x03 #define WORDBOX_SYMBOL_BIT_POS 2 #define WORDBOX_SYMBOL_SHIFT WORDBOX_SYMBOL_BIT_POS #define WORDBOX_SYMBOL_FLAG (1 << (WORDBOX_SYMBOL_BIT_POS - 1)) @@ -68,7 +69,9 @@ enum mrb_special_consts { #define WORDBOX_SYMBOL_MASK 0x1f #endif +#ifndef WORDBOX_IMMEDIATE_MASK #define WORDBOX_IMMEDIATE_MASK 0x07 +#endif #define WORDBOX_SET_SHIFT_VALUE(o,n,v) \ ((o).w = (((uintptr_t)(v)) << WORDBOX_##n##_SHIFT) | WORDBOX_##n##_FLAG) @@ -107,7 +110,7 @@ enum mrb_special_consts { * undef : ...0001 0100 * fixnum: ...IIII III1 * symbol: ...SSSS SS10 - * object: ...PPPP P000 (any bits are 1) + * object: ...PPPP PP00 (any bits are 1) */ typedef struct mrb_value { uintptr_t w; @@ -145,7 +148,11 @@ MRB_API mrb_value mrb_word_boxing_float_value(struct mrb_state*, mrb_float); #endif MRB_API mrb_value mrb_boxing_int_value(struct mrb_state*, mrb_int); +#if WORDBOX_IMMEDIATE_MASK == 0x3 +#define mrb_immediate_p(o) ((o).w & WORDBOX_IMMEDIATE_MASK || (o).w <= MRB_Qundef) +#else #define mrb_immediate_p(o) ((o).w & WORDBOX_IMMEDIATE_MASK || (o).w == MRB_Qnil) +#endif #define mrb_ptr(o) mrb_val_union(o).p #define mrb_cptr(o) mrb_val_union(o).vp->p From 4af6fe87c42ef728e20460ea4158f54a5d58eab6 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 31 Mar 2025 21:42:01 +0900 Subject: [PATCH 34/68] doc/mruby3.4.md: remove wrong spelling from the pull-request description --- doc/mruby3.4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mruby3.4.md b/doc/mruby3.4.md index ed623bc9b..ab1b7f14b 100644 --- a/doc/mruby3.4.md +++ b/doc/mruby3.4.md @@ -131,7 +131,7 @@ - [#6243](https://github.com/mruby/mruby/pull/6243) Fixed base64 decoding in `mruby-pack` - [#6244](https://github.com/mruby/mruby/pull/6244) Revise scope of role of `mrb_vm_run()` - [#6246](https://github.com/mruby/mruby/pull/6246) Fix typo in `test/t/hash.rb` -- [#6249](https://github.com/mruby/mruby/pull/6249) Fix grammar in `src/vm.c`; `catched` -> `caught` +- [#6249](https://github.com/mruby/mruby/pull/6249) Fix spelling in `src/vm.c` - [#6250](https://github.com/mruby/mruby/pull/6250) Fix spelling - [#6251](https://github.com/mruby/mruby/pull/6251) Clean up root move `CODEOWNERS` to `.github` directory - [#6253](https://github.com/mruby/mruby/pull/6253) Allow recycling fibers by GC if not referenced directly From 5c2c77c71ca267d11d655838dd7d6aaecac8459f Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 1 Apr 2025 11:00:25 +0900 Subject: [PATCH 35/68] mruby-string (str_succ_bang): fixed sample string; close #6500 To avoid spellcheck failure in pre-commit. --- mrbgems/mruby-string-ext/src/string.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 43b2b78aa..2410612b4 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -909,8 +909,8 @@ int_chr(mrb_state *mrb, mrb_value num) * * Returns next sequence of the string; * - * a = "abc" - * a.succ #=> "abd" + * a = "bed" + * a.succ #=> "bee" */ static mrb_value str_succ_bang(mrb_state *mrb, mrb_value self) From 3684a27209a2eec29eead007347a1d495dbb9edd Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 1 Apr 2025 11:35:49 +0900 Subject: [PATCH 36/68] tools/lrama: update to lrama 0.7.0; close #6384 --- tools/lrama/NEWS.md | 377 +++- tools/lrama/exe/lrama | 1 + tools/lrama/lib/lrama.rb | 39 +- tools/lrama/lib/lrama/bitmap.rb | 7 +- tools/lrama/lib/lrama/command.rb | 25 +- tools/lrama/lib/lrama/context.rb | 20 +- tools/lrama/lib/lrama/counterexamples.rb | 60 +- .../lib/lrama/counterexamples/derivation.rb | 13 +- .../lib/lrama/counterexamples/example.rb | 13 +- tools/lrama/lib/lrama/counterexamples/path.rb | 6 + .../lrama/counterexamples/production_path.rb | 2 + .../lib/lrama/counterexamples/start_path.rb | 2 + .../lib/lrama/counterexamples/state_item.rb | 2 + .../lrama/counterexamples/transition_path.rb | 2 + .../lrama/lib/lrama/counterexamples/triple.rb | 2 + tools/lrama/lib/lrama/diagnostics.rb | 36 + tools/lrama/lib/lrama/digraph.rb | 32 + tools/lrama/lib/lrama/grammar.rb | 92 +- tools/lrama/lib/lrama/grammar/auxiliary.rb | 2 + tools/lrama/lib/lrama/grammar/binding.rb | 63 +- tools/lrama/lib/lrama/grammar/code.rb | 12 +- .../lib/lrama/grammar/code/destructor_code.rb | 2 + .../lrama/grammar/code/initial_action_code.rb | 2 + .../lrama/grammar/code/no_reference_code.rb | 2 + .../lib/lrama/grammar/code/printer_code.rb | 2 + .../lib/lrama/grammar/code/rule_action.rb | 10 +- tools/lrama/lib/lrama/grammar/counter.rb | 2 + tools/lrama/lib/lrama/grammar/destructor.rb | 2 + tools/lrama/lib/lrama/grammar/error_token.rb | 2 + .../lib/lrama/grammar/parameterizing_rule.rb | 2 + .../grammar/parameterizing_rule/resolver.rb | 8 +- .../lrama/grammar/parameterizing_rule/rhs.rb | 9 +- .../lrama/grammar/parameterizing_rule/rule.rb | 6 + tools/lrama/lib/lrama/grammar/percent_code.rb | 2 + tools/lrama/lib/lrama/grammar/precedence.rb | 2 + tools/lrama/lib/lrama/grammar/printer.rb | 2 + tools/lrama/lib/lrama/grammar/reference.rb | 2 + tools/lrama/lib/lrama/grammar/rule.rb | 21 +- tools/lrama/lib/lrama/grammar/rule_builder.rb | 145 +- tools/lrama/lib/lrama/grammar/symbol.rb | 2 + tools/lrama/lib/lrama/grammar/symbols.rb | 2 + .../lib/lrama/grammar/symbols/resolver.rb | 10 +- tools/lrama/lib/lrama/grammar/type.rb | 2 + tools/lrama/lib/lrama/grammar/union.rb | 2 + tools/lrama/lib/lrama/grammar_validator.rb | 37 + tools/lrama/lib/lrama/lexer.rb | 29 +- tools/lrama/lib/lrama/lexer/grammar_file.rb | 11 +- tools/lrama/lib/lrama/lexer/location.rb | 20 +- tools/lrama/lib/lrama/lexer/token.rb | 28 +- tools/lrama/lib/lrama/lexer/token/char.rb | 3 + tools/lrama/lib/lrama/lexer/token/ident.rb | 3 + .../lib/lrama/lexer/token/instantiate_rule.rb | 9 +- tools/lrama/lib/lrama/lexer/token/tag.rb | 6 +- .../lrama/lib/lrama/lexer/token/user_code.rb | 12 +- .../lrama/lib/lrama/{warning.rb => logger.rb} | 22 +- tools/lrama/lib/lrama/option_parser.rb | 99 +- tools/lrama/lib/lrama/options.rb | 8 +- tools/lrama/lib/lrama/output.rb | 107 +- tools/lrama/lib/lrama/parser.rb | 1590 ++++++++--------- tools/lrama/lib/lrama/report.rb | 6 +- tools/lrama/lib/lrama/report/duration.rb | 2 + tools/lrama/lib/lrama/report/profile.rb | 2 + tools/lrama/lib/lrama/state.rb | 301 +++- tools/lrama/lib/lrama/state/reduce.rb | 6 +- .../lib/lrama/state/reduce_reduce_conflict.rb | 2 + .../lib/lrama/state/resolved_conflict.rb | 4 +- tools/lrama/lib/lrama/state/shift.rb | 2 + .../lib/lrama/state/shift_reduce_conflict.rb | 2 + tools/lrama/lib/lrama/states.rb | 123 +- tools/lrama/lib/lrama/states/item.rb | 16 +- tools/lrama/lib/lrama/states_reporter.rb | 79 +- tools/lrama/lib/lrama/trace_reporter.rb | 45 + tools/lrama/lib/lrama/version.rb | 4 +- tools/lrama/template/bison/yacc.c | 45 +- 74 files changed, 2345 insertions(+), 1327 deletions(-) create mode 100644 tools/lrama/lib/lrama/diagnostics.rb create mode 100644 tools/lrama/lib/lrama/grammar_validator.rb rename tools/lrama/lib/lrama/{warning.rb => logger.rb} (52%) create mode 100644 tools/lrama/lib/lrama/trace_reporter.rb diff --git a/tools/lrama/NEWS.md b/tools/lrama/NEWS.md index e652091ae..dcf40b50b 100644 --- a/tools/lrama/NEWS.md +++ b/tools/lrama/NEWS.md @@ -1,12 +1,327 @@ # NEWS for Lrama +## Lrama 0.7.0 (2025-01-21) + +## [EXPERIMENTAL] Support the generation of the IELR(1) parser described in this paper + +Support the generation of the IELR(1) parser described in this paper. +https://www.sciencedirect.com/science/article/pii/S0167642309001191 + +If you use IELR(1) parser, you can write the following directive in your grammar file. + +```yacc +%define lr.type ielr +``` + +But, currently IELR(1) parser is experimental feature. If you find any bugs, please report it to us. Thank you. + +## Support `-t` option as same as `--debug` option + +Support to `-t` option as same as `--debug` option. +These options align with Bison behavior. So same as `--debug` option. + +## Trace only explicit rules + +Support to trace only explicit rules. +If you use `--trace=rules` option, it shows include mid-rule actions. If you want to show only explicit rules, you can use `--trace=only-explicit-rules` option. + +Example: + +```yacc +%{ +%} +%union { + int i; +} +%token number +%type program +%% +program : number { printf("%d", $1); } number { $$ = $1 + $3; } + ; +%% +``` + +Result of `--trace=rules`: + +```console +$ exe/lrama --trace=rules sample.y +Grammar rules: +$accept -> program YYEOF +$@1 -> ε +program -> number $@1 number +``` + +Result of `--trace=only-explicit-rules`: + +```console +$ exe/lrama --trace=explicit-rules sample.y +Grammar rules: +$accept -> program YYEOF +program -> number number +``` + +## Lrama 0.6.11 (2024-12-23) + +### Add support for %type declarations using %nterm in Nonterminal Symbols + +Allow to use `%nterm` in Nonterminal Symbols for `%type` declarations. + +```yacc +%nterm nonterminal… +``` + +This directive is also supported for compatibility with Bison, and only non-terminal symbols are allowed. In other words, definitions like the following will result in an error: + +```yacc +%{ +// Prologue +%} + +%token EOI 0 "EOI" +%nterm EOI + +%% + +program: /* empty */ + ; +``` + +It show an error message like the following: + +```command +❯ exe/lrama nterm.y +nterm.y:6:7: symbol EOI redeclared as a nonterminal +%nterm EOI + ^^^ +``` + +## Lrama 0.6.10 (2024-09-11) + +### Aliased Named References for actions of RHS in parameterizing rules + +Allow to use aliased named references for actions of RHS in parameterizing rules. + +```yacc +%rule sum(X, Y): X[summand] '+' Y[addend] { $$ = $summand + $addend } + ; +``` + +https://github.com/ruby/lrama/pull/410 + +### Named References for actions of RHS in parameterizing rules caller side + +Allow to use named references for actions of RHS in parameterizing rules caller side. + +```yacc +opt_nl: '\n'?[nl] { $$ = $nl; } + ; +``` + +https://github.com/ruby/lrama/pull/414 + +### Widen the definable position of parameterizing rules + +Allow to define parameterizing rules in the middle of the grammar. + +```yacc +%rule defined_option(X): /* empty */ + | X + ; + +%% + +program : defined_option(number) + | defined_list(number) + ; + +%rule defined_list(X): /* empty */ /* <--- here */ + | defined_list(X) number + ; +``` + +https://github.com/ruby/lrama/pull/420 + +### Report unused terminal symbols + +Support to report unused terminal symbols. +Run `exe/lrama --report=terms` to show unused terminal symbols. + +```console +$ exe/lrama --report=terms sample/calc.y + 11 Unused Terms + 0 YYerror + 1 YYUNDEF + 2 '\\\\' + 3 '\\13' + 4 keyword_class2 + 5 tNUMBER + 6 tPLUS + 7 tMINUS + 8 tEQ + 9 tEQEQ + 10 '>' +``` + +https://github.com/ruby/lrama/pull/439 + +### Report unused rules + +Support to report unused rules. +Run `exe/lrama --report=rules` to show unused rules. + +```console +$ exe/lrama --report=rules sample/calc.y + 3 Unused Rules + 0 unused_option + 1 unused_list + 2 unused_nonempty_list +``` + +https://github.com/ruby/lrama/pull/441 + +### Ensure compatibility with Bison for `%locations` directive + +Support `%locations` directive to ensure compatibility with Bison. +Change to `%locations` directive not set by default. + +https://github.com/ruby/lrama/pull/446 + +### Diagnostics report for parameterizing rules redefine + +Support to warning redefined parameterizing rules. +Run `exe/lrama -W` or `exe/lrama --warnings` to show redefined parameterizing rules. + +```console +$ exe/lrama -W sample/calc.y +parameterizing rule redefined: redefined_method(X) +parameterizing rule redefined: redefined_method(X) +``` + +https://github.com/ruby/lrama/pull/448 + +### Support `-v` and `--verbose` option + +Support to `-v` and `--verbose` option. +These options align with Bison behavior. So same as '--report=state' option. + +https://github.com/ruby/lrama/pull/457 + +## Lrama 0.6.9 (2024-05-02) + +### Callee side tag specification of parameterizing rules + +Allow to specify tag on callee side of parameterizing rules. + +```yacc +%union { + int i; +} + +%rule with_tag(X) : X { $$ = $1; } + ; +``` + +### Named References for actions of RHS in parameterizing rules + +Allow to use named references for actions of RHS in parameterizing rules. + +```yacc +%rule option(number): /* empty */ + | number { $$ = $number; } + ; +``` + +## Lrama 0.6.8 (2024-04-29) + +### Nested parameterizing rules with tag + +Allow to nested parameterizing rules with tag. + +```yacc +%union { + int i; +} + +%rule nested_nested_option(X): /* empty */ + | X + ; + +%rule nested_option(X): /* empty */ + | nested_nested_option(X) + ; + +%rule option(Y): /* empty */ + | nested_option(Y) + ; +``` + +## Lrama 0.6.7 (2024-04-28) + +### RHS of user defined parameterizing rules contains `'symbol'?`, `'symbol'+` and `'symbol'*`. + +User can use `'symbol'?`, `'symbol'+` and `'symbol'*` in RHS of user defined parameterizing rules. + +``` +%rule with_word_seps(X): /* empty */ + | X ' '+ + ; +``` + +## Lrama 0.6.6 (2024-04-27) + +### Trace actions + +Support trace actions for debugging. +Run `exe/lrama --trace=actions` to show grammar rules with actions. + +```console +$ exe/lrama --trace=actions sample/calc.y +Grammar rules with actions: +$accept -> list, YYEOF {} +list -> ε {} +list -> list, LF {} +list -> list, expr, LF { printf("=> %d\n", $2); } +expr -> NUM {} +expr -> expr, '+', expr { $$ = $1 + $3; } +expr -> expr, '-', expr { $$ = $1 - $3; } +expr -> expr, '*', expr { $$ = $1 * $3; } +expr -> expr, '/', expr { $$ = $1 / $3; } +expr -> '(', expr, ')' { $$ = $2; } +``` + +### Inlining + +Support inlining for rules. +The `%inline` directive causes all references to symbols to be replaced with its definition. + +```yacc +%rule %inline op: PLUS { + } + | TIMES { * } + ; + +%% + +expr : number { $$ = $1; } + | expr op expr { $$ = $1 $2 $3; } + ; +``` + +as same as + +```yacc +expr : number { $$ = $1; } + | expr '+' expr { $$ = $1 + $3; } + | expr '*' expr { $$ = $1 * $3; } + ; +``` + ## Lrama 0.6.5 (2024-03-25) ### Typed Midrule Actions User can specify the type of mid rule action by tag (``) instead of specifying it with in an action. -``` +```yacc primary: k_case expr_value terms? { $$ = p->case_labels; @@ -21,7 +336,7 @@ primary: k_case expr_value terms? can be written as -``` +```yacc primary: k_case expr_value terms? { $$ = p->case_labels; @@ -46,7 +361,7 @@ Bison supports this feature from 3.1. Support `preceded`, `terminated` and `delimited` rules. -``` +```text program: preceded(opening, X) // Expanded to @@ -73,7 +388,7 @@ program: delimited_opening_X_closing delimited_opening_X_closing: opening X closing ``` - +https://github.com/ruby/lrama/pull/382 ### Support `%destructor` declaration @@ -82,7 +397,7 @@ In general, these resources are freed by actions or after parsing. However if syntax error happens in parsing, these codes may not be executed. Codes associated to `%destructor` are executed when semantic value is popped from the stack by an error. -``` +```yacc %token NUM %type expr2 %type expr @@ -102,7 +417,7 @@ Codes associated to `%destructor` are executed when semantic value is popped fro Bison supports this feature from 1.75b. - +https://github.com/ruby/lrama/pull/385 ## Lrama 0.6.3 (2024-02-15) @@ -130,7 +445,7 @@ Lrama provides these five callbacks. Registered functions are called when each e User also needs to access semantic value of their stack in grammar action. `$:n` provides the way to access to it. `$:n` is translated to the minus index from the top of the stack. For example -``` +```yacc primary: k_if expr_value then compstmt if_tail k_end { /*% ripper: if!($:2, $:4, $:5) %*/ @@ -138,7 +453,7 @@ primary: k_if expr_value then compstmt if_tail k_end } ``` - +https://github.com/ruby/lrama/pull/367 ## Lrama 0.6.2 (2024-01-27) @@ -147,7 +462,7 @@ primary: k_if expr_value then compstmt if_tail k_end If `%no-stdlib` directive is set, Lrama doesn't load Lrama standard library for parameterizing rules, stdlib.y. - +https://github.com/ruby/lrama/pull/344 ## Lrama 0.6.1 (2024-01-13) @@ -155,7 +470,7 @@ parameterizing rules, stdlib.y. Allow to pass an instantiated rule to other parameterizing rules. -``` +```yacc %rule constant(X) : X ; @@ -172,7 +487,7 @@ program : option(constant(number)) // Nested rule Allow to use nested parameterizing rules when define parameterizing rules. -``` +```yacc %rule option(x) : /* empty */ | X ; @@ -191,7 +506,7 @@ program : double_opt(number) %% ``` - +https://github.com/ruby/lrama/pull/337 ## Lrama 0.6.0 (2023-12-25) @@ -199,7 +514,7 @@ program : double_opt(number) Allow to define parameterizing rule by `%rule` directive. -``` +```yacc %rule pair(X, Y): X Y { $$ = $1 + $2; } ; @@ -213,7 +528,7 @@ stmt: pair(ODD, EVEN) ; ``` - +https://github.com/ruby/lrama/pull/285 ## Lrama 0.5.11 (2023-12-02) @@ -222,7 +537,7 @@ stmt: pair(ODD, EVEN) Allow to specify type of rules by specifying tag, `` in below example. Tag is post-modification style. -``` +```yacc %union { int i; } @@ -234,7 +549,7 @@ program : option(number) ; ``` - +https://github.com/ruby/lrama/pull/272 ## Lrama 0.5.10 (2023-11-18) @@ -242,13 +557,13 @@ program : option(number) Support function call style parameterizing rules for `option`, `nonempty_list` and `list`. - +https://github.com/ruby/lrama/pull/197 ### Parameterizing rules (separated_list) Support `separated_list` and `separated_nonempty_list` parameterizing rules. -``` +```text program: separated_list(',', number) // Expanded to @@ -270,7 +585,7 @@ separated_nonempty_list_number: number separated_nonempty_list_number: separated_nonempty_list_number ',' number ``` - +https://github.com/ruby/lrama/pull/204 ## Lrama 0.5.9 (2023-11-05) @@ -279,7 +594,7 @@ separated_nonempty_list_number: separated_nonempty_list_number ',' number Parameterizing rules are template of rules. It's very common pattern to write "list" grammar rule like: -``` +```yacc opt_args: /* none */ | args ; @@ -294,18 +609,18 @@ Lrama supports these suffixes: - `+`: nonempty list - `*`: list -Idea of Parameterizing rules comes from Menhir LR(1) parser generator (). +Idea of Parameterizing rules comes from Menhir LR(1) parser generator (https://gallium.inria.fr/~fpottier/menhir/manual.html#sec32). - +https://github.com/ruby/lrama/pull/181 ## Lrama 0.5.7 (2023-10-23) ### Racc parser Replace Lrama's parser from hand written parser to LR parser generated by Racc. -Lrama uses `--embedded` option to generate LR parser because Racc is changed from default gem to bundled gem by Ruby 3.3 (). +Lrama uses `--embedded` option to generate LR parser because Racc is changed from default gem to bundled gem by Ruby 3.3 (https://github.com/ruby/lrama/pull/132). - +https://github.com/ruby/lrama/pull/62 ## Lrama 0.5.4 (2023-08-17) @@ -316,7 +631,7 @@ Make error recovery function configurable on runtime by two new macros. - `YYMAXREPAIR`: Expected to return max length of repair operations. `%parse-param` is passed to this function. - `YYERROR_RECOVERY_ENABLED`: Expected to return bool value to determine error recovery is enabled or not. `%parse-param` is passed to this function. - +https://github.com/ruby/lrama/pull/74 ## Lrama 0.5.3 (2023-08-05) @@ -325,7 +640,7 @@ Make error recovery function configurable on runtime by two new macros. Support token insert base Error Recovery. `-e` option is needed to generate parser with error recovery functions. - +https://github.com/ruby/lrama/pull/44 ## Lrama 0.5.2 (2023-06-14) @@ -334,7 +649,7 @@ Support token insert base Error Recovery. Instead of positional references like `$1` or `$$`, named references allow to access to symbol by name. -``` +```yacc primary: k_class cpath superclass bodystmt k_end { $primary = new_class($cpath, $bodystmt, $superclass); @@ -343,7 +658,7 @@ primary: k_class cpath superclass bodystmt k_end Alias name can be declared. -``` +```yacc expr[result]: expr[ex-left] '+' expr[ex.right] { $result = $[ex-left] + $[ex.right]; @@ -363,9 +678,9 @@ Bison supports this feature from 2.5. - `YY_REDUCE_PRINT` - `yysyntax_error` - +https://github.com/ruby/lrama/pull/40 -See also: +See also: https://github.com/ruby/ruby/pull/7807 ## Lrama 0.5.0 (2023-05-17) @@ -373,7 +688,7 @@ See also: When `-` is given as grammar file name, reads the grammar source from STDIN, and takes the next argument as the input file name. This mode helps pre-process a grammar source. - +https://github.com/ruby/lrama/pull/8 ## Lrama 0.4.0 (2023-05-13) diff --git a/tools/lrama/exe/lrama b/tools/lrama/exe/lrama index ba5fb06c8..1aece5d14 100755 --- a/tools/lrama/exe/lrama +++ b/tools/lrama/exe/lrama @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true $LOAD_PATH << File.join(__dir__, "../lib") require "lrama" diff --git a/tools/lrama/lib/lrama.rb b/tools/lrama/lib/lrama.rb index 9e517b0d7..fe2e05807 100644 --- a/tools/lrama/lib/lrama.rb +++ b/tools/lrama/lib/lrama.rb @@ -1,17 +1,22 @@ -require "lrama/bitmap" -require "lrama/command" -require "lrama/context" -require "lrama/counterexamples" -require "lrama/digraph" -require "lrama/grammar" -require "lrama/lexer" -require "lrama/option_parser" -require "lrama/options" -require "lrama/output" -require "lrama/parser" -require "lrama/report" -require "lrama/state" -require "lrama/states" -require "lrama/states_reporter" -require "lrama/version" -require "lrama/warning" +# frozen_string_literal: true + +require_relative "lrama/bitmap" +require_relative "lrama/command" +require_relative "lrama/context" +require_relative "lrama/counterexamples" +require_relative "lrama/diagnostics" +require_relative "lrama/digraph" +require_relative "lrama/grammar" +require_relative "lrama/grammar_validator" +require_relative "lrama/lexer" +require_relative "lrama/logger" +require_relative "lrama/option_parser" +require_relative "lrama/options" +require_relative "lrama/output" +require_relative "lrama/parser" +require_relative "lrama/report" +require_relative "lrama/state" +require_relative "lrama/states" +require_relative "lrama/states_reporter" +require_relative "lrama/trace_reporter" +require_relative "lrama/version" diff --git a/tools/lrama/lib/lrama/bitmap.rb b/tools/lrama/lib/lrama/bitmap.rb index 8349a23c3..098c6e0b7 100644 --- a/tools/lrama/lib/lrama/bitmap.rb +++ b/tools/lrama/lib/lrama/bitmap.rb @@ -1,5 +1,9 @@ +# rbs_inline: enabled +# frozen_string_literal: true + module Lrama module Bitmap + # @rbs (Array[Integer] ary) -> Integer def self.from_array(ary) bit = 0 @@ -10,8 +14,9 @@ module Lrama bit end + # @rbs (Integer int) -> Array[Integer] def self.to_array(int) - a = [] + a = [] #: Array[Integer] i = 0 while int > 0 do diff --git a/tools/lrama/lib/lrama/command.rb b/tools/lrama/lib/lrama/command.rb index 12fc4fc7e..3ff39d578 100644 --- a/tools/lrama/lib/lrama/command.rb +++ b/tools/lrama/lib/lrama/command.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Command LRAMA_LIB = File.realpath(File.join(File.dirname(__FILE__))) @@ -14,11 +16,10 @@ module Lrama Report::Duration.enable if options.trace_opts[:time] - warning = Lrama::Warning.new text = options.y.read options.y.close if options.y != STDIN begin - grammar = Lrama::Parser.new(text, options.grammar_file, options.debug).parse + grammar = Lrama::Parser.new(text, options.grammar_file, options.debug, options.define).parse unless grammar.no_stdlib stdlib_grammar = Lrama::Parser.new(File.read(STDLIB_FILE_PATH), STDLIB_FILE_PATH, options.debug).parse grammar.insert_before_parameterizing_rules(stdlib_grammar.parameterizing_rules) @@ -31,8 +32,9 @@ module Lrama message = message.gsub(/.+/, "\e[1m\\&\e[m") if Exception.to_tty? abort message end - states = Lrama::States.new(grammar, warning, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure])) + states = Lrama::States.new(grammar, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure])) states.compute + states.compute_ielr if grammar.ielr_defined? context = Lrama::Context.new(states) if options.report_file @@ -42,15 +44,8 @@ module Lrama end end - if options.trace_opts && options.trace_opts[:rules] - puts "Grammar rules:" - puts grammar.rules - end - - if options.trace_opts && options.trace_opts[:actions] - puts "Grammar rules with actions:" - grammar.rules.each { |rule| puts rule.with_actions } - end + reporter = Lrama::TraceReporter.new(grammar) + reporter.report(**options.trace_opts) File.open(options.outfile, "w+") do |f| Lrama::Output.new( @@ -65,9 +60,9 @@ module Lrama ).render end - if warning.has_error? - exit false - end + logger = Lrama::Logger.new + exit false unless Lrama::GrammarValidator.new(grammar, states, logger).valid? + Lrama::Diagnostics.new(grammar, states, logger).run(options.diagnostic) end end end diff --git a/tools/lrama/lib/lrama/context.rb b/tools/lrama/lib/lrama/context.rb index 32017e65f..9f406f8de 100644 --- a/tools/lrama/lib/lrama/context.rb +++ b/tools/lrama/lib/lrama/context.rb @@ -1,4 +1,6 @@ -require "lrama/report/duration" +# frozen_string_literal: true + +require_relative "report/duration" module Lrama # This is passed to a template @@ -253,7 +255,7 @@ module Lrama # If no default_reduction_rule, default behavior is an # error then replace ErrorActionNumber with zero. - if !state.default_reduction_rule + unless state.default_reduction_rule actions.map! do |e| if e == ErrorActionNumber 0 @@ -301,10 +303,7 @@ module Lrama end @states.nterms.each do |nterm| - if !(states = nterm_to_next_states[nterm]) - default_goto = 0 - not_default_gotos = [] - else + if (states = nterm_to_next_states[nterm]) default_state = states.map(&:last).group_by {|s| s }.max_by {|_, v| v.count }.first default_goto = default_state.id not_default_gotos = [] @@ -312,6 +311,9 @@ module Lrama next if to_state.id == default_goto not_default_gotos << [from_state.id, to_state.id] end + else + default_goto = 0 + not_default_gotos = [] end k = nterm_number_to_sequence_number(nterm.number) @@ -403,7 +405,7 @@ module Lrama @check = [] # Key is froms_and_tos, value is index position pushed = {} - userd_res = {} + used_res = {} lowzero = 0 high = 0 @@ -428,7 +430,7 @@ module Lrama end end - if ok && userd_res[res] + if ok && used_res[res] ok = false end @@ -456,7 +458,7 @@ module Lrama @base[state_id] = res pushed[froms_and_tos] = res - userd_res[res] = true + used_res[res] = true end @yylast = high diff --git a/tools/lrama/lib/lrama/counterexamples.rb b/tools/lrama/lib/lrama/counterexamples.rb index 046265da5..ee2b5d595 100644 --- a/tools/lrama/lib/lrama/counterexamples.rb +++ b/tools/lrama/lib/lrama/counterexamples.rb @@ -1,13 +1,15 @@ +# frozen_string_literal: true + require "set" -require "lrama/counterexamples/derivation" -require "lrama/counterexamples/example" -require "lrama/counterexamples/path" -require "lrama/counterexamples/production_path" -require "lrama/counterexamples/start_path" -require "lrama/counterexamples/state_item" -require "lrama/counterexamples/transition_path" -require "lrama/counterexamples/triple" +require_relative "counterexamples/derivation" +require_relative "counterexamples/example" +require_relative "counterexamples/path" +require_relative "counterexamples/production_path" +require_relative "counterexamples/start_path" +require_relative "counterexamples/state_item" +require_relative "counterexamples/transition_path" +require_relative "counterexamples/triple" module Lrama # See: https://www.cs.cornell.edu/andru/papers/cupex/cupex.pdf @@ -30,8 +32,10 @@ module Lrama conflict_state.conflicts.flat_map do |conflict| case conflict.type when :shift_reduce + # @type var conflict: State::ShiftReduceConflict shift_reduce_example(conflict_state, conflict) when :reduce_reduce + # @type var conflict: State::ReduceReduceConflict reduce_reduce_examples(conflict_state, conflict) end end.compact @@ -46,7 +50,7 @@ module Lrama @reverse_transitions = {} @states.states.each do |src_state| - trans = {} + trans = {} #: Hash[Grammar::Symbol, State] src_state.transitions.each do |shift, next_state| trans[shift.next_sym] = next_state @@ -64,6 +68,7 @@ module Lrama @transitions[[src_state_item, sym]] = dest_state_item + # @type var key: [StateItem, Grammar::Symbol] key = [dest_state_item, sym] @reverse_transitions[key] ||= Set.new @reverse_transitions[key] << src_state_item @@ -80,7 +85,7 @@ module Lrama @states.states.each do |state| # LHS => Set(Item) - h = {} + h = {} #: Hash[Grammar::Symbol, Set[States::Item]] state.closure.each do |item| sym = item.lhs @@ -95,6 +100,7 @@ module Lrama sym = item.next_sym state_item = StateItem.new(state, item) + # @type var key: [State, Grammar::Symbol] key = [state, sym] @productions[state_item] = h[sym] @@ -107,6 +113,7 @@ module Lrama def shift_reduce_example(conflict_state, conflict) conflict_symbol = conflict.symbols.first + # @type var shift_conflict_item: ::Lrama::States::Item shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol } path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol) path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item) @@ -151,12 +158,14 @@ module Lrama prev_state_item = prev_path&.to if target_state_item == state_item || target_state_item.item.start_item? - result.concat(reversed_reduce_path[_j..-1].map(&:to)) + result.concat( + reversed_reduce_path[_j..-1] #: Array[StartPath|TransitionPath|ProductionPath] + .map(&:to)) break end if target_state_item.item.beginning_of_rule? - queue = [] + queue = [] #: Array[Array[StateItem]] queue << [target_state_item] # Find reverse production @@ -171,10 +180,18 @@ module Lrama break end - if !si.item.beginning_of_rule? + if si.item.beginning_of_rule? + # @type var key: [State, Grammar::Symbol] + key = [si.state, si.item.lhs] + @reverse_productions[key].each do |item| + state_item = StateItem.new(si.state, item) + queue << (sis + [state_item]) + end + else + # @type var key: [StateItem, Grammar::Symbol] key = [si, si.item.previous_sym] @reverse_transitions[key].each do |prev_target_state_item| - next if prev_target_state_item.state != prev_state_item.state + next if prev_target_state_item.state != prev_state_item&.state sis.shift result.concat(sis) result << prev_target_state_item @@ -183,19 +200,14 @@ module Lrama queue.clear break end - else - key = [si.state, si.item.lhs] - @reverse_productions[key].each do |item| - state_item = StateItem.new(si.state, item) - queue << (sis + [state_item]) - end end end else # Find reverse transition + # @type var key: [StateItem, Grammar::Symbol] key = [target_state_item, target_state_item.item.previous_sym] @reverse_transitions[key].each do |prev_target_state_item| - next if prev_target_state_item.state != prev_state_item.state + next if prev_target_state_item.state != prev_state_item&.state result << prev_target_state_item target_state_item = prev_target_state_item i = j @@ -222,9 +234,9 @@ module Lrama def shortest_path(conflict_state, conflict_reduce_item, conflict_term) # queue: is an array of [Triple, [Path]] - queue = [] - visited = {} - start_state = @states.states.first + queue = [] #: Array[[Triple, Array[StartPath|TransitionPath|ProductionPath]]] + visited = {} #: Hash[Triple, true] + start_state = @states.states.first #: Lrama::State raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1 start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol])) diff --git a/tools/lrama/lib/lrama/counterexamples/derivation.rb b/tools/lrama/lib/lrama/counterexamples/derivation.rb index 691e93535..368d7f103 100644 --- a/tools/lrama/lib/lrama/counterexamples/derivation.rb +++ b/tools/lrama/lib/lrama/counterexamples/derivation.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Counterexamples class Derivation @@ -16,7 +18,7 @@ module Lrama alias :inspect :to_s def render_strings_for_report - result = [] + result = [] #: Array[String] _render_for_report(self, 0, result, 0) result.map(&:rstrip) end @@ -42,18 +44,19 @@ module Lrama str << "#{item.next_sym.display_name}" length = _render_for_report(derivation.left, len, strings, index + 1) # I want String#ljust! - str << " " * (length - str.length) + str << " " * (length - str.length) if length > str.length else str << " • #{item.symbols_after_dot.map(&:display_name).join(" ")} " return str.length end if derivation.right&.left - length = _render_for_report(derivation.right.left, str.length, strings, index + 1) - str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " + left = derivation.right&.left #: Derivation + length = _render_for_report(left, str.length, strings, index + 1) + str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore str << " " * (length - str.length) if length > str.length elsif item.next_next_sym - str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " + str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore end return str.length diff --git a/tools/lrama/lib/lrama/counterexamples/example.rb b/tools/lrama/lib/lrama/counterexamples/example.rb index 62244a77e..bb08428fc 100644 --- a/tools/lrama/lib/lrama/counterexamples/example.rb +++ b/tools/lrama/lib/lrama/counterexamples/example.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Counterexamples class Example @@ -36,9 +38,10 @@ module Lrama private def _derivations(paths) - derivation = nil + derivation = nil #: Derivation current = :production - lookahead_sym = paths.last.to.item.end_of_rule? ? @conflict_symbol : nil + last_path = paths.last #: Path + lookahead_sym = last_path.to.item.end_of_rule? ? @conflict_symbol : nil paths.reverse_each do |path| item = path.to.item @@ -55,12 +58,14 @@ module Lrama when ProductionPath derivation = Derivation.new(item, derivation) current = :production + else + raise "Unexpected. #{path}" end if lookahead_sym && item.next_next_sym && item.next_next_sym.first_set.include?(lookahead_sym) state_item = @counterexamples.transitions[[path.to, item.next_sym]] derivation2 = find_derivation_for_symbol(state_item, lookahead_sym) - derivation.right = derivation2 + derivation.right = derivation2 # steep:ignore lookahead_sym = nil end @@ -87,7 +92,7 @@ module Lrama end def find_derivation_for_symbol(state_item, sym) - queue = [] + queue = [] #: Array[Array[StateItem]] queue << [state_item] while (sis = queue.shift) diff --git a/tools/lrama/lib/lrama/counterexamples/path.rb b/tools/lrama/lib/lrama/counterexamples/path.rb index edba67a3b..0a5823dd2 100644 --- a/tools/lrama/lib/lrama/counterexamples/path.rb +++ b/tools/lrama/lib/lrama/counterexamples/path.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Counterexamples class Path @@ -18,6 +20,10 @@ module Lrama "#" end alias :inspect :to_s + + def type + raise NotImplementedError + end end end end diff --git a/tools/lrama/lib/lrama/counterexamples/production_path.rb b/tools/lrama/lib/lrama/counterexamples/production_path.rb index d7db68851..0a230c7fc 100644 --- a/tools/lrama/lib/lrama/counterexamples/production_path.rb +++ b/tools/lrama/lib/lrama/counterexamples/production_path.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Counterexamples class ProductionPath < Path diff --git a/tools/lrama/lib/lrama/counterexamples/start_path.rb b/tools/lrama/lib/lrama/counterexamples/start_path.rb index 4a6821cd0..c0351c824 100644 --- a/tools/lrama/lib/lrama/counterexamples/start_path.rb +++ b/tools/lrama/lib/lrama/counterexamples/start_path.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Counterexamples class StartPath < Path diff --git a/tools/lrama/lib/lrama/counterexamples/state_item.rb b/tools/lrama/lib/lrama/counterexamples/state_item.rb index 930ff4a5f..c91981832 100644 --- a/tools/lrama/lib/lrama/counterexamples/state_item.rb +++ b/tools/lrama/lib/lrama/counterexamples/state_item.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Counterexamples class StateItem < Struct.new(:state, :item) diff --git a/tools/lrama/lib/lrama/counterexamples/transition_path.rb b/tools/lrama/lib/lrama/counterexamples/transition_path.rb index 96e611612..47bfbc4f9 100644 --- a/tools/lrama/lib/lrama/counterexamples/transition_path.rb +++ b/tools/lrama/lib/lrama/counterexamples/transition_path.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Counterexamples class TransitionPath < Path diff --git a/tools/lrama/lib/lrama/counterexamples/triple.rb b/tools/lrama/lib/lrama/counterexamples/triple.rb index e802beccf..64014ee22 100644 --- a/tools/lrama/lib/lrama/counterexamples/triple.rb +++ b/tools/lrama/lib/lrama/counterexamples/triple.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Counterexamples # s: state diff --git a/tools/lrama/lib/lrama/diagnostics.rb b/tools/lrama/lib/lrama/diagnostics.rb new file mode 100644 index 000000000..e9da398c8 --- /dev/null +++ b/tools/lrama/lib/lrama/diagnostics.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Lrama + class Diagnostics + def initialize(grammar, states, logger) + @grammar = grammar + @states = states + @logger = logger + end + + def run(diagnostic) + if diagnostic + diagnose_conflict + diagnose_parameterizing_redefined + end + end + + private + + def diagnose_conflict + if @states.sr_conflicts_count != 0 + @logger.warn("shift/reduce conflicts: #{@states.sr_conflicts_count} found") + end + + if @states.rr_conflicts_count != 0 + @logger.warn("reduce/reduce conflicts: #{@states.rr_conflicts_count} found") + end + end + + def diagnose_parameterizing_redefined + @grammar.parameterizing_rule_resolver.redefined_rules.each do |rule| + @logger.warn("parameterizing rule redefined: #{rule}") + end + end + end +end diff --git a/tools/lrama/lib/lrama/digraph.rb b/tools/lrama/lib/lrama/digraph.rb index bbaa86019..2161f3047 100644 --- a/tools/lrama/lib/lrama/digraph.rb +++ b/tools/lrama/lib/lrama/digraph.rb @@ -1,21 +1,52 @@ +# rbs_inline: enabled +# frozen_string_literal: true + module Lrama # Algorithm Digraph of https://dl.acm.org/doi/pdf/10.1145/69622.357187 (P. 625) + # + # @rbs generic X < Object -- Type of a member of `sets` + # @rbs generic Y < _Or -- Type of sets assigned to a member of `sets` class Digraph + # TODO: rbs-inline 0.10.0 doesn't support instance variables. + # Move these type declarations above instance variable definitions, once it's supported. + # + # @rbs! + # interface _Or + # def |: (self) -> self + # end + # @sets: Array[X] + # @relation: Hash[X, Array[X]] + # @base_function: Hash[X, Y] + # @stack: Array[X] + # @h: Hash[X, (Integer|Float)?] + # @result: Hash[X, Y] + + # @rbs sets: Array[X] + # @rbs relation: Hash[X, Array[X]] + # @rbs base_function: Hash[X, Y] + # @rbs return: void def initialize(sets, relation, base_function) + # X in the paper @sets = sets + # R in the paper @relation = relation + # F' in the paper @base_function = base_function + # S in the paper @stack = [] + # N in the paper @h = Hash.new(0) + # F in the paper @result = {} end + # @rbs () -> Hash[X, Y] def compute @sets.each do |x| next if @h[x] != 0 @@ -27,6 +58,7 @@ module Lrama private + # @rbs (X x) -> void def traverse(x) @stack.push(x) d = @stack.count diff --git a/tools/lrama/lib/lrama/grammar.rb b/tools/lrama/lib/lrama/grammar.rb index a816b8261..214ca1a3f 100644 --- a/tools/lrama/lib/lrama/grammar.rb +++ b/tools/lrama/lib/lrama/grammar.rb @@ -1,44 +1,41 @@ +# frozen_string_literal: true + require "forwardable" -require "lrama/grammar/auxiliary" -require "lrama/grammar/binding" -require "lrama/grammar/code" -require "lrama/grammar/counter" -require "lrama/grammar/destructor" -require "lrama/grammar/error_token" -require "lrama/grammar/parameterizing_rule" -require "lrama/grammar/percent_code" -require "lrama/grammar/precedence" -require "lrama/grammar/printer" -require "lrama/grammar/reference" -require "lrama/grammar/rule" -require "lrama/grammar/rule_builder" -require "lrama/grammar/symbol" -require "lrama/grammar/symbols" -require "lrama/grammar/type" -require "lrama/grammar/union" -require "lrama/lexer" +require_relative "grammar/auxiliary" +require_relative "grammar/binding" +require_relative "grammar/code" +require_relative "grammar/counter" +require_relative "grammar/destructor" +require_relative "grammar/error_token" +require_relative "grammar/parameterizing_rule" +require_relative "grammar/percent_code" +require_relative "grammar/precedence" +require_relative "grammar/printer" +require_relative "grammar/reference" +require_relative "grammar/rule" +require_relative "grammar/rule_builder" +require_relative "grammar/symbol" +require_relative "grammar/symbols" +require_relative "grammar/type" +require_relative "grammar/union" +require_relative "lexer" module Lrama # Grammar is the result of parsing an input grammar file class Grammar extend Forwardable - attr_reader :percent_codes, :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux - attr_accessor :union, :expect, - :printers, :error_tokens, - :lex_param, :parse_param, :initial_action, + attr_reader :percent_codes, :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux, :parameterizing_rule_resolver + attr_accessor :union, :expect, :printers, :error_tokens, :lex_param, :parse_param, :initial_action, :after_shift, :before_reduce, :after_reduce, :after_shift_error_token, :after_pop_stack, - :symbols_resolver, :types, - :rules, :rule_builders, - :sym_to_rules, :no_stdlib + :symbols_resolver, :types, :rules, :rule_builders, :sym_to_rules, :no_stdlib, :locations, :define - def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term, + def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term, :find_term_by_s_value, :find_symbol_by_number!, :find_symbol_by_id!, :token_to_symbol, :find_symbol_by_s_value!, :fill_symbol_number, :fill_nterm_type, :fill_printer, :fill_destructor, :fill_error_token, :sort_by_number! - - def initialize(rule_counter) + def initialize(rule_counter, define = {}) @rule_counter = rule_counter # Code defined by "%code" @@ -59,10 +56,16 @@ module Lrama @accept_symbol = nil @aux = Auxiliary.new @no_stdlib = false + @locations = false + @define = define.map {|d| d.split('=') }.to_h append_special_symbols end + def create_rule_builder(rule_counter, midrule_action_counter) + RuleBuilder.new(rule_counter, midrule_action_counter, @parameterizing_rule_resolver) + end + def add_percent_code(id:, code:) @percent_codes << PercentCode.new(id.s_value, code.s_value) end @@ -141,6 +144,7 @@ module Lrama end def prepare + resolve_inline_rules normalize_rules collect_symbols set_lhs_and_rhs @@ -149,6 +153,7 @@ module Lrama fill_sym_to_rules compute_nullable compute_first_set + set_locations end # TODO: More validation methods @@ -167,6 +172,10 @@ module Lrama @sym_to_rules[sym.number] end + def ielr_defined? + @define.key?('lr.type') && @define['lr.type'] == 'ielr' + end + private def compute_nullable @@ -255,7 +264,7 @@ module Lrama def setup_rules @rule_builders.each do |builder| - builder.setup_rules(@parameterizing_rule_resolver) + builder.setup_rules end end @@ -289,10 +298,23 @@ module Lrama @accept_symbol = term end + def resolve_inline_rules + while @rule_builders.any?(&:has_inline_rules?) do + @rule_builders = @rule_builders.flat_map do |builder| + if builder.has_inline_rules? + builder.resolve_inline_rules + else + builder + end + end + end + end + def normalize_rules # Add $accept rule to the top of rules - lineno = @rule_builders.first ? @rule_builders.first.line : 0 - @rules << Rule.new(id: @rule_counter.increment, _lhs: @accept_symbol.id, _rhs: [@rule_builders.first.lhs, @eof_symbol.id], token_code: nil, lineno: lineno) + rule_builder = @rule_builders.first # : RuleBuilder + lineno = rule_builder ? rule_builder.line : 0 + @rules << Rule.new(id: @rule_counter.increment, _lhs: @accept_symbol.id, _rhs: [rule_builder.lhs, @eof_symbol.id], token_code: nil, lineno: lineno) setup_rules @@ -365,17 +387,21 @@ module Lrama end def validate_rule_lhs_is_nterm! - errors = [] + errors = [] #: Array[String] rules.each do |rule| next if rule.lhs.nterm? - errors << "[BUG] LHS of #{rule} (line: #{rule.lineno}) is term. It should be nterm." + errors << "[BUG] LHS of #{rule.display_name} (line: #{rule.lineno}) is term. It should be nterm." end return if errors.empty? raise errors.join("\n") end + + def set_locations + @locations = @locations || @rules.any? {|rule| rule.contains_at_reference? } + end end end diff --git a/tools/lrama/lib/lrama/grammar/auxiliary.rb b/tools/lrama/lib/lrama/grammar/auxiliary.rb index 933574b0f..2bacee6f1 100644 --- a/tools/lrama/lib/lrama/grammar/auxiliary.rb +++ b/tools/lrama/lib/lrama/grammar/auxiliary.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar # Grammar file information not used by States but by Output diff --git a/tools/lrama/lib/lrama/grammar/binding.rb b/tools/lrama/lib/lrama/grammar/binding.rb index e5ea3fb03..2efb918a0 100644 --- a/tools/lrama/lib/lrama/grammar/binding.rb +++ b/tools/lrama/lib/lrama/grammar/binding.rb @@ -1,22 +1,65 @@ +# rbs_inline: enabled +# frozen_string_literal: true + module Lrama class Grammar class Binding - attr_reader :actual_args, :count + # @rbs @actual_args: Array[Lexer::Token] + # @rbs @param_to_arg: Hash[String, Lexer::Token] - def initialize(parameterizing_rule, actual_args) - @parameters = parameterizing_rule.parameters + # @rbs (Array[Lexer::Token] params, Array[Lexer::Token] actual_args) -> void + def initialize(params, actual_args) @actual_args = actual_args - @parameter_to_arg = @parameters.zip(actual_args).map do |param, arg| + @param_to_arg = map_params_to_args(params, @actual_args) + end + + # @rbs (Lexer::Token sym) -> Lexer::Token + def resolve_symbol(sym) + if sym.is_a?(Lexer::Token::InstantiateRule) + Lrama::Lexer::Token::InstantiateRule.new( + s_value: sym.s_value, location: sym.location, args: resolved_args(sym), lhs_tag: sym.lhs_tag + ) + else + param_to_arg(sym) + end + end + + # @rbs (Lexer::Token::InstantiateRule token) -> String + def concatenated_args_str(token) + "#{token.rule_name}_#{token_to_args_s_values(token).join('_')}" + end + + private + + # @rbs (Array[Lexer::Token] params, Array[Lexer::Token] actual_args) -> Hash[String, Lexer::Token] + def map_params_to_args(params, actual_args) + params.zip(actual_args).map do |param, arg| [param.s_value, arg] end.to_h end - def resolve_symbol(symbol) - if symbol.is_a?(Lexer::Token::InstantiateRule) - resolved_args = symbol.args.map { |arg| resolve_symbol(arg) } - Lrama::Lexer::Token::InstantiateRule.new(s_value: symbol.s_value, location: symbol.location, args: resolved_args, lhs_tag: symbol.lhs_tag) - else - @parameter_to_arg[symbol.s_value] || symbol + # @rbs (Lexer::Token::InstantiateRule sym) -> Array[Lexer::Token] + def resolved_args(sym) + sym.args.map { |arg| resolve_symbol(arg) } + end + + # @rbs (Lexer::Token sym) -> Lexer::Token + def param_to_arg(sym) + if (arg = @param_to_arg[sym.s_value].dup) + arg.alias_name = sym.alias_name + end + arg || sym + end + + # @rbs (Lexer::Token::InstantiateRule token) -> Array[String] + def token_to_args_s_values(token) + token.args.flat_map do |arg| + resolved = resolve_symbol(arg) + if resolved.is_a?(Lexer::Token::InstantiateRule) + [resolved.s_value] + resolved.args.map(&:s_value) + else + [resolved.s_value] + end end end end diff --git a/tools/lrama/lib/lrama/grammar/code.rb b/tools/lrama/lib/lrama/grammar/code.rb index 3bad599da..b6c1cc49e 100644 --- a/tools/lrama/lib/lrama/grammar/code.rb +++ b/tools/lrama/lib/lrama/grammar/code.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require "forwardable" -require "lrama/grammar/code/destructor_code" -require "lrama/grammar/code/initial_action_code" -require "lrama/grammar/code/no_reference_code" -require "lrama/grammar/code/printer_code" -require "lrama/grammar/code/rule_action" +require_relative "code/destructor_code" +require_relative "code/initial_action_code" +require_relative "code/no_reference_code" +require_relative "code/printer_code" +require_relative "code/rule_action" module Lrama class Grammar diff --git a/tools/lrama/lib/lrama/grammar/code/destructor_code.rb b/tools/lrama/lib/lrama/grammar/code/destructor_code.rb index 70360eb90..794017257 100644 --- a/tools/lrama/lib/lrama/grammar/code/destructor_code.rb +++ b/tools/lrama/lib/lrama/grammar/code/destructor_code.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Code diff --git a/tools/lrama/lib/lrama/grammar/code/initial_action_code.rb b/tools/lrama/lib/lrama/grammar/code/initial_action_code.rb index a694f193c..02f2badc9 100644 --- a/tools/lrama/lib/lrama/grammar/code/initial_action_code.rb +++ b/tools/lrama/lib/lrama/grammar/code/initial_action_code.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Code diff --git a/tools/lrama/lib/lrama/grammar/code/no_reference_code.rb b/tools/lrama/lib/lrama/grammar/code/no_reference_code.rb index 6e614cc64..ab12f32e2 100644 --- a/tools/lrama/lib/lrama/grammar/code/no_reference_code.rb +++ b/tools/lrama/lib/lrama/grammar/code/no_reference_code.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Code diff --git a/tools/lrama/lib/lrama/grammar/code/printer_code.rb b/tools/lrama/lib/lrama/grammar/code/printer_code.rb index ffccd8939..c0b8d2430 100644 --- a/tools/lrama/lib/lrama/grammar/code/printer_code.rb +++ b/tools/lrama/lib/lrama/grammar/code/printer_code.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Code diff --git a/tools/lrama/lib/lrama/grammar/code/rule_action.rb b/tools/lrama/lib/lrama/grammar/code/rule_action.rb index d3c0eab64..363ecdf25 100644 --- a/tools/lrama/lib/lrama/grammar/code/rule_action.rb +++ b/tools/lrama/lib/lrama/grammar/code/rule_action.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Code @@ -41,6 +43,7 @@ module Lrama when ref.type == :dollar && ref.name == "$" # $$ tag = ref.ex_tag || lhs.tag raise_tag_not_found_error(ref) unless tag + # @type var tag: Lexer::Token::Tag "(yyval.#{tag.member})" when ref.type == :at && ref.name == "$" # @$ "(yyloc)" @@ -50,6 +53,7 @@ module Lrama i = -position_in_rhs + ref.index tag = ref.ex_tag || rhs[ref.index - 1].tag raise_tag_not_found_error(ref) unless tag + # @type var tag: Lexer::Token::Tag "(yyvsp[#{i}].#{tag.member})" when ref.type == :at # @n i = -position_in_rhs + ref.index @@ -69,18 +73,18 @@ module Lrama @rule.position_in_original_rule_rhs || @rule.rhs.count end - # If this is midrule action, RHS is a RHS of the original rule. + # If this is midrule action, RHS is an RHS of the original rule. def rhs (@rule.original_rule || @rule).rhs end - # Unlike `rhs`, LHS is always a LHS of the rule. + # Unlike `rhs`, LHS is always an LHS of the rule. def lhs @rule.lhs end def raise_tag_not_found_error(ref) - raise "Tag is not specified for '$#{ref.value}' in '#{@rule}'" + raise "Tag is not specified for '$#{ref.value}' in '#{@rule.display_name}'" end end end diff --git a/tools/lrama/lib/lrama/grammar/counter.rb b/tools/lrama/lib/lrama/grammar/counter.rb index c13f4ec3e..dc91b87b7 100644 --- a/tools/lrama/lib/lrama/grammar/counter.rb +++ b/tools/lrama/lib/lrama/grammar/counter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Counter diff --git a/tools/lrama/lib/lrama/grammar/destructor.rb b/tools/lrama/lib/lrama/grammar/destructor.rb index 4b7059e92..a2b6fde0e 100644 --- a/tools/lrama/lib/lrama/grammar/destructor.rb +++ b/tools/lrama/lib/lrama/grammar/destructor.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Destructor < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true) diff --git a/tools/lrama/lib/lrama/grammar/error_token.rb b/tools/lrama/lib/lrama/grammar/error_token.rb index 8efde7df3..50eaafeeb 100644 --- a/tools/lrama/lib/lrama/grammar/error_token.rb +++ b/tools/lrama/lib/lrama/grammar/error_token.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class ErrorToken < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true) diff --git a/tools/lrama/lib/lrama/grammar/parameterizing_rule.rb b/tools/lrama/lib/lrama/grammar/parameterizing_rule.rb index d371805f4..ddc1a467c 100644 --- a/tools/lrama/lib/lrama/grammar/parameterizing_rule.rb +++ b/tools/lrama/lib/lrama/grammar/parameterizing_rule.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'parameterizing_rule/resolver' require_relative 'parameterizing_rule/rhs' require_relative 'parameterizing_rule/rule' diff --git a/tools/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb b/tools/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb index d8f3ae789..06f2f1cef 100644 --- a/tools/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb +++ b/tools/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class ParameterizingRule @@ -18,13 +20,17 @@ module Lrama end def find_inline(token) - @rules.select { |rule| rule.name == token.s_value && rule.is_inline }.last + @rules.reverse.find { |rule| rule.name == token.s_value && rule.is_inline } end def created_lhs(lhs_s_value) @created_lhs_list.reverse.find { |created_lhs| created_lhs.s_value == lhs_s_value } end + def redefined_rules + @rules.select { |rule| @rules.count { |r| r.name == rule.name && r.required_parameters_count == rule.required_parameters_count } > 1 } + end + private def select_rules(rules, token) diff --git a/tools/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb b/tools/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb index 3eb92f8ef..f60781c05 100644 --- a/tools/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb +++ b/tools/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class ParameterizingRule @@ -13,7 +15,8 @@ module Lrama def resolve_user_code(bindings) return unless user_code - var_to_arg = {} + resolved = Lexer::Token::UserCode.new(s_value: user_code.s_value, location: user_code.location) + var_to_arg = {} #: Hash[String, String] symbols.each do |sym| resolved_sym = bindings.resolve_symbol(sym) if resolved_sym != sym @@ -22,14 +25,14 @@ module Lrama end var_to_arg.each do |var, arg| - user_code.references.each do |ref| + resolved.references.each do |ref| if ref.name == var ref.name = arg end end end - return user_code + return resolved end end end diff --git a/tools/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb b/tools/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb index 38f0fca4e..cc200d2fb 100644 --- a/tools/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb +++ b/tools/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class ParameterizingRule @@ -12,6 +14,10 @@ module Lrama @is_inline = is_inline @required_parameters_count = parameters.count end + + def to_s + "#{@name}(#{@parameters.map(&:s_value).join(', ')})" + end end end end diff --git a/tools/lrama/lib/lrama/grammar/percent_code.rb b/tools/lrama/lib/lrama/grammar/percent_code.rb index 8cbc5aef2..416a2d275 100644 --- a/tools/lrama/lib/lrama/grammar/percent_code.rb +++ b/tools/lrama/lib/lrama/grammar/percent_code.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class PercentCode diff --git a/tools/lrama/lib/lrama/grammar/precedence.rb b/tools/lrama/lib/lrama/grammar/precedence.rb index fed739b3c..13cf960c3 100644 --- a/tools/lrama/lib/lrama/grammar/precedence.rb +++ b/tools/lrama/lib/lrama/grammar/precedence.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Precedence < Struct.new(:type, :precedence, keyword_init: true) diff --git a/tools/lrama/lib/lrama/grammar/printer.rb b/tools/lrama/lib/lrama/grammar/printer.rb index 8984a96e1..b78459e81 100644 --- a/tools/lrama/lib/lrama/grammar/printer.rb +++ b/tools/lrama/lib/lrama/grammar/printer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Printer < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true) diff --git a/tools/lrama/lib/lrama/grammar/reference.rb b/tools/lrama/lib/lrama/grammar/reference.rb index c56e7673a..b044516bd 100644 --- a/tools/lrama/lib/lrama/grammar/reference.rb +++ b/tools/lrama/lib/lrama/grammar/reference.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar # type: :dollar or :at diff --git a/tools/lrama/lib/lrama/grammar/rule.rb b/tools/lrama/lib/lrama/grammar/rule.rb index 0e06edc80..445752ae0 100644 --- a/tools/lrama/lib/lrama/grammar/rule.rb +++ b/tools/lrama/lib/lrama/grammar/rule.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar # _rhs holds original RHS element. Use rhs to refer to Symbol. @@ -16,10 +18,17 @@ module Lrama self.lineno == other.lineno end - # TODO: Change this to display_name - def to_s + def display_name l = lhs.id.s_value r = empty_rule? ? "ε" : rhs.map {|r| r.id.s_value }.join(" ") + "#{l} -> #{r}" + end + + def display_name_without_action + l = lhs.id.s_value + r = empty_rule? ? "ε" : rhs.map do |r| + r.id.s_value if r.first_set.any? + end.compact.join(" ") "#{l} -> #{r}" end @@ -33,7 +42,7 @@ module Lrama end def with_actions - "#{to_s} {#{token_code&.s_value}}" + "#{display_name} {#{token_code&.s_value}}" end # opt_nl: ε <-- empty_rule @@ -55,6 +64,12 @@ module Lrama Code::RuleAction.new(type: :rule_action, token_code: token_code, rule: self).translated_code end + + def contains_at_reference? + return false unless token_code + + token_code.references.any? {|r| r.type == :at } + end end end end diff --git a/tools/lrama/lib/lrama/grammar/rule_builder.rb b/tools/lrama/lib/lrama/grammar/rule_builder.rb index ccb41e67f..481a3780f 100644 --- a/tools/lrama/lib/lrama/grammar/rule_builder.rb +++ b/tools/lrama/lib/lrama/grammar/rule_builder.rb @@ -1,12 +1,15 @@ +# frozen_string_literal: true + module Lrama class Grammar class RuleBuilder attr_accessor :lhs, :line attr_reader :lhs_tag, :rhs, :user_code, :precedence_sym - def initialize(rule_counter, midrule_action_counter, position_in_original_rule_rhs = nil, lhs_tag: nil, skip_preprocess_references: false) + def initialize(rule_counter, midrule_action_counter, parameterizing_rule_resolver, position_in_original_rule_rhs = nil, lhs_tag: nil, skip_preprocess_references: false) @rule_counter = rule_counter @midrule_action_counter = midrule_action_counter + @parameterizing_rule_resolver = parameterizing_rule_resolver @position_in_original_rule_rhs = position_in_original_rule_rhs @skip_preprocess_references = skip_preprocess_references @@ -19,16 +22,12 @@ module Lrama @rules = [] @rule_builders_for_parameterizing_rules = [] @rule_builders_for_derived_rules = [] - @rule_builders_for_inline_rules = [] @parameterizing_rules = [] - @inline_rules = [] @midrule_action_rules = [] end def add_rhs(rhs) - if !@line - @line = rhs.line - end + @line ||= rhs.line flush_user_code @@ -36,9 +35,7 @@ module Lrama end def user_code=(user_code) - if !@line - @line = user_code&.line - end + @line ||= user_code&.line flush_user_code @@ -55,18 +52,41 @@ module Lrama freeze_rhs end - def setup_rules(parameterizing_rule_resolver) + def setup_rules preprocess_references unless @skip_preprocess_references - if rhs.any? { |token| parameterizing_rule_resolver.find_inline(token) } - resolve_inline(parameterizing_rule_resolver) - else - process_rhs(parameterizing_rule_resolver) - end + process_rhs build_rules end def rules - @parameterizing_rules + @inline_rules + @midrule_action_rules + @rules + @parameterizing_rules + @midrule_action_rules + @rules + end + + def has_inline_rules? + rhs.any? { |token| @parameterizing_rule_resolver.find_inline(token) } + end + + def resolve_inline_rules + resolved_builders = [] #: Array[RuleBuilder] + rhs.each_with_index do |token, i| + if (inline_rule = @parameterizing_rule_resolver.find_inline(token)) + inline_rule.rhs_list.each do |inline_rhs| + rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, lhs_tag: lhs_tag) + if token.is_a?(Lexer::Token::InstantiateRule) + resolve_inline_rhs(rule_builder, inline_rhs, i, Binding.new(inline_rule.parameters, token.args)) + else + resolve_inline_rhs(rule_builder, inline_rhs, i) + end + rule_builder.lhs = lhs + rule_builder.line = line + rule_builder.precedence_sym = precedence_sym + rule_builder.user_code = replace_inline_user_code(inline_rhs, i) + resolved_builders << rule_builder + end + break + end + end + resolved_builders end private @@ -82,31 +102,25 @@ module Lrama def build_rules tokens = @replaced_rhs - if tokens - rule = Rule.new( - id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code, - position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line - ) - @rules = [rule] - @parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder| - rule_builder.rules - end.flatten - @midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder| - rule_builder.rules - end.flatten - @midrule_action_rules.each do |r| - r.original_rule = rule - end - else - @inline_rules = @rule_builders_for_inline_rules.map do |rule_builder| - rule_builder.rules - end.flatten + rule = Rule.new( + id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code, + position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line + ) + @rules = [rule] + @parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder| + rule_builder.rules + end.flatten + @midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder| + rule_builder.rules + end.flatten + @midrule_action_rules.each do |r| + r.original_rule = rule end end # rhs is a mixture of variety type of tokens like `Ident`, `InstantiateRule`, `UserCode` and so on. # `#process_rhs` replaces some kind of tokens to `Ident` so that all `@replaced_rhs` are `Ident` or `Char`. - def process_rhs(parameterizing_rule_resolver) + def process_rhs return if @replaced_rhs @replaced_rhs = [] @@ -118,26 +132,26 @@ module Lrama when Lrama::Lexer::Token::Ident @replaced_rhs << token when Lrama::Lexer::Token::InstantiateRule - parameterizing_rule = parameterizing_rule_resolver.find_rule(token) + parameterizing_rule = @parameterizing_rule_resolver.find_rule(token) raise "Unexpected token. #{token}" unless parameterizing_rule - bindings = Binding.new(parameterizing_rule, token.args) - lhs_s_value = lhs_s_value(token, bindings) - if (created_lhs = parameterizing_rule_resolver.created_lhs(lhs_s_value)) + bindings = Binding.new(parameterizing_rule.parameters, token.args) + lhs_s_value = bindings.concatenated_args_str(token) + if (created_lhs = @parameterizing_rule_resolver.created_lhs(lhs_s_value)) @replaced_rhs << created_lhs else lhs_token = Lrama::Lexer::Token::Ident.new(s_value: lhs_s_value, location: token.location) @replaced_rhs << lhs_token - parameterizing_rule_resolver.created_lhs_list << lhs_token + @parameterizing_rule_resolver.created_lhs_list << lhs_token parameterizing_rule.rhs_list.each do |r| - rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: token.lhs_tag || parameterizing_rule.tag) + rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, lhs_tag: token.lhs_tag || parameterizing_rule.tag) rule_builder.lhs = lhs_token r.symbols.each { |sym| rule_builder.add_rhs(bindings.resolve_symbol(sym)) } rule_builder.line = line rule_builder.precedence_sym = r.precedence_sym rule_builder.user_code = r.resolve_user_code(bindings) rule_builder.complete_input - rule_builder.setup_rules(parameterizing_rule_resolver) + rule_builder.setup_rules @rule_builders_for_parameterizing_rules << rule_builder end end @@ -147,11 +161,11 @@ module Lrama new_token = Lrama::Lexer::Token::Ident.new(s_value: prefix + @midrule_action_counter.increment.to_s) @replaced_rhs << new_token - rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, i, lhs_tag: tag, skip_preprocess_references: true) + rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, i, lhs_tag: tag, skip_preprocess_references: true) rule_builder.lhs = new_token rule_builder.user_code = token rule_builder.complete_input - rule_builder.setup_rules(parameterizing_rule_resolver) + rule_builder.setup_rules @rule_builders_for_derived_rules << rule_builder else @@ -160,39 +174,10 @@ module Lrama end end - def lhs_s_value(token, bindings) - s_values = token.args.map do |arg| - resolved = bindings.resolve_symbol(arg) - if resolved.is_a?(Lexer::Token::InstantiateRule) - [resolved.s_value, resolved.args.map(&:s_value)] - else - resolved.s_value - end - end - "#{token.rule_name}_#{s_values.join('_')}" - end - - def resolve_inline(parameterizing_rule_resolver) - rhs.each_with_index do |token, i| - if inline_rule = parameterizing_rule_resolver.find_inline(token) - inline_rule.rhs_list.each_with_index do |inline_rhs| - rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: lhs_tag, skip_preprocess_references: true) - resolve_inline_rhs(rule_builder, inline_rhs, i) - rule_builder.lhs = lhs - rule_builder.line = line - rule_builder.user_code = replace_inline_user_code(inline_rhs, i) - rule_builder.complete_input - rule_builder.setup_rules(parameterizing_rule_resolver) - @rule_builders_for_inline_rules << rule_builder - end - end - end - end - - def resolve_inline_rhs(rule_builder, inline_rhs, index) + def resolve_inline_rhs(rule_builder, inline_rhs, index, bindings = nil) rhs.each_with_index do |token, i| if index == i - inline_rhs.symbols.each { |sym| rule_builder.add_rhs(sym) } + inline_rhs.symbols.each { |sym| rule_builder.add_rhs(bindings.nil? ? sym : bindings.resolve_symbol(sym)) } else rule_builder.add_rhs(token) end @@ -204,6 +189,11 @@ module Lrama return user_code if user_code.nil? code = user_code.s_value.gsub(/\$#{index + 1}/, inline_rhs.user_code.s_value) + user_code.references.each do |ref| + next if ref.index.nil? || ref.index <= index # nil is a case for `$$` + code = code.gsub(/\$#{ref.index}/, "$#{ref.index + (inline_rhs.symbols.count-1)}") + code = code.gsub(/@#{ref.index}/, "@#{ref.index + (inline_rhs.symbols.count-1)}") + end Lrama::Lexer::Token::UserCode.new(s_value: code, location: user_code.location) end @@ -238,9 +228,6 @@ module Lrama end if ref.number - # TODO: When Inlining is implemented, for example, if `$1` is expanded to multiple RHS tokens, - # `$2` needs to access `$2 + n` to actually access it. So, after the Inlining implementation, - # it needs resolves from number to index. ref.index = ref.number end diff --git a/tools/lrama/lib/lrama/grammar/symbol.rb b/tools/lrama/lib/lrama/grammar/symbol.rb index deb67ad9a..f9dffcad6 100644 --- a/tools/lrama/lib/lrama/grammar/symbol.rb +++ b/tools/lrama/lib/lrama/grammar/symbol.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Symbol is both of nterm and term # `number` is both for nterm and term # `token_id` is tokentype for term, internal sequence number for nterm diff --git a/tools/lrama/lib/lrama/grammar/symbols.rb b/tools/lrama/lib/lrama/grammar/symbols.rb index cc9b4ec55..337241d1b 100644 --- a/tools/lrama/lib/lrama/grammar/symbols.rb +++ b/tools/lrama/lib/lrama/grammar/symbols.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + require_relative "symbols/resolver" diff --git a/tools/lrama/lib/lrama/grammar/symbols/resolver.rb b/tools/lrama/lib/lrama/grammar/symbols/resolver.rb index 1788ed63f..52f4ff90b 100644 --- a/tools/lrama/lib/lrama/grammar/symbols/resolver.rb +++ b/tools/lrama/lib/lrama/grammar/symbols/resolver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Symbols @@ -42,7 +44,9 @@ module Lrama end def add_nterm(id:, alias_name: nil, tag: nil) - return if find_symbol_by_id(id) + if (sym = find_symbol_by_id(id)) + return sym + end @symbols = nil nterm = Symbol.new( @@ -53,6 +57,10 @@ module Lrama nterm end + def find_term_by_s_value(s_value) + terms.find { |s| s.id.s_value == s_value } + end + def find_symbol_by_s_value(s_value) symbols.find { |s| s.id.s_value == s_value } end diff --git a/tools/lrama/lib/lrama/grammar/type.rb b/tools/lrama/lib/lrama/grammar/type.rb index 6b4b0961a..65537288b 100644 --- a/tools/lrama/lib/lrama/grammar/type.rb +++ b/tools/lrama/lib/lrama/grammar/type.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Type diff --git a/tools/lrama/lib/lrama/grammar/union.rb b/tools/lrama/lib/lrama/grammar/union.rb index 854bffb5c..5f1bee006 100644 --- a/tools/lrama/lib/lrama/grammar/union.rb +++ b/tools/lrama/lib/lrama/grammar/union.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Grammar class Union < Struct.new(:code, :lineno, keyword_init: true) diff --git a/tools/lrama/lib/lrama/grammar_validator.rb b/tools/lrama/lib/lrama/grammar_validator.rb new file mode 100644 index 000000000..779049958 --- /dev/null +++ b/tools/lrama/lib/lrama/grammar_validator.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Lrama + class GrammarValidator + def initialize(grammar, states, logger) + @grammar = grammar + @states = states + @logger = logger + end + + def valid? + conflicts_within_threshold? + end + + private + + def conflicts_within_threshold? + return true unless @grammar.expect + + [sr_conflicts_within_threshold(@grammar.expect), rr_conflicts_within_threshold(0)].all? + end + + def sr_conflicts_within_threshold(expected) + return true if expected == @states.sr_conflicts_count + + @logger.error("shift/reduce conflicts: #{@states.sr_conflicts_count} found, #{expected} expected") + false + end + + def rr_conflicts_within_threshold(expected) + return true if expected == @states.rr_conflicts_count + + @logger.error("reduce/reduce conflicts: #{@states.rr_conflicts_count} found, #{expected} expected") + false + end + end +end diff --git a/tools/lrama/lib/lrama/lexer.rb b/tools/lrama/lib/lrama/lexer.rb index 40622a51b..c50af82ae 100644 --- a/tools/lrama/lib/lrama/lexer.rb +++ b/tools/lrama/lib/lrama/lexer.rb @@ -1,19 +1,22 @@ +# frozen_string_literal: true + require "strscan" -require "lrama/lexer/grammar_file" -require "lrama/lexer/location" -require "lrama/lexer/token" +require_relative "lexer/grammar_file" +require_relative "lexer/location" +require_relative "lexer/token" module Lrama class Lexer attr_reader :head_line, :head_column, :line attr_accessor :status, :end_symbol - SYMBOLS = ['%{', '%}', '%%', '{', '}', '\[', '\]', '\(', '\)', '\,', ':', '\|', ';'] + SYMBOLS = ['%{', '%}', '%%', '{', '}', '\[', '\]', '\(', '\)', '\,', ':', '\|', ';'].freeze PERCENT_TOKENS = %w( %union %token %type + %nterm %left %right %nonassoc @@ -38,7 +41,8 @@ module Lrama %rule %no-stdlib %inline - ) + %locations + ).freeze def initialize(grammar_file) @grammar_file = grammar_file @@ -71,7 +75,7 @@ module Lrama end def lex_token - while !@scanner.eos? do + until @scanner.eos? do case when @scanner.scan(/\n/) newline @@ -126,7 +130,7 @@ module Lrama code = '' reset_first_position - while !@scanner.eos? do + until @scanner.eos? do case when @scanner.scan(/{/) code += @scanner.matched @@ -163,14 +167,13 @@ module Lrama private def lex_comment - while !@scanner.eos? do + until @scanner.eos? do case - when @scanner.scan(/\n/) - newline - when @scanner.scan(/\*\//) + when @scanner.scan_until(/[\s\S]*?\*\//) + @scanner.matched.count("\n").times { newline } return - else - @scanner.getch + when @scanner.scan_until(/\n/) + newline end end end diff --git a/tools/lrama/lib/lrama/lexer/grammar_file.rb b/tools/lrama/lib/lrama/lexer/grammar_file.rb index 3d3368625..37e82ff18 100644 --- a/tools/lrama/lib/lrama/lexer/grammar_file.rb +++ b/tools/lrama/lib/lrama/lexer/grammar_file.rb @@ -1,28 +1,37 @@ +# rbs_inline: enabled +# frozen_string_literal: true + module Lrama class Lexer class GrammarFile class Text < String + # @rbs () -> String def inspect length <= 50 ? super : "#{self[0..47]}...".inspect end end - attr_reader :path, :text + attr_reader :path #: String + attr_reader :text #: String + # @rbs (String path, String text) -> void def initialize(path, text) @path = path @text = Text.new(text).freeze end + # @rbs () -> String def inspect "<#{self.class}: @path=#{path}, @text=#{text.inspect}>" end + # @rbs (GrammarFile other) -> bool def ==(other) self.class == other.class && self.path == other.path end + # @rbs () -> Array[String] def lines @lines ||= text.split("\n") end diff --git a/tools/lrama/lib/lrama/lexer/location.rb b/tools/lrama/lib/lrama/lexer/location.rb index aefce3e16..defdbf8a0 100644 --- a/tools/lrama/lib/lrama/lexer/location.rb +++ b/tools/lrama/lib/lrama/lexer/location.rb @@ -1,8 +1,16 @@ +# rbs_inline: enabled +# frozen_string_literal: true + module Lrama class Lexer class Location - attr_reader :grammar_file, :first_line, :first_column, :last_line, :last_column + attr_reader :grammar_file #: GrammarFile + attr_reader :first_line #: Integer + attr_reader :first_column #: Integer + attr_reader :last_line #: Integer + attr_reader :last_column #: Integer + # @rbs (grammar_file: GrammarFile, first_line: Integer, first_column: Integer, last_line: Integer, last_column: Integer) -> void def initialize(grammar_file:, first_line:, first_column:, last_line:, last_column:) @grammar_file = grammar_file @first_line = first_line @@ -11,6 +19,7 @@ module Lrama @last_column = last_column end + # @rbs (Location other) -> bool def ==(other) self.class == other.class && self.grammar_file == other.grammar_file && @@ -20,6 +29,7 @@ module Lrama self.last_column == other.last_column end + # @rbs (Integer left, Integer right) -> Location def partial_location(left, right) offset = -first_column new_first_line = -1 @@ -50,10 +60,12 @@ module Lrama ) end + # @rbs () -> String def to_s "#{path} (#{first_line},#{first_column})-(#{last_line},#{last_column})" end + # @rbs (String error_message) -> String def generate_error_message(error_message) <<~ERROR.chomp #{path}:#{first_line}:#{first_column}: #{error_message} @@ -61,6 +73,7 @@ module Lrama ERROR end + # @rbs () -> String def line_with_carets <<~TEXT #{text} @@ -70,22 +83,27 @@ module Lrama private + # @rbs () -> String def path grammar_file.path end + # @rbs () -> String def blanks (text[0...first_column] or raise "#{first_column} is invalid").gsub(/[^\t]/, ' ') end + # @rbs () -> String def carets blanks + '^' * (last_column - first_column) end + # @rbs () -> String def text @text ||= _text.join("\n") end + # @rbs () -> Array[String] def _text @_text ||=begin range = (first_line - 1)...last_line diff --git a/tools/lrama/lib/lrama/lexer/token.rb b/tools/lrama/lib/lrama/lexer/token.rb index 59b49d5fb..63da8be4a 100644 --- a/tools/lrama/lib/lrama/lexer/token.rb +++ b/tools/lrama/lib/lrama/lexer/token.rb @@ -1,15 +1,21 @@ -require 'lrama/lexer/token/char' -require 'lrama/lexer/token/ident' -require 'lrama/lexer/token/instantiate_rule' -require 'lrama/lexer/token/tag' -require 'lrama/lexer/token/user_code' +# rbs_inline: enabled +# frozen_string_literal: true + +require_relative 'token/char' +require_relative 'token/ident' +require_relative 'token/instantiate_rule' +require_relative 'token/tag' +require_relative 'token/user_code' module Lrama class Lexer class Token - attr_reader :s_value, :location - attr_accessor :alias_name, :referred + attr_reader :s_value #: String + attr_reader :location #: Location + attr_accessor :alias_name #: String + attr_accessor :referred #: bool + # @rbs (s_value: String, ?alias_name: String, ?location: Location) -> void def initialize(s_value:, alias_name: nil, location: nil) s_value.freeze @s_value = s_value @@ -17,36 +23,44 @@ module Lrama @location = location end + # @rbs () -> String def to_s "value: `#{s_value}`, location: #{location}" end + # @rbs (String string) -> bool def referred_by?(string) [self.s_value, self.alias_name].compact.include?(string) end + # @rbs (Token other) -> bool def ==(other) self.class == other.class && self.s_value == other.s_value end + # @rbs () -> Integer def first_line location.first_line end alias :line :first_line + # @rbs () -> Integer def first_column location.first_column end alias :column :first_column + # @rbs () -> Integer def last_line location.last_line end + # @rbs () -> Integer def last_column location.last_column end + # @rbs (Lrama::Grammar::Reference ref, String message) -> bot def invalid_ref(ref, message) location = self.location.partial_location(ref.first_column, ref.last_column) raise location.generate_error_message(message) diff --git a/tools/lrama/lib/lrama/lexer/token/char.rb b/tools/lrama/lib/lrama/lexer/token/char.rb index ec3560ca0..fcab7a588 100644 --- a/tools/lrama/lib/lrama/lexer/token/char.rb +++ b/tools/lrama/lib/lrama/lexer/token/char.rb @@ -1,3 +1,6 @@ +# rbs_inline: enabled +# frozen_string_literal: true + module Lrama class Lexer class Token diff --git a/tools/lrama/lib/lrama/lexer/token/ident.rb b/tools/lrama/lib/lrama/lexer/token/ident.rb index e576eaecc..8b1328a04 100644 --- a/tools/lrama/lib/lrama/lexer/token/ident.rb +++ b/tools/lrama/lib/lrama/lexer/token/ident.rb @@ -1,3 +1,6 @@ +# rbs_inline: enabled +# frozen_string_literal: true + module Lrama class Lexer class Token diff --git a/tools/lrama/lib/lrama/lexer/token/instantiate_rule.rb b/tools/lrama/lib/lrama/lexer/token/instantiate_rule.rb index 1c4d1095c..37d412aa8 100644 --- a/tools/lrama/lib/lrama/lexer/token/instantiate_rule.rb +++ b/tools/lrama/lib/lrama/lexer/token/instantiate_rule.rb @@ -1,19 +1,26 @@ +# rbs_inline: enabled +# frozen_string_literal: true + module Lrama class Lexer class Token class InstantiateRule < Token - attr_reader :args, :lhs_tag + attr_reader :args #: Array[Lexer::Token] + attr_reader :lhs_tag #: Lexer::Token::Tag? + # @rbs (s_value: String, ?alias_name: String, ?location: Location, ?args: Array[Lexer::Token], ?lhs_tag: Lexer::Token::Tag?) -> void def initialize(s_value:, alias_name: nil, location: nil, args: [], lhs_tag: nil) super s_value: s_value, alias_name: alias_name, location: location @args = args @lhs_tag = lhs_tag end + # @rbs () -> String def rule_name s_value end + # @rbs () -> Integer def args_count args.count end diff --git a/tools/lrama/lib/lrama/lexer/token/tag.rb b/tools/lrama/lib/lrama/lexer/token/tag.rb index e54d77391..b346ef7c5 100644 --- a/tools/lrama/lib/lrama/lexer/token/tag.rb +++ b/tools/lrama/lib/lrama/lexer/token/tag.rb @@ -1,9 +1,13 @@ +# rbs_inline: enabled +# frozen_string_literal: true + module Lrama class Lexer class Token class Tag < Token - # Omit "<>" + # @rbs () -> String def member + # Omit "<>" s_value[1..-2] or raise "Unexpected Tag format (#{s_value})" end end diff --git a/tools/lrama/lib/lrama/lexer/token/user_code.rb b/tools/lrama/lib/lrama/lexer/token/user_code.rb index 4d487bf01..4ef40e6dc 100644 --- a/tools/lrama/lib/lrama/lexer/token/user_code.rb +++ b/tools/lrama/lib/lrama/lexer/token/user_code.rb @@ -1,22 +1,27 @@ +# rbs_inline: enabled +# frozen_string_literal: true + require "strscan" module Lrama class Lexer class Token class UserCode < Token - attr_accessor :tag + attr_accessor :tag #: Lexer::Token::Tag + # @rbs () -> Array[Lrama::Grammar::Reference] def references @references ||= _references end private + # @rbs () -> Array[Lrama::Grammar::Reference] def _references scanner = StringScanner.new(s_value) - references = [] + references = [] #: Array[Grammar::Reference] - while !scanner.eos? do + until scanner.eos? do case when reference = scan_reference(scanner) references << reference @@ -30,6 +35,7 @@ module Lrama references end + # @rbs (StringScanner scanner) -> Lrama::Grammar::Reference? def scan_reference(scanner) start = scanner.pos case diff --git a/tools/lrama/lib/lrama/warning.rb b/tools/lrama/lib/lrama/logger.rb similarity index 52% rename from tools/lrama/lib/lrama/warning.rb rename to tools/lrama/lib/lrama/logger.rb index 3c99791eb..88bb92096 100644 --- a/tools/lrama/lib/lrama/warning.rb +++ b/tools/lrama/lib/lrama/logger.rb @@ -1,25 +1,21 @@ -module Lrama - class Warning - attr_reader :errors, :warns +# rbs_inline: enabled +# frozen_string_literal: true +module Lrama + class Logger + # @rbs (IO out) -> void def initialize(out = STDERR) @out = out - @errors = [] - @warns = [] - end - - def error(message) - @out << message << "\n" - @errors << message end + # @rbs (String message) -> void def warn(message) @out << message << "\n" - @warns << message end - def has_error? - !@errors.empty? + # @rbs (String message) -> void + def error(message) + @out << message << "\n" end end end diff --git a/tools/lrama/lib/lrama/option_parser.rb b/tools/lrama/lib/lrama/option_parser.rb index 1e4d448fd..23988a5fb 100644 --- a/tools/lrama/lib/lrama/option_parser.rb +++ b/tools/lrama/lib/lrama/option_parser.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'optparse' module Lrama @@ -16,7 +18,7 @@ module Lrama @options.report_opts = validate_report(@report) @options.grammar_file = argv.shift - if !@options.grammar_file + unless @options.grammar_file abort "File should be specified\n" end @@ -57,26 +59,42 @@ module Lrama o.separator '' o.separator 'Tuning the Parser:' o.on('-S', '--skeleton=FILE', 'specify the skeleton to use') {|v| @options.skeleton = v } - o.on('-t', 'reserved, do nothing') { } - o.on('--debug', 'display debugging outputs of internal parser') {|v| @options.debug = true } + o.on('-t', '--debug', 'display debugging outputs of internal parser') {|v| @options.debug = true } + o.on('-D', '--define=NAME[=VALUE]', Array, "similar to '%define NAME VALUE'") {|v| @options.define = v } o.separator '' o.separator 'Output:' o.on('-H', '--header=[FILE]', 'also produce a header file named FILE') {|v| @options.header = true; @options.header_file = v } o.on('-d', 'also produce a header file') { @options.header = true } - o.on('-r', '--report=THINGS', Array, 'also produce details on the automaton') {|v| @report = v } + o.on('-r', '--report=REPORTS', Array, 'also produce details on the automaton') {|v| @report = v } o.on_tail '' - o.on_tail 'Valid Reports:' - o.on_tail " #{VALID_REPORTS.join(' ')}" - + o.on_tail 'REPORTS is a list of comma-separated words that can include:' + o.on_tail ' states describe the states' + o.on_tail ' itemsets complete the core item sets with their closure' + o.on_tail ' lookaheads explicitly associate lookahead tokens to items' + o.on_tail ' solved describe shift/reduce conflicts solving' + o.on_tail ' counterexamples, cex generate conflict counterexamples' + o.on_tail ' rules list unused rules' + o.on_tail ' terms list unused terminals' + o.on_tail ' verbose report detailed internal state and analysis results' + o.on_tail ' all include all the above reports' + o.on_tail ' none disable all reports' o.on('--report-file=FILE', 'also produce details on the automaton output to a file named FILE') {|v| @options.report_file = v } o.on('-o', '--output=FILE', 'leave output to FILE') {|v| @options.outfile = v } - - o.on('--trace=THINGS', Array, 'also output trace logs at runtime') {|v| @trace = v } + o.on('--trace=TRACES', Array, 'also output trace logs at runtime') {|v| @trace = v } o.on_tail '' - o.on_tail 'Valid Traces:' - o.on_tail " #{VALID_TRACES.join(' ')}" - - o.on('-v', 'reserved, do nothing') { } + o.on_tail 'TRACES is a list of comma-separated words that can include:' + o.on_tail ' automaton display states' + o.on_tail ' closure display states' + o.on_tail ' rules display grammar rules' + o.on_tail ' only-explicit-rules display only explicit grammar rules' + o.on_tail ' actions display grammar rules with actions' + o.on_tail ' time display generation time' + o.on_tail ' all include all the above traces' + o.on_tail ' none disable all traces' + o.on('-v', '--verbose', "same as '--report=state'") {|_v| @report << 'states' } + o.separator '' + o.separator 'Diagnostics:' + o.on('-W', '--warnings', 'report the warnings') {|v| @options.diagnostic = true } o.separator '' o.separator 'Error Recovery:' o.on('-e', 'enable error recovery') {|v| @options.error_recovery = true } @@ -89,48 +107,57 @@ module Lrama end end - BISON_REPORTS = %w[states itemsets lookaheads solved counterexamples cex all none] - OTHER_REPORTS = %w[verbose] - NOT_SUPPORTED_REPORTS = %w[cex none] - VALID_REPORTS = BISON_REPORTS + OTHER_REPORTS - NOT_SUPPORTED_REPORTS + ALIASED_REPORTS = { cex: :counterexamples }.freeze + VALID_REPORTS = %i[states itemsets lookaheads solved counterexamples rules terms verbose].freeze def validate_report(report) - list = VALID_REPORTS h = { grammar: true } + return h if report.empty? + return {} if report == ['none'] + if report == ['all'] + VALID_REPORTS.each { |r| h[r] = true } + return h + end report.each do |r| - if list.include?(r) - h[r.to_sym] = true + aliased = aliased_report_option(r) + if VALID_REPORTS.include?(aliased) + h[aliased] = true else raise "Invalid report option \"#{r}\"." end end - if h[:all] - (BISON_REPORTS - NOT_SUPPORTED_REPORTS).each do |r| - h[r.to_sym] = true - end - - h.delete(:all) - end - return h end + def aliased_report_option(opt) + (ALIASED_REPORTS[opt.to_sym] || opt).to_sym + end + VALID_TRACES = %w[ - none locations scan parse automaton bitsets - closure grammar rules actions resource - sets muscles tools m4-early m4 skeleton time - ielr cex all - ] + locations scan parse automaton bitsets closure + grammar rules only-explicit-rules actions resource + sets muscles tools m4-early m4 skeleton time ielr cex + ].freeze + NOT_SUPPORTED_TRACES = %w[ + locations scan parse bitsets grammar resource + sets muscles tools m4-early m4 skeleton ielr cex + ].freeze + SUPPORTED_TRACES = VALID_TRACES - NOT_SUPPORTED_TRACES def validate_trace(trace) - list = VALID_TRACES h = {} + return h if trace.empty? || trace == ['none'] + all_traces = SUPPORTED_TRACES - %w[only-explicit-rules] + if trace == ['all'] + all_traces.each { |t| h[t.gsub(/-/, '_').to_sym] = true } + return h + end trace.each do |t| - if list.include?(t) - h[t.to_sym] = true + if SUPPORTED_TRACES.include?(t) + h[t.gsub(/-/, '_').to_sym] = true else raise "Invalid trace option \"#{t}\"." end diff --git a/tools/lrama/lib/lrama/options.rb b/tools/lrama/lib/lrama/options.rb index 739ca16f5..08f75a770 100644 --- a/tools/lrama/lib/lrama/options.rb +++ b/tools/lrama/lib/lrama/options.rb @@ -1,14 +1,17 @@ +# frozen_string_literal: true + module Lrama # Command line options. class Options attr_accessor :skeleton, :header, :header_file, :report_file, :outfile, :error_recovery, :grammar_file, - :trace_opts, :report_opts, :y, - :debug + :trace_opts, :report_opts, + :diagnostic, :y, :debug, :define def initialize @skeleton = "bison/yacc.c" + @define = {} @header = false @header_file = nil @report_file = nil @@ -17,6 +20,7 @@ module Lrama @grammar_file = nil @trace_opts = nil @report_opts = nil + @diagnostic = false @y = STDIN @debug = false end diff --git a/tools/lrama/lib/lrama/output.rb b/tools/lrama/lib/lrama/output.rb index 642c8b470..3c7316ac6 100644 --- a/tools/lrama/lib/lrama/output.rb +++ b/tools/lrama/lib/lrama/output.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + require "erb" require "forwardable" -require "lrama/report/duration" +require_relative "report/duration" module Lrama class Output @@ -63,37 +65,29 @@ module Lrama # A part of b4_token_enums def token_enums - str = "" - - @context.yytokentype.each do |s_value, token_id, display_name| + @context.yytokentype.map do |s_value, token_id, display_name| s = sprintf("%s = %d%s", s_value, token_id, token_id == yymaxutok ? "" : ",") if display_name - str << sprintf(" %-30s /* %s */\n", s, display_name) + sprintf(" %-30s /* %s */\n", s, display_name) else - str << sprintf(" %s\n", s) + sprintf(" %s\n", s) end - end - - str + end.join end # b4_symbol_enum def symbol_enum - str = "" - last_sym_number = @context.yysymbol_kind_t.last[1] - @context.yysymbol_kind_t.each do |s_value, sym_number, display_name| + @context.yysymbol_kind_t.map do |s_value, sym_number, display_name| s = sprintf("%s = %d%s", s_value, sym_number, (sym_number == last_sym_number) ? "" : ",") if display_name - str << sprintf(" %-40s /* %s */\n", s, display_name) + sprintf(" %-40s /* %s */\n", s, display_name) else - str << sprintf(" %s\n", s) + sprintf(" %s\n", s) end - end - - str + end.join end def yytranslate @@ -132,12 +126,10 @@ module Lrama end def symbol_actions_for_printer - str = "" - - @grammar.symbols.each do |sym| + @grammar.symbols.map do |sym| next unless sym.printer - str << <<-STR + <<-STR case #{sym.enum_name}: /* #{sym.comment} */ #line #{sym.printer.lineno} "#{@grammar_file_path}" {#{sym.printer.translated_code(sym.tag)}} @@ -145,18 +137,14 @@ module Lrama break; STR - end - - str + end.join end def symbol_actions_for_destructor - str = "" - - @grammar.symbols.each do |sym| + @grammar.symbols.map do |sym| next unless sym.destructor - str << <<-STR + <<-STR case #{sym.enum_name}: /* #{sym.comment} */ #line #{sym.destructor.lineno} "#{@grammar_file_path}" {#{sym.destructor.translated_code(sym.tag)}} @@ -164,9 +152,7 @@ module Lrama break; STR - end - - str + end.join end # b4_user_initial_action @@ -236,12 +222,10 @@ module Lrama end def symbol_actions_for_error_token - str = "" - - @grammar.symbols.each do |sym| + @grammar.symbols.map do |sym| next unless sym.error_token - str << <<-STR + <<-STR case #{sym.enum_name}: /* #{sym.comment} */ #line #{sym.error_token.lineno} "#{@grammar_file_path}" {#{sym.error_token.translated_code(sym.tag)}} @@ -249,22 +233,18 @@ module Lrama break; STR - end - - str + end.join end # b4_user_actions def user_actions - str = "" - - @context.states.rules.each do |rule| + action = @context.states.rules.map do |rule| next unless rule.token_code code = rule.token_code spaces = " " * (code.column - 1) - str << <<-STR + <<-STR case #{rule.id + 1}: /* #{rule.as_comment} */ #line #{code.line} "#{@grammar_file_path}" #{spaces}{#{rule.translated_code}} @@ -272,14 +252,12 @@ module Lrama break; STR - end + end.join - str << <<-STR + action + <<-STR #line [@oline@] [@ofile@] STR - - str end def omit_blanks(param) @@ -343,7 +321,7 @@ module Lrama # b4_parse_param_use def parse_param_use(val, loc) - str = <<-STR + str = <<-STR.dup YY_USE (#{val}); YY_USE (#{loc}); STR @@ -357,7 +335,8 @@ module Lrama # b4_yylex_formals def yylex_formals - ary = ["&yylval", "&yylloc"] + ary = ["&yylval"] + ary << "&yylloc" if @grammar.locations if @grammar.lex_param ary << lex_param_name @@ -397,17 +376,9 @@ module Lrama def int_array_to_string(ary) last = ary.count - 1 - s = ary.each_with_index.each_slice(10).map do |slice| - str = " " - - slice.each do |e, i| - str << sprintf("%6d%s", e, (i == last) ? "" : ",") - end - - str - end - - s.join("\n") + ary.each_with_index.each_slice(10).map do |slice| + " " + slice.map { |e, i| sprintf("%6d%s", e, (i == last) ? "" : ",") }.join + end.join("\n") end def spec_mapped_header_file @@ -457,26 +428,24 @@ module Lrama end def template_dir - File.expand_path("../../../template", __FILE__) + File.expand_path('../../template', __dir__) end def string_array_to_string(ary) - str = "" + result = "" tmp = " " ary.each do |s| - s = s.gsub('\\', '\\\\\\\\') - s = s.gsub('"', '\\"') - - if (tmp + s + " \"\",").length > 75 - str << tmp << "\n" - tmp = " \"#{s}\"," + replaced = s.gsub('\\', '\\\\\\\\').gsub('"', '\\"') + if (tmp + replaced + " \"\",").length > 75 + result = "#{result}#{tmp}\n" + tmp = " \"#{replaced}\"," else - tmp << " \"#{s}\"," + tmp = "#{tmp} \"#{replaced}\"," end end - str << tmp + result + tmp end def replace_special_variables(str, ofile) diff --git a/tools/lrama/lib/lrama/parser.rb b/tools/lrama/lib/lrama/parser.rb index 04603105b..177e784e5 100644 --- a/tools/lrama/lib/lrama/parser.rb +++ b/tools/lrama/lib/lrama/parser.rb @@ -1,6 +1,6 @@ # # DO NOT MODIFY!!!! -# This file is automatically generated by Racc 1.7.3 +# This file is automatically generated by Racc 1.8.1 # from Racc grammar file "parser.y". # @@ -23,7 +23,7 @@ unless $".find {|p| p.end_with?('/racc/info.rb')} $".push "#{__dir__}/racc/info.rb" module Racc - VERSION = '1.7.3' + VERSION = '1.8.1' Version = VERSION Copyright = 'Copyright (c) 1999-2006 Minero Aoki' end @@ -31,10 +31,6 @@ end end -unless defined?(NotImplementedError) - NotImplementedError = NotImplementError # :nodoc: -end - module Racc class ParseError < StandardError; end end @@ -42,7 +38,7 @@ unless defined?(::ParseError) ParseError = Racc::ParseError # :nodoc: end -# Racc is a LALR(1) parser generator. +# Racc is an LALR(1) parser generator. # It is written in Ruby itself, and generates Ruby programs. # # == Command-line Reference @@ -658,21 +654,22 @@ end module Lrama class Parser < Racc::Parser -module_eval(<<'...end parser.y/module_eval...', 'parser.y', 536) +module_eval(<<'...end parser.y/module_eval...', 'parser.y', 428) include Lrama::Report::Duration -def initialize(text, path, debug = false) +def initialize(text, path, debug = false, define = {}) @grammar_file = Lrama::Lexer::GrammarFile.new(path, text) @yydebug = debug @rule_counter = Lrama::Grammar::Counter.new(0) @midrule_action_counter = Lrama::Grammar::Counter.new(1) + @define = define end def parse report_duration(:parse) do @lexer = Lrama::Lexer.new(@grammar_file) - @grammar = Lrama::Grammar.new(@rule_counter) + @grammar = Lrama::Grammar.new(@rule_counter, @define) @precedence_number = 0 reset_precs do_parse @@ -732,322 +729,306 @@ end ##### State transition tables begin ### racc_action_table = [ - 98, 51, 99, 163, 88, 79, 51, 51, 180, 163, - 79, 79, 51, 162, 180, 156, 79, 165, 157, 51, - 3, 50, 181, 165, 70, 51, 8, 50, 181, 79, - 75, 51, 6, 50, 7, 161, 82, 47, 51, 51, - 50, 50, 89, 82, 82, 166, 41, 51, 100, 50, - 182, 166, 82, 51, 48, 50, 182, 23, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 47, 51, 51, 50, 50, 93, 79, 197, - 51, 51, 50, 50, 79, 197, 51, 51, 50, 50, - 79, 197, 23, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 9, 51, 54, - 50, 14, 15, 16, 17, 18, 19, 54, 54, 20, - 21, 22, 23, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 51, 51, - 50, 50, 79, 197, 51, 51, 50, 50, 79, 197, - 51, 51, 50, 50, 79, 197, 51, 51, 50, 50, - 79, 79, 51, 51, 50, 50, 79, 79, 51, 51, - 50, 50, 79, 79, 51, 51, 50, 207, 79, 79, - 51, 51, 207, 207, 79, 79, 51, 51, 50, 50, - 79, 187, 188, 189, 96, 187, 188, 189, 96, 217, - 221, 229, 218, 218, 218, 51, 51, 50, 50, 187, - 188, 189, 57, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 90, 94, 96, 101, 101, 101, 103, - 109, 113, 114, 117, 117, 117, 117, 120, 47, 124, - 125, 127, 129, 130, 131, 132, 133, 136, 140, 141, - 142, 143, 146, 147, 148, 150, 160, 168, 170, 171, - 172, 173, 174, 176, 177, 178, 146, 184, 192, 193, - 200, 160, 204, 176, 211, 160, 215, 216, 178, 176, - 226, 176, 228, 96, 96, 176 ] + 89, 49, 90, 167, 49, 101, 173, 49, 101, 167, + 49, 101, 173, 6, 101, 80, 49, 49, 48, 48, + 41, 76, 76, 49, 49, 48, 48, 42, 76, 76, + 49, 49, 48, 48, 101, 96, 113, 49, 87, 48, + 150, 101, 96, 151, 45, 171, 169, 170, 151, 176, + 170, 91, 169, 170, 81, 176, 170, 20, 24, 25, + 26, 27, 28, 29, 30, 31, 87, 32, 33, 34, + 35, 36, 37, 38, 39, 49, 4, 48, 5, 101, + 96, 181, 182, 183, 128, 20, 24, 25, 26, 27, + 28, 29, 30, 31, 46, 32, 33, 34, 35, 36, + 37, 38, 39, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 53, 20, 24, 25, 26, 27, 28, 29, + 30, 31, 53, 32, 33, 34, 35, 36, 37, 38, + 39, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 44, 20, 24, 25, 26, 27, 28, 29, 30, 31, + 53, 32, 33, 34, 35, 36, 37, 38, 39, 49, + 4, 48, 5, 101, 96, 49, 49, 48, 48, 101, + 101, 49, 49, 48, 48, 101, 101, 49, 49, 48, + 197, 101, 101, 49, 49, 197, 48, 101, 101, 49, + 49, 197, 48, 101, 181, 182, 183, 128, 204, 210, + 217, 205, 205, 205, 49, 49, 48, 48, 49, 49, + 48, 48, 49, 49, 48, 48, 181, 182, 183, 116, + 117, 56, 53, 53, 53, 53, 53, 62, 63, 64, + 65, 66, 68, 68, 68, 82, 53, 53, 104, 108, + 108, 115, 122, 123, 125, 128, 129, 133, 139, 140, + 141, 142, 144, 145, 101, 154, 139, 157, 154, 161, + 162, 68, 164, 165, 172, 177, 154, 184, 128, 188, + 154, 190, 128, 154, 199, 154, 128, 68, 165, 206, + 165, 68, 68, 215, 128, 68 ] racc_action_check = [ - 49, 145, 49, 145, 39, 145, 159, 183, 159, 183, - 159, 183, 201, 144, 201, 139, 201, 145, 139, 33, - 1, 33, 159, 183, 33, 34, 3, 34, 201, 34, - 34, 35, 2, 35, 2, 144, 35, 9, 36, 37, - 36, 37, 39, 36, 37, 145, 7, 38, 49, 38, - 159, 183, 38, 15, 14, 15, 201, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 42, 69, 172, 69, 172, 42, 172, 172, - 173, 70, 173, 70, 173, 173, 174, 81, 174, 81, - 174, 174, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 4, 82, 16, - 82, 4, 4, 4, 4, 4, 4, 17, 18, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 194, 109, - 194, 109, 194, 194, 198, 111, 198, 111, 198, 198, - 199, 117, 199, 117, 199, 199, 74, 75, 74, 75, - 74, 75, 114, 116, 114, 116, 114, 116, 137, 166, - 137, 166, 137, 166, 182, 184, 182, 184, 182, 184, - 204, 216, 204, 216, 204, 216, 218, 119, 218, 119, - 218, 164, 164, 164, 164, 179, 179, 179, 179, 208, - 214, 223, 208, 214, 223, 134, 138, 134, 138, 209, - 209, 209, 19, 20, 23, 25, 26, 27, 28, 29, - 30, 31, 32, 40, 45, 46, 53, 55, 56, 57, - 68, 72, 73, 80, 85, 86, 87, 88, 89, 95, - 96, 102, 104, 105, 106, 107, 108, 112, 120, 121, - 122, 123, 124, 125, 126, 128, 141, 149, 151, 152, - 153, 154, 155, 156, 157, 158, 161, 163, 167, 169, - 175, 178, 180, 186, 190, 200, 205, 207, 213, 217, - 220, 221, 222, 226, 228, 230 ] + 47, 153, 47, 153, 159, 153, 159, 178, 159, 178, + 189, 178, 189, 1, 189, 39, 35, 36, 35, 36, + 5, 35, 36, 37, 38, 37, 38, 6, 37, 38, + 59, 74, 59, 74, 59, 59, 74, 60, 45, 60, + 138, 60, 60, 138, 9, 156, 153, 153, 156, 159, + 159, 47, 178, 178, 39, 189, 189, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 83, 45, 45, 45, + 45, 45, 45, 45, 45, 61, 0, 61, 0, 61, + 61, 166, 166, 166, 166, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 11, 83, 83, 83, 83, 83, + 83, 83, 83, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 13, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 14, 3, 3, 3, 3, 3, 3, 3, + 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 15, 8, 8, 8, 8, 8, 8, 8, 8, 97, + 2, 97, 2, 97, 97, 71, 108, 71, 108, 71, + 108, 109, 169, 109, 169, 109, 169, 176, 184, 176, + 184, 176, 184, 190, 205, 190, 205, 190, 205, 206, + 12, 206, 12, 206, 174, 174, 174, 174, 196, 201, + 214, 196, 201, 214, 69, 76, 69, 76, 104, 105, + 104, 105, 111, 113, 111, 113, 198, 198, 198, 81, + 81, 16, 17, 20, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 40, 51, 56, 67, 70, + 72, 80, 84, 85, 86, 87, 93, 107, 115, 116, + 117, 118, 127, 128, 134, 140, 141, 143, 144, 145, + 146, 150, 151, 152, 158, 163, 165, 167, 168, 171, + 172, 173, 175, 177, 187, 188, 192, 193, 195, 197, + 200, 202, 204, 209, 210, 216 ] racc_action_pointer = [ - nil, 20, 22, 26, 98, nil, nil, 39, nil, 33, - nil, nil, nil, nil, 48, 50, 90, 98, 99, 207, - 194, nil, nil, 195, nil, 196, 197, 198, 213, 214, - 215, 216, 217, 16, 22, 28, 35, 36, 44, -1, - 221, nil, 68, nil, nil, 201, 174, nil, nil, -5, - nil, nil, nil, 207, nil, 208, 209, 210, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 222, 70, - 78, nil, 225, 224, 153, 154, nil, nil, nil, nil, - 225, 84, 105, nil, nil, 226, 227, 228, 197, 234, - nil, nil, nil, nil, nil, 197, 235, nil, nil, nil, - nil, nil, 239, nil, 240, 241, 242, 243, 244, 136, - nil, 142, 240, nil, 159, nil, 160, 148, nil, 184, - 243, 207, 239, 249, 206, 201, 252, nil, 253, nil, - nil, nil, nil, nil, 202, nil, nil, 165, 203, -26, - nil, 210, nil, nil, -10, -2, nil, nil, nil, 237, - nil, 238, 239, 240, 241, 242, 255, 259, 220, 3, - nil, 220, nil, 227, 143, nil, 166, 248, nil, 249, - nil, nil, 71, 77, 83, 228, nil, nil, 225, 147, - 232, nil, 171, 4, 172, nil, 265, nil, nil, nil, - 272, nil, nil, nil, 135, nil, nil, nil, 141, 147, - 229, 9, nil, nil, 177, 274, nil, 237, 158, 161, - nil, nil, nil, 233, 159, nil, 178, 271, 183, nil, - 260, 273, 262, 160, nil, nil, 232, nil, 233, nil, - 277, nil, nil ] + 66, 13, 150, 90, nil, 13, 27, nil, 118, 35, + nil, 88, 187, 63, 73, 101, 216, 173, nil, nil, + 174, nil, nil, nil, 175, 176, 177, 222, 223, 224, + 225, 226, 224, 225, 226, 13, 14, 20, 21, 10, + 233, nil, nil, nil, nil, 34, nil, -5, nil, nil, + nil, 187, nil, nil, nil, nil, 188, nil, nil, 27, + 34, 72, nil, nil, nil, nil, nil, 230, nil, 201, + 231, 162, 232, nil, 28, nil, 202, nil, nil, nil, + 200, 215, nil, 62, 233, 221, 222, 191, nil, nil, + nil, nil, nil, 244, nil, nil, nil, 156, nil, nil, + nil, nil, nil, nil, 205, 206, nil, 241, 163, 168, + nil, 209, nil, 210, nil, 243, 206, 209, 240, nil, + nil, nil, nil, nil, nil, nil, nil, 209, 248, nil, + nil, nil, nil, nil, 247, nil, nil, nil, -2, nil, + 208, 251, nil, 255, 211, 204, 210, nil, nil, nil, + 253, 257, 217, -2, nil, nil, 3, nil, 218, 1, + nil, nil, nil, 222, nil, 219, 30, 226, 214, 169, + nil, 226, 223, 230, 143, 218, 174, 226, 4, nil, + nil, nil, nil, nil, 175, nil, nil, 272, 228, 7, + 180, nil, 222, 269, nil, 232, 156, 238, 165, nil, + 234, 157, 273, nil, 274, 181, 186, nil, nil, 233, + 230, nil, nil, nil, 158, nil, 277, nil, nil ] racc_action_default = [ - -2, -138, -8, -138, -138, -3, -4, -138, 233, -138, - -9, -10, -11, -12, -138, -138, -138, -138, -138, -138, - -138, -24, -25, -138, -29, -138, -138, -138, -138, -138, - -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, - -138, -7, -123, -96, -98, -138, -120, -122, -13, -127, - -94, -95, -126, -15, -85, -16, -17, -138, -21, -26, - -30, -33, -36, -39, -40, -41, -42, -43, -44, -50, - -138, -53, -71, -45, -75, -138, -78, -80, -81, -135, - -46, -88, -138, -91, -93, -47, -48, -49, -138, -138, - -5, -1, -97, -124, -99, -138, -138, -14, -128, -129, - -130, -82, -138, -18, -138, -138, -138, -138, -138, -138, - -54, -51, -73, -72, -138, -79, -76, -138, -92, -89, - -138, -138, -138, -138, -104, -138, -138, -86, -138, -22, - -27, -31, -34, -37, -52, -55, -74, -77, -90, -138, - -58, -62, -6, -125, -100, -101, -105, -121, -83, -138, - -19, -138, -138, -138, -138, -138, -136, -138, -57, -60, - -63, -104, -103, -94, -120, -109, -138, -138, -87, -138, - -23, -28, -138, -138, -138, -138, -137, -59, -62, -120, - -94, -67, -138, -102, -138, -106, -136, -113, -114, -115, - -138, -112, -84, -20, -32, -131, -133, -134, -35, -38, - -62, -61, -64, -65, -138, -138, -70, -94, -138, -116, - -107, -110, -132, -56, -138, -68, -138, -136, -138, -118, - -138, -136, -138, -138, -108, -117, -120, -66, -120, -119, - -136, -69, -111 ] + -1, -128, -1, -3, -10, -128, -128, -2, -3, -128, + -16, -128, -128, -128, -128, -128, -128, -128, -24, -25, + -128, -32, -33, -34, -128, -128, -128, -128, -128, -128, + -128, -128, -50, -50, -50, -128, -128, -128, -128, -128, + -128, -13, 219, -4, -26, -128, -17, -123, -93, -94, + -122, -14, -19, -85, -20, -21, -128, -23, -31, -128, + -128, -128, -38, -39, -40, -41, -42, -43, -51, -128, + -44, -128, -45, -46, -88, -90, -128, -47, -48, -49, + -128, -128, -11, -5, -7, -95, -128, -68, -18, -124, + -125, -126, -15, -128, -22, -27, -28, -29, -35, -83, + -84, -127, -36, -37, -128, -52, -54, -56, -128, -79, + -81, -88, -89, -128, -91, -128, -128, -128, -128, -6, + -8, -9, -120, -96, -97, -98, -69, -128, -128, -86, + -30, -55, -53, -57, -76, -82, -80, -92, -128, -62, + -66, -128, -12, -128, -66, -128, -128, -58, -77, -78, + -50, -128, -60, -64, -67, -70, -128, -121, -99, -100, + -102, -119, -87, -128, -63, -66, -68, -93, -68, -128, + -116, -128, -66, -93, -68, -68, -128, -66, -65, -71, + -72, -108, -109, -110, -128, -74, -75, -128, -66, -101, + -128, -103, -68, -50, -107, -59, -128, -93, -111, -117, + -61, -128, -50, -106, -50, -128, -128, -112, -113, -128, + -68, -104, -73, -114, -128, -118, -50, -115, -105 ] racc_goto_table = [ - 76, 95, 69, 52, 74, 158, 110, 175, 118, 119, - 145, 208, 1, 212, 186, 2, 43, 212, 212, 4, - 42, 72, 91, 84, 84, 84, 84, 5, 40, 203, - 122, 214, 80, 85, 86, 87, 10, 210, 11, 111, - 115, 76, 12, 223, 138, 116, 118, 183, 110, 92, - 53, 55, 56, 194, 198, 199, 13, 72, 72, 219, - 49, 97, 128, 169, 213, 118, 104, 151, 224, 84, - 84, 110, 227, 105, 152, 106, 153, 107, 134, 154, - 76, 232, 115, 108, 137, 155, 68, 73, 112, 135, - 139, 121, 201, 205, 222, 126, 167, 72, 102, 72, - 149, 144, 190, 115, 220, 84, 123, 84, nil, nil, - nil, 164, nil, nil, nil, nil, nil, nil, nil, 185, - nil, nil, 72, nil, nil, 179, 84, nil, nil, nil, - nil, nil, 191, nil, 202, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 206, 164, - 209, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 179, nil, nil, - 209, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 230, 209, 231, 225 ] + 69, 109, 50, 152, 57, 127, 84, 58, 112, 160, + 114, 59, 60, 61, 86, 52, 54, 55, 98, 102, + 103, 159, 106, 110, 175, 74, 74, 74, 74, 138, + 9, 1, 3, 180, 7, 43, 120, 160, 109, 109, + 195, 192, 121, 94, 119, 112, 40, 137, 118, 189, + 47, 200, 86, 92, 175, 156, 130, 131, 132, 107, + 135, 136, 88, 196, 111, 207, 111, 70, 72, 201, + 73, 77, 78, 79, 67, 147, 134, 178, 148, 149, + 93, 146, 124, 166, 179, 214, 185, 158, 208, 174, + 187, 209, 191, 193, 107, 107, 143, nil, nil, 186, + nil, 111, nil, 111, nil, nil, 194, nil, 166, nil, + 202, nil, nil, nil, 198, nil, nil, nil, 163, 174, + 198, nil, nil, nil, nil, nil, nil, nil, 216, nil, + nil, nil, nil, nil, nil, 213, 198, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 203, nil, nil, nil, nil, nil, nil, nil, nil, + 211, nil, 212, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 218 ] racc_goto_check = [ - 43, 44, 33, 35, 49, 40, 34, 39, 56, 55, - 60, 46, 1, 64, 45, 2, 57, 64, 64, 3, - 4, 35, 5, 35, 35, 35, 35, 6, 7, 45, - 8, 46, 32, 32, 32, 32, 9, 39, 10, 33, - 43, 43, 11, 46, 55, 49, 56, 60, 34, 57, - 15, 15, 15, 21, 21, 21, 12, 35, 35, 45, - 13, 14, 16, 17, 40, 56, 18, 19, 39, 35, - 35, 34, 39, 22, 23, 24, 25, 26, 33, 27, - 43, 39, 43, 28, 49, 29, 30, 31, 36, 37, - 38, 41, 42, 47, 48, 51, 52, 35, 53, 35, - 54, 59, 61, 43, 62, 35, 63, 35, nil, nil, - nil, 43, nil, nil, nil, nil, nil, nil, nil, 44, - nil, nil, 35, nil, nil, 43, 35, nil, nil, nil, - nil, nil, 43, nil, 44, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 43, 43, - 43, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 43, nil, nil, - 43, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 44, 43, 44, 43 ] + 27, 20, 29, 33, 15, 40, 8, 15, 46, 39, + 46, 15, 15, 15, 12, 16, 16, 16, 22, 22, + 22, 50, 28, 43, 38, 29, 29, 29, 29, 32, + 7, 1, 6, 36, 6, 7, 5, 39, 20, 20, + 33, 36, 9, 15, 8, 46, 10, 46, 11, 50, + 13, 33, 12, 16, 38, 32, 22, 28, 28, 29, + 43, 43, 14, 37, 29, 36, 29, 24, 24, 37, + 25, 25, 25, 25, 23, 30, 31, 34, 41, 42, + 44, 45, 48, 20, 40, 37, 40, 49, 51, 20, + 52, 53, 40, 40, 29, 29, 54, nil, nil, 20, + nil, 29, nil, 29, nil, nil, 20, nil, 20, nil, + 40, nil, nil, nil, 20, nil, nil, nil, 27, 20, + 20, nil, nil, nil, nil, nil, nil, nil, 40, nil, + nil, nil, nil, nil, nil, 20, 20, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 27, nil, nil, nil, nil, nil, nil, nil, nil, + 27, nil, 27, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 27 ] racc_goto_pointer = [ - nil, 12, 15, 17, 11, -20, 25, 22, -60, 32, - 34, 38, 52, 45, 12, 34, -41, -87, 8, -62, - nil, -119, 14, -56, 15, -55, 16, -53, 21, -48, - 53, 53, -3, -31, -63, -12, 16, -23, -30, -149, - -136, 2, -86, -34, -45, -150, -173, -88, -121, -30, - nil, -6, -52, 44, -27, -73, -73, 7, nil, -23, - -114, -63, -107, 13, -181 ] + nil, 31, nil, nil, nil, -48, 32, 27, -39, -42, + 42, -34, -31, 38, 15, -13, 2, nil, nil, nil, + -70, nil, -41, 42, 34, 35, nil, -32, -47, -10, + -59, -31, -86, -137, -88, nil, -133, -121, -135, -135, + -82, -56, -55, -48, 27, -48, -66, nil, -3, -57, + -123, -110, -80, -108, -26 ] racc_goto_default = [ - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 45, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 24, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 71, 77, nil, nil, nil, nil, - nil, 46, 159, 196, nil, nil, nil, nil, nil, nil, - 78, nil, nil, nil, nil, 81, 83, nil, 44, nil, - nil, nil, nil, nil, 195 ] + nil, nil, 2, 8, 83, nil, nil, nil, nil, nil, + nil, nil, 10, nil, nil, 51, nil, 21, 22, 23, + 95, 97, nil, nil, nil, nil, 105, 71, nil, 99, + nil, nil, nil, nil, 153, 126, nil, nil, 168, 155, + nil, 100, nil, nil, nil, nil, 75, 85, nil, nil, + nil, nil, nil, nil, nil ] racc_reduce_table = [ 0, 0, :racc_error, - 5, 55, :_reduce_none, - 0, 56, :_reduce_none, - 2, 56, :_reduce_none, - 0, 61, :_reduce_4, - 0, 62, :_reduce_5, - 5, 60, :_reduce_6, + 0, 63, :_reduce_1, + 2, 63, :_reduce_2, + 0, 64, :_reduce_3, + 2, 64, :_reduce_4, + 1, 65, :_reduce_5, + 2, 65, :_reduce_6, + 0, 66, :_reduce_none, + 1, 66, :_reduce_none, + 5, 58, :_reduce_none, + 0, 67, :_reduce_10, + 0, 68, :_reduce_11, + 5, 59, :_reduce_12, + 2, 59, :_reduce_none, + 1, 73, :_reduce_14, + 2, 73, :_reduce_15, + 1, 60, :_reduce_none, + 2, 60, :_reduce_17, + 3, 60, :_reduce_18, 2, 60, :_reduce_none, - 0, 57, :_reduce_8, - 2, 57, :_reduce_none, - 1, 63, :_reduce_none, - 1, 63, :_reduce_none, - 1, 63, :_reduce_none, - 2, 63, :_reduce_13, - 3, 63, :_reduce_none, - 2, 63, :_reduce_none, - 2, 63, :_reduce_16, - 2, 63, :_reduce_17, - 0, 70, :_reduce_18, - 0, 71, :_reduce_19, - 7, 63, :_reduce_20, - 0, 72, :_reduce_21, - 0, 73, :_reduce_22, - 6, 63, :_reduce_23, - 1, 63, :_reduce_24, - 1, 63, :_reduce_none, - 0, 76, :_reduce_26, - 0, 77, :_reduce_27, - 6, 64, :_reduce_28, - 1, 64, :_reduce_none, - 0, 78, :_reduce_30, - 0, 79, :_reduce_31, - 7, 64, :_reduce_32, - 0, 80, :_reduce_33, - 0, 81, :_reduce_34, - 7, 64, :_reduce_35, - 0, 82, :_reduce_36, - 0, 83, :_reduce_37, - 7, 64, :_reduce_38, - 2, 64, :_reduce_39, - 2, 64, :_reduce_40, - 2, 64, :_reduce_41, - 2, 64, :_reduce_42, - 2, 64, :_reduce_43, + 2, 60, :_reduce_20, + 2, 60, :_reduce_21, + 3, 60, :_reduce_22, + 2, 60, :_reduce_23, + 1, 60, :_reduce_24, + 1, 60, :_reduce_25, + 2, 60, :_reduce_none, + 1, 78, :_reduce_27, + 1, 78, :_reduce_28, + 1, 79, :_reduce_29, + 2, 79, :_reduce_30, + 2, 69, :_reduce_31, + 1, 69, :_reduce_none, + 1, 69, :_reduce_none, + 1, 69, :_reduce_none, + 3, 69, :_reduce_35, + 3, 69, :_reduce_36, + 3, 69, :_reduce_37, + 2, 69, :_reduce_38, + 2, 69, :_reduce_39, + 2, 69, :_reduce_40, + 2, 69, :_reduce_41, + 2, 69, :_reduce_42, 2, 74, :_reduce_none, + 2, 74, :_reduce_44, 2, 74, :_reduce_45, 2, 74, :_reduce_46, 2, 74, :_reduce_47, 2, 74, :_reduce_48, 2, 74, :_reduce_49, - 1, 84, :_reduce_50, - 2, 84, :_reduce_51, - 3, 84, :_reduce_52, - 1, 87, :_reduce_53, - 2, 87, :_reduce_54, - 3, 88, :_reduce_55, - 8, 65, :_reduce_56, - 5, 66, :_reduce_57, - 1, 92, :_reduce_58, - 3, 92, :_reduce_59, - 1, 94, :_reduce_60, - 3, 94, :_reduce_61, - 0, 96, :_reduce_62, - 1, 96, :_reduce_63, - 3, 96, :_reduce_64, - 3, 96, :_reduce_65, - 6, 96, :_reduce_66, - 0, 101, :_reduce_67, - 0, 102, :_reduce_68, - 7, 96, :_reduce_69, - 3, 96, :_reduce_70, - 0, 90, :_reduce_none, - 1, 90, :_reduce_none, - 0, 91, :_reduce_none, - 1, 91, :_reduce_none, - 1, 85, :_reduce_75, - 2, 85, :_reduce_76, - 3, 85, :_reduce_77, - 1, 103, :_reduce_78, - 2, 103, :_reduce_79, + 0, 84, :_reduce_none, + 1, 84, :_reduce_none, + 1, 85, :_reduce_52, + 2, 85, :_reduce_53, + 2, 80, :_reduce_54, + 3, 80, :_reduce_55, + 0, 88, :_reduce_none, + 1, 88, :_reduce_none, + 3, 83, :_reduce_58, + 8, 75, :_reduce_59, + 5, 76, :_reduce_60, + 8, 76, :_reduce_61, + 1, 89, :_reduce_62, + 3, 89, :_reduce_63, + 1, 90, :_reduce_64, + 3, 90, :_reduce_65, + 0, 96, :_reduce_none, + 1, 96, :_reduce_none, + 0, 97, :_reduce_none, 1, 97, :_reduce_none, - 1, 97, :_reduce_none, - 0, 105, :_reduce_82, - 0, 106, :_reduce_83, - 6, 69, :_reduce_84, - 0, 107, :_reduce_85, - 0, 108, :_reduce_86, - 5, 69, :_reduce_87, - 1, 86, :_reduce_88, - 2, 86, :_reduce_89, - 3, 86, :_reduce_90, - 1, 109, :_reduce_91, - 2, 109, :_reduce_92, - 1, 110, :_reduce_none, - 1, 89, :_reduce_94, - 1, 89, :_reduce_95, - 1, 58, :_reduce_none, - 2, 58, :_reduce_none, - 1, 111, :_reduce_none, - 2, 111, :_reduce_none, - 4, 112, :_reduce_100, - 1, 113, :_reduce_101, - 3, 113, :_reduce_102, - 2, 113, :_reduce_none, - 0, 114, :_reduce_104, - 1, 114, :_reduce_105, - 3, 114, :_reduce_106, - 4, 114, :_reduce_107, - 6, 114, :_reduce_108, - 0, 115, :_reduce_109, - 0, 116, :_reduce_110, - 8, 114, :_reduce_111, - 3, 114, :_reduce_112, - 1, 99, :_reduce_113, - 1, 99, :_reduce_114, - 1, 99, :_reduce_115, - 1, 100, :_reduce_116, - 3, 100, :_reduce_117, - 2, 100, :_reduce_118, - 4, 100, :_reduce_119, - 0, 98, :_reduce_none, - 3, 98, :_reduce_121, - 1, 95, :_reduce_none, - 0, 59, :_reduce_none, - 0, 117, :_reduce_124, - 3, 59, :_reduce_125, - 1, 67, :_reduce_none, - 0, 68, :_reduce_none, - 1, 68, :_reduce_none, - 1, 68, :_reduce_none, - 1, 68, :_reduce_none, - 1, 75, :_reduce_131, - 2, 75, :_reduce_132, - 1, 118, :_reduce_none, - 1, 118, :_reduce_none, - 1, 104, :_reduce_135, - 0, 93, :_reduce_none, - 1, 93, :_reduce_none ] + 1, 91, :_reduce_70, + 3, 91, :_reduce_71, + 3, 91, :_reduce_72, + 6, 91, :_reduce_73, + 3, 91, :_reduce_74, + 3, 91, :_reduce_75, + 0, 99, :_reduce_none, + 1, 99, :_reduce_none, + 1, 87, :_reduce_78, + 1, 100, :_reduce_79, + 2, 100, :_reduce_80, + 2, 81, :_reduce_81, + 3, 81, :_reduce_82, + 1, 77, :_reduce_none, + 1, 77, :_reduce_none, + 0, 101, :_reduce_85, + 0, 102, :_reduce_86, + 5, 72, :_reduce_87, + 1, 103, :_reduce_88, + 2, 103, :_reduce_89, + 1, 82, :_reduce_90, + 2, 82, :_reduce_91, + 3, 82, :_reduce_92, + 1, 86, :_reduce_93, + 1, 86, :_reduce_94, + 0, 105, :_reduce_none, + 1, 105, :_reduce_none, + 2, 61, :_reduce_none, + 2, 61, :_reduce_none, + 4, 104, :_reduce_99, + 1, 106, :_reduce_100, + 3, 106, :_reduce_101, + 1, 107, :_reduce_102, + 3, 107, :_reduce_103, + 5, 107, :_reduce_104, + 7, 107, :_reduce_105, + 4, 107, :_reduce_106, + 3, 107, :_reduce_107, + 1, 93, :_reduce_108, + 1, 93, :_reduce_109, + 1, 93, :_reduce_110, + 0, 108, :_reduce_none, + 1, 108, :_reduce_none, + 2, 94, :_reduce_113, + 3, 94, :_reduce_114, + 4, 94, :_reduce_115, + 0, 109, :_reduce_116, + 0, 110, :_reduce_117, + 5, 95, :_reduce_118, + 3, 92, :_reduce_119, + 0, 111, :_reduce_120, + 3, 62, :_reduce_121, + 1, 70, :_reduce_none, + 0, 71, :_reduce_none, + 1, 71, :_reduce_none, + 1, 71, :_reduce_none, + 1, 71, :_reduce_none, + 1, 98, :_reduce_127 ] -racc_reduce_n = 138 +racc_reduce_n = 128 -racc_shift_n = 233 +racc_shift_n = 219 racc_token_table = { false => 0, @@ -1069,43 +1050,46 @@ racc_token_table = { "%lex-param" => 16, "%parse-param" => 17, "%code" => 18, - "{" => 19, - "}" => 20, - "%initial-action" => 21, - "%no-stdlib" => 22, - ";" => 23, - "%union" => 24, - "%destructor" => 25, - "%printer" => 26, - "%error-token" => 27, - "%after-shift" => 28, - "%before-reduce" => 29, - "%after-reduce" => 30, - "%after-shift-error-token" => 31, - "%after-pop-stack" => 32, + "%initial-action" => 19, + "%no-stdlib" => 20, + "%locations" => 21, + ";" => 22, + "%union" => 23, + "%destructor" => 24, + "%printer" => 25, + "%error-token" => 26, + "%after-shift" => 27, + "%before-reduce" => 28, + "%after-reduce" => 29, + "%after-shift-error-token" => 30, + "%after-pop-stack" => 31, + "-temp-group" => 32, "%token" => 33, "%type" => 34, - "%left" => 35, - "%right" => 36, - "%precedence" => 37, - "%nonassoc" => 38, - "%rule" => 39, - "(" => 40, - ")" => 41, - ":" => 42, - "%inline" => 43, - "," => 44, - "|" => 45, - "%empty" => 46, - "%prec" => 47, - "?" => 48, - "+" => 49, - "*" => 50, - "[" => 51, - "]" => 52, - "{...}" => 53 } + "%nterm" => 35, + "%left" => 36, + "%right" => 37, + "%precedence" => 38, + "%nonassoc" => 39, + "%rule" => 40, + "(" => 41, + ")" => 42, + ":" => 43, + "%inline" => 44, + "," => 45, + "|" => 46, + "%empty" => 47, + "%prec" => 48, + "{" => 49, + "}" => 50, + "?" => 51, + "+" => 52, + "*" => 53, + "[" => 54, + "]" => 55, + "{...}" => 56 } -racc_nt_base = 54 +racc_nt_base = 57 racc_use_result_var = true @@ -1146,10 +1130,9 @@ Racc_token_to_s_table = [ "\"%lex-param\"", "\"%parse-param\"", "\"%code\"", - "\"{\"", - "\"}\"", "\"%initial-action\"", "\"%no-stdlib\"", + "\"%locations\"", "\";\"", "\"%union\"", "\"%destructor\"", @@ -1160,8 +1143,10 @@ Racc_token_to_s_table = [ "\"%after-reduce\"", "\"%after-shift-error-token\"", "\"%after-pop-stack\"", + "\"-temp-group\"", "\"%token\"", "\"%type\"", + "\"%nterm\"", "\"%left\"", "\"%right\"", "\"%precedence\"", @@ -1175,6 +1160,8 @@ Racc_token_to_s_table = [ "\"|\"", "\"%empty\"", "\"%prec\"", + "\"{\"", + "\"}\"", "\"?\"", "\"+\"", "\"*\"", @@ -1183,69 +1170,59 @@ Racc_token_to_s_table = [ "\"{...}\"", "$start", "input", - "prologue_declarations", - "bison_declarations", - "grammar", - "epilogue_opt", "prologue_declaration", + "bison_declaration", + "rules_or_grammar_declaration", + "epilogue_declaration", + "\"-many@prologue_declaration\"", + "\"-many@bison_declaration\"", + "\"-many1@rules_or_grammar_declaration\"", + "\"-option@epilogue_declaration\"", "@1", "@2", - "bison_declaration", "grammar_declaration", - "rule_declaration", - "inline_declaration", "variable", "value", - "params", - "@3", - "@4", - "@5", - "@6", + "param", + "\"-many1@param\"", "symbol_declaration", - "generic_symlist", - "@7", - "@8", - "@9", - "@10", - "@11", - "@12", - "@13", - "@14", + "rule_declaration", + "inline_declaration", + "symbol", + "\"-group@symbol|TAG\"", + "\"-many1@-group@symbol|TAG\"", "token_declarations", "symbol_declarations", "token_declarations_for_precedence", - "token_declaration_list", "token_declaration", + "\"-option@TAG\"", + "\"-many1@token_declaration\"", "id", - "int_opt", "alias", + "\"-option@INTEGER\"", "rule_args", - "tag_opt", "rule_rhs_list", - "id_colon", "rule_rhs", - "symbol", - "named_ref_opt", + "named_ref", "parameterizing_suffix", "parameterizing_args", - "@15", - "@16", - "symbol_declaration_list", + "midrule_action", + "\"-option@%empty\"", + "\"-option@named_ref\"", "string_as_id", - "@17", - "@18", - "@19", - "@20", - "token_declaration_list_for_precedence", - "token_declaration_for_precedence", - "rules_or_grammar_declaration", + "\"-option@string_as_id\"", + "\"-many1@symbol\"", + "@3", + "@4", + "\"-many1@id\"", "rules", + "\"-option@;\"", "rhs_list", "rhs", - "@21", - "@22", - "@23", - "generic_symlist_item" ] + "\"-option@parameterizing_suffix\"", + "@5", + "@6", + "@7" ] Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor) Racc_debug_parser = true @@ -1254,14 +1231,56 @@ Racc_debug_parser = true # reduce 0 omitted -# reduce 1 omitted +module_eval(<<'.,.,', 'parser.y', 11) + def _reduce_1(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., -# reduce 2 omitted +module_eval(<<'.,.,', 'parser.y', 11) + def _reduce_2(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., -# reduce 3 omitted +module_eval(<<'.,.,', 'parser.y', 11) + def _reduce_3(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., -module_eval(<<'.,.,', 'parser.y', 14) +module_eval(<<'.,.,', 'parser.y', 11) def _reduce_4(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 11) + def _reduce_5(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 11) + def _reduce_6(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., + +# reduce 7 omitted + +# reduce 8 omitted + +# reduce 9 omitted + +module_eval(<<'.,.,', 'parser.y', 12) + def _reduce_10(val, _values, result) begin_c_declaration("%}") @grammar.prologue_first_lineno = @lexer.line @@ -1269,52 +1288,58 @@ module_eval(<<'.,.,', 'parser.y', 14) end .,., -module_eval(<<'.,.,', 'parser.y', 19) - def _reduce_5(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 17) + def _reduce_11(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 23) - def _reduce_6(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 21) + def _reduce_12(val, _values, result) @grammar.prologue = val[2].s_value result end .,., -# reduce 7 omitted +# reduce 13 omitted -module_eval(<<'.,.,', 'parser.y', 27) - def _reduce_8(val, _values, result) - result = "" +module_eval(<<'.,.,', 'parser.y', 54) + def _reduce_14(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val result end .,., -# reduce 9 omitted +module_eval(<<'.,.,', 'parser.y', 54) + def _reduce_15(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., -# reduce 10 omitted +# reduce 16 omitted -# reduce 11 omitted - -# reduce 12 omitted - -module_eval(<<'.,.,', 'parser.y', 33) - def _reduce_13(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 26) + def _reduce_17(val, _values, result) @grammar.expect = val[1] result end .,., -# reduce 14 omitted +module_eval(<<'.,.,', 'parser.y', 27) + def _reduce_18(val, _values, result) + @grammar.define[val[1].s_value] = val[2]&.s_value + result + end +.,., -# reduce 15 omitted +# reduce 19 omitted -module_eval(<<'.,.,', 'parser.y', 38) - def _reduce_16(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 31) + def _reduce_20(val, _values, result) val[1].each {|token| @grammar.lex_param = Grammar::Code::NoReferenceCode.new(type: :lex_param, token_code: token).token_code.s_value } @@ -1323,8 +1348,8 @@ module_eval(<<'.,.,', 'parser.y', 38) end .,., -module_eval(<<'.,.,', 'parser.y', 44) - def _reduce_17(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 37) + def _reduce_21(val, _values, result) val[1].each {|token| @grammar.parse_param = Grammar::Code::NoReferenceCode.new(type: :parse_param, token_code: token).token_code.s_value } @@ -1333,220 +1358,163 @@ module_eval(<<'.,.,', 'parser.y', 44) end .,., -module_eval(<<'.,.,', 'parser.y', 50) - def _reduce_18(val, _values, result) - begin_c_declaration("}") - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 54) - def _reduce_19(val, _values, result) - end_c_declaration - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 58) - def _reduce_20(val, _values, result) - @grammar.add_percent_code(id: val[1], code: val[4]) - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 62) - def _reduce_21(val, _values, result) - begin_c_declaration("}") - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 66) +module_eval(<<'.,.,', 'parser.y', 43) def _reduce_22(val, _values, result) - end_c_declaration + @grammar.add_percent_code(id: val[1], code: val[2]) result end .,., -module_eval(<<'.,.,', 'parser.y', 70) +module_eval(<<'.,.,', 'parser.y', 47) def _reduce_23(val, _values, result) - @grammar.initial_action = Grammar::Code::InitialActionCode.new(type: :initial_action, token_code: val[3]) + @grammar.initial_action = Grammar::Code::InitialActionCode.new(type: :initial_action, token_code: val[1]) result end .,., -module_eval(<<'.,.,', 'parser.y', 72) +module_eval(<<'.,.,', 'parser.y', 49) def _reduce_24(val, _values, result) @grammar.no_stdlib = true result end .,., -# reduce 25 omitted +module_eval(<<'.,.,', 'parser.y', 50) + def _reduce_25(val, _values, result) + @grammar.locations = true + result + end +.,., -module_eval(<<'.,.,', 'parser.y', 77) - def _reduce_26(val, _values, result) - begin_c_declaration("}") +# reduce 26 omitted + +module_eval(<<'.,.,', 'parser.y', 109) + def _reduce_27(val, _values, result) + result = val + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 109) + def _reduce_28(val, _values, result) + result = val + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 109) + def _reduce_29(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 109) + def _reduce_30(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 55) + def _reduce_31(val, _values, result) + @grammar.set_union( + Grammar::Code::NoReferenceCode.new(type: :union, token_code: val[1]), + val[1].line + ) + + result + end +.,., + +# reduce 32 omitted + +# reduce 33 omitted + +# reduce 34 omitted + +module_eval(<<'.,.,', 'parser.y', 65) + def _reduce_35(val, _values, result) + @grammar.add_destructor( + ident_or_tags: val[2].flatten, + token_code: val[1], + lineno: val[1].line + ) + + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 73) + def _reduce_36(val, _values, result) + @grammar.add_printer( + ident_or_tags: val[2].flatten, + token_code: val[1], + lineno: val[1].line + ) result end .,., module_eval(<<'.,.,', 'parser.y', 81) - def _reduce_27(val, _values, result) - end_c_declaration - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 85) - def _reduce_28(val, _values, result) - @grammar.set_union( - Grammar::Code::NoReferenceCode.new(type: :union, token_code: val[3]), - val[3].line - ) - - result - end -.,., - -# reduce 29 omitted - -module_eval(<<'.,.,', 'parser.y', 93) - def _reduce_30(val, _values, result) - begin_c_declaration("}") - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 97) - def _reduce_31(val, _values, result) - end_c_declaration - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 101) - def _reduce_32(val, _values, result) - @grammar.add_destructor( - ident_or_tags: val[6], - token_code: val[3], - lineno: val[3].line - ) - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 109) - def _reduce_33(val, _values, result) - begin_c_declaration("}") - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 113) - def _reduce_34(val, _values, result) - end_c_declaration - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 117) - def _reduce_35(val, _values, result) - @grammar.add_printer( - ident_or_tags: val[6], - token_code: val[3], - lineno: val[3].line - ) - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 125) - def _reduce_36(val, _values, result) - begin_c_declaration("}") - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 129) def _reduce_37(val, _values, result) - end_c_declaration - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 133) - def _reduce_38(val, _values, result) @grammar.add_error_token( - ident_or_tags: val[6], - token_code: val[3], - lineno: val[3].line + ident_or_tags: val[2].flatten, + token_code: val[1], + lineno: val[1].line ) result end .,., -module_eval(<<'.,.,', 'parser.y', 141) - def _reduce_39(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 89) + def _reduce_38(val, _values, result) @grammar.after_shift = val[1] result end .,., -module_eval(<<'.,.,', 'parser.y', 145) - def _reduce_40(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 93) + def _reduce_39(val, _values, result) @grammar.before_reduce = val[1] result end .,., -module_eval(<<'.,.,', 'parser.y', 149) - def _reduce_41(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 97) + def _reduce_40(val, _values, result) @grammar.after_reduce = val[1] result end .,., -module_eval(<<'.,.,', 'parser.y', 153) - def _reduce_42(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 101) + def _reduce_41(val, _values, result) @grammar.after_shift_error_token = val[1] result end .,., -module_eval(<<'.,.,', 'parser.y', 157) - def _reduce_43(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 105) + def _reduce_42(val, _values, result) @grammar.after_pop_stack = val[1] result end .,., -# reduce 44 omitted +# reduce 43 omitted -module_eval(<<'.,.,', 'parser.y', 163) - def _reduce_45(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 111) + def _reduce_44(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @grammar.add_type(id: id, tag: hash[:tag]) @@ -1557,7 +1525,23 @@ module_eval(<<'.,.,', 'parser.y', 163) end .,., -module_eval(<<'.,.,', 'parser.y', 171) +module_eval(<<'.,.,', 'parser.y', 119) + def _reduce_45(val, _values, result) + val[1].each {|hash| + hash[:tokens].each {|id| + if @grammar.find_term_by_s_value(id.s_value) + on_action_error("symbol #{id.s_value} redeclared as a nonterminal", id) + else + @grammar.add_type(id: id, tag: hash[:tag]) + end + } + } + + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 131) def _reduce_46(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @@ -1571,7 +1555,7 @@ module_eval(<<'.,.,', 'parser.y', 171) end .,., -module_eval(<<'.,.,', 'parser.y', 181) +module_eval(<<'.,.,', 'parser.y', 141) def _reduce_47(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @@ -1585,7 +1569,7 @@ module_eval(<<'.,.,', 'parser.y', 181) end .,., -module_eval(<<'.,.,', 'parser.y', 191) +module_eval(<<'.,.,', 'parser.y', 151) def _reduce_48(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @@ -1599,7 +1583,7 @@ module_eval(<<'.,.,', 'parser.y', 191) end .,., -module_eval(<<'.,.,', 'parser.y', 201) +module_eval(<<'.,.,', 'parser.y', 161) def _reduce_49(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @@ -1613,18 +1597,26 @@ module_eval(<<'.,.,', 'parser.y', 201) end .,., -module_eval(<<'.,.,', 'parser.y', 212) - def _reduce_50(val, _values, result) - val[0].each {|token_declaration| - @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: nil, replace: true) - } +# reduce 50 omitted +# reduce 51 omitted + +module_eval(<<'.,.,', 'parser.y', 184) + def _reduce_52(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 218) - def _reduce_51(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 184) + def _reduce_53(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 172) + def _reduce_54(val, _values, result) val[1].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[0], replace: true) } @@ -1633,8 +1625,8 @@ module_eval(<<'.,.,', 'parser.y', 218) end .,., -module_eval(<<'.,.,', 'parser.y', 224) - def _reduce_52(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 178) + def _reduce_55(val, _values, result) val[2].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[1], replace: true) } @@ -1643,29 +1635,19 @@ module_eval(<<'.,.,', 'parser.y', 224) end .,., -module_eval(<<'.,.,', 'parser.y', 229) - def _reduce_53(val, _values, result) - result = [val[0]] - result - end -.,., +# reduce 56 omitted -module_eval(<<'.,.,', 'parser.y', 230) - def _reduce_54(val, _values, result) - result = val[0].append(val[1]) - result - end -.,., +# reduce 57 omitted -module_eval(<<'.,.,', 'parser.y', 232) - def _reduce_55(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 183) + def _reduce_58(val, _values, result) result = val result end .,., -module_eval(<<'.,.,', 'parser.y', 236) - def _reduce_56(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 187) + def _reduce_59(val, _values, result) rule = Grammar::ParameterizingRule::Rule.new(val[1].s_value, val[3], val[7], tag: val[5]) @grammar.add_parameterizing_rule(rule) @@ -1673,8 +1655,8 @@ module_eval(<<'.,.,', 'parser.y', 236) end .,., -module_eval(<<'.,.,', 'parser.y', 242) - def _reduce_57(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 193) + def _reduce_60(val, _values, result) rule = Grammar::ParameterizingRule::Rule.new(val[2].s_value, [], val[4], is_inline: true) @grammar.add_parameterizing_rule(rule) @@ -1682,22 +1664,31 @@ module_eval(<<'.,.,', 'parser.y', 242) end .,., -module_eval(<<'.,.,', 'parser.y', 246) - def _reduce_58(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 198) + def _reduce_61(val, _values, result) + rule = Grammar::ParameterizingRule::Rule.new(val[2].s_value, val[4], val[7], is_inline: true) + @grammar.add_parameterizing_rule(rule) + + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 202) + def _reduce_62(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 247) - def _reduce_59(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 203) + def _reduce_63(val, _values, result) result = val[0].append(val[2]) result end .,., -module_eval(<<'.,.,', 'parser.y', 251) - def _reduce_60(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 207) + def _reduce_64(val, _values, result) builder = val[0] result = [builder] @@ -1705,8 +1696,8 @@ module_eval(<<'.,.,', 'parser.y', 251) end .,., -module_eval(<<'.,.,', 'parser.y', 256) - def _reduce_61(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 212) + def _reduce_65(val, _values, result) builder = val[2] result = val[0].append(builder) @@ -1714,8 +1705,16 @@ module_eval(<<'.,.,', 'parser.y', 256) end .,., -module_eval(<<'.,.,', 'parser.y', 262) - def _reduce_62(val, _values, result) +# reduce 66 omitted + +# reduce 67 omitted + +# reduce 68 omitted + +# reduce 69 omitted + +module_eval(<<'.,.,', 'parser.y', 218) + def _reduce_70(val, _values, result) reset_precs result = Grammar::ParameterizingRule::Rhs.new @@ -1723,17 +1722,8 @@ module_eval(<<'.,.,', 'parser.y', 262) end .,., -module_eval(<<'.,.,', 'parser.y', 267) - def _reduce_63(val, _values, result) - reset_precs - result = Grammar::ParameterizingRule::Rhs.new - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 272) - def _reduce_64(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 223) + def _reduce_71(val, _values, result) token = val[1] token.alias_name = val[2] builder = val[0] @@ -1744,8 +1734,8 @@ module_eval(<<'.,.,', 'parser.y', 272) end .,., -module_eval(<<'.,.,', 'parser.y', 280) - def _reduce_65(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 231) + def _reduce_72(val, _values, result) builder = val[0] builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], location: @lexer.location, args: [val[1]]) result = builder @@ -1754,8 +1744,8 @@ module_eval(<<'.,.,', 'parser.y', 280) end .,., -module_eval(<<'.,.,', 'parser.y', 286) - def _reduce_66(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 237) + def _reduce_73(val, _values, result) builder = val[0] builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3], lhs_tag: val[5]) result = builder @@ -1764,30 +1754,10 @@ module_eval(<<'.,.,', 'parser.y', 286) end .,., -module_eval(<<'.,.,', 'parser.y', 292) - def _reduce_67(val, _values, result) - if @prec_seen - on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec - @code_after_prec = true - end - begin_c_declaration("}") - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 300) - def _reduce_68(val, _values, result) - end_c_declaration - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 304) - def _reduce_69(val, _values, result) - user_code = val[3] - user_code.alias_name = val[6] +module_eval(<<'.,.,', 'parser.y', 243) + def _reduce_74(val, _values, result) + user_code = val[1] + user_code.alias_name = val[2] builder = val[0] builder.user_code = user_code result = builder @@ -1796,8 +1766,8 @@ module_eval(<<'.,.,', 'parser.y', 304) end .,., -module_eval(<<'.,.,', 'parser.y', 312) - def _reduce_70(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 251) + def _reduce_75(val, _values, result) sym = @grammar.find_symbol_by_id!(val[2]) @prec_seen = true builder = val[0] @@ -1808,168 +1778,137 @@ module_eval(<<'.,.,', 'parser.y', 312) end .,., -# reduce 71 omitted +# reduce 76 omitted -# reduce 72 omitted +# reduce 77 omitted -# reduce 73 omitted - -# reduce 74 omitted - -module_eval(<<'.,.,', 'parser.y', 327) - def _reduce_75(val, _values, result) - result = [{tag: nil, tokens: val[0]}] - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 331) - def _reduce_76(val, _values, result) - result = [{tag: val[0], tokens: val[1]}] - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 335) - def _reduce_77(val, _values, result) - result = val[0].append({tag: val[1], tokens: val[2]}) - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 338) +module_eval(<<'.,.,', 'parser.y', 258) def _reduce_78(val, _values, result) - result = [val[0]] + result = val[0].s_value if val[0] result end .,., -module_eval(<<'.,.,', 'parser.y', 339) +module_eval(<<'.,.,', 'parser.y', 271) def _reduce_79(val, _values, result) - result = val[0].append(val[1]) + result = val[1] ? val[1].unshift(val[0]) : val result end .,., -# reduce 80 omitted +module_eval(<<'.,.,', 'parser.y', 271) + def _reduce_80(val, _values, result) + result = val[1] ? val[1].unshift(val[0]) : val + result + end +.,., -# reduce 81 omitted +module_eval(<<'.,.,', 'parser.y', 262) + def _reduce_81(val, _values, result) + result = if val[0] + [{tag: val[0], tokens: val[1]}] + else + [{tag: nil, tokens: val[1]}] + end -module_eval(<<'.,.,', 'parser.y', 346) + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 268) def _reduce_82(val, _values, result) - begin_c_declaration("}") - + result = val[0].append({tag: val[1], tokens: val[2]}) result end .,., -module_eval(<<'.,.,', 'parser.y', 350) - def _reduce_83(val, _values, result) - end_c_declaration +# reduce 83 omitted - result - end -.,., +# reduce 84 omitted -module_eval(<<'.,.,', 'parser.y', 354) - def _reduce_84(val, _values, result) - result = val[0].append(val[3]) - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 358) +module_eval(<<'.,.,', 'parser.y', 274) def _reduce_85(val, _values, result) - begin_c_declaration("}") + begin_c_declaration("}") result end .,., -module_eval(<<'.,.,', 'parser.y', 362) +module_eval(<<'.,.,', 'parser.y', 278) def _reduce_86(val, _values, result) - end_c_declaration + end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 366) +module_eval(<<'.,.,', 'parser.y', 282) def _reduce_87(val, _values, result) - result = [val[2]] + result = val[2] result end .,., -module_eval(<<'.,.,', 'parser.y', 371) +module_eval(<<'.,.,', 'parser.y', 290) def _reduce_88(val, _values, result) - result = [{tag: nil, tokens: val[0]}] - + result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 375) +module_eval(<<'.,.,', 'parser.y', 290) def _reduce_89(val, _values, result) - result = [{tag: val[0], tokens: val[1]}] - + result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 379) +module_eval(<<'.,.,', 'parser.y', 285) def _reduce_90(val, _values, result) - result = val[0].append({tag: val[1], tokens: val[2]}) - + result = [{tag: nil, tokens: val[0]}] result end .,., -module_eval(<<'.,.,', 'parser.y', 382) +module_eval(<<'.,.,', 'parser.y', 286) def _reduce_91(val, _values, result) - result = [val[0]] + result = [{tag: val[0], tokens: val[1]}] result end .,., -module_eval(<<'.,.,', 'parser.y', 383) +module_eval(<<'.,.,', 'parser.y', 287) def _reduce_92(val, _values, result) - result = val[0].append(val[1]) + result = val[0].append({tag: val[1], tokens: val[2]}) result end .,., -# reduce 93 omitted - -module_eval(<<'.,.,', 'parser.y', 387) - def _reduce_94(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 289) + def _reduce_93(val, _values, result) on_action_error("ident after %prec", val[0]) if @prec_seen result end .,., -module_eval(<<'.,.,', 'parser.y', 388) - def _reduce_95(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 290) + def _reduce_94(val, _values, result) on_action_error("char after %prec", val[0]) if @prec_seen result end .,., +# reduce 95 omitted + # reduce 96 omitted # reduce 97 omitted # reduce 98 omitted -# reduce 99 omitted - -module_eval(<<'.,.,', 'parser.y', 398) - def _reduce_100(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 298) + def _reduce_99(val, _values, result) lhs = val[0] lhs.alias_name = val[1] val[3].each do |builder| @@ -1982,8 +1921,8 @@ module_eval(<<'.,.,', 'parser.y', 398) end .,., -module_eval(<<'.,.,', 'parser.y', 409) - def _reduce_101(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 309) + def _reduce_100(val, _values, result) builder = val[0] if !builder.line builder.line = @lexer.line - 1 @@ -1994,8 +1933,8 @@ module_eval(<<'.,.,', 'parser.y', 409) end .,., -module_eval(<<'.,.,', 'parser.y', 417) - def _reduce_102(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 317) + def _reduce_101(val, _values, result) builder = val[2] if !builder.line builder.line = @lexer.line - 1 @@ -2006,28 +1945,17 @@ module_eval(<<'.,.,', 'parser.y', 417) end .,., -# reduce 103 omitted - -module_eval(<<'.,.,', 'parser.y', 427) - def _reduce_104(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 326) + def _reduce_102(val, _values, result) reset_precs - result = Grammar::RuleBuilder.new(@rule_counter, @midrule_action_counter) + result = @grammar.create_rule_builder(@rule_counter, @midrule_action_counter) result end .,., -module_eval(<<'.,.,', 'parser.y', 432) - def _reduce_105(val, _values, result) - reset_precs - result = Grammar::RuleBuilder.new(@rule_counter, @midrule_action_counter) - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 437) - def _reduce_106(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 331) + def _reduce_103(val, _values, result) token = val[1] token.alias_name = val[2] builder = val[0] @@ -2038,9 +1966,9 @@ module_eval(<<'.,.,', 'parser.y', 437) end .,., -module_eval(<<'.,.,', 'parser.y', 445) - def _reduce_107(val, _values, result) - token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], location: @lexer.location, args: [val[1]], lhs_tag: val[3]) +module_eval(<<'.,.,', 'parser.y', 339) + def _reduce_104(val, _values, result) + token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], alias_name: val[3], location: @lexer.location, args: [val[1]], lhs_tag: val[4]) builder = val[0] builder.add_rhs(token) builder.line = val[1].first_line @@ -2050,9 +1978,9 @@ module_eval(<<'.,.,', 'parser.y', 445) end .,., -module_eval(<<'.,.,', 'parser.y', 453) - def _reduce_108(val, _values, result) - token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3], lhs_tag: val[5]) +module_eval(<<'.,.,', 'parser.y', 347) + def _reduce_105(val, _values, result) + token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, alias_name: val[5], location: @lexer.location, args: val[3], lhs_tag: val[6]) builder = val[0] builder.add_rhs(token) builder.line = val[1].first_line @@ -2062,31 +1990,11 @@ module_eval(<<'.,.,', 'parser.y', 453) end .,., -module_eval(<<'.,.,', 'parser.y', 461) - def _reduce_109(val, _values, result) - if @prec_seen - on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec - @code_after_prec = true - end - begin_c_declaration("}") - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 469) - def _reduce_110(val, _values, result) - end_c_declaration - - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 473) - def _reduce_111(val, _values, result) - user_code = val[3] - user_code.alias_name = val[6] - user_code.tag = val[7] +module_eval(<<'.,.,', 'parser.y', 355) + def _reduce_106(val, _values, result) + user_code = val[1] + user_code.alias_name = val[2] + user_code.tag = val[3] builder = val[0] builder.user_code = user_code result = builder @@ -2095,8 +2003,8 @@ module_eval(<<'.,.,', 'parser.y', 473) end .,., -module_eval(<<'.,.,', 'parser.y', 482) - def _reduce_112(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 364) + def _reduce_107(val, _values, result) sym = @grammar.find_symbol_by_id!(val[2]) @prec_seen = true builder = val[0] @@ -2107,125 +2015,127 @@ module_eval(<<'.,.,', 'parser.y', 482) end .,., -module_eval(<<'.,.,', 'parser.y', 489) - def _reduce_113(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 371) + def _reduce_108(val, _values, result) result = "option" result end .,., -module_eval(<<'.,.,', 'parser.y', 490) - def _reduce_114(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 372) + def _reduce_109(val, _values, result) result = "nonempty_list" result end .,., -module_eval(<<'.,.,', 'parser.y', 491) - def _reduce_115(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 373) + def _reduce_110(val, _values, result) result = "list" result end .,., -module_eval(<<'.,.,', 'parser.y', 493) - def _reduce_116(val, _values, result) - result = [val[0]] +# reduce 111 omitted + +# reduce 112 omitted + +module_eval(<<'.,.,', 'parser.y', 377) + def _reduce_113(val, _values, result) + result = if val[1] + [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[0])] + else + [val[0]] + end + result end .,., -module_eval(<<'.,.,', 'parser.y', 494) - def _reduce_117(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 383) + def _reduce_114(val, _values, result) result = val[0].append(val[2]) result end .,., -module_eval(<<'.,.,', 'parser.y', 495) - def _reduce_118(val, _values, result) - result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[0])] - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 496) - def _reduce_119(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 384) + def _reduce_115(val, _values, result) result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[0].s_value, location: @lexer.location, args: val[2])] result end .,., -# reduce 120 omitted +module_eval(<<'.,.,', 'parser.y', 388) + def _reduce_116(val, _values, result) + if @prec_seen + on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec + @code_after_prec = true + end + begin_c_declaration("}") -module_eval(<<'.,.,', 'parser.y', 499) - def _reduce_121(val, _values, result) + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 396) + def _reduce_117(val, _values, result) + end_c_declaration + + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 400) + def _reduce_118(val, _values, result) + result = val[2] + + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 403) + def _reduce_119(val, _values, result) result = val[1].s_value result end .,., +module_eval(<<'.,.,', 'parser.y', 407) + def _reduce_120(val, _values, result) + begin_c_declaration('\Z') + @grammar.epilogue_first_lineno = @lexer.line + 1 + + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 412) + def _reduce_121(val, _values, result) + end_c_declaration + @grammar.epilogue = val[2].s_value + + result + end +.,., + # reduce 122 omitted # reduce 123 omitted -module_eval(<<'.,.,', 'parser.y', 506) - def _reduce_124(val, _values, result) - begin_c_declaration('\Z') - @grammar.epilogue_first_lineno = @lexer.line + 1 +# reduce 124 omitted - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 511) - def _reduce_125(val, _values, result) - end_c_declaration - @grammar.epilogue = val[2].s_value - - result - end -.,., +# reduce 125 omitted # reduce 126 omitted -# reduce 127 omitted - -# reduce 128 omitted - -# reduce 129 omitted - -# reduce 130 omitted - -module_eval(<<'.,.,', 'parser.y', 522) - def _reduce_131(val, _values, result) - result = [val[0]] - result - end -.,., - -module_eval(<<'.,.,', 'parser.y', 523) - def _reduce_132(val, _values, result) - result = val[0].append(val[1]) - result - end -.,., - -# reduce 133 omitted - -# reduce 134 omitted - -module_eval(<<'.,.,', 'parser.y', 528) - def _reduce_135(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 423) + def _reduce_127(val, _values, result) result = Lrama::Lexer::Token::Ident.new(s_value: val[0]) result end .,., -# reduce 136 omitted - -# reduce 137 omitted - def _reduce_none(val, _values, result) val[0] end diff --git a/tools/lrama/lib/lrama/report.rb b/tools/lrama/lib/lrama/report.rb index 650ac09d5..890e5f1e8 100644 --- a/tools/lrama/lib/lrama/report.rb +++ b/tools/lrama/lib/lrama/report.rb @@ -1,2 +1,4 @@ -require 'lrama/report/duration' -require 'lrama/report/profile' +# frozen_string_literal: true + +require_relative 'report/duration' +require_relative 'report/profile' diff --git a/tools/lrama/lib/lrama/report/duration.rb b/tools/lrama/lib/lrama/report/duration.rb index 7afe284f1..fe09a0d02 100644 --- a/tools/lrama/lib/lrama/report/duration.rb +++ b/tools/lrama/lib/lrama/report/duration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Report module Duration diff --git a/tools/lrama/lib/lrama/report/profile.rb b/tools/lrama/lib/lrama/report/profile.rb index 36156800a..10488cf91 100644 --- a/tools/lrama/lib/lrama/report/profile.rb +++ b/tools/lrama/lib/lrama/report/profile.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class Report module Profile diff --git a/tools/lrama/lib/lrama/state.rb b/tools/lrama/lib/lrama/state.rb index ceb74d856..3008786ce 100644 --- a/tools/lrama/lib/lrama/state.rb +++ b/tools/lrama/lib/lrama/state.rb @@ -1,14 +1,16 @@ -require "lrama/state/reduce" -require "lrama/state/reduce_reduce_conflict" -require "lrama/state/resolved_conflict" -require "lrama/state/shift" -require "lrama/state/shift_reduce_conflict" +# frozen_string_literal: true + +require_relative "state/reduce" +require_relative "state/reduce_reduce_conflict" +require_relative "state/resolved_conflict" +require_relative "state/shift" +require_relative "state/shift_reduce_conflict" module Lrama class State attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts, :default_reduction_rule, :closure, :items - attr_accessor :shifts, :reduces + attr_accessor :shifts, :reduces, :ielr_isocores, :lalr_isocore def initialize(id, accessing_symbol, kernels) @id = id @@ -21,6 +23,12 @@ module Lrama @conflicts = [] @resolved_conflicts = [] @default_reduction_rule = nil + @predecessors = [] + @lalr_isocore = self + @ielr_isocores = [self] + @internal_dependencies = {} + @successor_dependencies = {} + @always_follows = {} end def closure=(closure) @@ -82,6 +90,18 @@ module Lrama @transitions ||= shifts.map {|shift| [shift, @items_to_state[shift.next_items]] } end + def update_transition(shift, next_state) + set_items_to_state(shift.next_items, next_state) + next_state.append_predecessor(self) + clear_transitions_cache + end + + def clear_transitions_cache + @nterm_transitions = nil + @term_transitions = nil + @transitions = nil + end + def selected_term_transitions term_transitions.reject do |shift, next_state| shift.not_selected @@ -140,5 +160,274 @@ module Lrama conflict.type == :reduce_reduce end end + + def propagate_lookaheads(next_state) + next_state.kernels.map {|item| + lookahead_sets = + if item.position == 1 + goto_follow_set(item.lhs) + else + kernel = kernels.find {|k| k.predecessor_item_of?(item) } + item_lookahead_set[kernel] + end + + [item, lookahead_sets & next_state.lookahead_set_filters[item]] + }.to_h + end + + def lookaheads_recomputed + !@item_lookahead_set.nil? + end + + def compatible_lookahead?(filtered_lookahead) + !lookaheads_recomputed || + @lalr_isocore.annotation_list.all? {|token, actions| + a = dominant_contribution(token, actions, item_lookahead_set) + b = dominant_contribution(token, actions, filtered_lookahead) + a.nil? || b.nil? || a == b + } + end + + def lookahead_set_filters + kernels.map {|kernel| + [kernel, + @lalr_isocore.annotation_list.select {|token, actions| + token.term? && actions.any? {|action, contributions| + !contributions.nil? && contributions.key?(kernel) && contributions[kernel] + } + }.map {|token, _| token } + ] + }.to_h + end + + def dominant_contribution(token, actions, lookaheads) + a = actions.select {|action, contributions| + contributions.nil? || contributions.any? {|item, contributed| contributed && lookaheads[item].include?(token) } + }.map {|action, _| action } + return nil if a.empty? + a.reject {|action| + if action.is_a?(State::Shift) + action.not_selected + elsif action.is_a?(State::Reduce) + action.not_selected_symbols.include?(token) + end + } + end + + def inadequacy_list + return @inadequacy_list if @inadequacy_list + + shift_contributions = shifts.map {|shift| + [shift.next_sym, [shift]] + }.to_h + reduce_contributions = reduces.map {|reduce| + (reduce.look_ahead || []).map {|sym| + [sym, [reduce]] + }.to_h + }.reduce(Hash.new([])) {|hash, cont| + hash.merge(cont) {|_, a, b| a | b } + } + + list = shift_contributions.merge(reduce_contributions) {|_, a, b| a | b } + @inadequacy_list = list.select {|token, actions| token.term? && actions.size > 1 } + end + + def annotation_list + return @annotation_list if @annotation_list + + @annotation_list = annotate_manifestation + @annotation_list = @items_to_state.values.map {|next_state| next_state.annotate_predecessor(self) } + .reduce(@annotation_list) {|result, annotations| + result.merge(annotations) {|_, actions_a, actions_b| + if actions_a.nil? || actions_b.nil? + actions_a || actions_b + else + actions_a.merge(actions_b) {|_, contributions_a, contributions_b| + if contributions_a.nil? || contributions_b.nil? + next contributions_a || contributions_b + end + + contributions_a.merge(contributions_b) {|_, contributed_a, contributed_b| + contributed_a || contributed_b + } + } + end + } + } + end + + def annotate_manifestation + inadequacy_list.transform_values {|actions| + actions.map {|action| + if action.is_a?(Shift) + [action, nil] + elsif action.is_a?(Reduce) + if action.rule.empty_rule? + [action, lhs_contributions(action.rule.lhs, inadequacy_list.key(actions))] + else + contributions = kernels.map {|kernel| [kernel, kernel.rule == action.rule && kernel.end_of_rule?] }.to_h + [action, contributions] + end + end + }.to_h + } + end + + def annotate_predecessor(predecessor) + annotation_list.transform_values {|actions| + token = annotation_list.key(actions) + actions.transform_values {|inadequacy| + next nil if inadequacy.nil? + lhs_adequacy = kernels.any? {|kernel| + inadequacy[kernel] && kernel.position == 1 && predecessor.lhs_contributions(kernel.lhs, token).nil? + } + if lhs_adequacy + next nil + else + predecessor.kernels.map {|pred_k| + [pred_k, kernels.any? {|k| + inadequacy[k] && ( + pred_k.predecessor_item_of?(k) && predecessor.item_lookahead_set[pred_k].include?(token) || + k.position == 1 && predecessor.lhs_contributions(k.lhs, token)[pred_k] + ) + }] + }.to_h + end + } + } + end + + def lhs_contributions(sym, token) + shift, next_state = nterm_transitions.find {|sh, _| sh.next_sym == sym } + if always_follows(shift, next_state).include?(token) + nil + else + kernels.map {|kernel| [kernel, follow_kernel_items(shift, next_state, kernel) && item_lookahead_set[kernel].include?(token)] }.to_h + end + end + + def follow_kernel_items(shift, next_state, kernel) + queue = [[self, shift, next_state]] + until queue.empty? + st, sh, next_st = queue.pop + return true if kernel.next_sym == sh.next_sym && kernel.symbols_after_transition.all?(&:nullable) + st.internal_dependencies(sh, next_st).each {|v| queue << v } + end + false + end + + def item_lookahead_set + return @item_lookahead_set if @item_lookahead_set + + kernels.map {|item| + value = + if item.lhs.accept_symbol? + [] + elsif item.position > 1 + prev_items = predecessors_with_item(item) + prev_items.map {|st, i| st.item_lookahead_set[i] }.reduce([]) {|acc, syms| acc |= syms } + elsif item.position == 1 + prev_state = @predecessors.find {|p| p.shifts.any? {|shift| shift.next_sym == item.lhs } } + shift, next_state = prev_state.nterm_transitions.find {|shift, _| shift.next_sym == item.lhs } + prev_state.goto_follows(shift, next_state) + end + [item, value] + }.to_h + end + + def item_lookahead_set=(k) + @item_lookahead_set = k + end + + def predecessors_with_item(item) + result = [] + @predecessors.each do |pre| + pre.items.each do |i| + result << [pre, i] if i.predecessor_item_of?(item) + end + end + result + end + + def append_predecessor(prev_state) + @predecessors << prev_state + @predecessors.uniq! + end + + def goto_follow_set(nterm_token) + return [] if nterm_token.accept_symbol? + shift, next_state = @lalr_isocore.nterm_transitions.find {|sh, _| sh.next_sym == nterm_token } + + @kernels + .select {|kernel| follow_kernel_items(shift, next_state, kernel) } + .map {|kernel| item_lookahead_set[kernel] } + .reduce(always_follows(shift, next_state)) {|result, terms| result |= terms } + end + + def goto_follows(shift, next_state) + queue = internal_dependencies(shift, next_state) + predecessor_dependencies(shift, next_state) + terms = always_follows(shift, next_state) + until queue.empty? + st, sh, next_st = queue.pop + terms |= st.always_follows(sh, next_st) + st.internal_dependencies(sh, next_st).each {|v| queue << v } + st.predecessor_dependencies(sh, next_st).each {|v| queue << v } + end + terms + end + + def always_follows(shift, next_state) + return @always_follows[[shift, next_state]] if @always_follows[[shift, next_state]] + + queue = internal_dependencies(shift, next_state) + successor_dependencies(shift, next_state) + terms = [] + until queue.empty? + st, sh, next_st = queue.pop + terms |= next_st.term_transitions.map {|sh, _| sh.next_sym } + st.internal_dependencies(sh, next_st).each {|v| queue << v } + st.successor_dependencies(sh, next_st).each {|v| queue << v } + end + @always_follows[[shift, next_state]] = terms + end + + def internal_dependencies(shift, next_state) + return @internal_dependencies[[shift, next_state]] if @internal_dependencies[[shift, next_state]] + + syms = @items.select {|i| + i.next_sym == shift.next_sym && i.symbols_after_transition.all?(&:nullable) && i.position == 0 + }.map(&:lhs).uniq + @internal_dependencies[[shift, next_state]] = nterm_transitions.select {|sh, _| syms.include?(sh.next_sym) }.map {|goto| [self, *goto] } + end + + def successor_dependencies(shift, next_state) + return @successor_dependencies[[shift, next_state]] if @successor_dependencies[[shift, next_state]] + + @successor_dependencies[[shift, next_state]] = + next_state.nterm_transitions + .select {|next_shift, _| next_shift.next_sym.nullable } + .map {|transition| [next_state, *transition] } + end + + def predecessor_dependencies(shift, next_state) + state_items = [] + @kernels.select {|kernel| + kernel.next_sym == shift.next_sym && kernel.symbols_after_transition.all?(&:nullable) + }.each do |item| + queue = predecessors_with_item(item) + until queue.empty? + st, i = queue.pop + if i.position == 0 + state_items << [st, i] + else + st.predecessors_with_item(i).each {|v| queue << v } + end + end + end + + state_items.map {|state, item| + sh, next_st = state.nterm_transitions.find {|shi, _| shi.next_sym == item.lhs } + [state, sh, next_st] + } + end end end diff --git a/tools/lrama/lib/lrama/state/reduce.rb b/tools/lrama/lib/lrama/state/reduce.rb index 8ba51f45f..54ab87b46 100644 --- a/tools/lrama/lib/lrama/state/reduce.rb +++ b/tools/lrama/lib/lrama/state/reduce.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class State class Reduce @@ -24,8 +26,8 @@ module Lrama end def selected_look_ahead - if @look_ahead - @look_ahead - @not_selected_symbols + if look_ahead + look_ahead - @not_selected_symbols else [] end diff --git a/tools/lrama/lib/lrama/state/reduce_reduce_conflict.rb b/tools/lrama/lib/lrama/state/reduce_reduce_conflict.rb index 0a0e4dc20..736d08376 100644 --- a/tools/lrama/lib/lrama/state/reduce_reduce_conflict.rb +++ b/tools/lrama/lib/lrama/state/reduce_reduce_conflict.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class State class ReduceReduceConflict < Struct.new(:symbols, :reduce1, :reduce2, keyword_init: true) diff --git a/tools/lrama/lib/lrama/state/resolved_conflict.rb b/tools/lrama/lib/lrama/state/resolved_conflict.rb index 02ea89214..3bb3d1446 100644 --- a/tools/lrama/lib/lrama/state/resolved_conflict.rb +++ b/tools/lrama/lib/lrama/state/resolved_conflict.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class State # * symbol: A symbol under discussion @@ -6,7 +8,7 @@ module Lrama class ResolvedConflict < Struct.new(:symbol, :reduce, :which, :same_prec, keyword_init: true) def report_message s = symbol.display_name - r = reduce.rule.precedence_sym.display_name + r = reduce.rule.precedence_sym&.display_name case when which == :shift && same_prec msg = "resolved as #{which} (%right #{s})" diff --git a/tools/lrama/lib/lrama/state/shift.rb b/tools/lrama/lib/lrama/state/shift.rb index 2021eb61f..81ef013a1 100644 --- a/tools/lrama/lib/lrama/state/shift.rb +++ b/tools/lrama/lib/lrama/state/shift.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class State class Shift diff --git a/tools/lrama/lib/lrama/state/shift_reduce_conflict.rb b/tools/lrama/lib/lrama/state/shift_reduce_conflict.rb index f80bd5f35..fd6683453 100644 --- a/tools/lrama/lib/lrama/state/shift_reduce_conflict.rb +++ b/tools/lrama/lib/lrama/state/shift_reduce_conflict.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class State class ShiftReduceConflict < Struct.new(:symbols, :shift, :reduce, keyword_init: true) diff --git a/tools/lrama/lib/lrama/states.rb b/tools/lrama/lib/lrama/states.rb index 290e996b8..fd8ded905 100644 --- a/tools/lrama/lib/lrama/states.rb +++ b/tools/lrama/lib/lrama/states.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + require "forwardable" -require "lrama/report/duration" -require "lrama/states/item" +require_relative "report/duration" +require_relative "states/item" module Lrama # States is passed to a template file @@ -16,9 +18,8 @@ module Lrama attr_reader :states, :reads_relation, :includes_relation, :lookback_relation - def initialize(grammar, warning, trace_state: false) + def initialize(grammar, trace_state: false) @grammar = grammar - @warning = warning @trace_state = trace_state @states = [] @@ -40,6 +41,8 @@ module Lrama # value is array of [state.id, nterm.token_id]. @reads_relation = {} + # `Read(p, A) =s DR(p, A) ∪ ∪{Read(r, C) | (p, A) reads (r, C)}` + # # `@read_sets` is a hash whose # key is [state.id, nterm.token_id], # value is bitmap of term. @@ -61,6 +64,8 @@ module Lrama # value is array of [state.id, nterm.token_id]. @lookback_relation = {} + # `Follow(p, A) =s Read(p, A) ∪ ∪{Follow(p', B) | (p, A) includes (p', B)}` + # # `@follow_sets` is a hash whose # key is [state.id, rule.id], # value is bitmap of term. @@ -89,8 +94,20 @@ module Lrama report_duration(:compute_conflicts) { compute_conflicts } report_duration(:compute_default_reduction) { compute_default_reduction } + end - check_conflicts + def compute_ielr + report_duration(:split_states) { split_states } + report_duration(:compute_direct_read_sets) { compute_direct_read_sets } + report_duration(:compute_reads_relation) { compute_reads_relation } + report_duration(:compute_read_sets) { compute_read_sets } + report_duration(:compute_includes_relation) { compute_includes_relation } + report_duration(:compute_lookback_relation) { compute_lookback_relation } + report_duration(:compute_follow_sets) { compute_follow_sets } + report_duration(:compute_look_ahead_sets) { compute_look_ahead_sets } + report_duration(:compute_conflicts) { compute_conflicts } + + report_duration(:compute_default_reduction) { compute_default_reduction } end def reporter @@ -125,16 +142,16 @@ module Lrama end end + def sr_conflicts_count + @sr_conflicts_count ||= @states.flat_map(&:sr_conflicts).count + end + + def rr_conflicts_count + @rr_conflicts_count ||= @states.flat_map(&:rr_conflicts).count + end + private - def sr_conflicts - @states.flat_map(&:sr_conflicts) - end - - def rr_conflicts - @states.flat_map(&:rr_conflicts) - end - def trace_state if @trace_state yield STDERR @@ -236,7 +253,7 @@ module Lrama # Trace previous = state.kernels.first.previous_sym trace_state do |out| - out << sprintf("state_list_append (state = %d, symbol = %d (%s))", + out << sprintf("state_list_append (state = %d, symbol = %d (%s))\n", @states.count, previous.number, previous.display_name) end @@ -266,7 +283,10 @@ module Lrama state.shifts.each do |shift| new_state, created = create_state(shift.next_sym, shift.next_items, states_created) state.set_items_to_state(shift.next_items, new_state) - enqueue_state(states, new_state) if created + if created + enqueue_state(states, new_state) + new_state.append_predecessor(state) + end end end end @@ -350,7 +370,7 @@ module Lrama # TODO: need to omit if state == state2 ? @includes_relation[key] ||= [] @includes_relation[key] << [state.id, nterm.token_id] - break if !sym.nullable + break unless sym.nullable i -= 1 end end @@ -385,7 +405,7 @@ module Lrama @states.each do |state| rules.each do |rule| ary = @lookback_relation[[state.id, rule.id]] - next if !ary + next unless ary ary.each do |state2_id, nterm_token_id| # q = state, A -> ω = rule, p = state2, A = nterm @@ -428,7 +448,7 @@ module Lrama sym = shift.next_sym next unless reduce.look_ahead - next if !reduce.look_ahead.include?(sym) + next unless reduce.look_ahead.include?(sym) # Shift/Reduce conflict shift_prec = sym.precedence @@ -492,17 +512,17 @@ module Lrama states.each do |state| count = state.reduces.count - for i in 0...count do + (0...count).each do |i| reduce1 = state.reduces[i] next if reduce1.look_ahead.nil? - for j in (i+1)...count do + ((i+1)...count).each do |j| reduce2 = state.reduces[j] next if reduce2.look_ahead.nil? intersection = reduce1.look_ahead & reduce2.look_ahead - if !intersection.empty? + unless intersection.empty? state.conflicts << State::ReduceReduceConflict.new(symbols: intersection, reduce1: reduce1, reduce2: reduce2) end end @@ -514,7 +534,7 @@ module Lrama states.each do |state| next if state.reduces.empty? # Do not set, if conflict exist - next if !state.conflicts.empty? + next unless state.conflicts.empty? # Do not set, if shift with `error` exists. next if state.shifts.map(&:next_sym).include?(@grammar.error_symbol) @@ -526,30 +546,49 @@ module Lrama end end - def check_conflicts - sr_count = sr_conflicts.count - rr_count = rr_conflicts.count - - if @grammar.expect - - expected_sr_conflicts = @grammar.expect - expected_rr_conflicts = 0 - - if expected_sr_conflicts != sr_count - @warning.error("shift/reduce conflicts: #{sr_count} found, #{expected_sr_conflicts} expected") + def split_states + @states.each do |state| + state.transitions.each do |shift, next_state| + compute_state(state, shift, next_state) end + end + end - if expected_rr_conflicts != rr_count - @warning.error("reduce/reduce conflicts: #{rr_count} found, #{expected_rr_conflicts} expected") + def merge_lookaheads(state, filtered_lookaheads) + return if state.kernels.all? {|item| (filtered_lookaheads[item] - state.item_lookahead_set[item]).empty? } + + state.item_lookahead_set = state.item_lookahead_set.merge {|_, v1, v2| v1 | v2 } + state.transitions.each do |shift, next_state| + next if next_state.lookaheads_recomputed + compute_state(state, shift, next_state) + end + end + + def compute_state(state, shift, next_state) + filtered_lookaheads = state.propagate_lookaheads(next_state) + s = next_state.ielr_isocores.find {|st| st.compatible_lookahead?(filtered_lookaheads) } + + if s.nil? + s = next_state.ielr_isocores.last + new_state = State.new(@states.count, s.accessing_symbol, s.kernels) + new_state.closure = s.closure + new_state.compute_shifts_reduces + s.transitions.each do |sh, next_state| + new_state.set_items_to_state(sh.next_items, next_state) end + @states << new_state + new_state.lalr_isocore = s + s.ielr_isocores << new_state + s.ielr_isocores.each do |st| + st.ielr_isocores = s.ielr_isocores + end + new_state.item_lookahead_set = filtered_lookaheads + state.update_transition(shift, new_state) + elsif(!s.lookaheads_recomputed) + s.item_lookahead_set = filtered_lookaheads else - if sr_count != 0 - @warning.warn("shift/reduce conflicts: #{sr_count} found") - end - - if rr_count != 0 - @warning.warn("reduce/reduce conflicts: #{rr_count} found") - end + state.update_transition(shift, s) + merge_lookaheads(s, filtered_lookaheads) end end end diff --git a/tools/lrama/lib/lrama/states/item.rb b/tools/lrama/lib/lrama/states/item.rb index 31b74b9d3..e89cb9695 100644 --- a/tools/lrama/lib/lrama/states/item.rb +++ b/tools/lrama/lib/lrama/states/item.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # TODO: Validate position is not over rule rhs require "forwardable" @@ -54,14 +56,18 @@ module Lrama Item.new(rule: rule, position: position + 1) end - def symbols_before_dot + def symbols_before_dot # steep:ignore rhs[0...position] end - def symbols_after_dot + def symbols_after_dot # steep:ignore rhs[position..-1] end + def symbols_after_transition + rhs[position+1..-1] + end + def to_s "#{lhs.id.s_value}: #{display_name}" end @@ -73,9 +79,13 @@ module Lrama # Right after position def display_rest - r = rhs[position..-1].map(&:display_name).join(" ") + r = symbols_after_dot.map(&:display_name).join(" ") ". #{r} (rule #{rule_id})" end + + def predecessor_item_of?(other_item) + rule == other_item.rule && position == other_item.position - 1 + end end end end diff --git a/tools/lrama/lib/lrama/states_reporter.rb b/tools/lrama/lib/lrama/states_reporter.rb index 6f96cc6f6..64ff4de10 100644 --- a/tools/lrama/lib/lrama/states_reporter.rb +++ b/tools/lrama/lib/lrama/states_reporter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama class StatesReporter include Lrama::Report::Duration @@ -14,15 +16,54 @@ module Lrama private - def _report(io, grammar: false, states: false, itemsets: false, lookaheads: false, solved: false, counterexamples: false, verbose: false) - # TODO: Unused terms - # TODO: Unused rules - + def _report(io, grammar: false, rules: false, terms: false, states: false, itemsets: false, lookaheads: false, solved: false, counterexamples: false, verbose: false) + report_unused_rules(io) if rules + report_unused_terms(io) if terms report_conflicts(io) report_grammar(io) if grammar report_states(io, itemsets, lookaheads, solved, counterexamples, verbose) end + def report_unused_terms(io) + look_aheads = @states.states.each do |state| + state.reduces.flat_map do |reduce| + reduce.look_ahead unless reduce.look_ahead.nil? + end + end + + next_terms = @states.states.flat_map do |state| + state.shifts.map(&:next_sym).select(&:term?) + end + + unused_symbols = @states.terms.select do |term| + !(look_aheads + next_terms).include?(term) + end + + unless unused_symbols.empty? + io << "#{unused_symbols.count} Unused Terms\n\n" + unused_symbols.each_with_index do |term, index| + io << sprintf("%5d %s\n", index, term.id.s_value) + end + io << "\n\n" + end + end + + def report_unused_rules(io) + used_rules = @states.rules.flat_map(&:rhs) + + unused_rules = @states.rules.map(&:lhs).select do |rule| + !used_rules.include?(rule) && rule.token_id != 0 + end + + unless unused_rules.empty? + io << "#{unused_rules.count} Unused Rules\n\n" + unused_rules.each_with_index do |rule, index| + io << sprintf("%5d %s\n", index, rule.display_name) + end + io << "\n\n" + end + end + def report_conflicts(io) has_conflict = false @@ -37,7 +78,7 @@ module Lrama messages << "#{cs[:reduce_reduce].count} reduce/reduce" end - if !messages.empty? + unless messages.empty? has_conflict = true io << "State #{state.id} conflicts: #{messages.join(', ')}\n" end @@ -98,7 +139,7 @@ module Lrama if lookaheads && item.end_of_rule? reduce = state.find_reduce_by_item!(item) look_ahead = reduce.selected_look_ahead - if !look_ahead.empty? + unless look_ahead.empty? la = " [#{look_ahead.map(&:display_name).join(", ")}]" end end @@ -118,7 +159,7 @@ module Lrama tmp.each do |term, state_id| io << " #{term.display_name.ljust(max_len)} shift, and go to state #{state_id}\n" end - io << "\n" if !tmp.empty? + io << "\n" unless tmp.empty? # Report error caused by %nonassoc nl = false @@ -132,7 +173,7 @@ module Lrama nl = true io << " #{name.ljust(max_len)} error (nonassociative)\n" end - io << "\n" if !tmp.empty? + io << "\n" unless tmp.empty? # Report reduces nl = false @@ -181,14 +222,14 @@ module Lrama tmp.each do |nterm, state_id| io << " #{nterm.id.s_value.ljust(max_len)} go to state #{state_id}\n" end - io << "\n" if !tmp.empty? + io << "\n" unless tmp.empty? if solved # Report conflict resolutions state.resolved_conflicts.each do |resolved| io << " #{resolved.report_message}\n" end - io << "\n" if !state.resolved_conflicts.empty? + io << "\n" unless state.resolved_conflicts.empty? end if counterexamples && state.has_conflicts? @@ -219,7 +260,7 @@ module Lrama direct_read_sets = @states.direct_read_sets @states.nterms.each do |nterm| terms = direct_read_sets[[state.id, nterm.token_id]] - next if !terms + next unless terms next if terms.empty? str = terms.map {|sym| sym.id.s_value }.join(", ") @@ -231,7 +272,7 @@ module Lrama io << " [Reads Relation]\n" @states.nterms.each do |nterm| a = @states.reads_relation[[state.id, nterm.token_id]] - next if !a + next unless a a.each do |state_id2, nterm_id2| n = @states.nterms.find {|n| n.token_id == nterm_id2 } @@ -245,7 +286,7 @@ module Lrama read_sets = @states.read_sets @states.nterms.each do |nterm| terms = read_sets[[state.id, nterm.token_id]] - next if !terms + next unless terms next if terms.empty? terms.each do |sym| @@ -258,7 +299,7 @@ module Lrama io << " [Includes Relation]\n" @states.nterms.each do |nterm| a = @states.includes_relation[[state.id, nterm.token_id]] - next if !a + next unless a a.each do |state_id2, nterm_id2| n = @states.nterms.find {|n| n.token_id == nterm_id2 } @@ -271,11 +312,11 @@ module Lrama io << " [Lookback Relation]\n" @states.rules.each do |rule| a = @states.lookback_relation[[state.id, rule.id]] - next if !a + next unless a a.each do |state_id2, nterm_id2| n = @states.nterms.find {|n| n.token_id == nterm_id2 } - io << " (Rule: #{rule}) -> (State #{state_id2}, #{n.id.s_value})\n" + io << " (Rule: #{rule.display_name}) -> (State #{state_id2}, #{n.id.s_value})\n" end end io << "\n" @@ -286,7 +327,7 @@ module Lrama @states.nterms.each do |nterm| terms = follow_sets[[state.id, nterm.token_id]] - next if !terms + next unless terms terms.each do |sym| io << " #{nterm.id.s_value} -> #{sym.id.s_value}\n" @@ -300,7 +341,7 @@ module Lrama max_len = 0 @states.rules.each do |rule| syms = @states.la[[state.id, rule.id]] - next if !syms + next unless syms tmp << [rule, syms] max_len = ([max_len] + syms.map {|s| s.id.s_value.length }).max @@ -310,7 +351,7 @@ module Lrama io << " #{sym.id.s_value.ljust(max_len)} reduce using rule #{rule.id} (#{rule.lhs.id.s_value})\n" end end - io << "\n" if !tmp.empty? + io << "\n" unless tmp.empty? end # End of Report State diff --git a/tools/lrama/lib/lrama/trace_reporter.rb b/tools/lrama/lib/lrama/trace_reporter.rb new file mode 100644 index 000000000..bcf1ef1e5 --- /dev/null +++ b/tools/lrama/lib/lrama/trace_reporter.rb @@ -0,0 +1,45 @@ +# rbs_inline: enabled +# frozen_string_literal: true + +module Lrama + class TraceReporter + # @rbs (Lrama::Grammar grammar) -> void + def initialize(grammar) + @grammar = grammar + end + + # @rbs (**Hash[Symbol, bool] options) -> void + def report(**options) + _report(**options) + end + + private + + # @rbs rules: (bool rules, bool actions, bool only_explicit_rules, **untyped _) -> void + def _report(rules: false, actions: false, only_explicit_rules: false, **_) + report_rules if rules && !only_explicit_rules + report_only_explicit_rules if only_explicit_rules + report_actions if actions + end + + # @rbs () -> void + def report_rules + puts "Grammar rules:" + @grammar.rules.each { |rule| puts rule.display_name } + end + + # @rbs () -> void + def report_only_explicit_rules + puts "Grammar rules:" + @grammar.rules.each do |rule| + puts rule.display_name_without_action if rule.lhs.first_set.any? + end + end + + # @rbs () -> void + def report_actions + puts "Grammar rules with actions:" + @grammar.rules.each { |rule| puts rule.with_actions } + end + end +end diff --git a/tools/lrama/lib/lrama/version.rb b/tools/lrama/lib/lrama/version.rb index ef840ce43..12ece5a8f 100644 --- a/tools/lrama/lib/lrama/version.rb +++ b/tools/lrama/lib/lrama/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Lrama - VERSION = "0.6.9".freeze + VERSION = "0.7.0".freeze end diff --git a/tools/lrama/template/bison/yacc.c b/tools/lrama/template/bison/yacc.c index 42d875605..5577c5f07 100644 --- a/tools/lrama/template/bison/yacc.c +++ b/tools/lrama/template/bison/yacc.c @@ -1166,9 +1166,9 @@ yydestruct (const char *yymsg, #endif enum yy_repair_type { - insert, - delete, - shift, + inserting, + deleting, + shifting, }; struct yy_repair { @@ -1401,27 +1401,27 @@ yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar<%= output.user_formals if (current->repair_length + 1 > YYMAXREPAIR(<%= output.parse_param_name %>)) continue; - yy_repairs *new = (yy_repairs *) YYMALLOC (sizeof (yy_repairs)); - new->id = count; - new->next = 0; - new->stack_length = stack_length; - new->states = (yy_state_t *) YYMALLOC (sizeof (yy_state_t) * (stack_length)); - new->state = new->states + (current->state - current->states); - YYCOPY (new->states, current->states, current->state - current->states + 1); - new->repair_length = current->repair_length + 1; - new->prev_repair = current; - new->repair.type = insert; - new->repair.term = (yysymbol_kind_t) yyx; + yy_repairs *reps = (yy_repairs *) YYMALLOC (sizeof (yy_repairs)); + reps->id = count; + reps->next = 0; + reps->stack_length = stack_length; + reps->states = (yy_state_t *) YYMALLOC (sizeof (yy_state_t) * (stack_length)); + reps->state = reps->states + (current->state - current->states); + YYCOPY (reps->states, current->states, current->state - current->states + 1); + reps->repair_length = current->repair_length + 1; + reps->prev_repair = current; + reps->repair.type = inserting; + reps->repair.term = (yysymbol_kind_t) yyx; /* Process PDA assuming next token is yyx */ - if (! yy_process_repairs (new, yyx)) + if (! yy_process_repairs (reps, (yysymbol_kind_t)yyx)) { - YYFREE (new); + YYFREE (reps); continue; } - tail->next = new; - tail = new; + tail->next = reps; + tail = reps; count++; if (yyx == yytoken) @@ -1437,7 +1437,7 @@ yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar<%= output.user_formals YYDPRINTF ((stderr, "New repairs is enqueued. count: %d, yystate: %d, yyx: %d\n", count, yystate, yyx)); - yy_print_repairs (new<%= output.user_args %>); + yy_print_repairs (reps<%= output.user_args %>); } } } @@ -1475,7 +1475,12 @@ int yychar; /* The semantic value of the lookahead symbol. */ /* Default value used for initialization, for pacifying older GCCs or non-GCC compilers. */ -YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +#ifdef __cplusplus +static const YYSTYPE yyval_default = {}; +(void) yyval_default; +#else +YY_INITIAL_VALUE (static const YYSTYPE yyval_default;) +#endif YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); /* Location data for the lookahead symbol. */ From b88680ee9ee52ed3dc55266142375e3f5585f7f7 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 2 Apr 2025 16:42:57 +0900 Subject: [PATCH 37/68] mruby-pack (pack_unpack): use mrb_bool instead of int --- mrbgems/mruby-pack/src/pack.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mrbgems/mruby-pack/src/pack.c b/mrbgems/mruby-pack/src/pack.c index 306bb4a23..2dcea953d 100644 --- a/mrbgems/mruby-pack/src/pack.c +++ b/mrbgems/mruby-pack/src/pack.c @@ -1627,7 +1627,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary) } static mrb_value -pack_unpack(mrb_state *mrb, mrb_value str, int single) +pack_unpack(mrb_state *mrb, mrb_value str, mrb_bool single) { mrb_value result; struct tmpl tmpl; @@ -1749,13 +1749,13 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single) static mrb_value mrb_pack_unpack(mrb_state *mrb, mrb_value str) { - return pack_unpack(mrb, str, 0); + return pack_unpack(mrb, str, FALSE); } static mrb_value mrb_pack_unpack1(mrb_state *mrb, mrb_value str) { - return pack_unpack(mrb, str, 1); + return pack_unpack(mrb, str, TRUE); } void From 07d7aa6fdb16ba6413651cb40551027a6347328b Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 2 Apr 2025 16:45:11 +0900 Subject: [PATCH 38/68] mruby-pack: fix int and mrb_int mixture --- mrbgems/mruby-pack/src/pack.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mrbgems/mruby-pack/src/pack.c b/mrbgems/mruby-pack/src/pack.c index 2dcea953d..af73be371 100644 --- a/mrbgems/mruby-pack/src/pack.c +++ b/mrbgems/mruby-pack/src/pack.c @@ -1188,7 +1188,7 @@ pack_nul(mrb_state *mrb, mrb_value dst, mrb_int didx, int count) } static void -check_x(mrb_state *mrb, int a, int count, char c) +check_x(mrb_state *mrb, mrb_int a, mrb_int count, char c) { if (a < count) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "%c outside of string", c); @@ -1209,7 +1209,7 @@ has_tmpl(const struct tmpl *tmpl) } static enum pack_dir -read_tmpl(mrb_state *mrb, struct tmpl *tmpl, enum pack_type *typep, int *sizep, int *countp, unsigned int *flagsp) +read_tmpl(mrb_state *mrb, struct tmpl *tmpl, enum pack_type *typep, mrb_int *sizep, mrb_int *countp, unsigned int *flagsp) { mrb_int t, tlen; int ch, size = 0; @@ -1505,7 +1505,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary) mrb_value o, result; struct tmpl tmpl; enum pack_type type; - int count, size; + mrb_int count, size; unsigned int flags; enum pack_dir dir; @@ -1631,12 +1631,11 @@ pack_unpack(mrb_state *mrb, mrb_value str, mrb_bool single) { mrb_value result; struct tmpl tmpl; - int count; + mrb_int count; unsigned int flags; enum pack_dir dir; enum pack_type type; - int size; - int srcidx, srclen; + mrb_int size, srcidx, srclen; const unsigned char *sptr; prepare_tmpl(mrb, &tmpl); From 76f7678db2be3be7713bf497acb5b1df0fabeb95 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 3 Apr 2025 22:56:43 +0900 Subject: [PATCH 39/68] AUTHORS: update entries [ci skip] --- AUTHORS | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1960768ee..185ab8376 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,17 +1,17 @@ # Authors of mruby (mruby developers) -## The List of Contributors sorted by number of commits (as of 2025-03-01 8ba2ed6) +## The List of Contributors sorted by number of commits (as of 2025-04-02 07d7aa6) - 6083 Yukihiro "Matz" Matsumoto (@matz)* - 673 dearblue (@dearblue)* + 6146 Yukihiro "Matz" Matsumoto (@matz)* + 676 dearblue (@dearblue)* 587 KOBAYASHI Shuji (@shuujii) 353 Daniel Bovensiepen (@bovi)* 345 Takeshi Watanabe (@take-cheeze)* 333 Masaki Muranaka (@monaka) 234 Jun Hiroe (@suzukaze) 228 Tomoyuki Sahara (@tsahara)* + 225 John Bampton (@jbampton) 220 Cremno (@cremno)* - 219 John Bampton (@jbampton) 209 Yuki Kurihara (@ksss)+ 144 Yasuhiro Matsumoto (@mattn)* 113 Carson McDonald (@carsonmcdonald) @@ -103,6 +103,7 @@ 5 Chris Reuter (@suetanvil) 5 Davide D'Agostino (@DAddYE) 5 Eric Hodel (@drbrain) + 5 HASUMI Hitoshi (@hasumikin) 5 Hendrik (@Asmod4n) 5 Ichito Nagata (@i110) 5 Keita Obo (@ktaobo)* @@ -117,7 +118,6 @@ 5 xuejianqing (@joans321) 4 Dante Catalfamo (@dantecatalfamo) 4 Goro Kikuchi (@gorogit) - 4 HASUMI Hitoshi (@hasumikin) 4 Herwin Weststrate (@herwinw) 4 Horimoto Yasuhiro (@komainu8) 4 Jon Moss (@maclover7) @@ -126,6 +126,7 @@ 4 Lanza (@LanzaSchneider) 4 Li Yazhou (@flaneur2020) 4 Marcus Stollsteimer (@stomar) + 4 Mark Delk (@jethrodaniel) 4 NARUSE, Yui (@nurse) 4 Ravil Bayramgalin (@brainopia)*+ 4 Satoshi Odawara (@SatoshiOdawara) @@ -143,7 +144,6 @@ 3 Jan Berdajs (@mrbrdo) 3 Jonas Minnberg (@sasq64) 3 Joseph McCullough (@joequery) - 3 Mark Delk (@jethrodaniel) 3 Mark McCurry (@fundamental) 3 Nobuhiro Iwamatsu (@iwamatsu) 3 Per Lundberg (@perlun)* @@ -303,6 +303,7 @@ 1 arton (@arton) 1 duangsuse (@duangsuse) 1 fl0l0u (@fl0l0u) + 1 Bradley Whited (@esotericpig) 1 hhc0null (@hhc0null) 1 iTitou (@titouanc) 1 javier ramírez (@javier) From b1d910f6f4c3e5094e9e31c8117cd5f5d4c789bd Mon Sep 17 00:00:00 2001 From: John Bampton Date: Fri, 4 Apr 2025 15:05:13 +1000 Subject: [PATCH 40/68] mrbgems: fix spelling --- mrbgems/mruby-compiler/core/parse.y | 2 +- mrbgems/mruby-io/src/file.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index 5b40eb513..0eb3a92a8 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -1540,7 +1540,7 @@ prohibit_literals(parser_state *p, node *n) modifier_while "'while' modifier" modifier_until "'until' modifier" modifier_rescue "'rescue' modifier" - keyword_alias "'alis'" + keyword_alias "'alias'" keyword_BEGIN "'BEGIN'" keyword_END "'END'" keyword__LINE__ "'__LINE__'" diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 8754e02da..4dfbe5bd7 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -456,7 +456,7 @@ path_expand(mrb_state *mrb, const char *path, const char *base, mrb_bool tilda) { mrb_value ary; - // split path conponents as array and normalization + // split path components as array and normalization if (tilda && path[0] == '~') { base = path_gethome(mrb, &path); ary = path_split(mrb, path, base, NULL); From 9750513c25d58b494421809071ad1b15de4eb2ab Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 4 Apr 2025 23:55:44 +0900 Subject: [PATCH 41/68] mruby-bigint (mpz_get_str): add type cast to stop warning --- mrbgems/mruby-bigint/core/bigint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index 4a561c182..73ad1ad75 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -882,7 +882,7 @@ mpz_get_str(mrb_state *mrb, char *s, mrb_int sz, mrb_int base, mpz_t *x) if ((base & (base - 1)) == 0) { // base is a power of 2 int shift = 0; while ((1 << shift) < base) shift++; - mp_limb mask = base - 1; + mp_limb mask = (mp_limb)base - 1; mp_dbl_limb value = 0; int bits = 0; From cb8cd9388c9ac0c312e5413b7e224762927af32f Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 5 Apr 2025 21:44:49 +0900 Subject: [PATCH 42/68] mruby-enum-ext: filter_map should not ignore index; fix #6504 --- mrbgems/mruby-enum-ext/mrblib/enum.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index 987cd8df0..3445256a4 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -820,8 +820,8 @@ module Enumerable return to_enum(:filter_map) unless blk ary = [] - self.each do |x| - x = blk.call(x) + self.each do |*x| + x = blk.call(*x) ary.push x if x end ary From dd68681002f570233e6603360e70c32b2414eeba Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 5 Apr 2025 22:12:51 +0900 Subject: [PATCH 43/68] mruby-enum-ext: tally should respect multi-values; fix #6507 --- mrbgems/mruby-enum-ext/mrblib/enum.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index 3445256a4..a87cdfba7 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -851,7 +851,8 @@ module Enumerable # ["a", "b", "c", "b"].tally #=> {"a"=>1, "b"=>2, "c"=>1} def tally hash = {} - self.each do |x| + self.each do |*x| + x = x.__svalue hash[x] = (hash[x]||0)+1 end hash From 96855d31b5f51854f8cc8606ae55160cda01a7dd Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 6 Apr 2025 22:04:56 +0900 Subject: [PATCH 44/68] mruby-enum-ext (sum): need to honor index; fix #6508 --- mrbgems/mruby-enum-ext/mrblib/enum.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index a87cdfba7..985199abe 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -871,12 +871,12 @@ module Enumerable def sum(init=0,&block) result=init if block - self.each do |e| - result += block.call(e) + self.each do |*e| + result += block.call(*e) end else - self.each do |e| - result += e + self.each do |*e| + result += e.__svalue end end result From 6c4b799b4288f84a782430b2e80ad9aa4f69066b Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 6 Apr 2025 22:05:57 +0900 Subject: [PATCH 45/68] mruby-enum-ext (cycle): need to splat block parameters; #6508 --- mrbgems/mruby-enum-ext/mrblib/enum.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index 985199abe..6fca05657 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -670,7 +670,7 @@ module Enumerable return nil if ary.empty? while n < 0 || 0 < (n -= 1) - ary.each do |i| + ary.each do |*i| yield(*i) end end From 8c2ea232ea873ac400b2b5489486e5424e719bb7 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 7 Apr 2025 08:23:52 +0900 Subject: [PATCH 46/68] Revert "mruby-enum-ext (cycle): need to splat block parameters; #6508" This reverts commit 6c4b799b4288f84a782430b2e80ad9aa4f69066b. --- mrbgems/mruby-enum-ext/mrblib/enum.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index 6fca05657..985199abe 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -670,7 +670,7 @@ module Enumerable return nil if ary.empty? while n < 0 || 0 < (n -= 1) - ary.each do |*i| + ary.each do |i| yield(*i) end end From 1fdd96104180cc0fb5d3cb086b05ab6458911bb9 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 7 Apr 2025 11:35:19 +0900 Subject: [PATCH 47/68] mruby-compiler (scope_new): need to initialize nregs; fix #6509 `nregs` should not be smaller than `nlocals`. --- mrbgems/mruby-compiler/core/codegen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index be0bf875a..9deaa7fec 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -4012,7 +4012,7 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *nlv) s->lv = nlv; s->sp += node_len(nlv)+1; /* add self */ - s->nlocals = s->sp; + s->nlocals = s->nregs = s->sp; if (nlv) { mrb_sym *lv; node *n = nlv; From 84e1b0045c11e26cccaa648110baa1c229aebe30 Mon Sep 17 00:00:00 2001 From: dearblue Date: Mon, 7 Apr 2025 21:40:56 +0900 Subject: [PATCH 48/68] Fixed class method visibility via `module_function` The following code should work ```ruby module M def me p self end module_function :me end M.me ``` --- src/class.c | 2 +- test/t/module.rb | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/class.c b/src/class.c index 1c1986744..cf03a0197 100644 --- a/src/class.c +++ b/src/class.c @@ -2789,7 +2789,7 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod) mrb_method_t m = mrb_method_search(mrb, rclass, mid); prepare_singleton_class(mrb, (struct RBasic*)rclass); - MRB_METHOD_SET_VISIBILITY(m, MT_PRIVATE); + MRB_METHOD_SET_VISIBILITY(m, MT_PUBLIC); mrb_define_method_raw(mrb, rclass->c, mid, m); mrb_gc_arena_restore(mrb, ai); } diff --git a/test/t/module.rb b/test/t/module.rb index dee47c814..1cfef87a3 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -838,6 +838,9 @@ assert('Module#module_function') do end assert_true M.respond_to?(:modfunc) + assert_equal nil do + M.modfunc + end end assert('module with non-class/module outer raises TypeError') do From 40095bf75531f488ac9cbbc6334d81920947318e Mon Sep 17 00:00:00 2001 From: dearblue Date: Tue, 8 Apr 2025 20:54:05 +0900 Subject: [PATCH 49/68] Exclude the external project "lrama" from pre-commit --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 63426089f..05bdce77e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,6 +7,7 @@ default_language_version: # force all unspecified Node hooks to run Node.js v22.14.0 LTS node: 22.14.0 minimum_pre_commit_version: "3.2.0" +exclude: "^tools/lrama/" repos: - repo: meta hooks: From 0d5fecf833ce43b467e9b6f8997799b937c4d589 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 9 Apr 2025 22:29:17 +0900 Subject: [PATCH 50/68] variable.c (mrb_const_get): adjust local variable scope --- src/variable.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/variable.c b/src/variable.c index 57f2b15aa..ce55952af 100644 --- a/src/variable.c +++ b/src/variable.c @@ -825,17 +825,15 @@ mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) mrb_value mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) { - struct RClass *c; - struct RClass *c2; - mrb_value v; const struct RProc *proc = mrb->c->ci->proc; + struct RClass *c = MRB_PROC_TARGET_CLASS(proc); + mrb_value v; - c = MRB_PROC_TARGET_CLASS(proc); if (!c) c = mrb->object_class; if (iv_get(mrb, class_iv_ptr(c), sym, &v)) { return v; } - c2 = c; + struct RClass *c2 = c; while (c2 && c2->tt == MRB_TT_SCLASS) { mrb_value klass; From 29964b3ba79923c67f6361ac601a69ff4ba2f4bd Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 10 Apr 2025 22:58:54 +0900 Subject: [PATCH 51/68] mruby-io (mrb_file_size): add check for MRB_32BIT and MRB_INT64 --- mrbgems/mruby-io/src/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 4dfbe5bd7..ed9d7a4de 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -654,7 +654,7 @@ mrb_file_size(mrb_state *mrb, mrb_value self) mrb_sys_fail(mrb, "fstat"); } - if (st.st_size > MRB_INT_MAX) { + if (sizeof(st.st_size) >= sizeof(mrb_int) && st.st_size > MRB_INT_MAX) { #ifdef MRB_NO_FLOAT mrb_raise(mrb, E_RUNTIME_ERROR, "File#size too large for MRB_NO_FLOAT"); #else From 82522a2e71b3d74ef0559a0d6d69dfc9200d2776 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 10 Apr 2025 23:00:30 +0900 Subject: [PATCH 52/68] mruby-bigint (mrb_bint_to_s): add check for MRB_32BIT and MRB_INT64 --- mrbgems/mruby-bigint/core/bigint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index 73ad1ad75..f8d5e52e5 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -1861,7 +1861,7 @@ mrb_bint_to_s(mrb_state *mrb, mrb_value x, mrb_int base) return mrb_str_new_lit(mrb, "0"); } size_t len = mpz_sizeinbase(&a, (int)base); - if (MRB_INT_MAX-2 < len) { + if (sizeof(size_t) >= sizeof(mrb_int) && MRB_INT_MAX-2 < len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "too long string from Integer"); } mrb_value str = mrb_str_new(mrb, NULL, len+2); From cda551ce25d4087188f85a61d22fedc3a257b910 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 11 Apr 2025 22:49:30 +0900 Subject: [PATCH 53/68] mruby-metaprog (mrb_mod_s_constants): call Module#constants if possible When the optional argument is given or the receiver is not `Module`. --- mrbgems/mruby-metaprog/src/metaprog.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mrbgems/mruby-metaprog/src/metaprog.c b/mrbgems/mruby-metaprog/src/metaprog.c index a6a6dfd35..794e1720f 100644 --- a/mrbgems/mruby-metaprog/src/metaprog.c +++ b/mrbgems/mruby-metaprog/src/metaprog.c @@ -643,6 +643,9 @@ mrb_mod_remove_method(mrb_state *mrb, mrb_value mod) static mrb_value mrb_mod_s_constants(mrb_state *mrb, mrb_value mod) { + if (mrb_get_argc(mrb) > 0 || mrb_class_ptr(mod) != mrb->module_class) { + return mrb_mod_constants(mrb, mod); + } mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented"); return mrb_nil_value(); /* not reached */ } From 118e13d85ce49c8e1a67e35c4534f460785cf0a7 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 12 Apr 2025 16:27:42 +0900 Subject: [PATCH 54/68] variable.c: a new function mrb_mod_const_at() And mrb_mod_constants() use the new function now. --- include/mruby/internal.h | 5 +++-- src/variable.c | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/mruby/internal.h b/include/mruby/internal.h index b8387b303..0c719ce27 100644 --- a/include/mruby/internal.h +++ b/include/mruby/internal.h @@ -176,11 +176,12 @@ size_t mrb_obj_iv_tbl_memsize(mrb_value); mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*); void mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod); +mrb_value mrb_mod_const_at(mrb_state *mrb, struct RClass *c, mrb_value ary); mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self); mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value); mrb_value mrb_mod_class_variables(mrb_state*, mrb_value); -mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym); -mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym); +mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym); +mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass *c, mrb_sym sym); mrb_bool mrb_ident_p(const char *s, mrb_int len); mrb_value mrb_exc_const_get(mrb_state *mrb, mrb_sym sym); diff --git a/src/variable.c b/src/variable.c index ce55952af..c72093dfa 100644 --- a/src/variable.c +++ b/src/variable.c @@ -924,6 +924,13 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) return 0; } +mrb_value +mrb_mod_const_at(mrb_state *mrb, struct RClass *c, mrb_value ary) +{ + iv_foreach(mrb, class_iv_ptr(c), const_i, &ary); + return ary; +} + /* 15.2.2.4.24 */ /* * call-seq: @@ -941,7 +948,7 @@ mrb_mod_constants(mrb_state *mrb, mrb_value mod) mrb_get_args(mrb, "|b", &inherit); ary = mrb_ary_new(mrb); while (c) { - iv_foreach(mrb, class_iv_ptr(c), const_i, &ary); + mrb_mod_const_at(mrb, c, ary); if (!inherit) break; c = c->super; if (c == mrb->object_class) break; From 90d77e71f506b202228de2468d4ff22237e37b06 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 12 Apr 2025 21:16:46 +0900 Subject: [PATCH 55/68] variable.c (mrb_mod_cv_set): small refactoring The argument c should not be NULL. --- src/variable.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/variable.c b/src/variable.c index c72093dfa..0e43e695f 100644 --- a/src/variable.c +++ b/src/variable.c @@ -669,7 +669,7 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) c = c->super; } - if (cls && cls->tt == MRB_TT_SCLASS) { + if (cls->tt == MRB_TT_SCLASS) { mrb_value klass; klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__)); @@ -684,7 +684,7 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) break; } } - else if (cls && cls->tt == MRB_TT_ICLASS) { + else if (cls->tt == MRB_TT_ICLASS) { c = cls->c; } else { From e08452585e62e29eebed39c592a1bdf5c7b2a0b8 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 12 Apr 2025 21:45:25 +0900 Subject: [PATCH 56/68] variable.c (mrb_mod_cv_get): c must not be NULL as an argument --- src/variable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/variable.c b/src/variable.c index 0e43e695f..1cd4e85d6 100644 --- a/src/variable.c +++ b/src/variable.c @@ -624,7 +624,7 @@ mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym) c = c->super; } if (given) return v; - if (cls && cls->tt == MRB_TT_SCLASS) { + if (cls->tt == MRB_TT_SCLASS) { mrb_value klass; klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__)); From 95f9de4877bc97d35c0bc1f5e5c0ba9b21729416 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 13 Apr 2025 23:35:44 +0900 Subject: [PATCH 57/68] mruby-metaprog (mrb_mod_s_constants): Module.constants implemented The method returns the list of all constants accessible from the point of call. --- mrbgems/mruby-metaprog/src/metaprog.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-metaprog/src/metaprog.c b/mrbgems/mruby-metaprog/src/metaprog.c index 794e1720f..dc453dbfa 100644 --- a/mrbgems/mruby-metaprog/src/metaprog.c +++ b/mrbgems/mruby-metaprog/src/metaprog.c @@ -646,8 +646,26 @@ mrb_mod_s_constants(mrb_state *mrb, mrb_value mod) if (mrb_get_argc(mrb) > 0 || mrb_class_ptr(mod) != mrb->module_class) { return mrb_mod_constants(mrb, mod); } - mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented"); - return mrb_nil_value(); /* not reached */ + + const struct RProc *proc = mrb->c->ci[-1].proc; + struct RClass *c = MRB_PROC_TARGET_CLASS(proc); + mrb_value ary = mrb_ary_new(mrb); + + if (!c) c = mrb->object_class; + mrb_mod_const_at(mrb, c, ary); + proc = proc->upper; + while (proc) { + struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc); + if (!c2) c2 = mrb->object_class; + mrb_mod_const_at(mrb, c2, ary); + proc = proc->upper; + } + while (c) { + mrb_mod_const_at(mrb, c, ary); + c = c->super; + if (c == mrb->object_class) break; + } + return ary; } static mrb_value From 953ebdf3345b24057a528d1979ff30c17f4e9dc7 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 14 Apr 2025 14:10:16 +0900 Subject: [PATCH 58/68] variable.c (mrb_vm_const_get): refactoring for singleton classes --- src/variable.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/variable.c b/src/variable.c index 1cd4e85d6..f897aabc1 100644 --- a/src/variable.c +++ b/src/variable.c @@ -826,32 +826,29 @@ mrb_value mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) { const struct RProc *proc = mrb->c->ci->proc; - struct RClass *c = MRB_PROC_TARGET_CLASS(proc); + struct RClass *c = MRB_PROC_TARGET_CLASS(proc), *c2; mrb_value v; if (!c) c = mrb->object_class; if (iv_get(mrb, class_iv_ptr(c), sym, &v)) { return v; } - struct RClass *c2 = c; - while (c2 && c2->tt == MRB_TT_SCLASS) { - mrb_value klass; - - if (!iv_get(mrb, class_iv_ptr(c2), MRB_SYM(__attached__), &klass)) { - c2 = NULL; - break; - } - c2 = mrb_class_ptr(klass); - } - if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2; - proc = proc->upper; - while (proc) { + for (proc = proc->upper; proc; proc = proc->upper) { c2 = MRB_PROC_TARGET_CLASS(proc); if (!c2) c2 = mrb->object_class; - if (c2 && iv_get(mrb, class_iv_ptr(c2), sym, &v)) { + if (iv_get(mrb, class_iv_ptr(c2), sym, &v)) { return v; } - proc = proc->upper; + } + if (c->tt == MRB_TT_SCLASS) { + mrb_value klass; + for (c2 = c; c2 && c2->tt == MRB_TT_SCLASS; c2 = mrb_class_ptr(klass)) { + if (!iv_get(mrb, class_iv_ptr(c2), MRB_SYM(__attached__), &klass)) { + c2 = NULL; + break; + } + } + if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2; } return const_get(mrb, c, sym, TRUE); } From 1969100fb47457e8770e3eabe078c866f44c3669 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 14 Apr 2025 17:51:22 +0900 Subject: [PATCH 59/68] variable.c (mrb_vm_get_const): seek singleton class superclass; fix #6506 Also ensure we don't revive #2515, #3003, #3598 --- src/variable.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/variable.c b/src/variable.c index f897aabc1..317d4acfa 100644 --- a/src/variable.c +++ b/src/variable.c @@ -841,6 +841,11 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) } } if (c->tt == MRB_TT_SCLASS) { + v = const_get_nohook(mrb, c, sym, TRUE); + if (!mrb_undef_p(v)) { + return v; + } + mrb_value klass; for (c2 = c; c2 && c2->tt == MRB_TT_SCLASS; c2 = mrb_class_ptr(klass)) { if (!iv_get(mrb, class_iv_ptr(c2), MRB_SYM(__attached__), &klass)) { From 9cc6d399560f8db781ff654bca88884a6cdcde6f Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 14 Apr 2025 22:08:50 +0900 Subject: [PATCH 60/68] test/module.rb: add test for #6506 --- test/t/module.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/t/module.rb b/test/t/module.rb index 1cfef87a3..7ec723a2d 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -889,3 +889,23 @@ assert('shared empty iv_tbl (prepend)') do m2::CONST2 = 2 assert_equal 2, c::CONST2 end + +assert('constant lookup #6506') do + Module.new do + module X + module A + class WWW; end + end + end + + module X::Y; end + + module X::Y::Z + extend X::A + + class << self + assert_nothing_raised{WWW} + end + end + end +end From 8b213c4fbf0d7c0189c44aee7e4182c0b317181e Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 15 Apr 2025 07:11:09 +0900 Subject: [PATCH 61/68] string.c (sub_replace): check if `found` argument is within proper range Since it is an internal method, we didn't check the argument range, but if it's called directly, out-of-range argument could cause SEGV. --- src/string.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/string.c b/src/string.c index abb8b4e36..df7363d3d 100644 --- a/src/string.c +++ b/src/string.c @@ -3046,6 +3046,9 @@ sub_replace(mrb_state *mrb, mrb_value self) mrb_value result; mrb_get_args(mrb, "ssi", &p, &plen, &match, &mlen, &found); + if (found < 0 || RSTRING_LEN(self) < found) { + mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range"); + } result = mrb_str_new(mrb, 0, 0); for (mrb_int i=0; i Date: Wed, 16 Apr 2025 22:34:27 +0900 Subject: [PATCH 62/68] pre-commit autoupdate --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 05bdce77e..0cd987c81 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: language: node additional_dependencies: ["prettier@3.5.3"] - repo: https://github.com/gitleaks/gitleaks - rev: v8.24.2 + rev: v8.24.3 hooks: - id: gitleaks name: run gitleaks From dee661913f2213bb90bbf541532b326872b049cb Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 16 Apr 2025 22:37:06 +0900 Subject: [PATCH 63/68] mruby-numeric-ext (int_sqrt): Integer.sqrt implemented This is the first version. It should support bigint eventually. --- mrbgems/mruby-numeric-ext/src/numeric_ext.c | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/mrbgems/mruby-numeric-ext/src/numeric_ext.c index 180780eda..ca1067c7b 100644 --- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c +++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c @@ -262,6 +262,37 @@ flo_remainder(mrb_state *mrb, mrb_value self) } #endif +static mrb_int +isqrt(mrb_int n) +{ + mrb_assert(n >= 0); + if (n < 2) return n; + + mrb_int x = n; + mrb_int y = (x + 1) / 2; + + // Babylonian method (integer version) + while (y < x) { + x = y; + y = (x + n / x) / 2; + } + + return x; +} + +static mrb_value +int_sqrt(mrb_state *mrb, mrb_value self) +{ + mrb_int n; + mrb_get_args(mrb, "i", &n); + if (n < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "non-negative integer required"); + } + + mrb_int result = isqrt(n); + return mrb_int_value(mrb, result); +} + void mrb_mruby_numeric_ext_gem_init(mrb_state* mrb) { @@ -275,6 +306,7 @@ mrb_mruby_numeric_ext_gem_init(mrb_state* mrb) mrb_define_method_id(mrb, ic, MRB_SYM(size), int_size, MRB_ARGS_NONE()); mrb_define_method_id(mrb, ic, MRB_SYM_Q(odd), int_odd, MRB_ARGS_NONE()); mrb_define_method_id(mrb, ic, MRB_SYM_Q(even), int_even, MRB_ARGS_NONE()); + mrb_define_class_method_id(mrb, ic, MRB_SYM(sqrt), int_sqrt, MRB_ARGS_REQ(1)); #ifndef MRB_NO_FLOAT struct RClass *fc = mrb->float_class; From ddfe65763e7410cceda799d98d939d20e8da62df Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 16 Apr 2025 23:08:23 +0900 Subject: [PATCH 64/68] mruby-numeric-ext (int_sqrt): support bigint --- include/mruby/internal.h | 1 + mrbgems/mruby-bigint/core/bigint.c | 74 +++++++++++++++++++++ mrbgems/mruby-numeric-ext/src/numeric_ext.c | 23 +++++-- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/include/mruby/internal.h b/include/mruby/internal.h index 0c719ce27..168286618 100644 --- a/include/mruby/internal.h +++ b/include/mruby/internal.h @@ -248,6 +248,7 @@ void mrb_gc_free_bint(mrb_state *mrb, struct RBasic *x); void mrb_bint_copy(mrb_state *mrb, mrb_value x, mrb_value y); size_t mrb_bint_memsize(mrb_value x); mrb_value mrb_bint_hash(mrb_state *mrb, mrb_value x); +mrb_value mrb_bint_sqrt(mrb_state *mrb, mrb_value x); #endif #endif /* MRUBY_INTERNAL_H */ diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index f8d5e52e5..abf8617eb 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -1246,6 +1246,63 @@ mpz_gcd(mrb_state *mrb, mpz_t *gg, mpz_t *aa, mpz_t *bb) } #endif +static size_t +mpz_bit_length(const mpz_t *x) +{ + if (x->sz == 0 || x->sn == 0) return 0; + + // 最上位 limb を取得(リトルエンディアン: 末尾) + size_t i = x->sz - 1; + mp_limb high = x->p[i]; + + // high の中で最上位のビットを探す + size_t bits = 0; + while (high != 0) { + high >>= 1; + bits++; + } + + return i * (sizeof(mp_limb) * 8) + bits; +} + +static void +mpz_sqrt(mrb_state *mrb, mpz_t *z, mpz_t *x) +{ + mrb_assert(x->sn >= 0); + + if (x->sz == 0) { + // sqrt(0) = 0 + z->sn = 0; + z->sz = 0; + return; + } + + // 初期値の設定: bit-length の半分の位置に 1 を立てる + size_t xbits = mpz_bit_length(x); + size_t sbit = (xbits + 1) / 2; + mpz_t s, t; + mpz_init_set_int(mrb, &s, 1); + mpz_mul_2exp(mrb, &s, &s, sbit); + + mpz_init(mrb, &t); + + // ループ: s = (s + x / s) / 2 + for (;;) { + mpz_mdiv(mrb, &t, x, &s); // t = x / s + mpz_add(mrb, &t, &t, &s); // t = s + x/s + mpz_div_2exp(mrb, &t, &t, 1); // t = (s + x/s) / 2 + + if (mpz_cmp(mrb, &t, &s) >= 0) { + break; // 収束 + } + + mpz_set(mrb, &s, &t); + } + + mpz_move(mrb, z, &s); + mpz_clear(mrb, &t); +} + /* --- mruby functions --- */ /* initialize mpz_t from RBigint (not need to clear) */ static void @@ -2014,6 +2071,23 @@ mrb_bint_memsize(mrb_value x) return z.sz * sizeof(mp_limb); } +mrb_value +mrb_bint_sqrt(mrb_state *mrb, mrb_value x) +{ + mpz_t a; + + bint_as_mpz(RBIGINT(x), &a); + if (a.sn < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "square root of negative number"); + } + + mpz_t z; + mpz_init(mrb, &z); + mpz_sqrt(mrb, &z, &a); + + return bint_norm(mrb, bint_new(mrb, &z)); +} + mrb_value mrb_bint_hash(mrb_state *mrb, mrb_value x) { diff --git a/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/mrbgems/mruby-numeric-ext/src/numeric_ext.c index ca1067c7b..b8a8bb3d7 100644 --- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c +++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c @@ -283,14 +283,23 @@ isqrt(mrb_int n) static mrb_value int_sqrt(mrb_state *mrb, mrb_value self) { - mrb_int n; - mrb_get_args(mrb, "i", &n); - if (n < 0) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "non-negative integer required"); - } + mrb_value arg = mrb_get_arg1(mrb); - mrb_int result = isqrt(n); - return mrb_int_value(mrb, result); + if (mrb_integer_p(arg)) { + mrb_int n = mrb_integer(arg); + if (n < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "non-negative integer required"); + } + return mrb_int_value(mrb, isqrt(n)); + } +#ifdef MRB_USE_BIGINT + else if (mrb_bigint_p(arg)) { + return mrb_bint_sqrt(mrb, arg); + } +#endif + else { + mrb_raise(mrb, E_TYPE_ERROR, "expected Integer"); + } } void From e42523fcf4c094d0479a168ca997d0d0f3924826 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 16 Apr 2025 23:30:23 +0900 Subject: [PATCH 65/68] mruby-numeric-ext: add test for Integer.sqrt() --- mrbgems/mruby-numeric-ext/test/numeric.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mrbgems/mruby-numeric-ext/test/numeric.rb b/mrbgems/mruby-numeric-ext/test/numeric.rb index eb2702e67..bb4c461d6 100644 --- a/mrbgems/mruby-numeric-ext/test/numeric.rb +++ b/mrbgems/mruby-numeric-ext/test/numeric.rb @@ -81,3 +81,9 @@ assert('Integer#digits') do assert_equal([4, 6, 6, 0, 5], 12345.digits(7)) assert_equal([45, 23, 1], 12345.digits(100)) end + +assert('Integer.sqrt') do + assert_equal(4, Integer.sqrt(16)) + assert_equal(10, Integer.sqrt(100)) + assert_equal(85, Integer.sqrt(7244)) +end From 16198dc985301a791dae155d729fd548ba9ffc0c Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 17 Apr 2025 01:42:41 +0900 Subject: [PATCH 66/68] mruby-numeric-ext (int_sqrt): translate Japanese comment (oops) --- mrbgems/mruby-bigint/core/bigint.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index abf8617eb..50fb42a32 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -1251,11 +1251,11 @@ mpz_bit_length(const mpz_t *x) { if (x->sz == 0 || x->sn == 0) return 0; - // 最上位 limb を取得(リトルエンディアン: 末尾) + // Get the most significant limb (last element in little-endian order) size_t i = x->sz - 1; mp_limb high = x->p[i]; - // high の中で最上位のビットを探す + // Count the number of bits in the most significant limb size_t bits = 0; while (high != 0) { high >>= 1; @@ -1277,7 +1277,7 @@ mpz_sqrt(mrb_state *mrb, mpz_t *z, mpz_t *x) return; } - // 初期値の設定: bit-length の半分の位置に 1 を立てる + // Estimate initial value: 1 << (bit_length(x) / 2) size_t xbits = mpz_bit_length(x); size_t sbit = (xbits + 1) / 2; mpz_t s, t; @@ -1286,14 +1286,16 @@ mpz_sqrt(mrb_state *mrb, mpz_t *z, mpz_t *x) mpz_init(mrb, &t); - // ループ: s = (s + x / s) / 2 + // Iteratively refine s using Newton-Raphson method: + // s = (s + x / s) / 2 for (;;) { mpz_mdiv(mrb, &t, x, &s); // t = x / s mpz_add(mrb, &t, &t, &s); // t = s + x/s mpz_div_2exp(mrb, &t, &t, 1); // t = (s + x/s) / 2 if (mpz_cmp(mrb, &t, &s) >= 0) { - break; // 収束 + // Converged: t >= s + break; } mpz_set(mrb, &s, &t); From 061521ead5a02b141b6523dae6160b8439dc4f7b Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 17 Apr 2025 08:10:34 +0900 Subject: [PATCH 67/68] mruby-bigint (mpz_bits): renamed from mpz_bit_length() --- mrbgems/mruby-bigint/core/bigint.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index 50fb42a32..abcdf328b 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -1247,7 +1247,7 @@ mpz_gcd(mrb_state *mrb, mpz_t *gg, mpz_t *aa, mpz_t *bb) #endif static size_t -mpz_bit_length(const mpz_t *x) +mpz_bits(const mpz_t *x) { if (x->sz == 0 || x->sn == 0) return 0; @@ -1278,7 +1278,7 @@ mpz_sqrt(mrb_state *mrb, mpz_t *z, mpz_t *x) } // Estimate initial value: 1 << (bit_length(x) / 2) - size_t xbits = mpz_bit_length(x); + size_t xbits = mpz_bits(x); size_t sbit = (xbits + 1) / 2; mpz_t s, t; mpz_init_set_int(mrb, &s, 1); From baea6e284e6a1eda46d0421d6f5b72b6cc9dc528 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 17 Apr 2025 08:11:27 +0900 Subject: [PATCH 68/68] mruby-bigint (mpz_bits): use lzb() --- mrbgems/mruby-bigint/core/bigint.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index abcdf328b..7bf440e8e 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -1251,18 +1251,14 @@ mpz_bits(const mpz_t *x) { if (x->sz == 0 || x->sn == 0) return 0; - // Get the most significant limb (last element in little-endian order) + size_t limb_bits = sizeof(mp_limb) * 8; + + // Get the most significant limb size_t i = x->sz - 1; mp_limb high = x->p[i]; - // Count the number of bits in the most significant limb - size_t bits = 0; - while (high != 0) { - high >>= 1; - bits++; - } - - return i * (sizeof(mp_limb) * 8) + bits; + // Number of bits = total full limbs + significant bits in top limb + return i * limb_bits + (limb_bits - lzb(high)); } static void