From 08c517c966ee6020db8dc030dc8bdaa380d75310 Mon Sep 17 00:00:00 2001 From: playX Date: Fri, 3 Sep 2021 12:16:07 +0300 Subject: [PATCH] strconv,v.gen.js: proper optionals returns, strconv compiles to the JS backend (#11364) --- vlib/builtin/js/builtin.js.v | 6 +- vlib/builtin/js/utf8.js.v | 9 + vlib/strconv/{atof.v => atof.c.v} | 0 vlib/strconv/atof.js.v | 9 + vlib/strconv/{atofq.v => atofq.c.v} | 0 vlib/strconv/atoi.js.v | 1 + vlib/strconv/atoi.v | 1 + vlib/strconv/{f32_str.v => f32_str.c.v} | 0 vlib/strconv/{f64_str.v => f64_str.c.v} | 0 vlib/strconv/{format_mem.v => format_mem.c.v} | 0 vlib/strconv/{ftoa.v => ftoa.c.v} | 0 .../{number_to_base.v => number_to_base.c.v} | 0 vlib/strconv/utilities.c.v | 384 ++++++++++++++++++ vlib/strconv/utilities.v | 382 +---------------- vlib/strconv/{vprintf.v => vprintf.c.v} | 0 vlib/v/gen/js/js.v | 21 +- vlib/v/gen/js/tests/testdata/array.out | 6 +- vlib/v/pref/pref.v | 1 + 18 files changed, 432 insertions(+), 388 deletions(-) create mode 100644 vlib/builtin/js/utf8.js.v rename vlib/strconv/{atof.v => atof.c.v} (100%) create mode 100644 vlib/strconv/atof.js.v rename vlib/strconv/{atofq.v => atofq.c.v} (100%) create mode 100644 vlib/strconv/atoi.js.v rename vlib/strconv/{f32_str.v => f32_str.c.v} (100%) rename vlib/strconv/{f64_str.v => f64_str.c.v} (100%) rename vlib/strconv/{format_mem.v => format_mem.c.v} (100%) rename vlib/strconv/{ftoa.v => ftoa.c.v} (100%) rename vlib/strconv/{number_to_base.v => number_to_base.c.v} (100%) create mode 100644 vlib/strconv/utilities.c.v rename vlib/strconv/{vprintf.v => vprintf.c.v} (100%) diff --git a/vlib/builtin/js/builtin.js.v b/vlib/builtin/js/builtin.js.v index f64da0070c..dd610feef7 100644 --- a/vlib/builtin/js/builtin.js.v +++ b/vlib/builtin/js/builtin.js.v @@ -57,5 +57,9 @@ pub fn unwrap(opt string) string { if o.state != 0 { js_throw(o.err) } - return opt + + mut res := '' + #res = opt.data + + return res } diff --git a/vlib/builtin/js/utf8.js.v b/vlib/builtin/js/utf8.js.v new file mode 100644 index 0000000000..8682a2955a --- /dev/null +++ b/vlib/builtin/js/utf8.js.v @@ -0,0 +1,9 @@ +module builtin + +pub fn utf8_str_visible_length(s string) int { + // todo: proper implementation + res := 0 + #res.val = s.str.length; + + return res +} diff --git a/vlib/strconv/atof.v b/vlib/strconv/atof.c.v similarity index 100% rename from vlib/strconv/atof.v rename to vlib/strconv/atof.c.v diff --git a/vlib/strconv/atof.js.v b/vlib/strconv/atof.js.v new file mode 100644 index 0000000000..45e2cd2968 --- /dev/null +++ b/vlib/strconv/atof.js.v @@ -0,0 +1,9 @@ +module strconv + +// atof64 return a f64 from a string doing a parsing operation +pub fn atof64(s string) f64 { + res := 0.0 + #res.val = Number(s.str) + + return res +} diff --git a/vlib/strconv/atofq.v b/vlib/strconv/atofq.c.v similarity index 100% rename from vlib/strconv/atofq.v rename to vlib/strconv/atofq.c.v diff --git a/vlib/strconv/atoi.js.v b/vlib/strconv/atoi.js.v new file mode 100644 index 0000000000..8c0de8ea4b --- /dev/null +++ b/vlib/strconv/atoi.js.v @@ -0,0 +1 @@ +module atoi diff --git a/vlib/strconv/atoi.v b/vlib/strconv/atoi.v index 3334919675..31a0b7e9be 100644 --- a/vlib/strconv/atoi.v +++ b/vlib/strconv/atoi.v @@ -86,6 +86,7 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) { for i in start_index .. s.len { c := s[i] cl := byte_to_lower(c) + mut d := byte(0) if c == `_` && base0 { // underscore_ok already called diff --git a/vlib/strconv/f32_str.v b/vlib/strconv/f32_str.c.v similarity index 100% rename from vlib/strconv/f32_str.v rename to vlib/strconv/f32_str.c.v diff --git a/vlib/strconv/f64_str.v b/vlib/strconv/f64_str.c.v similarity index 100% rename from vlib/strconv/f64_str.v rename to vlib/strconv/f64_str.c.v diff --git a/vlib/strconv/format_mem.v b/vlib/strconv/format_mem.c.v similarity index 100% rename from vlib/strconv/format_mem.v rename to vlib/strconv/format_mem.c.v diff --git a/vlib/strconv/ftoa.v b/vlib/strconv/ftoa.c.v similarity index 100% rename from vlib/strconv/ftoa.v rename to vlib/strconv/ftoa.c.v diff --git a/vlib/strconv/number_to_base.v b/vlib/strconv/number_to_base.c.v similarity index 100% rename from vlib/strconv/number_to_base.v rename to vlib/strconv/number_to_base.c.v diff --git a/vlib/strconv/utilities.c.v b/vlib/strconv/utilities.c.v new file mode 100644 index 0000000000..1eb520fa93 --- /dev/null +++ b/vlib/strconv/utilities.c.v @@ -0,0 +1,384 @@ +module strconv + +// import math + +/* +f32/f64 to string utilities + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains the f32/f64 to string utilities functions + +These functions are based on the work of: +Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN +Conference on Programming Language Design and ImplementationJune 2018 +Pages 270–282 https://doi.org/10.1145/3192366.3192369 + +inspired by the Go version here: +https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea +*/ + +/* +f64 to string with string format +*/ + +// TODO: Investigate precision issues +// f32_to_str_l return a string with the f32 converted in a string in decimal notation +[manualfree] +pub fn f32_to_str_l(f f32) string { + s := f32_to_str(f, 6) + res := fxx_to_str_l_parse(s) + unsafe { s.free() } + return res +} + +[manualfree] +pub fn f32_to_str_l_no_dot(f f32) string { + s := f32_to_str(f, 6) + res := fxx_to_str_l_parse_no_dot(s) + unsafe { s.free() } + return res +} + +[manualfree] +pub fn f64_to_str_l(f f64) string { + s := f64_to_str(f, 18) + res := fxx_to_str_l_parse(s) + unsafe { s.free() } + return res +} + +[manualfree] +pub fn f64_to_str_l_no_dot(f f64) string { + s := f64_to_str(f, 18) + res := fxx_to_str_l_parse_no_dot(s) + unsafe { s.free() } + return res +} + +// f64_to_str_l return a string with the f64 converted in a string in decimal notation +[manualfree] +pub fn fxx_to_str_l_parse(s string) string { + // check for +inf -inf Nan + if s.len > 2 && (s[0] == `n` || s[1] == `i`) { + return s.clone() + } + + m_sgn_flag := false + mut sgn := 1 + mut b := [26]byte{} + mut d_pos := 1 + mut i := 0 + mut i1 := 0 + mut exp := 0 + mut exp_sgn := 1 + + // get sign and decimal parts + for c in s { + if c == `-` { + sgn = -1 + i++ + } else if c == `+` { + sgn = 1 + i++ + } else if c >= `0` && c <= `9` { + b[i1] = c + i1++ + i++ + } else if c == `.` { + if sgn > 0 { + d_pos = i + } else { + d_pos = i - 1 + } + i++ + } else if c == `e` { + i++ + break + } else { + return 'Float conversion error!!' + } + } + b[i1] = 0 + + // get exponent + if s[i] == `-` { + exp_sgn = -1 + i++ + } else if s[i] == `+` { + exp_sgn = 1 + i++ + } + + mut c := i + for c < s.len { + exp = exp * 10 + int(s[c] - `0`) + c++ + } + + // allocate exp+32 chars for the return string + mut res := []byte{len: exp + 32, init: 0} + mut r_i := 0 // result string buffer index + + // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}") + + if sgn == 1 { + if m_sgn_flag { + res[r_i] = `+` + r_i++ + } + } else { + res[r_i] = `-` + r_i++ + } + + i = 0 + if exp_sgn >= 0 { + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + if i >= d_pos && exp >= 0 { + if exp == 0 { + res[r_i] = `.` + r_i++ + } + exp-- + } + } + for exp >= 0 { + res[r_i] = `0` + r_i++ + exp-- + } + } else { + mut dot_p := true + for exp > 0 { + res[r_i] = `0` + r_i++ + exp-- + if dot_p { + res[r_i] = `.` + r_i++ + dot_p = false + } + } + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + } + } + /* + // remove the dot form the numbers like 2. + if r_i > 1 && res[r_i-1] == `.` { + r_i-- + } + */ + res[r_i] = 0 + return unsafe { tos(res.data, r_i) } +} + +// f64_to_str_l return a string with the f64 converted in a string in decimal notation +[manualfree] +pub fn fxx_to_str_l_parse_no_dot(s string) string { + // check for +inf -inf Nan + if s.len > 2 && (s[0] == `n` || s[1] == `i`) { + return s.clone() + } + + m_sgn_flag := false + mut sgn := 1 + mut b := [26]byte{} + mut d_pos := 1 + mut i := 0 + mut i1 := 0 + mut exp := 0 + mut exp_sgn := 1 + + // get sign and decimal parts + for c in s { + if c == `-` { + sgn = -1 + i++ + } else if c == `+` { + sgn = 1 + i++ + } else if c >= `0` && c <= `9` { + b[i1] = c + i1++ + i++ + } else if c == `.` { + if sgn > 0 { + d_pos = i + } else { + d_pos = i - 1 + } + i++ + } else if c == `e` { + i++ + break + } else { + return 'Float conversion error!!' + } + } + b[i1] = 0 + + // get exponent + if s[i] == `-` { + exp_sgn = -1 + i++ + } else if s[i] == `+` { + exp_sgn = 1 + i++ + } + + mut c := i + for c < s.len { + exp = exp * 10 + int(s[c] - `0`) + c++ + } + + // allocate exp+32 chars for the return string + mut res := []byte{len: exp + 32, init: 0} + mut r_i := 0 // result string buffer index + + // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}") + + if sgn == 1 { + if m_sgn_flag { + res[r_i] = `+` + r_i++ + } + } else { + res[r_i] = `-` + r_i++ + } + + i = 0 + if exp_sgn >= 0 { + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + if i >= d_pos && exp >= 0 { + if exp == 0 { + res[r_i] = `.` + r_i++ + } + exp-- + } + } + for exp >= 0 { + res[r_i] = `0` + r_i++ + exp-- + } + } else { + mut dot_p := true + for exp > 0 { + res[r_i] = `0` + r_i++ + exp-- + if dot_p { + res[r_i] = `.` + r_i++ + dot_p = false + } + } + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + } + } + + // remove the dot form the numbers like 2. + if r_i > 1 && res[r_i - 1] == `.` { + r_i-- + } + + res[r_i] = 0 + return unsafe { tos(res.data, r_i) } +} + +// dec_digits return the number of decimal digit of an u64 +pub fn dec_digits(n u64) int { + if n <= 9_999_999_999 { // 1-10 + if n <= 99_999 { // 5 + if n <= 99 { // 2 + if n <= 9 { // 1 + return 1 + } else { + return 2 + } + } else { + if n <= 999 { // 3 + return 3 + } else { + if n <= 9999 { // 4 + return 4 + } else { + return 5 + } + } + } + } else { + if n <= 9_999_999 { // 7 + if n <= 999_999 { // 6 + return 6 + } else { + return 7 + } + } else { + if n <= 99_999_999 { // 8 + return 8 + } else { + if n <= 999_999_999 { // 9 + return 9 + } + return 10 + } + } + } + } else { + if n <= 999_999_999_999_999 { // 5 + if n <= 999_999_999_999 { // 2 + if n <= 99_999_999_999 { // 1 + return 11 + } else { + return 12 + } + } else { + if n <= 9_999_999_999_999 { // 3 + return 13 + } else { + if n <= 99_999_999_999_999 { // 4 + return 14 + } else { + return 15 + } + } + } + } else { + if n <= 99_999_999_999_999_999 { // 7 + if n <= 9_999_999_999_999_999 { // 6 + return 16 + } else { + return 17 + } + } else { + if n <= 999_999_999_999_999_999 { // 8 + return 18 + } else { + if n <= 9_999_999_999_999_999_999 { // 9 + return 19 + } + return 20 + } + } + } + } +} diff --git a/vlib/strconv/utilities.v b/vlib/strconv/utilities.v index 2098a73f64..f60a1a1c62 100644 --- a/vlib/strconv/utilities.v +++ b/vlib/strconv/utilities.v @@ -1,25 +1,8 @@ module strconv import math.bits -// import math -/* -f32/f64 to string utilities - -Copyright (c) 2019-2021 Dario Deledda. All rights reserved. -Use of this source code is governed by an MIT license -that can be found in the LICENSE file. - -This file contains the f32/f64 to string utilities functions - -These functions are based on the work of: -Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN -Conference on Programming Language Design and ImplementationJune 2018 -Pages 270–282 https://doi.org/10.1145/3192366.3192369 - -inspired by the Go version here: -https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea -*/ +// general utilities // General Utilities [if debug_strconv ?] @@ -191,366 +174,3 @@ fn multiple_of_power_of_five_64(v u64, p u32) bool { fn multiple_of_power_of_two_64(v u64, p u32) bool { return u32(bits.trailing_zeros_64(v)) >= p } - -/* -f64 to string with string format -*/ - -// TODO: Investigate precision issues -// f32_to_str_l return a string with the f32 converted in a string in decimal notation -[manualfree] -pub fn f32_to_str_l(f f32) string { - s := f32_to_str(f, 6) - res := fxx_to_str_l_parse(s) - unsafe { s.free() } - return res -} - -[manualfree] -pub fn f32_to_str_l_no_dot(f f32) string { - s := f32_to_str(f, 6) - res := fxx_to_str_l_parse_no_dot(s) - unsafe { s.free() } - return res -} - -[manualfree] -pub fn f64_to_str_l(f f64) string { - s := f64_to_str(f, 18) - res := fxx_to_str_l_parse(s) - unsafe { s.free() } - return res -} - -[manualfree] -pub fn f64_to_str_l_no_dot(f f64) string { - s := f64_to_str(f, 18) - res := fxx_to_str_l_parse_no_dot(s) - unsafe { s.free() } - return res -} - -// f64_to_str_l return a string with the f64 converted in a string in decimal notation -[manualfree] -pub fn fxx_to_str_l_parse(s string) string { - // check for +inf -inf Nan - if s.len > 2 && (s[0] == `n` || s[1] == `i`) { - return s.clone() - } - - m_sgn_flag := false - mut sgn := 1 - mut b := [26]byte{} - mut d_pos := 1 - mut i := 0 - mut i1 := 0 - mut exp := 0 - mut exp_sgn := 1 - - // get sign and decimal parts - for c in s { - if c == `-` { - sgn = -1 - i++ - } else if c == `+` { - sgn = 1 - i++ - } else if c >= `0` && c <= `9` { - b[i1] = c - i1++ - i++ - } else if c == `.` { - if sgn > 0 { - d_pos = i - } else { - d_pos = i - 1 - } - i++ - } else if c == `e` { - i++ - break - } else { - return 'Float conversion error!!' - } - } - b[i1] = 0 - - // get exponent - if s[i] == `-` { - exp_sgn = -1 - i++ - } else if s[i] == `+` { - exp_sgn = 1 - i++ - } - - mut c := i - for c < s.len { - exp = exp * 10 + int(s[c] - `0`) - c++ - } - - // allocate exp+32 chars for the return string - mut res := []byte{len: exp + 32, init: 0} - mut r_i := 0 // result string buffer index - - // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}") - - if sgn == 1 { - if m_sgn_flag { - res[r_i] = `+` - r_i++ - } - } else { - res[r_i] = `-` - r_i++ - } - - i = 0 - if exp_sgn >= 0 { - for b[i] != 0 { - res[r_i] = b[i] - r_i++ - i++ - if i >= d_pos && exp >= 0 { - if exp == 0 { - res[r_i] = `.` - r_i++ - } - exp-- - } - } - for exp >= 0 { - res[r_i] = `0` - r_i++ - exp-- - } - } else { - mut dot_p := true - for exp > 0 { - res[r_i] = `0` - r_i++ - exp-- - if dot_p { - res[r_i] = `.` - r_i++ - dot_p = false - } - } - for b[i] != 0 { - res[r_i] = b[i] - r_i++ - i++ - } - } - /* - // remove the dot form the numbers like 2. - if r_i > 1 && res[r_i-1] == `.` { - r_i-- - } - */ - res[r_i] = 0 - return unsafe { tos(res.data, r_i) } -} - -// f64_to_str_l return a string with the f64 converted in a string in decimal notation -[manualfree] -pub fn fxx_to_str_l_parse_no_dot(s string) string { - // check for +inf -inf Nan - if s.len > 2 && (s[0] == `n` || s[1] == `i`) { - return s.clone() - } - - m_sgn_flag := false - mut sgn := 1 - mut b := [26]byte{} - mut d_pos := 1 - mut i := 0 - mut i1 := 0 - mut exp := 0 - mut exp_sgn := 1 - - // get sign and decimal parts - for c in s { - if c == `-` { - sgn = -1 - i++ - } else if c == `+` { - sgn = 1 - i++ - } else if c >= `0` && c <= `9` { - b[i1] = c - i1++ - i++ - } else if c == `.` { - if sgn > 0 { - d_pos = i - } else { - d_pos = i - 1 - } - i++ - } else if c == `e` { - i++ - break - } else { - return 'Float conversion error!!' - } - } - b[i1] = 0 - - // get exponent - if s[i] == `-` { - exp_sgn = -1 - i++ - } else if s[i] == `+` { - exp_sgn = 1 - i++ - } - - mut c := i - for c < s.len { - exp = exp * 10 + int(s[c] - `0`) - c++ - } - - // allocate exp+32 chars for the return string - mut res := []byte{len: exp + 32, init: 0} - mut r_i := 0 // result string buffer index - - // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}") - - if sgn == 1 { - if m_sgn_flag { - res[r_i] = `+` - r_i++ - } - } else { - res[r_i] = `-` - r_i++ - } - - i = 0 - if exp_sgn >= 0 { - for b[i] != 0 { - res[r_i] = b[i] - r_i++ - i++ - if i >= d_pos && exp >= 0 { - if exp == 0 { - res[r_i] = `.` - r_i++ - } - exp-- - } - } - for exp >= 0 { - res[r_i] = `0` - r_i++ - exp-- - } - } else { - mut dot_p := true - for exp > 0 { - res[r_i] = `0` - r_i++ - exp-- - if dot_p { - res[r_i] = `.` - r_i++ - dot_p = false - } - } - for b[i] != 0 { - res[r_i] = b[i] - r_i++ - i++ - } - } - - // remove the dot form the numbers like 2. - if r_i > 1 && res[r_i - 1] == `.` { - r_i-- - } - - res[r_i] = 0 - return unsafe { tos(res.data, r_i) } -} - -// dec_digits return the number of decimal digit of an u64 -pub fn dec_digits(n u64) int { - if n <= 9_999_999_999 { // 1-10 - if n <= 99_999 { // 5 - if n <= 99 { // 2 - if n <= 9 { // 1 - return 1 - } else { - return 2 - } - } else { - if n <= 999 { // 3 - return 3 - } else { - if n <= 9999 { // 4 - return 4 - } else { - return 5 - } - } - } - } else { - if n <= 9_999_999 { // 7 - if n <= 999_999 { // 6 - return 6 - } else { - return 7 - } - } else { - if n <= 99_999_999 { // 8 - return 8 - } else { - if n <= 999_999_999 { // 9 - return 9 - } - return 10 - } - } - } - } else { - if n <= 999_999_999_999_999 { // 5 - if n <= 999_999_999_999 { // 2 - if n <= 99_999_999_999 { // 1 - return 11 - } else { - return 12 - } - } else { - if n <= 9_999_999_999_999 { // 3 - return 13 - } else { - if n <= 99_999_999_999_999 { // 4 - return 14 - } else { - return 15 - } - } - } - } else { - if n <= 99_999_999_999_999_999 { // 7 - if n <= 9_999_999_999_999_999 { // 6 - return 16 - } else { - return 17 - } - } else { - if n <= 999_999_999_999_999_999 { // 8 - return 18 - } else { - if n <= 9_999_999_999_999_999_999 { // 9 - return 19 - } - return 20 - } - } - } - } -} diff --git a/vlib/strconv/vprintf.v b/vlib/strconv/vprintf.c.v similarity index 100% rename from vlib/strconv/vprintf.v rename to vlib/strconv/vprintf.c.v diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index d35feed7c9..ee71ed2cf0 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -856,8 +856,7 @@ fn (mut g JsGen) expr(node ast.Expr) { g.gen_type_cast_expr(node) } ast.CharLiteral { - // todo(playX): char type? - g.write("new builtin.string('$node.val')") + g.write("new builtin.byte('$node.val')") } ast.Comment {} ast.ConcatExpr { @@ -1673,7 +1672,23 @@ fn (mut g JsGen) gen_return_stmt(it ast.Return) { return } } - + if fn_return_is_optional { + tmp := g.new_tmp_var() + g.write('const $tmp = new ') + if g.ns.name != 'builtin' { + g.write('builtin.') + } + g.writeln('Option({});') + g.write('${tmp}.data = ') + if it.exprs.len == 1 { + g.expr(it.exprs[0]) + } else { // Multi return + g.gen_array_init_values(it.exprs) + } + g.writeln('') + g.write('return $tmp;') + return + } g.write('return ') if it.exprs.len == 1 { g.expr(it.exprs[0]) diff --git a/vlib/v/gen/js/tests/testdata/array.out b/vlib/v/gen/js/tests/testdata/array.out index a7d0c13016..36afbc1236 100644 --- a/vlib/v/gen/js/tests/testdata/array.out +++ b/vlib/v/gen/js/tests/testdata/array.out @@ -135,8 +135,8 @@ true 0 2 -1 -1 -2 +-1 +-1 -1 2 3 @@ -226,7 +226,7 @@ true true [1, 3, 5, hi] [-3, 7, 42, 67, 108] -[a, b, c, d, e, f] +[97, 98, 99, 100, 101, 102] 0 1 79 diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index bdffc364f4..bc8a77654a 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -542,6 +542,7 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences res.out_name = cmdline.option(current_args, arg, '') if res.out_name.ends_with('.js') { res.backend = .js_node + res.output_cross_c = true } if !os.is_abs_path(res.out_name) { res.out_name = os.join_path(os.getwd(), res.out_name)