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

800 lines
16 KiB
V
Raw Normal View History

2021-05-05 04:12:18 +03:00
/*=============================================================================
2022-01-04 12:21:08 +03:00
Copyright (c) 2019-2022 Dario Deledda. All rights reserved.
2021-05-05 04:12:18 +03:00
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
2021-06-18 17:59:56 +03:00
2021-05-05 04:12:18 +03:00
import strings
enum Char_parse_state {
start
norm_char
field_char
pad_ch
len_set_start
len_set_in
check_type
check_float
check_float_in
reset_params
}
// v_printf prints a sprintf-like formated `string` to the terminal.
[deprecated: 'use string interpolation instead']
2021-06-18 17:59:56 +03:00
pub fn v_printf(str string, pt ...voidptr) {
print(v_sprintf(str, ...pt))
2021-05-05 04:12:18 +03:00
}
// v_sprintf returns a sprintf-like formated `string`.
//
// Example:
// ```v
// x := 3.141516
// assert strconv.v_sprintf('aaa %G', x) == 'aaa 3.141516'
// ```
[deprecated: 'use string interpolation instead']
[direct_array_access; manualfree]
2021-06-18 17:59:56 +03:00
pub fn v_sprintf(str string, pt ...voidptr) string {
2021-05-05 04:12:18 +03:00
mut res := strings.new_builder(pt.len * 16)
defer {
unsafe { res.free() }
}
2021-05-05 04:12:18 +03:00
2021-06-18 17:59:56 +03:00
mut i := 0 // main string index
mut p_index := 0 // parameter index
mut sign := false // sign flag
mut allign := Align_text.right
mut len0 := -1 // forced length, if -1 free length
mut len1 := -1 // decimal part for floats
def_len1 := 6 // default value for len1
2022-04-15 14:58:56 +03:00
mut pad_ch := u8(` `) // pad char
2021-05-05 04:12:18 +03:00
// prefix chars for Length field
2021-06-18 17:59:56 +03:00
mut ch1 := `0` // +1 char if present else `0`
mut ch2 := `0` // +2 char if present else `0`
2021-05-05 04:12:18 +03:00
mut status := Char_parse_state.norm_char
for i < str.len {
if status == .reset_params {
2021-06-18 17:59:56 +03:00
sign = false
allign = .right
len0 = -1
len1 = -1
pad_ch = ` `
2021-05-05 04:12:18 +03:00
status = .norm_char
ch1 = `0`
ch2 = `0`
continue
}
ch := str[i]
if ch != `%` && status == .norm_char {
2022-04-15 14:58:56 +03:00
res.write_u8(ch)
2021-05-05 04:12:18 +03:00
i++
continue
}
if ch == `%` && status == .norm_char {
status = .field_char
i++
continue
}
// single char, manage it here
if ch == `c` && status == .field_char {
v_sprintf_panic(p_index, pt.len)
2022-04-15 14:58:56 +03:00
d1 := unsafe { *(&u8(pt[p_index])) }
res.write_u8(d1)
2021-05-05 04:12:18 +03:00
status = .reset_params
p_index++
i++
continue
}
// pointer, manage it here
if ch == `p` && status == .field_char {
v_sprintf_panic(p_index, pt.len)
2021-06-18 17:59:56 +03:00
res.write_string('0x')
res.write_string(ptr_str(unsafe { pt[p_index] }))
2021-05-05 04:12:18 +03:00
status = .reset_params
p_index++
i++
continue
}
if status == .field_char {
mut fc_ch1 := `0`
mut fc_ch2 := `0`
if (i + 1) < str.len {
2021-06-18 17:59:56 +03:00
fc_ch1 = str[i + 1]
2021-05-05 04:12:18 +03:00
if (i + 2) < str.len {
2021-06-18 17:59:56 +03:00
fc_ch2 = str[i + 2]
2021-05-05 04:12:18 +03:00
}
}
if ch == `+` {
sign = true
i++
continue
} else if ch == `-` {
allign = .left
i++
continue
2021-06-18 17:59:56 +03:00
} else if ch in [`0`, ` `] {
2021-05-05 04:12:18 +03:00
if allign == .right {
pad_ch = ch
}
i++
continue
2021-08-06 06:21:28 +03:00
} else if ch == `'` {
2021-05-05 04:12:18 +03:00
i++
continue
} else if ch == `.` && fc_ch1 >= `1` && fc_ch1 <= `9` {
status = .check_float
i++
continue
}
// manage "%.*s" precision field
else if ch == `.` && fc_ch1 == `*` && fc_ch2 == `s` {
v_sprintf_panic(p_index, pt.len)
2021-06-18 17:59:56 +03:00
len := unsafe { *(&int(pt[p_index])) }
2021-05-05 04:12:18 +03:00
p_index++
v_sprintf_panic(p_index, pt.len)
2021-06-18 17:59:56 +03:00
mut s := unsafe { *(&string(pt[p_index])) }
2021-05-05 04:12:18 +03:00
s = s[..len]
p_index++
res.write_string(s)
status = .reset_params
i += 3
continue
}
status = .len_set_start
continue
}
if status == .len_set_start {
if ch >= `1` && ch <= `9` {
len0 = int(ch - `0`)
status = .len_set_in
i++
continue
}
if ch == `.` {
status = .check_float
i++
continue
}
status = .check_type
continue
}
if status == .len_set_in {
if ch >= `0` && ch <= `9` {
len0 *= 10
len0 += int(ch - `0`)
i++
continue
}
if ch == `.` {
status = .check_float
i++
continue
}
status = .check_type
continue
}
if status == .check_float {
if ch >= `0` && ch <= `9` {
len1 = int(ch - `0`)
status = .check_float_in
i++
continue
}
status = .check_type
continue
}
if status == .check_float_in {
if ch >= `0` && ch <= `9` {
len1 *= 10
len1 += int(ch - `0`)
i++
continue
}
status = .check_type
continue
}
if status == .check_type {
if ch == `l` {
if ch1 == `0` {
ch1 = `l`
i++
continue
} else {
ch2 = `l`
i++
continue
}
2021-06-18 17:59:56 +03:00
} else if ch == `h` {
2021-05-05 04:12:18 +03:00
if ch1 == `0` {
ch1 = `h`
i++
continue
} else {
ch2 = `h`
i++
continue
}
}
// signed integer
2021-06-18 17:59:56 +03:00
else if ch in [`d`, `i`] {
2021-05-05 04:12:18 +03:00
mut d1 := u64(0)
mut positive := true
2021-06-18 17:59:56 +03:00
// println("$ch1 $ch2")
2021-05-05 04:12:18 +03:00
match ch1 {
// h for 16 bit int
// hh fot 8 bit int
`h` {
if ch2 == `h` {
v_sprintf_panic(p_index, pt.len)
2021-06-18 17:59:56 +03:00
x := unsafe { *(&i8(pt[p_index])) }
2021-05-05 04:12:18 +03:00
positive = if x >= 0 { true } else { false }
d1 = if positive { u64(x) } else { u64(-x) }
} else {
2021-06-18 17:59:56 +03:00
x := unsafe { *(&i16(pt[p_index])) }
2021-05-05 04:12:18 +03:00
positive = if x >= 0 { true } else { false }
d1 = if positive { u64(x) } else { u64(-x) }
}
}
// l i64
// ll i64 for now
`l` {
// placeholder for future 128bit integer code
/*
if ch2 == `l` {
v_sprintf_panic(p_index, pt.len)
x := *(&i128(pt[p_index]))
positive = if x >= 0 { true } else { false }
d1 = if positive { u128(x) } else { u128(-x) }
} else {
v_sprintf_panic(p_index, pt.len)
x := *(&i64(pt[p_index]))
positive = if x >= 0 { true } else { false }
d1 = if positive { u64(x) } else { u64(-x) }
}
*/
v_sprintf_panic(p_index, pt.len)
2021-06-18 17:59:56 +03:00
x := unsafe { *(&i64(pt[p_index])) }
2021-05-05 04:12:18 +03:00
positive = if x >= 0 { true } else { false }
d1 = if positive { u64(x) } else { u64(-x) }
}
// default int
else {
v_sprintf_panic(p_index, pt.len)
2021-06-18 17:59:56 +03:00
x := unsafe { *(&int(pt[p_index])) }
2021-05-05 04:12:18 +03:00
positive = if x >= 0 { true } else { false }
d1 = if positive { u64(x) } else { u64(-x) }
}
}
tmp := format_dec_old(d1,
2021-06-18 17:59:56 +03:00
pad_ch: pad_ch
len0: len0
len1: 0
positive: positive
sign_flag: sign
allign: allign
)
res.write_string(tmp)
unsafe { tmp.free() }
2021-05-05 04:12:18 +03:00
status = .reset_params
p_index++
i++
ch1 = `0`
ch2 = `0`
continue
}
// unsigned integer
else if ch == `u` {
mut d1 := u64(0)
positive := true
v_sprintf_panic(p_index, pt.len)
match ch1 {
// h for 16 bit unsigned int
// hh fot 8 bit unsigned int
`h` {
if ch2 == `h` {
2022-04-15 14:58:56 +03:00
d1 = u64(unsafe { *(&u8(pt[p_index])) })
2021-05-05 04:12:18 +03:00
} else {
2021-06-18 17:59:56 +03:00
d1 = u64(unsafe { *(&u16(pt[p_index])) })
2021-05-05 04:12:18 +03:00
}
}
// l u64
// ll u64 for now
`l` {
// placeholder for future 128bit integer code
/*
if ch2 == `l` {
d1 = u128(*(&u128(pt[p_index])))
} else {
d1 = u64(*(&u64(pt[p_index])))
}
*/
2021-06-18 17:59:56 +03:00
d1 = u64(unsafe { *(&u64(pt[p_index])) })
2021-05-05 04:12:18 +03:00
}
// default int
else {
2021-06-18 17:59:56 +03:00
d1 = u64(unsafe { *(&u32(pt[p_index])) })
2021-05-05 04:12:18 +03:00
}
}
tmp := format_dec_old(d1,
2021-06-18 17:59:56 +03:00
pad_ch: pad_ch
len0: len0
len1: 0
positive: positive
sign_flag: sign
allign: allign
)
res.write_string(tmp)
unsafe { tmp.free() }
2021-05-05 04:12:18 +03:00
status = .reset_params
p_index++
i++
continue
}
// hex
else if ch in [`x`, `X`] {
v_sprintf_panic(p_index, pt.len)
2021-06-18 17:59:56 +03:00
mut s := ''
2021-05-05 04:12:18 +03:00
match ch1 {
// h for 16 bit int
// hh fot 8 bit int
`h` {
if ch2 == `h` {
2021-06-18 17:59:56 +03:00
x := unsafe { *(&i8(pt[p_index])) }
2021-05-05 04:12:18 +03:00
s = x.hex()
} else {
2021-06-18 17:59:56 +03:00
x := unsafe { *(&i16(pt[p_index])) }
2021-05-05 04:12:18 +03:00
s = x.hex()
}
}
// l i64
// ll i64 for now
`l` {
// placeholder for future 128bit integer code
/*
if ch2 == `l` {
x := *(&i128(pt[p_index]))
s = x.hex()
} else {
x := *(&i64(pt[p_index]))
s = x.hex()
}
*/
2021-06-18 17:59:56 +03:00
x := unsafe { *(&i64(pt[p_index])) }
2021-05-05 04:12:18 +03:00
s = x.hex()
}
else {
2021-06-18 17:59:56 +03:00
x := unsafe { *(&int(pt[p_index])) }
2021-05-05 04:12:18 +03:00
s = x.hex()
}
}
if ch == `X` {
tmp := s
2021-05-05 04:12:18 +03:00
s = s.to_upper()
unsafe { tmp.free() }
2021-05-05 04:12:18 +03:00
}
tmp := format_str(s,
2021-06-18 17:59:56 +03:00
pad_ch: pad_ch
len0: len0
len1: 0
positive: true
sign_flag: false
allign: allign
)
res.write_string(tmp)
unsafe { tmp.free() }
unsafe { s.free() }
2021-05-05 04:12:18 +03:00
status = .reset_params
p_index++
i++
continue
}
// float and double
if ch in [`f`, `F`] {
2021-09-13 19:06:19 +03:00
$if !nofloat ? {
v_sprintf_panic(p_index, pt.len)
x := unsafe { *(&f64(pt[p_index])) }
positive := x >= f64(0.0)
len1 = if len1 >= 0 { len1 } else { def_len1 }
s := format_fl_old(f64(x),
2021-06-18 17:59:56 +03:00
pad_ch: pad_ch
len0: len0
len1: len1
positive: positive
sign_flag: sign
allign: allign
)
if ch == `F` {
tmp := s.to_upper()
res.write_string(tmp)
unsafe { tmp.free() }
} else {
res.write_string(s)
}
unsafe { s.free() }
2021-09-13 19:06:19 +03:00
}
status = .reset_params
p_index++
i++
continue
} else if ch in [`e`, `E`] {
$if !nofloat ? {
v_sprintf_panic(p_index, pt.len)
x := unsafe { *(&f64(pt[p_index])) }
positive := x >= f64(0.0)
len1 = if len1 >= 0 { len1 } else { def_len1 }
s := format_es_old(f64(x),
2021-06-18 17:59:56 +03:00
pad_ch: pad_ch
len0: len0
len1: len1
positive: positive
sign_flag: sign
allign: allign
)
if ch == `E` {
tmp := s.to_upper()
res.write_string(tmp)
unsafe { tmp.free() }
} else {
res.write_string(s)
}
unsafe { s.free() }
2021-09-13 19:06:19 +03:00
}
status = .reset_params
p_index++
i++
continue
} else if ch in [`g`, `G`] {
$if !nofloat ? {
v_sprintf_panic(p_index, pt.len)
x := unsafe { *(&f64(pt[p_index])) }
positive := x >= f64(0.0)
mut s := ''
tx := fabs(x)
if tx < 999_999.0 && tx >= 0.00001 {
// println("Here g format_fl [$tx]")
len1 = if len1 >= 0 { len1 + 1 } else { def_len1 }
tmp := s
2021-09-13 19:06:19 +03:00
s = format_fl_old(x,
pad_ch: pad_ch
len0: len0
len1: len1
positive: positive
sign_flag: sign
allign: allign
rm_tail_zero: true
)
unsafe { tmp.free() }
2021-09-13 19:06:19 +03:00
} else {
len1 = if len1 >= 0 { len1 + 1 } else { def_len1 }
tmp := s
2021-09-13 19:06:19 +03:00
s = format_es_old(x,
pad_ch: pad_ch
len0: len0
len1: len1
positive: positive
sign_flag: sign
allign: allign
rm_tail_zero: true
)
unsafe { tmp.free() }
}
if ch == `G` {
tmp := s.to_upper()
res.write_string(tmp)
unsafe { tmp.free() }
} else {
res.write_string(s)
2021-09-13 19:06:19 +03:00
}
unsafe { s.free() }
2021-05-05 04:12:18 +03:00
}
status = .reset_params
p_index++
i++
continue
}
// string
else if ch == `s` {
v_sprintf_panic(p_index, pt.len)
2021-06-18 17:59:56 +03:00
s1 := unsafe { *(&string(pt[p_index])) }
2021-05-05 04:12:18 +03:00
pad_ch = ` `
tmp := format_str(s1,
2021-06-18 17:59:56 +03:00
pad_ch: pad_ch
len0: len0
len1: 0
positive: true
sign_flag: false
allign: allign
)
res.write_string(tmp)
unsafe { tmp.free() }
2021-05-05 04:12:18 +03:00
status = .reset_params
p_index++
i++
continue
}
}
status = .reset_params
p_index++
i++
}
if p_index != pt.len {
2021-06-18 17:59:56 +03:00
panic('$p_index % conversion specifiers, but given $pt.len args')
2021-05-05 04:12:18 +03:00
}
return res.str()
}
[inline]
fn v_sprintf_panic(idx int, len int) {
if idx >= len {
2021-06-18 17:59:56 +03:00
panic('${idx + 1} % conversion specifiers, but given only $len args')
2021-05-05 04:12:18 +03:00
}
}
fn fabs(x f64) f64 {
if x < 0.0 {
return -x
}
return x
}
// strings.Builder version of format_fl
[direct_array_access; manualfree]
2021-05-05 04:12:18 +03:00
pub fn format_fl_old(f f64, p BF_param) string {
2021-06-18 17:59:56 +03:00
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)
2021-05-05 04:12:18 +03:00
// error!!
if fs[0] == `[` {
s.free()
return fs
}
if p.rm_tail_zero {
tmp := fs
fs = remove_tail_zeros_old(fs)
tmp.free()
}
2021-06-18 17:59:56 +03:00
mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len })
defer {
res.free()
}
2021-05-05 04:12:18 +03:00
mut sign_len_diff := 0
if p.pad_ch == `0` {
if p.positive {
if p.sign_flag {
2022-04-15 14:58:56 +03:00
res.write_u8(`+`)
2021-05-05 04:12:18 +03:00
sign_len_diff = -1
}
} else {
2022-04-15 14:58:56 +03:00
res.write_u8(`-`)
2021-05-05 04:12:18 +03:00
sign_len_diff = -1
}
tmp := s
s = fs.clone()
tmp.free()
} else {
if p.positive {
if p.sign_flag {
tmp := s
2021-06-18 17:59:56 +03:00
s = '+' + fs
2021-05-05 04:12:18 +03:00
tmp.free()
} else {
tmp := s
s = fs.clone()
tmp.free()
}
} else {
tmp := s
2021-06-18 17:59:56 +03:00
s = '-' + fs
2021-05-05 04:12:18 +03:00
tmp.free()
}
}
dif := p.len0 - s.len + sign_len_diff
if p.allign == .right {
2021-06-18 17:59:56 +03:00
for i1 := 0; i1 < dif; i1++ {
2022-04-15 14:58:56 +03:00
res.write_u8(p.pad_ch)
2021-05-05 04:12:18 +03:00
}
}
res.write_string(s)
if p.allign == .left {
2021-06-18 17:59:56 +03:00
for i1 := 0; i1 < dif; i1++ {
2022-04-15 14:58:56 +03:00
res.write_u8(p.pad_ch)
2021-05-05 04:12:18 +03:00
}
}
s.free()
fs.free()
return res.str()
2021-05-05 04:12:18 +03:00
}
}
[manualfree]
fn format_es_old(f f64, p BF_param) string {
2021-06-18 17:59:56 +03:00
unsafe {
mut s := ''
mut fs := f64_to_str_pad(if f > 0 { f } else { -f }, p.len1)
2021-05-05 04:12:18 +03:00
if p.rm_tail_zero {
tmp := fs
2021-05-05 04:12:18 +03:00
fs = remove_tail_zeros_old(fs)
tmp.free()
2021-05-05 04:12:18 +03:00
}
2021-06-18 17:59:56 +03:00
mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len })
defer {
res.free()
fs.free()
s.free()
}
2021-05-05 04:12:18 +03:00
mut sign_len_diff := 0
if p.pad_ch == `0` {
if p.positive {
if p.sign_flag {
2022-04-15 14:58:56 +03:00
res.write_u8(`+`)
2021-05-05 04:12:18 +03:00
sign_len_diff = -1
}
} else {
2022-04-15 14:58:56 +03:00
res.write_u8(`-`)
2021-05-05 04:12:18 +03:00
sign_len_diff = -1
}
tmp := s
s = fs.clone()
tmp.free()
} else {
if p.positive {
if p.sign_flag {
tmp := s
2021-06-18 17:59:56 +03:00
s = '+' + fs
2021-05-05 04:12:18 +03:00
tmp.free()
} else {
tmp := s
s = fs.clone()
tmp.free()
}
} else {
tmp := s
2021-06-18 17:59:56 +03:00
s = '-' + fs
2021-05-05 04:12:18 +03:00
tmp.free()
}
}
dif := p.len0 - s.len + sign_len_diff
if p.allign == .right {
2021-06-18 17:59:56 +03:00
for i1 := 0; i1 < dif; i1++ {
2022-04-15 14:58:56 +03:00
res.write_u8(p.pad_ch)
2021-05-05 04:12:18 +03:00
}
}
res.write_string(s)
if p.allign == .left {
2021-06-18 17:59:56 +03:00
for i1 := 0; i1 < dif; i1++ {
2022-04-15 14:58:56 +03:00
res.write_u8(p.pad_ch)
2021-05-05 04:12:18 +03:00
}
}
return res.str()
2021-05-05 04:12:18 +03:00
}
}
fn remove_tail_zeros_old(s string) string {
2021-05-05 04:12:18 +03:00
mut i := 0
mut last_zero_start := -1
2021-06-18 17:59:56 +03:00
mut dot_pos := -1
2021-05-05 04:12:18 +03:00
mut in_decimal := false
2022-04-15 14:58:56 +03:00
mut prev_ch := u8(0)
2021-05-05 04:12:18 +03:00
for i < s.len {
2021-06-18 17:59:56 +03:00
ch := unsafe { s.str[i] }
2021-05-05 04:12:18 +03:00
if ch == `.` {
in_decimal = true
dot_pos = i
2021-06-18 17:59:56 +03:00
} else if in_decimal {
2021-05-05 04:12:18 +03:00
if ch == `0` && prev_ch != `0` {
last_zero_start = i
} else if ch >= `1` && ch <= `9` {
last_zero_start = -1
} else if ch == `e` {
break
}
}
prev_ch = ch
i++
}
2021-06-18 17:59:56 +03:00
mut tmp := ''
2021-05-05 04:12:18 +03:00
if last_zero_start > 0 {
2021-06-18 17:59:56 +03:00
if last_zero_start == dot_pos + 1 {
2021-05-05 04:12:18 +03:00
tmp = s[..dot_pos] + s[i..]
2021-06-18 17:59:56 +03:00
} else {
2021-05-05 04:12:18 +03:00
tmp = s[..last_zero_start] + s[i..]
}
} else {
tmp = s.clone()
2021-05-05 04:12:18 +03:00
}
2021-06-18 17:59:56 +03:00
if unsafe { tmp.str[tmp.len - 1] } == `.` {
return tmp[..tmp.len - 1]
2021-05-05 04:12:18 +03:00
}
return tmp
}
// max int64 9223372036854775807
[manualfree]
2021-05-05 04:12:18 +03:00
pub fn format_dec_old(d u64, p BF_param) string {
2021-06-18 17:59:56 +03:00
mut s := ''
2021-05-05 04:12:18 +03:00
mut res := strings.new_builder(20)
defer {
unsafe { res.free() }
unsafe { s.free() }
}
2021-05-05 04:12:18 +03:00
mut sign_len_diff := 0
if p.pad_ch == `0` {
if p.positive {
if p.sign_flag {
2022-04-15 14:58:56 +03:00
res.write_u8(`+`)
2021-05-05 04:12:18 +03:00
sign_len_diff = -1
}
} else {
2022-04-15 14:58:56 +03:00
res.write_u8(`-`)
2021-05-05 04:12:18 +03:00
sign_len_diff = -1
}
tmp := s
2021-05-05 04:12:18 +03:00
s = d.str()
unsafe { tmp.free() }
2021-05-05 04:12:18 +03:00
} else {
if p.positive {
if p.sign_flag {
tmp := s
2021-06-18 17:59:56 +03:00
s = '+' + d.str()
unsafe { tmp.free() }
2021-05-05 04:12:18 +03:00
} else {
tmp := s
2021-05-05 04:12:18 +03:00
s = d.str()
unsafe { tmp.free() }
2021-05-05 04:12:18 +03:00
}
} else {
tmp := s
2021-06-18 17:59:56 +03:00
s = '-' + d.str()
unsafe { tmp.free() }
2021-05-05 04:12:18 +03:00
}
}
dif := p.len0 - s.len + sign_len_diff
if p.allign == .right {
2021-06-18 17:59:56 +03:00
for i1 := 0; i1 < dif; i1++ {
2022-04-15 14:58:56 +03:00
res.write_u8(p.pad_ch)
2021-05-05 04:12:18 +03:00
}
}
res.write_string(s)
if p.allign == .left {
2021-06-18 17:59:56 +03:00
for i1 := 0; i1 < dif; i1++ {
2022-04-15 14:58:56 +03:00
res.write_u8(p.pad_ch)
2021-05-05 04:12:18 +03:00
}
}
return res.str()
2021-06-18 17:59:56 +03:00
}