summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortompng <[email protected]>2024-06-07 02:05:22 +0900
committerYusuke Endoh <[email protected]>2024-10-03 18:47:09 +0900
commita8a059125314a411eaf879a9fbfdc68d6c01a667 (patch)
treef85d3d1e48a18b49d031d260068fcea3d00976f2
parent2c0149d330d3068272eed785d627d9f0daa95bbf (diff)
Hash#inspect with colon style
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/10924
-rw-r--r--hash.c51
-rw-r--r--test/ruby/test_hash.rb27
2 files changed, 75 insertions, 3 deletions
diff --git a/hash.c b/hash.c
index 36be9efcb6..d48c763cd2 100644
--- a/hash.c
+++ b/hash.c
@@ -3405,20 +3405,65 @@ rb_hash_to_a(VALUE hash)
return ary;
}
+static bool
+symbol_key_needs_quote(VALUE str)
+{
+ long len = RSTRING_LEN(str);
+ if (len == 0 || !rb_str_symname_p(str)) return true;
+ const char *s = RSTRING_PTR(str);
+ char first = s[0];
+ if (first == '@' || first == '$' || first == '!') return true;
+ if (!at_char_boundary(s, s + len - 1, RSTRING_END(str), rb_enc_get(str))) return false;
+ switch (s[len - 1]) {
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '`':
+ case '%':
+ case '^':
+ case '&':
+ case '|':
+ case ']':
+ case '<':
+ case '=':
+ case '>':
+ case '~':
+ case '@':
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
inspect_i(VALUE key, VALUE value, VALUE str)
{
VALUE str2;
- str2 = rb_inspect(key);
+ bool is_symbol = SYMBOL_P(key);
+ bool quote = false;
+ if (is_symbol) {
+ str2 = rb_sym2str(key);
+ quote = symbol_key_needs_quote(str2);
+ }
+ else {
+ str2 = rb_inspect(key);
+ }
if (RSTRING_LEN(str) > 1) {
rb_str_buf_cat_ascii(str, ", ");
}
else {
rb_enc_copy(str, str2);
}
- rb_str_buf_append(str, str2);
- rb_str_buf_cat_ascii(str, "=>");
+ if (quote) {
+ rb_str_buf_append(str, rb_str_inspect(str2));
+ }
+ else {
+ rb_str_buf_append(str, str2);
+ }
+
+ rb_str_buf_cat_ascii(str, is_symbol ? ": " : " => ");
str2 = rb_inspect(value);
rb_str_buf_append(str, str2);
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index f60ba0cffd..8219abc678 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -869,6 +869,33 @@ class TestHash < Test::Unit::TestCase
$, = nil
end
+ def test_inspect
+ no_quote = '{a: 1, a!: 1, a?: 1}'
+ quote0 = '{"": 1}'
+ quote1 = '{"0": 1, "!": 1, "%": 1, "&": 1, "*": 1, "+": 1, "-": 1, "/": 1, "<": 1, ">": 1, "^": 1, "`": 1, "|": 1, "~": 1}'
+ quote2 = '{"@a": 1, "$a": 1, "+@": 1, "a=": 1, "[]": 1}'
+ quote3 = '{"a\"b": 1, "@@a": 1, "<=>": 1, "===": 1, "[]=": 1}'
+ assert_equal(no_quote, eval(no_quote).inspect)
+ assert_equal(quote0, eval(quote0).inspect)
+ assert_equal(quote1, eval(quote1).inspect)
+ assert_equal(quote2, eval(quote2).inspect)
+ assert_equal(quote3, eval(quote3).inspect)
+ begin
+ enc = Encoding.default_external
+ Encoding.default_external = Encoding::ASCII
+ utf8_ascii_hash = '{"\\u3042": 1}'
+ assert_equal(eval(utf8_ascii_hash).inspect, utf8_ascii_hash)
+ Encoding.default_external = Encoding::UTF_8
+ utf8_hash = "{\u3042: 1}"
+ assert_equal(eval(utf8_hash).inspect, utf8_hash)
+ Encoding.default_external = Encoding::Windows_31J
+ sjis_hash = "{\x87]: 1}".force_encoding('sjis')
+ assert_equal(eval(sjis_hash).inspect, sjis_hash)
+ ensure
+ Encoding.default_external = enc
+ end
+ end
+
def test_update
h1 = @cls[ 1 => 2, 2 => 3, 3 => 4 ]
h2 = @cls[ 2 => 'two', 4 => 'four' ]