strconv: change atof64 to return an error, if the parsed value is not a valid number (#13424)

pull/13429/head
Vincenzo Palazzo 2022-02-10 12:27:32 +01:00 committed by GitHub
parent 1c19573382
commit 7f29418c63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 35 additions and 9 deletions

View File

@ -511,12 +511,12 @@ pub fn (s string) i16() i16 {
// f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`. // f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`.
pub fn (s string) f32() f32 { pub fn (s string) f32() f32 {
return f32(strconv.atof64(s)) return f32(strconv.atof64(s) or { 0 })
} }
// f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`. // f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`.
pub fn (s string) f64() f64 { pub fn (s string) f64() f64 {
return strconv.atof64(s) return strconv.atof64(s) or { 0 }
} }
// u8 returns the value of the string as u8 `'1'.u8() == u8(1)`. // u8 returns the value of the string as u8 `'1'.u8() == u8(1)`.

View File

@ -104,6 +104,7 @@ pub const (
parser_mzero = 2 // number is negative, module smaller parser_mzero = 2 // number is negative, module smaller
parser_pinf = 3 // number is higher than +HUGE_VAL parser_pinf = 3 // number is higher than +HUGE_VAL
parser_minf = 4 // number is lower than -HUGE_VAL parser_minf = 4 // number is lower than -HUGE_VAL
parser_invalid_number = 5 // invalid number, used for '#@%^' for example
// //
// char constants // char constants
// Note: Modify these if working with non-ASCII encoding // Note: Modify these if working with non-ASCII encoding
@ -232,6 +233,9 @@ fn parser(s string) (int, PrepNumber) {
result = strconv.parser_pzero result = strconv.parser_pzero
} }
} }
if i == 0 && s.len > 0 {
return strconv.parser_invalid_number, pn
}
return result, pn return result, pn
} }
@ -403,9 +407,9 @@ fn converter(mut pn PrepNumber) u64 {
// Public functions // Public functions
// atof64 return a f64 from a string doing a parsing operation // atof64 return a f64 from a string doing a parsing operation
pub fn atof64(s string) f64 { pub fn atof64(s string) ?f64 {
if s.len == 0 { if s.len == 0 {
return 0 return error('expected a number found an empty string')
} }
mut pn := PrepNumber{} mut pn := PrepNumber{}
mut res_parsing := 0 mut res_parsing := 0
@ -428,7 +432,9 @@ pub fn atof64(s string) f64 {
strconv.parser_minf { strconv.parser_minf {
res.u = strconv.double_minus_infinity res.u = strconv.double_minus_infinity
} }
else {} else {
return error('not a number')
}
} }
return unsafe { res.f } return unsafe { res.f }
} }

View File

@ -1,7 +1,10 @@
module strconv module strconv
// atof64 return a f64 from a string doing a parsing operation // atof64 return a f64 from a string doing a parsing operation
pub fn atof64(s string) f64 { pub fn atof64(s string) ?f64 {
// TODO: handle parsing invalid numbers as close as possible to the pure V version
// that may be slower, but more portable, and will guarantee that higher level code
// works the same in the JS version, as well as in the C and Native versions.
res := 0.0 res := 0.0
#res.val = Number(s.str) #res.val = Number(s.str)

View File

@ -36,7 +36,8 @@ fn test_atof() {
// check conversion case 1 string <=> string // check conversion case 1 string <=> string
for c, x in src_num { for c, x in src_num {
// slow atof // slow atof
assert strconv.atof64(src_num_str[c]).strlong() == x.strlong() val := strconv.atof64(src_num_str[c]) or { panic(err) }
assert val.strlong() == x.strlong()
// quick atof // quick atof
mut s1 := (strconv.atof_quick(src_num_str[c]).str()) mut s1 := (strconv.atof_quick(src_num_str[c]).str())
@ -56,7 +57,8 @@ fn test_atof() {
// we don't test atof_quick beacuse we already know the rounding error // we don't test atof_quick beacuse we already know the rounding error
for c, x in src_num_str { for c, x in src_num_str {
b := src_num[c].strlong() b := src_num[c].strlong()
a1 := strconv.atof64(x).strlong() value := strconv.atof64(x) or { panic(err) }
a1 := value.strlong()
assert a1 == b assert a1 == b
} }
@ -73,3 +75,18 @@ fn test_atof() {
assert *ptr == u64(0x8000000000000000) assert *ptr == u64(0x8000000000000000)
println('DONE!') println('DONE!')
} }
fn test_atof_errors() {
if x := strconv.atof64('') {
eprintln('> x: $x')
assert false // strconv.atof64 should have failed
} else {
assert err.str() == 'expected a number found an empty string'
}
if x := strconv.atof64('####') {
eprintln('> x: $x')
assert false // strconv.atof64 should have failed
} else {
assert err.str() == 'not a number'
}
}

View File

@ -191,7 +191,7 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
}) // TODO: numbers larger than 2^63 (for u64) }) // TODO: numbers larger than 2^63 (for u64)
} }
ast.FloatLiteral { ast.FloatLiteral {
return f64(strconv.atof64(expr.val)) return f64(strconv.atof64(expr.val) or { e.error(err.str()) })
} }
ast.BoolLiteral { ast.BoolLiteral {
return expr.val return expr.val