Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 00bc6fdb3545
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: da3c38ec83ff
Choose a head ref
  • 3 commits
  • 12 files changed
  • 2 contributors

Commits on Jul 21, 2016

  1. Unify String and Char to integer/float conversion API

    * Add Char#to_i*, Char#to_i*?, Char#to_u* and Char#to_u*?
    * Add Char#to_f, Char#to_f?, Char#to_f* and Char#to_f*?
    * Add String#to_f? and String#to_f*?
    * Make to_f behavior consistent with to_i behavior
    * Remove Char#to_i with a block and second argument
    jhass committed Jul 21, 2016
    Copy the full SHA
    971831f View commit details

Commits on Jul 22, 2016

  1. Add String#to_f(strict: false)

    ysbaddaden authored and jhass committed Jul 22, 2016
    Copy the full SHA
    1489670 View commit details
  2. Merge pull request #3025 from jhass/to_num

    Unify String and Char to integer/float conversion API
    ysbaddaden authored Jul 22, 2016
    Copy the full SHA
    da3c38e View commit details
49 changes: 46 additions & 3 deletions spec/std/char_spec.cr
Original file line number Diff line number Diff line change
@@ -124,7 +124,38 @@ describe "Char" do
('0'..'9').each_with_index do |c, i|
c.to_i.should eq(i)
end
'a'.to_i.should eq(0)
expect_raises(ArgumentError) { 'a'.to_i }
'a'.to_i?.should be_nil

'1'.to_i8.should eq(1i8)
'1'.to_i16.should eq(1i16)
'1'.to_i32.should eq(1i32)
'1'.to_i64.should eq(1i64)

expect_raises(ArgumentError) { 'a'.to_i8 }
expect_raises(ArgumentError) { 'a'.to_i16 }
expect_raises(ArgumentError) { 'a'.to_i32 }
expect_raises(ArgumentError) { 'a'.to_i64 }

'a'.to_i8?.should be_nil
'a'.to_i16?.should be_nil
'a'.to_i32?.should be_nil
'a'.to_i64?.should be_nil

'1'.to_u8.should eq(1u8)
'1'.to_u16.should eq(1u16)
'1'.to_u32.should eq(1u32)
'1'.to_u64.should eq(1u64)

expect_raises(ArgumentError) { 'a'.to_u8 }
expect_raises(ArgumentError) { 'a'.to_u16 }
expect_raises(ArgumentError) { 'a'.to_u32 }
expect_raises(ArgumentError) { 'a'.to_u64 }

'a'.to_u8?.should be_nil
'a'.to_u16?.should be_nil
'a'.to_u32?.should be_nil
'a'.to_u64?.should be_nil
end

it "does to_i with 16 base" do
@@ -137,8 +168,8 @@ describe "Char" do
('A'..'F').each_with_index do |c, i|
c.to_i(16).should eq(10 + i)
end
'Z'.to_i(16).should eq(0)
'Z'.to_i(16, or_else: -1).should eq(-1)
expect_raises(ArgumentError) { 'Z'.to_i(16) }
'Z'.to_i?(16).should be_nil
end

it "does to_i with base 36" do
@@ -161,6 +192,18 @@ describe "Char" do
end
end

it "does to_f" do
('0'..'9').each.zip((0..9).each).each do |c, i|
c.to_f.should eq(i.to_f)
end
expect_raises(ArgumentError) { 'A'.to_f }
'1'.to_f32.should eq(1.0f32)
'1'.to_f64.should eq(1.0f64)
'a'.to_f?.should be_nil
'a'.to_f32?.should be_nil
'a'.to_f64?.should be_nil
end

it "does ord for multibyte char" do
'日'.ord.should eq(26085)
end
74 changes: 73 additions & 1 deletion spec/std/string_spec.cr
Original file line number Diff line number Diff line change
@@ -178,7 +178,7 @@ describe "String" do
end
end

describe "to_i" do
describe "i" do
assert { "1234".to_i.should eq(1234) }
assert { " +1234 ".to_i.should eq(1234) }
assert { " -1234 ".to_i.should eq(-1234) }
@@ -329,15 +329,87 @@ describe "String" do
end

