From b6a226e6f44441b89475ea2589c8ad6f18a824d4 Mon Sep 17 00:00:00 2001 From: Alexander Kireev Date: Fri, 3 Jul 2026 06:46:11 +0700 Subject: [PATCH] Fix sign loss when scanning negative sexagesimal scalars ScalarScanner#tokenize only applied the leading minus/plus to the first colon-separated segment (via String#to_i/#to_f), so a value like "-190:20:30" came out as -682770 instead of -685230 because the 20 and 30 parts kept their positive sign. Strip the sign once up front and apply it to the final sum instead, matching the yaml.org sexagesimal spec (and what PyYAML does). Added regression tests for the negative int and float cases. --- lib/psych/scalar_scanner.rb | 10 ++++++---- test/psych/test_scalar_scanner.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/psych/scalar_scanner.rb b/lib/psych/scalar_scanner.rb index 68e17ecf..c5cb0c1e 100644 --- a/lib/psych/scalar_scanner.rb +++ b/lib/psych/scalar_scanner.rb @@ -92,17 +92,19 @@ def tokenize string @symbol_cache[string] = class_loader.symbolize(string.sub(/^:/, '')) end elsif string.match?(/^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}$/) + sign = string.start_with?('-') ? -1 : 1 i = 0 - string.split(':').each_with_index do |n,e| + string.delete_prefix('-').delete_prefix('+').split(':').each_with_index do |n,e| i += (n.to_i * 60 ** (e - 2).abs) end - i + i * sign elsif string.match?(/^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}\.[0-9_]*$/) + sign = string.start_with?('-') ? -1 : 1 i = 0 - string.split(':').each_with_index do |n,e| + string.delete_prefix('-').delete_prefix('+').split(':').each_with_index do |n,e| i += (n.to_f * 60 ** (e - 2).abs) end - i + i * sign elsif string.match?(FLOAT) if string.match?(/\A[-+]?\.\Z/) string diff --git a/test/psych/test_scalar_scanner.rb b/test/psych/test_scalar_scanner.rb index bc6a74ad..fbaa6b26 100644 --- a/test/psych/test_scalar_scanner.rb +++ b/test/psych/test_scalar_scanner.rb @@ -105,6 +105,14 @@ def test_scan_sexagesimal_int assert_equal 685230, ss.tokenize('190:20:30') end + def test_scan_negative_sexagesimal_int + assert_equal(-685230, ss.tokenize('-190:20:30')) + end + + def test_scan_negative_sexagesimal_float + assert_equal(-685230.15, ss.tokenize('-190:20:30.15')) + end + def test_scan_float assert_equal 1.2, ss.tokenize('1.2') end