[ruby/psych] Add strict_integer option to parse numbers with commas as strings
Authored-by: Seth Boyles <sethboyles@gmail.com> https://github.com/ruby/psych/commit/75bebb37b8
This commit is contained in:
parent
b815a0bd75
commit
c1a6ff046d
@ -268,10 +268,10 @@ module Psych
|
|||||||
# YAML documents that are supplied via user input. Instead, please use the
|
# YAML documents that are supplied via user input. Instead, please use the
|
||||||
# load method or the safe_load method.
|
# load method or the safe_load method.
|
||||||
#
|
#
|
||||||
def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false
|
def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false
|
||||||
result = parse(yaml, filename: filename)
|
result = parse(yaml, filename: filename)
|
||||||
return fallback unless result
|
return fallback unless result
|
||||||
result.to_ruby(symbolize_names: symbolize_names, freeze: freeze)
|
result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer)
|
||||||
end
|
end
|
||||||
|
|
||||||
###
|
###
|
||||||
@ -319,13 +319,13 @@ module Psych
|
|||||||
# Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"}
|
# Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"}
|
||||||
# Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
|
# Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
|
||||||
#
|
#
|
||||||
def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false
|
def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false
|
||||||
result = parse(yaml, filename: filename)
|
result = parse(yaml, filename: filename)
|
||||||
return fallback unless result
|
return fallback unless result
|
||||||
|
|
||||||
class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s),
|
class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s),
|
||||||
permitted_symbols.map(&:to_s))
|
permitted_symbols.map(&:to_s))
|
||||||
scanner = ScalarScanner.new class_loader
|
scanner = ScalarScanner.new class_loader, strict_integer: strict_integer
|
||||||
visitor = if aliases
|
visitor = if aliases
|
||||||
Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
|
Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
|
||||||
else
|
else
|
||||||
@ -365,14 +365,15 @@ module Psych
|
|||||||
# Raises a TypeError when `yaml` parameter is NilClass. This method is
|
# Raises a TypeError when `yaml` parameter is NilClass. This method is
|
||||||
# similar to `safe_load` except that `Symbol` objects are allowed by default.
|
# similar to `safe_load` except that `Symbol` objects are allowed by default.
|
||||||
#
|
#
|
||||||
def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false
|
def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false
|
||||||
safe_load yaml, permitted_classes: permitted_classes,
|
safe_load yaml, permitted_classes: permitted_classes,
|
||||||
permitted_symbols: permitted_symbols,
|
permitted_symbols: permitted_symbols,
|
||||||
aliases: aliases,
|
aliases: aliases,
|
||||||
filename: filename,
|
filename: filename,
|
||||||
fallback: fallback,
|
fallback: fallback,
|
||||||
symbolize_names: symbolize_names,
|
symbolize_names: symbolize_names,
|
||||||
freeze: freeze
|
freeze: freeze,
|
||||||
|
strict_integer: strict_integer
|
||||||
end
|
end
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -46,8 +46,8 @@ module Psych
|
|||||||
# Convert this node to Ruby.
|
# Convert this node to Ruby.
|
||||||
#
|
#
|
||||||
# See also Psych::Visitors::ToRuby
|
# See also Psych::Visitors::ToRuby
|
||||||
def to_ruby(symbolize_names: false, freeze: false)
|
def to_ruby(symbolize_names: false, freeze: false, strict_integer: false)
|
||||||
Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze).accept(self)
|
Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer).accept(self)
|
||||||
end
|
end
|
||||||
alias :transform :to_ruby
|
alias :transform :to_ruby
|
||||||
|
|
||||||
|
@ -12,24 +12,32 @@ module Psych
|
|||||||
FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10))$/x
|
FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10))$/x
|
||||||
|
|
||||||
# Taken from http://yaml.org/type/int.html
|
# Taken from http://yaml.org/type/int.html
|
||||||
INTEGER = /^(?:[-+]?0b[0-1_,]+ (?# base 2)
|
INTEGER_STRICT = /^(?:[-+]?0b[0-1_]+ (?# base 2)
|
||||||
|[-+]?0[0-7_,]+ (?# base 8)
|
|[-+]?0[0-7_]+ (?# base 8)
|
||||||
|[-+]?(?:0|[1-9](?:[0-9]|,[0-9]|_[0-9])*) (?# base 10)
|
|[-+]?(0|[1-9][0-9_]*) (?# base 10)
|
||||||
|[-+]?0x[0-9a-fA-F_,]+ (?# base 16))$/x
|
|[-+]?0x[0-9a-fA-F_]+ (?# base 16))$/x
|
||||||
|
|
||||||
|
# Same as above, but allows commas.
|
||||||
|
# Not to YML spec, but kept for backwards compatibility
|
||||||
|
INTEGER_LEGACY = /^(?:[-+]?0b[0-1_,]+ (?# base 2)
|
||||||
|
|[-+]?0[0-7_,]+ (?# base 8)
|
||||||
|
|[-+]?(?:0|[1-9](?:[0-9]|,[0-9]|_[0-9])*) (?# base 10)
|
||||||
|
|[-+]?0x[0-9a-fA-F_,]+ (?# base 16))$/x
|
||||||
|
|
||||||
attr_reader :class_loader
|
attr_reader :class_loader
|
||||||
|
|
||||||
# Create a new scanner
|
# Create a new scanner
|
||||||
def initialize class_loader
|
def initialize class_loader, strict_integer: false
|
||||||
@symbol_cache = {}
|
@symbol_cache = {}
|
||||||
@class_loader = class_loader
|
@class_loader = class_loader
|
||||||
|
@strict_integer = strict_integer
|
||||||
end
|
end
|
||||||
|
|
||||||
# Tokenize +string+ returning the Ruby object
|
# Tokenize +string+ returning the Ruby object
|
||||||
def tokenize string
|
def tokenize string
|
||||||
return nil if string.empty?
|
return nil if string.empty?
|
||||||
return @symbol_cache[string] if @symbol_cache.key?(string)
|
return @symbol_cache[string] if @symbol_cache.key?(string)
|
||||||
|
integer_regex = @strict_integer ? INTEGER_STRICT : INTEGER_LEGACY
|
||||||
# Check for a String type, being careful not to get caught by hash keys, hex values, and
|
# Check for a String type, being careful not to get caught by hash keys, hex values, and
|
||||||
# special floats (e.g., -.inf).
|
# special floats (e.g., -.inf).
|
||||||
if string.match?(%r{^[^\d.:-]?[[:alpha:]_\s!@#$%\^&*(){}<>|/\\~;=]+}) || string.match?(/\n/)
|
if string.match?(%r{^[^\d.:-]?[[:alpha:]_\s!@#$%\^&*(){}<>|/\\~;=]+}) || string.match?(/\n/)
|
||||||
@ -89,7 +97,7 @@ module Psych
|
|||||||
else
|
else
|
||||||
Float(string.gsub(/[,_]|\.([Ee]|$)/, '\1'))
|
Float(string.gsub(/[,_]|\.([Ee]|$)/, '\1'))
|
||||||
end
|
end
|
||||||
elsif string.match?(INTEGER)
|
elsif string.match?(integer_regex)
|
||||||
parse_int string
|
parse_int string
|
||||||
else
|
else
|
||||||
string
|
string
|
||||||
|
@ -12,9 +12,9 @@ module Psych
|
|||||||
###
|
###
|
||||||
# This class walks a YAML AST, converting each node to Ruby
|
# This class walks a YAML AST, converting each node to Ruby
|
||||||
class ToRuby < Psych::Visitors::Visitor
|
class ToRuby < Psych::Visitors::Visitor
|
||||||
def self.create(symbolize_names: false, freeze: false)
|
def self.create(symbolize_names: false, freeze: false, strict_integer: false)
|
||||||
class_loader = ClassLoader.new
|
class_loader = ClassLoader.new
|
||||||
scanner = ScalarScanner.new class_loader
|
scanner = ScalarScanner.new class_loader, strict_integer: strict_integer
|
||||||
new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze)
|
new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,5 +43,16 @@ module Psych
|
|||||||
str = Psych.load('--- 1.1.1')
|
str = Psych.load('--- 1.1.1')
|
||||||
assert_equal '1.1.1', str
|
assert_equal '1.1.1', str
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This behavior is not to YML spec, but is kept for backwards compatibility
|
||||||
|
def test_string_with_commas
|
||||||
|
number = Psych.load('--- 12,34,56')
|
||||||
|
assert_equal 123456, number
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_with_commas_with_strict_integer
|
||||||
|
str = Psych.load('--- 12,34,56', strict_integer: true)
|
||||||
|
assert_equal '12,34,56', str
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -149,6 +149,31 @@ module Psych
|
|||||||
assert_equal 0x123456789abcdef, ss.tokenize('0x12_,34,_56,_789abcdef__')
|
assert_equal 0x123456789abcdef, ss.tokenize('0x12_,34,_56,_789abcdef__')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_scan_strict_int_commas_and_underscores
|
||||||
|
# this test is to ensure adherance to YML spec using the 'strict_integer' option
|
||||||
|
scanner = Psych::ScalarScanner.new ClassLoader.new, strict_integer: true
|
||||||
|
assert_equal 123_456_789, scanner.tokenize('123_456_789')
|
||||||
|
assert_equal '123,456,789', scanner.tokenize('123,456,789')
|
||||||
|
assert_equal '1_2,3,4_5,6_789', scanner.tokenize('1_2,3,4_5,6_789')
|
||||||
|
|
||||||
|
assert_equal 1, scanner.tokenize('1')
|
||||||
|
assert_equal 1, scanner.tokenize('+1')
|
||||||
|
assert_equal(-1, scanner.tokenize('-1'))
|
||||||
|
|
||||||
|
assert_equal 0b010101010, scanner.tokenize('0b010101010')
|
||||||
|
assert_equal 0b010101010, scanner.tokenize('0b01_01_01_010')
|
||||||
|
assert_equal '0b0,1_0,1_,0,1_01,0', scanner.tokenize('0b0,1_0,1_,0,1_01,0')
|
||||||
|
|
||||||
|
assert_equal 01234567, scanner.tokenize('01234567')
|
||||||
|
assert_equal '0_,,,1_2,_34567', scanner.tokenize('0_,,,1_2,_34567')
|
||||||
|
|
||||||
|
assert_equal 0x123456789abcdef, scanner.tokenize('0x123456789abcdef')
|
||||||
|
assert_equal 0x123456789abcdef, scanner.tokenize('0x12_34_56_789abcdef')
|
||||||
|
assert_equal '0x12_,34,_56,_789abcdef', scanner.tokenize('0x12_,34,_56,_789abcdef')
|
||||||
|
assert_equal '0x_12_,34,_56,_789abcdef', scanner.tokenize('0x_12_,34,_56,_789abcdef')
|
||||||
|
assert_equal '0x12_,34,_56,_789abcdef__', scanner.tokenize('0x12_,34,_56,_789abcdef__')
|
||||||
|
end
|
||||||
|
|
||||||
def test_scan_dot
|
def test_scan_dot
|
||||||
assert_equal '.', ss.tokenize('.')
|
assert_equal '.', ss.tokenize('.')
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user