it "does to_f" do
expect_raises(ArgumentError) { "".to_f }
"".to_f?.should be_nil
expect_raises(ArgumentError) { " ".to_f }
" ".to_f?.should be_nil
"0".to_f.should eq(0_f64)
"0.0".to_f.should eq(0_f64)
"+0.0".to_f.should eq(0_f64)
"-0.0".to_f.should eq(0_f64)
"1234.56".to_f.should eq(1234.56_f64)
"1234.56".to_f?.should eq(1234.56_f64)
"+1234.56".to_f?.should eq(1234.56_f64)
"-1234.56".to_f?.should eq(-1234.56_f64)
expect_raises(ArgumentError) { "foo".to_f }
"foo".to_f?.should be_nil
" 1234.56 ".to_f.should eq(1234.56_f64)
" 1234.56 ".to_f?.should eq(1234.56_f64)
expect_raises(ArgumentError) { " 1234.56 ".to_f(whitespace: false) }
" 1234.56 ".to_f?(whitespace: false).should be_nil
expect_raises(ArgumentError) { " 1234.56foo".to_f }
" 1234.56foo".to_f?.should be_nil
"123.45 x".to_f64(strict: false).should eq(123.45_f64)
expect_raises(ArgumentError) { "x1.2".to_f64 }
"x1.2".to_f64?.should be_nil
expect_raises(ArgumentError) { "x1.2".to_f64(strict: false) }
"x1.2".to_f64?(strict: false).should be_nil
end

it "does to_f32" do
expect_raises(ArgumentError) { "".to_f32 }
"".to_f32?.should be_nil
expect_raises(ArgumentError) { " ".to_f32 }
" ".to_f32?.should be_nil
"0".to_f32.should eq(0_f32)
"0.0".to_f32.should eq(0_f32)
"+0.0".to_f32.should eq(0_f32)
"-0.0".to_f32.should eq(0_f32)
"1234.56".to_f32.should eq(1234.56_f32)
"1234.56".to_f32?.should eq(1234.56_f32)
"+1234.56".to_f32?.should eq(1234.56_f32)
"-1234.56".to_f32?.should eq(-1234.56_f32)
expect_raises(ArgumentError) { "foo".to_f32 }
"foo".to_f32?.should be_nil
" 1234.56 ".to_f32.should eq(1234.56_f32)
" 1234.56 ".to_f32?.should eq(1234.56_f32)
expect_raises(ArgumentError) { " 1234.56 ".to_f32(whitespace: false) }
" 1234.56 ".to_f32?(whitespace: false).should be_nil
expect_raises(ArgumentError) { " 1234.56foo".to_f32 }
" 1234.56foo".to_f32?.should be_nil
"123.45 x".to_f32(strict: false).should eq(123.45_f32)
expect_raises(ArgumentError) { "x1.2".to_f32 }
"x1.2".to_f32?.should be_nil
expect_raises(ArgumentError) { "x1.2".to_f32(strict: false) }
"x1.2".to_f32?(strict: false).should be_nil
end

it "does to_f64" do
expect_raises(ArgumentError) { "".to_f64 }
"".to_f64?.should be_nil
expect_raises(ArgumentError) { " ".to_f64 }
" ".to_f64?.should be_nil
"0".to_f64.should eq(0_f64)
"0.0".to_f64.should eq(0_f64)
"+0.0".to_f64.should eq(0_f64)
"-0.0".to_f64.should eq(0_f64)
"1234.56".to_f64.should eq(1234.56_f64)
"1234.56".to_f64?.should eq(1234.56_f64)
"+1234.56".to_f?.should eq(1234.56_f64)
"-1234.56".to_f?.should eq(-1234.56_f64)
expect_raises(ArgumentError) { "foo".to_f64 }
"foo".to_f64?.should be_nil
" 1234.56 ".to_f64.should eq(1234.56_f64)
" 1234.56 ".to_f64?.should eq(1234.56_f64)
expect_raises(ArgumentError) { " 1234.56 ".to_f64(whitespace: false) }
" 1234.56 ".to_f64?(whitespace: false).should be_nil
expect_raises(ArgumentError) { " 1234.56foo".to_f64 }
" 1234.56foo".to_f64?.should be_nil
"123.45 x".to_f64(strict: false).should eq(123.45_f64)
expect_raises(ArgumentError) { "x1.2".to_f64 }
"x1.2".to_f64?.should be_nil
expect_raises(ArgumentError) { "x1.2".to_f64(strict: false) }
"x1.2".to_f64?(strict: false).should be_nil
end

