From 7f29418c63353bf0ccc5d3cc523c0f45cae7fd65 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 10 Feb 2022 12:27:32 +0100 Subject: [PATCH] strconv: change atof64 to return an error, if the parsed value is not a valid number (#13424) --- vlib/builtin/string.v | 4 ++-- vlib/strconv/atof.c.v | 12 +++++++++--- vlib/strconv/atof.js.v | 5 ++++- vlib/strconv/atof_test.v | 21 +++++++++++++++++++-- vlib/v/eval/expr.v | 2 +- 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 0504d828b3..15083c33be 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -511,12 +511,12 @@ pub fn (s string) i16() i16 { // f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`. 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)`. 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)`. diff --git a/vlib/strconv/atof.c.v b/vlib/strconv/atof.c.v index 304c0d3c53..e60f4a1e2f 100644 --- a/vlib/strconv/atof.c.v +++ b/vlib/strconv/atof.c.v @@ -104,6 +104,7 @@ pub const ( parser_mzero = 2 // number is negative, module smaller parser_pinf = 3 // number is higher than +HUGE_VAL parser_minf = 4 // number is lower than -HUGE_VAL + parser_invalid_number = 5 // invalid number, used for '#@%^' for example // // char constants // Note: Modify these if working with non-ASCII encoding @@ -232,6 +233,9 @@ fn parser(s string) (int, PrepNumber) { result = strconv.parser_pzero } } + if i == 0 && s.len > 0 { + return strconv.parser_invalid_number, pn + } return result, pn } @@ -403,9 +407,9 @@ fn converter(mut pn PrepNumber) u64 { // Public functions // 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 { - return 0 + return error('expected a number found an empty string') } mut pn := PrepNumber{} mut res_parsing := 0 @@ -428,7 +432,9 @@ pub fn atof64(s string) f64 { strconv.parser_minf { res.u = strconv.double_minus_infinity } - else {} + else { + return error('not a number') + } } return unsafe { res.f } } diff --git a/vlib/strconv/atof.js.v b/vlib/strconv/atof.js.v index 45e2cd2968..217851d231 100644 --- a/vlib/strconv/atof.js.v +++ b/vlib/strconv/atof.js.v @@ -1,7 +1,10 @@ module strconv // 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.val = Number(s.str) diff --git a/vlib/strconv/atof_test.v b/vlib/strconv/atof_test.v index ca286c8a87..069ea79ee7 100644 --- a/vlib/strconv/atof_test.v +++ b/vlib/strconv/atof_test.v @@ -36,7 +36,8 @@ fn test_atof() { // check conversion case 1 string <=> string for c, x in src_num { // 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 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 for c, x in src_num_str { b := src_num[c].strlong() - a1 := strconv.atof64(x).strlong() + value := strconv.atof64(x) or { panic(err) } + a1 := value.strlong() assert a1 == b } @@ -73,3 +75,18 @@ fn test_atof() { assert *ptr == u64(0x8000000000000000) 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' + } +} diff --git a/vlib/v/eval/expr.v b/vlib/v/eval/expr.v index 009e709690..07609f31ef 100644 --- a/vlib/v/eval/expr.v +++ b/vlib/v/eval/expr.v @@ -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) } ast.FloatLiteral { - return f64(strconv.atof64(expr.val)) + return f64(strconv.atof64(expr.val) or { e.error(err.str()) }) } ast.BoolLiteral { return expr.val