1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

all: new string interpolation in pure V (#10181)

This commit is contained in:
penguindark
2021-05-24 04:20:45 +02:00
committed by GitHub
parent 603e57745f
commit d8d05e0106
28 changed files with 2624 additions and 640 deletions

View File

@@ -82,7 +82,14 @@ fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) {
Constants
*/
const (
pub const (
//
// f32 constants
//
single_plus_zero = u32(0x0000_0000)
single_minus_zero = u32(0x8000_0000)
single_plus_infinity = u32(0x7F80_0000)
single_minus_infinity = u32(0xFF80_0000)
//
// f64 constants
//
@@ -407,6 +414,7 @@ Public functions
// atof64 return a f64 from a string doing a parsing operation
pub fn atof64(s string) f64 {
mut pn := PrepNumber{}
mut res_parsing := 0
mut res := Float64u{}

View File

@@ -164,7 +164,7 @@ fn f32_to_decimal_exact_int(i_mant u32, exp u32) (Dec32,bool) {
return d, true
}
pub fn f32_to_decimal(mant u32, exp u32) Dec32 {
fn f32_to_decimal(mant u32, exp u32) Dec32 {
mut e2 := 0
mut m2 := u32(0)
if exp == 0 {

View File

@@ -25,6 +25,33 @@ pub enum Align_text {
Float conversion utility
*/
const(
// rounding value
dec_round = [
f64(0.5),
0.05,
0.005,
0.0005,
0.00005,
0.000005,
0.0000005,
0.00000005,
0.000000005,
0.0000000005,
0.00000000005,
0.000000000005,
0.0000000000005,
0.00000000000005,
0.000000000000005,
0.0000000000000005,
0.00000000000000005,
0.000000000000000005,
0.0000000000000000005,
0.00000000000000000005,
]
)
/*
const(
// rounding value
dec_round = [
@@ -50,7 +77,7 @@ const(
0.000000000000000000044,
]
)
*/
// max float 1.797693134862315708145274237317043567981e+308
/*

497
vlib/strconv/format_mem.v Normal file
View File

@@ -0,0 +1,497 @@
/*=============================================================================
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 string interpolation V functions
=============================================================================*/
module strconv
import strings
// strings.Builder version of format_str
pub fn format_str_sb(s string, p BF_param, mut sb strings.Builder) {
if p.len0 <= 0 {
sb.write_string(s)
return
}
dif := p.len0 - utf8_str_visible_length(s)
if dif <= 0 {
sb.write_string(s)
return
}
if p.allign == .right {
for i1 :=0; i1 < dif; i1++ {
sb.write_b(p.pad_ch)
}
}
sb.write_string(s)
if p.allign == .left {
for i1 :=0; i1 < dif; i1++ {
sb.write_b(p.pad_ch)
}
}
}
const (
// digit pairs in reverse order
digit_pairs = '00102030405060708090011121314151617181910212223242526272829203132333435363738393041424344454647484940515253545556575859506162636465666768696071727374757677787970818283848586878889809192939495969798999'
)
// format_dec_sb format a u64
[direct_array_access]
pub fn format_dec_sb(d u64, p BF_param, mut res strings.Builder) {
mut n_char := dec_digits(d)
sign_len := if !p.positive || p.sign_flag { 1 } else { 0 }
number_len := sign_len + n_char
dif := p.len0 - number_len
mut sign_written := false
if p.allign == .right {
if p.pad_ch == `0` {
if p.positive {
if p.sign_flag {
res.write_b(`+`)
sign_written = true
}
} else {
res.write_b(`-`)
sign_written = true
}
}
// write the pad chars
for i1 :=0; i1 < dif; i1++ {
res.write_b(p.pad_ch)
}
}
if !sign_written {
// no pad char, write the sign before the number
if p.positive {
if p.sign_flag {
res.write_b(`+`)
}
} else {
res.write_b(`-`)
}
}
/*
// Legacy version
// max u64 18446744073709551615 => 20 byte
mut buf := [32]byte{}
mut i := 20
mut d1 := d
for i >= (21 - n_char) {
buf[i] = byte(d1 % 10) + `0`
d1 = d1 / 10
i--
}
i++
*/
//===========================================
// Speed version
// max u64 18446744073709551615 => 20 byte
mut buf := [32]byte{}
mut i := 20
mut n := d
mut d_i := u64(0)
if n > 0 {
for n > 0 {
n1 := n / 100
// calculate the digit_pairs start index
d_i = (n - (n1 * 100)) << 1
n = n1
unsafe{ buf[i] = digit_pairs.str[d_i] }
i--
d_i++
unsafe{ buf[i] = digit_pairs.str[d_i] }
i--
}
i++
// remove head zero
if d_i < 20 {
i++
}
unsafe{ res.write_ptr(&buf[i],n_char) }
} else {
// we have a zero no need of more code!
res.write_b(`0`)
}
//===========================================
if p.allign == .left {
for i1 :=0; i1 < dif; i1++ {
res.write_b(p.pad_ch)
}
}
return
}
[manualfree]
[direct_array_access]
pub fn f64_to_str_lnd1(f f64, dec_digit int) string {
unsafe{
// we add the rounding value
s := f64_to_str(f + dec_round[dec_digit], 18)
// check for +inf -inf Nan
if s.len > 2 && (s[0] == `n` || s[1] == `i`) {
return s
}
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
mut dot_res_sp := -1
// get sign and deciaml 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 {
s.free()
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 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}")
// s no more needed
s.free()
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 {
dot_res_sp = r_i
res[r_i] = `.`
r_i++
}
exp--
}
}
for exp >= 0 {
res[r_i] = `0`
r_i++
exp--
}
//println("exp: $exp $r_i $dot_res_sp")
} else {
mut dot_p := true
for exp > 0 {
res[r_i] = `0`
r_i++
exp--
if dot_p {
dot_res_sp = r_i
res[r_i] = `.`
r_i++
dot_p = false
}
}
for b[i] != 0 {
res[r_i] = b[i]
r_i++
i++
}
}
// no more digits needed, stop here
if dec_digit <= 0 {
tmp_res := tos(res.data, dot_res_sp).clone()
res.free()
return tmp_res
}
//println("r_i-d_pos: ${r_i - d_pos}")
if dot_res_sp >= 0 {
if (r_i - dot_res_sp) > dec_digit {
r_i = dot_res_sp + dec_digit + 1
}
res[r_i] = 0
//println("result: [${tos(&res[0],r_i)}]")
tmp_res := tos(res.data, r_i).clone()
res.free()
return tmp_res
} else {
if dec_digit > 0 {
mut c1 := 0
res[r_i] = `.`
r_i++
for c1 < dec_digit {
res[r_i] = `0`
r_i++
c1++
}
res[r_i] = 0
}
tmp_res := tos(res.data, r_i).clone()
res.free()
return tmp_res
}
}
}
// strings.Builder version of format_fl
[manualfree]
pub fn format_fl(f f64, p BF_param) string {
unsafe{
mut s := ""
//mut fs := "1.2343"
mut fs := f64_to_str_lnd1(if f >= 0.0 {f} else {-f}, p.len1)
//println("Dario")
//println(fs)
// error!!
if fs[0] == `[` {
s.free()
return fs
}
if p.rm_tail_zero {
tmp := fs
fs = remove_tail_zeros(fs)
tmp.free()
}
mut res := strings.new_builder( if p.len0 > fs.len { p.len0 } else { fs.len })
mut sign_len_diff := 0
if p.pad_ch == `0` {
if p.positive {
if p.sign_flag {
res.write_b(`+`)
sign_len_diff = -1
}
} else {
res.write_b(`-`)
sign_len_diff = -1
}
tmp := s
s = fs.clone()
tmp.free()
} else {
if p.positive {
if p.sign_flag {
tmp := s
s = "+" + fs
tmp.free()
} else {
tmp := s
s = fs.clone()
tmp.free()
}
} else {
tmp := s
s = "-" + fs
tmp.free()
}
}
dif := p.len0 - s.len + sign_len_diff
if p.allign == .right {
for i1 :=0; i1 < dif; i1++ {
res.write_b(p.pad_ch)
}
}
res.write_string(s)
if p.allign == .left {
for i1 :=0; i1 < dif; i1++ {
res.write_b(p.pad_ch)
}
}
s.free()
fs.free()
tmp_res := res.str()
res.free()
return tmp_res
}
}
[manualfree]
pub fn format_es(f f64, p BF_param) string {
unsafe{
mut s := ""
mut fs := f64_to_str_pad(if f> 0 {f} else {-f},p.len1)
if p.rm_tail_zero {
fs = remove_tail_zeros(fs)
}
mut res := strings.new_builder( if p.len0 > fs.len { p.len0 } else { fs.len })
mut sign_len_diff := 0
if p.pad_ch == `0` {
if p.positive {
if p.sign_flag {
res.write_b(`+`)
sign_len_diff = -1
}
} else {
res.write_b(`-`)
sign_len_diff = -1
}
tmp := s
s = fs.clone()
tmp.free()
} else {
if p.positive {
if p.sign_flag {
tmp := s
s = "+" + fs
tmp.free()
} else {
tmp := s
s = fs.clone()
tmp.free()
}
} else {
tmp := s
s = "-" + fs
tmp.free()
}
}
dif := p.len0 - s.len + sign_len_diff
if p.allign == .right {
for i1 :=0; i1 < dif; i1++ {
res.write_b(p.pad_ch)
}
}
res.write_string(s)
if p.allign == .left {
for i1 :=0; i1 < dif; i1++ {
res.write_b(p.pad_ch)
}
}
s.free()
fs.free()
tmp_res := res.str()
res.free()
return tmp_res
}
}
[direct_array_access]
pub fn remove_tail_zeros(s string) string {
unsafe{
mut buf := malloc(s.len + 1)
mut i_d := 0
mut i_s := 0
// skip spaces
for i_s < s.len && s[i_s] !in [`-`,`+`] && s[i_s] >= `9` && s[i_s] <= `0`{
buf[i_d] = s[i_s]
i_s++
i_d++
}
// sign
if i_s < s.len && s[i_s] in [`-`,`+`] {
buf[i_d] = s[i_s]
i_s++
i_d++
}
// integer part
for i_s < s.len && s[i_s] >= `0` && s[i_s] <= `9` {
buf[i_d] = s[i_s]
i_s++
i_d++
}
// check decimals
if i_s < s.len && s[i_s] == `.` {
mut i_s1 := i_s + 1
mut sum := 0
for i_s1 < s.len && s[i_s1] >= `0` && s[i_s1] <= `9` {
sum += s[i_s1] - byte(`0`)
i_s1++
}
// decimal part must be copied
if sum > 0 {
for c_i in i_s .. i_s1 {
buf[i_d] = s[c_i]
i_d++
}
}
i_s = i_s1
}
if s[i_s] != `.` {
// check exponent
for i_s < s.len {
buf[i_d] = s[i_s]
i_s++
i_d++
}
}
buf[i_d] = 0
return tos(buf, i_d+1)
}
}

View File

@@ -4,42 +4,58 @@ const base_digits = '0123456789abcdefghijklmnopqrstuvwxyz'
// format_int returns the string representation of the number n in base `radix`
// for digit values > 10, this function uses the small latin leters a-z.
[manualfree]
pub fn format_int(n i64, radix int) string {
if radix < 2 || radix > 36 {
panic('invalid radix: $radix . It should be => 2 and <= 36')
unsafe{
if radix < 2 || radix > 36 {
panic('invalid radix: $radix . It should be => 2 and <= 36')
}
if n == 0 {
return '0'
}
mut n_copy := n
mut sign := ''
if n < 0 {
sign = '-'
n_copy = -n_copy
}
mut res := ''
for n_copy != 0 {
tmp_0 := res
tmp_1 := base_digits[n_copy % radix].ascii_str()
res = tmp_1 + res
tmp_0.free()
tmp_1.free()
//res = base_digits[n_copy % radix].ascii_str() + res
n_copy /= radix
}
return '$sign$res'
}
if n == 0 {
return '0'
}
mut n_copy := n
mut sign := ''
if n < 0 {
sign = '-'
n_copy = -n_copy
}
mut res := ''
for n_copy != 0 {
res = base_digits[n_copy % radix].ascii_str() + res
n_copy /= radix
}
return '$sign$res'
}
// format_uint returns the string representation of the number n in base `radix`
// for digit values > 10, this function uses the small latin leters a-z.
[manualfree]
pub fn format_uint(n u64, radix int) string {
if radix < 2 || radix > 36 {
panic('invalid radix: $radix . It should be => 2 and <= 36')
unsafe{
if radix < 2 || radix > 36 {
panic('invalid radix: $radix . It should be => 2 and <= 36')
}
if n == 0 {
return '0'
}
mut n_copy := n
mut res := ''
uradix := u64(radix)
for n_copy != 0 {
tmp_0 := res
tmp_1 := base_digits[n_copy % uradix].ascii_str()
res = tmp_1 + res
tmp_0.free()
tmp_1.free()
//res = base_digits[n_copy % uradix].ascii_str() + res
n_copy /= uradix
}
return res
}
if n == 0 {
return '0'
}
mut n_copy := n
mut res := ''
uradix := u64(radix)
for n_copy != 0 {
res = base_digits[n_copy % uradix].ascii_str() + res
n_copy /= uradix
}
return res
}

View File

@@ -42,8 +42,14 @@ mut:
u u64
}
union Float64u {
mut:
pub union Float64u {
pub mut:
f f64
u u64
}
pub union Float32u {
pub mut:
f f32
u u32
}

View File

@@ -81,7 +81,9 @@ fn get_string_special(neg bool, expZero bool, mantZero bool) string {
32 bit functions
*/
fn decimal_len_32(u u32) int {
// decimal_len_32 return the number of decimal digits of the input
[deprecated]
pub fn decimal_len_32(u u32) int {
// Function precondition: u is not a 10-digit number.
// (9 digits are sufficient for round-tripping.)
// This benchmarked faster than the log2 approach used for u64.
@@ -172,7 +174,9 @@ fn pow5_bits(e int) int {
64 bit functions
*/
fn decimal_len_64(u u64) int {
[deprecated]
// decimal_len_64 return the number of decimal digits of the input
pub fn decimal_len_64(u u64) int {
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
log2 := 64 - bits.leading_zeros_64(u) - 1
t := (log2 + 1) * 1233 >> 12
@@ -227,15 +231,44 @@ 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
pub fn f32_to_str_l(f f64) string {
return f64_to_str_l(f32(f))
[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
}
// f64_to_str_l return a string with the f64 converted in a string in decimal notation
[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
@@ -287,8 +320,11 @@ pub fn f64_to_str_l(f f64) string {
exp_sgn = 1
i++
}
for c in s[i..] {
exp = exp * 10 + int(c-`0`)
mut c := i
for c < s.len {
exp = exp * 10 + int(s[c]-`0`)
c++
}
// allocate exp+32 chars for the return string
@@ -296,7 +332,7 @@ pub fn f64_to_str_l(f f64) string {
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] = `+`
@@ -344,6 +380,136 @@ pub fn f64_to_str_l(f f64) string {
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
}
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) }
}

View File

@@ -455,170 +455,6 @@ fn fabs(x f64) f64 {
return x
}
[manualfree]
pub fn f64_to_str_lnd1(f f64, dec_digit int) string {
unsafe{
// we add the rounding value
s := f64_to_str(f + dec_round[dec_digit], 18)
// check for +inf -inf Nan
if s.len > 2 && (s[0] == `n` || s[1] == `i`) {
return s
}
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
mut dot_res_sp := -1
// get sign and deciaml 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 {
s.free()
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 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}")
// s no more needed
s.free()
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 {
dot_res_sp = r_i
res[r_i] = `.`
r_i++
}
exp--
}
}
for exp >= 0 {
res[r_i] = `0`
r_i++
exp--
}
//println("exp: $exp $r_i $dot_res_sp")
} else {
mut dot_p := true
for exp > 0 {
res[r_i] = `0`
r_i++
exp--
if dot_p {
dot_res_sp = r_i
res[r_i] = `.`
r_i++
dot_p = false
}
}
for b[i] != 0 {
res[r_i] = b[i]
r_i++
i++
}
}
// no more digits needed, stop here
if dec_digit <= 0 {
tmp_res := tos(res.data, dot_res_sp).clone()
res.free()
return tmp_res
}
//println("r_i-d_pos: ${r_i - d_pos}")
if dot_res_sp >= 0 {
if (r_i - dot_res_sp) > dec_digit {
r_i = dot_res_sp + dec_digit + 1
}
res[r_i] = 0
//println("result: [${tos(&res[0],r_i)}]")
tmp_res := tos(res.data, r_i).clone()
res.free()
return tmp_res
} else {
if dec_digit > 0 {
mut c1 := 0
res[r_i] = `.`
r_i++
for c1 < dec_digit {
res[r_i] = `0`
r_i++
c1++
}
res[r_i] = 0
}
tmp_res := tos(res.data, r_i).clone()
res.free()
return tmp_res
}
}
}
// strings.Builder version of format_fl
[manualfree]
pub fn format_fl_old(f f64, p BF_param) string {