it "compares strings: different size" do
130 changes: 87 additions & 43 deletions src/char.cr
Original file line number Diff line number Diff line change
@@ -108,7 +108,7 @@ struct Char
# 'z'.digit?(36) # => true
# ```
def digit?(base : Int = 10)
!!to_i(base) { false }
!!to_i?(base)
end

# Returns `true` if this char is a lowercase ASCII letter.
@@ -404,70 +404,114 @@ struct Char
end
end

# Returns the integer value of this char if it's an ASCII char denoting a digit,
# 0 otherwise.
# Returns the integer value of this char if it's an ASCII char denoting a digit
# in *base*, raises otherwise.
#
# ```
# '1'.to_i # => 1
# '8'.to_i # => 8
# 'c'.to_i # => 0
# '1'.to_i # => 1
# '8'.to_i # => 8
# 'c'.to_i # => ArgumentError
# '1'.to_i(16) # => 1
# 'a'.to_i(16) # => 10
# 'f'.to_i(16) # => 15
# 'z'.to_i(16) # => ArgumentError
# ```
def to_i
to_i { 0 }
def to_i(base : Int = 10)
to_i?(base) || raise ArgumentError.new("Invalid integer: #{self}")
end

# Returns the integer value of this char if it's an ASCII char denoting a digit,
# otherwise the value returned by the block.
# Returns the integer value of this char if it's an ASCII char denoting a digit
# in *base*, `nil` otherwise.
#
# ```
# '1'.to_i { 10 } # => 1
# '8'.to_i { 10 } # => 8
# 'c'.to_i { 10 } # => 10
# '1'.to_i # => 1
# '8'.to_i # => 8
# 'c'.to_i # => ArgumentError
# '1'.to_i(16) # => 1
# 'a'.to_i(16) # => 10
# 'f'.to_i(16) # => 15
# 'z'.to_i(16) # => ArgumentError
# ```
def to_i
if digit?
def to_i?(base : Int = 10)
raise ArgumentError.new "invalid base #{base}, expected 2 to 36" unless 2 <= base <= 36

if base == 10
return unless '0' <= self <= '9'
self - '0'
else
yield
ord = ord()
if 0 <= ord < 256
digit = String::CHAR_TO_DIGIT.to_unsafe[ord]
return if digit == -1 || digit >= base
digit
end
end
end

# Returns the integer value of this char if it's an ASCII char denoting a digit in *base*,
# otherwise the value of *or_else*.
# Same as `to_i`
def to_i32(base : Int = 10)
to_i(base)
end

# Same as `to_i?`
def to_i32?(base : Int = 10)
to_i?(base)
end

{% for type in %w(i8 i16 i64 u8 u16 u32 u64) %}
# See `to_i`
def to_{{type.id}}(base : Int = 10)
to_i(base).to_{{type.id}}
end

# See `to_i?`
def to_{{type.id}}?(base : Int = 10)
to_i?(base).try &.to_{{type.id}}
end
{% end %}

# Returns the integer value of this char as a float if it's an ASCII char denoting a digit,
# raises otherwise.
#
# ```
# '1'.to_i(16) # => 1
# 'a'.to_i(16) # => 10
# 'f'.to_i(16) # => 15
# 'z'.to_i(16) # => 0
# 'z'.to_i(16, 20) # => 20
# '1'.to_i # => 1.0
# '8'.to_i # => 8.0
# 'c'.to_i # => ArgumentError
# ```
def to_i(base, or_else = 0)
to_i(base) { or_else }
def to_f
to_f64
end

