diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 38733a1954..b15cb9954b 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -61,7 +61,7 @@ pub: pub fn vstrlen(s byteptr) int { return C.strlen(*char(s)) -} +} // Converts a C string to a V string. // String data is reused, not copied. @@ -187,11 +187,11 @@ pub fn (s string) replace(rep, with string) string { pub fn (s string) int() int { - return int(strconv.parse_int(s,0,32)) + return int(strconv.common_parse_int(s,0,32, false, false)) } pub fn (s string) i64() i64 { - return strconv.parse_int(s, 0, 64) + return strconv.common_parse_int(s, 0, 64, false, false) } pub fn (s string) f32() f32 { @@ -203,11 +203,11 @@ pub fn (s string) f64() f64 { } pub fn (s string) u32() u32 { - return u32(strconv.parse_uint(s, 0, 32)) + return u32(strconv.common_parse_uint(s, 0, 32, false, false)) } pub fn (s string) u64() u64 { - return strconv.parse_uint(s, 0, 64) + return strconv.common_parse_uint(s, 0, 64, false, false) } // == @@ -614,7 +614,7 @@ pub fn (s string) title() string { } title := tit.join(' ') - return title + return title } // 'hey [man] how you doin' diff --git a/vlib/builtin/string_int_test.v b/vlib/builtin/string_int_test.v new file mode 100644 index 0000000000..5e969832a4 --- /dev/null +++ b/vlib/builtin/string_int_test.v @@ -0,0 +1,11 @@ +fn test_common_atoi() { + assert "70zzz".int() == 70 + assert "2901issue".int() == 2901 + assert '234232w'.int() == 234232 + assert '-9009x'.int() == -9009 + assert '0y'.int() == 0 + for n in -10000 .. 100000 { + s := n.str()+"z" + assert s.int() == n + } +} diff --git a/vlib/strconv/atoi.v b/vlib/strconv/atoi.v index ad42fc30f3..7950a95df4 100644 --- a/vlib/strconv/atoi.v +++ b/vlib/strconv/atoi.v @@ -19,8 +19,9 @@ fn byte_to_lower(c byte) byte { return c | (`x` - `X`) } -// parse_uint is like parse_int but for unsigned numbers. -pub fn parse_uint(s string, _base int, _bit_size int) u64 { +// common_parse_uint is called by parse_uint and allows the parsing +// to stop on non or invalid digit characters and return the result so far +pub fn common_parse_uint(s string, _base int, _bit_size int, error_on_non_digit bool, error_on_high_digit bool) u64 { mut bit_size := _bit_size mut base := _base @@ -28,7 +29,7 @@ pub fn parse_uint(s string, _base int, _bit_size int) u64 { // return error('parse_uint: syntax error $s') return u64(0) } - + base0 := base == 0 mut start_index := 0 if 2 <= base && base <= 36 { @@ -89,12 +90,20 @@ pub fn parse_uint(s string, _base int, _bit_size int) u64 { else if `0` <= c && c <= `9` { d = c - `0` } else if `a` <= cl && cl <= `z` { d = cl - `a` + 10 } else { - // return error('parse_uint: syntax error $s') - return u64(0) + if error_on_non_digit { + // return error('parse_uint: syntax error $s') + return u64(0) + } else { + break + } } if d >= byte(base) { - // return error('parse_uint: syntax error $s') - return u64(0) + if error_on_high_digit { + // return error('parse_uint: syntax error $s') + return u64(0) + } else { + break + } } if n >= cutoff { // n*base overflows @@ -114,23 +123,17 @@ pub fn parse_uint(s string, _base int, _bit_size int) u64 { // return error('parse_uint: syntax error $s') return u64(0) } - - return n + return n } -// parse_int interprets a string s in the given base (0, 2 to 36) and -// bit size (0 to 64) and returns the corresponding value i. -// -// If the base argument is 0, the true base is implied by the string's -// prefix: 2 for "0b", 8 for "0" or "0o", 16 for "0x", and 10 otherwise. -// Also, for argument base 0 only, underscore characters are permitted -// as defined by the Go syntax for integer literals. -// -// The bitSize argument specifies the integer type -// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 -// correspond to int, int8, int16, int32, and int64. -// If bitSize is below 0 or above 64, an error is returned. -pub fn parse_int(_s string, base int, _bit_size int) i64 { +// parse_uint is like parse_int but for unsigned numbers. +pub fn parse_uint(s string, _base int, _bit_size int) u64 { + return common_parse_uint(s, _base, _bit_size, true, true) +} + +// common_parse_int is called by parse int and allows the parsing +// to stop on non or invalid digit characters and return the result so far +pub fn common_parse_int(_s string, base int, _bit_size int, error_on_non_digit bool, error_on_high_digit bool) i64 { mut s := _s mut bit_size := _bit_size @@ -151,7 +154,7 @@ pub fn parse_int(_s string, base int, _bit_size int) i64 { // un := parse_uint(s, base, bit_size) or { // return i64(0) // } - un := parse_uint(s, base, bit_size) + un := common_parse_uint(s, base, bit_size, error_on_non_digit, error_on_high_digit) if un == 0 { return i64(0) } @@ -173,7 +176,21 @@ pub fn parse_int(_s string, base int, _bit_size int) i64 { return if neg { -i64(un) } else { i64(un) } } - +// parse_int interprets a string s in the given base (0, 2 to 36) and +// bit size (0 to 64) and returns the corresponding value i. +// +// If the base argument is 0, the true base is implied by the string's +// prefix: 2 for "0b", 8 for "0" or "0o", 16 for "0x", and 10 otherwise. +// Also, for argument base 0 only, underscore characters are permitted +// as defined by the Go syntax for integer literals. +// +// The bitSize argument specifies the integer type +// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 +// correspond to int, int8, int16, int32, and int64. +// If bitSize is below 0 or above 64, an error is returned. +pub fn parse_int(_s string, base int, _bit_size int) i64 { + return common_parse_int(_s, base, _bit_size, true, true) +} // atoi is equivalent to parse_int(s, 10, 0), converted to type int. pub fn atoi(s string) int {