# Returns the integer value of this char if it's an ASCII char denoting a digit in *base*,
# otherwise the value return by the given block.
# Returns the integer value of this char as a float if it's an ASCII char denoting a digit,
# `nil` otherwise.
#
# ```
# '1'.to_i(16) { 20 } # => 1
# 'a'.to_i(16) { 20 } # => 10
# 'f'.to_i(16) { 20 } # => 15
# 'z'.to_i(16) { 20 } # => 20
# '1'.to_i # => 1.0
# '8'.to_i # => 8.0
# 'c'.to_i # => ArgumentError
# ```
def to_i(base)
raise ArgumentError.new "invalid base #{base}, expected 2 to 36" unless 2 <= base <= 36
def to_f?
to_f64?
end

ord = ord()
if 0 <= ord < 256
digit = String::CHAR_TO_DIGIT.to_unsafe[ord]
if digit == -1 || digit >= base
return yield
end
digit
else
return yield
end
# See `to_f`
def to_f32
to_i.to_f32
end

# See `to_f?`
def to_f32?
to_i?.try &.to_f32
end

# Same as `to_f`
def to_f64
to_i.to_f64
end

# Same as `to_f?`
def to_f64?
to_i?.try &.to_f64
end

# Yields each of the bytes of this char as encoded by UTF-8.
2 changes: 1 addition & 1 deletion src/json/lexer.cr
Original file line number Diff line number Diff line change
@@ -199,7 +199,7 @@ abstract class JSON::Lexer
hexnum = 0
4.times do
char = next_char
hexnum = (hexnum << 4) | char.to_i(16) { raise "unexpected char in hex number: #{char.inspect}" }
hexnum = (hexnum << 4) | (char.to_i?(16) || raise "unexpected char in hex number: #{char.inspect}")
end
hexnum
end
1 change: 1 addition & 0 deletions src/lib_c/i686-linux-gnu/c/stdlib.cr
Original file line number Diff line number Diff line change
@@ -19,5 +19,6 @@ lib LibC
fun realpath(name : Char*, resolved : Char*) : Char*
fun setenv(name : Char*, value : Char*, replace : Int) : Int
fun strtof(nptr : Char*, endptr : Char**) : Float
fun strtod(nptr : Char*, endptr : Char**) : Double
fun unsetenv(name : Char*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/i686-linux-musl/c/stdlib.cr
Original file line number Diff line number Diff line change
@@ -19,5 +19,6 @@ lib LibC
fun realpath(x0 : Char*, x1 : Char*) : Char*
fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int
fun strtof(x0 : Char*, x1 : Char**) : Float
fun strtod(x0 : Char*, x1 : Char**) : Double
fun unsetenv(x0 : Char*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-linux-gnu/c/stdlib.cr
Original file line number Diff line number Diff line change
@@ -19,5 +19,6 @@ lib LibC
fun realpath(name : Char*, resolved : Char*) : Char*
fun setenv(name : Char*, value : Char*, replace : Int) : Int
fun strtof(nptr : Char*, endptr : Char**) : Float
fun strtod(nptr : Char*, endptr : Char**) : Double
fun unsetenv(name : Char*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-linux-musl/c/stdlib.cr
Original file line number Diff line number Diff line change
@@ -19,5 +19,6 @@ lib LibC
fun realpath(x0 : Char*, x1 : Char*) : Char*
fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int
fun strtof(x0 : Char*, x1 : Char**) : Float
fun strtod(x0 : Char*, x1 : Char**) : Double
fun unsetenv(x0 : Char*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-macosx-darwin/c/stdlib.cr
Original file line number Diff line number Diff line change
@@ -19,5 +19,6 @@ lib LibC
fun realpath(x0 : Char*, x1 : Char*) : Char*
fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int
fun strtof(x0 : Char*, x1 : Char**) : Float
fun strtod(x0 : Char*, x1 : Char**) : Double
fun unsetenv(x0 : Char*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-portbld-freebsd/c/stdlib.cr
Original file line number Diff line number Diff line change
@@ -19,5 +19,6 @@ lib LibC
fun realpath(x0 : Char*, x1 : Char*) : Char*
fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int
fun strtof(x0 : Char*, x1 : Char**) : Float
fun strtod(x0 : Char*, x1 : Char**) : Double
fun unsetenv(x0 : Char*) : Int
end
Loading