mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
683 lines
16 KiB
V
683 lines
16 KiB
V
/*=============================================================================
|
|
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 builtin
|
|
|
|
import strconv
|
|
import strings
|
|
|
|
//=============================================================================
|
|
// Enum format types max 0x1F => 32 types
|
|
//=============================================================================
|
|
pub enum StrIntpType {
|
|
si_no_str = 0 // no parameter to print only fix string
|
|
si_c
|
|
si_u8
|
|
si_i8
|
|
si_u16
|
|
si_i16
|
|
si_u32
|
|
si_i32
|
|
si_u64
|
|
si_i64
|
|
si_e32
|
|
si_e64
|
|
si_f32
|
|
si_f64
|
|
si_g32
|
|
si_g64
|
|
si_s
|
|
si_p
|
|
si_vp
|
|
}
|
|
|
|
pub fn (x StrIntpType) str() string {
|
|
match x {
|
|
.si_no_str { return 'no_str' }
|
|
.si_c { return 'c' }
|
|
.si_u8 { return 'u8' }
|
|
.si_i8 { return 'i8' }
|
|
.si_u16 { return 'u16' }
|
|
.si_i16 { return 'i16' }
|
|
.si_u32 { return 'u32' }
|
|
.si_i32 { return 'i32' }
|
|
.si_u64 { return 'u64' }
|
|
.si_i64 { return 'i64' }
|
|
.si_f32 { return 'f32' }
|
|
.si_f64 { return 'f64' }
|
|
.si_g32 { return 'f32' } // g32 format use f32 data
|
|
.si_g64 { return 'f64' } // g64 format use f64 data
|
|
.si_e32 { return 'f32' } // e32 format use f32 data
|
|
.si_e64 { return 'f64' } // e64 format use f64 data
|
|
.si_s { return 's' }
|
|
.si_p { return 'p' }
|
|
.si_vp { return 'vp' }
|
|
}
|
|
}
|
|
|
|
/*
|
|
pub fn (x StrIntpType) data_str() string {
|
|
match x {
|
|
.si_no_str{ return "no_str" }
|
|
.si_c { return "d_c" }
|
|
|
|
.si_u8 { return "d_u8" }
|
|
.si_i8 { return "d_i8" }
|
|
.si_u16 { return "d_u16" }
|
|
.si_i16 { return "d_i16" }
|
|
.si_u32 { return "d_u32" }
|
|
.si_i32 { return "d_i32" }
|
|
.si_u64 { return "d_u64" }
|
|
.si_i64 { return "d_i64" }
|
|
|
|
.si_f32 { return "d_f32" }
|
|
.si_f64 { return "d_f64" }
|
|
.si_g32 { return "d_f32" } // g32 format use f32 data
|
|
.si_g64 { return "d_f64" } // g64 format use f64 data
|
|
.si_e32 { return "d_f32" } // e32 format use f32 data
|
|
.si_e64 { return "d_f64" } // e64 format use f64 data
|
|
|
|
.si_s { return "d_s" }
|
|
.si_p { return "d_p" }
|
|
.si_vp { return "d_vp" }
|
|
}
|
|
}
|
|
*/
|
|
|
|
//=============================================================================
|
|
// Union data
|
|
//=============================================================================
|
|
pub union StrIntpMem {
|
|
pub mut:
|
|
d_c u32
|
|
d_u8 byte
|
|
d_i8 i8
|
|
d_u16 u16
|
|
d_i16 i16
|
|
d_u32 u32
|
|
d_i32 int
|
|
d_u64 u64
|
|
d_i64 i64
|
|
d_f32 f32
|
|
d_f64 f64
|
|
d_s string
|
|
d_p voidptr
|
|
d_vp voidptr
|
|
}
|
|
|
|
fn fabs64(x f64) f64 {
|
|
if x < 0 {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|
|
|
|
fn fabs32(x f32) f32 {
|
|
if x < 0 {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|
|
|
|
fn abs64(x i64) u64 {
|
|
if x < 0 {
|
|
return u64(-x)
|
|
}
|
|
return u64(x)
|
|
}
|
|
|
|
//=========================================
|
|
//
|
|
// u32/u64 bit compact format
|
|
//
|
|
//___ 32 24 16 8
|
|
//___ | | | |
|
|
//_3333333333222222222211111111110000000000
|
|
//_9876543210987654321098765432109876543210
|
|
//_nPPPPPPPPBBBBWWWWWWWWWWTDDDDDDDSUAA=====
|
|
// = data type 5 bit max 32 data type
|
|
// A allign 2 bit Note: for now only 1 used!
|
|
// U uppercase 1 bit 0 do nothing, 1 do to_upper()
|
|
// S sign 1 bit show the sign if positive
|
|
// D decimals 7 bit number of decimals digit to show
|
|
// T tail zeros 1 bit 1 remove tail zeros, 0 do nothing
|
|
// W Width 10 bit number of char for padding and indentation
|
|
// B num base 4 bit start from 2, 0 for base 10
|
|
// P pad char 1/8 bit padding char (in u32 format reduced to 1 bit as flag for `0` padding)
|
|
// --------------
|
|
// TOTAL: 39/32 bit
|
|
//=========================================
|
|
|
|
// convert from data format to compact u64
|
|
pub fn get_str_intp_u64_format(fmt_type StrIntpType, in_width int, in_precision int, in_tail_zeros bool, in_sign bool, in_pad_ch u8, in_base int, in_upper_case bool) u64 {
|
|
width := if in_width != 0 { abs64(in_width) } else { u64(0) }
|
|
allign := if in_width > 0 { u64(1 << 5) } else { u64(0) } // two bit 0 .left 1 .rigth, for now we use only one
|
|
upper_case := if in_upper_case { u64(1 << 7) } else { u64(0) }
|
|
sign := if in_sign { u64(1 << 8) } else { u64(0) }
|
|
precision := if in_precision != 987698 {
|
|
(u64(in_precision & 0x7F) << 9)
|
|
} else {
|
|
u64(0x7F) << 9
|
|
}
|
|
tail_zeros := if in_tail_zeros { u32(1) << 16 } else { u32(0) }
|
|
base := u64((in_base & 0xf) << 27)
|
|
res := u64((u64(fmt_type) & 0x1F) | allign | upper_case | sign | precision | tail_zeros | (u64(width & 0x3FF) << 17) | base | (u64(in_pad_ch) << 31))
|
|
return res
|
|
}
|
|
|
|
// convert from data format to compact u32
|
|
pub fn get_str_intp_u32_format(fmt_type StrIntpType, in_width int, in_precision int, in_tail_zeros bool, in_sign bool, in_pad_ch u8, in_base int, in_upper_case bool) u32 {
|
|
width := if in_width != 0 { abs64(in_width) } else { u32(0) }
|
|
allign := if in_width > 0 { u32(1 << 5) } else { u32(0) } // two bit 0 .left 1 .rigth, for now we use only one
|
|
upper_case := if in_upper_case { u32(1 << 7) } else { u32(0) }
|
|
sign := if in_sign { u32(1 << 8) } else { u32(0) }
|
|
precision := if in_precision != 987698 {
|
|
(u32(in_precision & 0x7F) << 9)
|
|
} else {
|
|
u32(0x7F) << 9
|
|
}
|
|
tail_zeros := if in_tail_zeros { u32(1) << 16 } else { u32(0) }
|
|
base := u32((in_base & 0xf) << 27)
|
|
res := u32((u32(fmt_type) & 0x1F) | allign | upper_case | sign | precision | tail_zeros | (u32(width & 0x3FF) << 17) | base | (u32(in_pad_ch & 1) << 31))
|
|
return res
|
|
}
|
|
|
|
// convert from struct to formated string
|
|
[manualfree]
|
|
fn (data StrIntpData) get_fmt_format(mut sb strings.Builder) {
|
|
x := data.fmt
|
|
typ := StrIntpType(x & 0x1F)
|
|
allign := int((x >> 5) & 0x01)
|
|
upper_case := if ((x >> 7) & 0x01) > 0 { true } else { false }
|
|
sign := int((x >> 8) & 0x01)
|
|
precision := int((x >> 9) & 0x7F)
|
|
tail_zeros := if ((x >> 16) & 0x01) > 0 { true } else { false }
|
|
width := int(i16((x >> 17) & 0x3FF))
|
|
mut base := int(x >> 27) & 0xF
|
|
fmt_pad_ch := byte((x >> 31) & 0xFF)
|
|
|
|
// no string interpolation is needed, return empty string
|
|
if typ == .si_no_str {
|
|
return
|
|
}
|
|
|
|
// if width > 0 { println("${x.hex()} Type: ${x & 0x7F} Width: ${width} Precision: ${precision} allign:${allign}") }
|
|
|
|
// manage base if any
|
|
if base > 0 {
|
|
base += 2 // we start from 2, 0 == base 10
|
|
}
|
|
|
|
// mange pad char, for now only 0 allowed
|
|
mut pad_ch := byte(` `)
|
|
if fmt_pad_ch > 0 {
|
|
// pad_ch = fmt_pad_ch
|
|
pad_ch = `0`
|
|
}
|
|
|
|
len0_set := if width > 0 { width } else { -1 }
|
|
len1_set := if precision == 0x7F { -1 } else { precision }
|
|
sign_set := if sign == 1 { true } else { false }
|
|
|
|
mut bf := strconv.BF_param{
|
|
pad_ch: pad_ch // padding char
|
|
len0: len0_set // default len for whole the number or string
|
|
len1: len1_set // number of decimal digits, if needed
|
|
positive: true // mandatory: the sign of the number passed
|
|
sign_flag: sign_set // flag for print sign as prefix in padding
|
|
allign: .left // alignment of the string
|
|
rm_tail_zero: tail_zeros // false // remove the tail zeros from floats
|
|
}
|
|
|
|
// allign
|
|
if fmt_pad_ch == 0 {
|
|
match allign {
|
|
0 { bf.allign = .left }
|
|
1 { bf.allign = .right }
|
|
// 2 { bf.allign = .center }
|
|
else { bf.allign = .left }
|
|
}
|
|
} else {
|
|
bf.allign = .right
|
|
}
|
|
|
|
unsafe {
|
|
// strings
|
|
if typ == .si_s {
|
|
mut s := ''
|
|
if upper_case {
|
|
s = data.d.d_s.to_upper()
|
|
} else {
|
|
s = data.d.d_s.clone()
|
|
}
|
|
if width == 0 {
|
|
sb.write_string(s)
|
|
} else {
|
|
strconv.format_str_sb(s, bf, mut sb)
|
|
}
|
|
s.free()
|
|
return
|
|
}
|
|
|
|
// signed int
|
|
if typ in [.si_i8, .si_i16, .si_i32, .si_i64] {
|
|
mut d := data.d.d_i64
|
|
if typ == .si_i8 {
|
|
d = i64(data.d.d_i8)
|
|
} else if typ == .si_i16 {
|
|
d = i64(data.d.d_i16)
|
|
} else if typ == .si_i32 {
|
|
d = i64(data.d.d_i32)
|
|
}
|
|
|
|
if base == 0 {
|
|
if width == 0 {
|
|
d_str := d.str()
|
|
sb.write_string(d_str)
|
|
d_str.free()
|
|
return
|
|
}
|
|
if d < 0 {
|
|
bf.positive = false
|
|
}
|
|
strconv.format_dec_sb(abs64(d), bf, mut sb)
|
|
} else {
|
|
mut hx := strconv.format_int(d, base)
|
|
if upper_case {
|
|
tmp := hx
|
|
hx = hx.to_upper()
|
|
tmp.free()
|
|
}
|
|
if width == 0 {
|
|
sb.write_string(hx)
|
|
} else {
|
|
strconv.format_str_sb(hx, bf, mut sb)
|
|
}
|
|
hx.free()
|
|
}
|
|
return
|
|
}
|
|
|
|
// unsigned int and pointers
|
|
if typ in [.si_u8, .si_u16, .si_u32, .si_u64] {
|
|
mut d := data.d.d_u64
|
|
if typ == .si_u8 {
|
|
d = u64(data.d.d_u8)
|
|
} else if typ == .si_u16 {
|
|
d = u64(data.d.d_u16)
|
|
} else if typ == .si_u32 {
|
|
d = u64(data.d.d_u32)
|
|
}
|
|
if base == 0 {
|
|
if width == 0 {
|
|
d_str := d.str()
|
|
sb.write_string(d_str)
|
|
d_str.free()
|
|
return
|
|
}
|
|
strconv.format_dec_sb(d, bf, mut sb)
|
|
} else {
|
|
mut hx := strconv.format_uint(d, base)
|
|
if upper_case {
|
|
tmp := hx
|
|
hx = hx.to_upper()
|
|
tmp.free()
|
|
}
|
|
if width == 0 {
|
|
sb.write_string(hx)
|
|
} else {
|
|
strconv.format_str_sb(hx, bf, mut sb)
|
|
}
|
|
hx.free()
|
|
}
|
|
return
|
|
}
|
|
|
|
// pointers
|
|
if typ == .si_p {
|
|
mut d := data.d.d_u64
|
|
base = 16 // TODO: **** decide the behaviour of this flag! ****
|
|
if base == 0 {
|
|
if width == 0 {
|
|
d_str := d.str()
|
|
sb.write_string(d_str)
|
|
d_str.free()
|
|
return
|
|
}
|
|
strconv.format_dec_sb(d, bf, mut sb)
|
|
} else {
|
|
mut hx := strconv.format_uint(d, base)
|
|
if upper_case {
|
|
tmp := hx
|
|
hx = hx.to_upper()
|
|
tmp.free()
|
|
}
|
|
if width == 0 {
|
|
sb.write_string(hx)
|
|
} else {
|
|
strconv.format_str_sb(hx, bf, mut sb)
|
|
}
|
|
hx.free()
|
|
}
|
|
return
|
|
}
|
|
|
|
// default settings for floats
|
|
mut use_default_str := false
|
|
if width == 0 && precision == 0x7F {
|
|
bf.len1 = 3
|
|
use_default_str = true
|
|
}
|
|
if bf.len1 < 0 {
|
|
bf.len1 = 3
|
|
}
|
|
|
|
match typ {
|
|
// floating point
|
|
.si_f32 {
|
|
// println("HERE: f32")
|
|
if use_default_str {
|
|
mut f := data.d.d_f32.str()
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
} else {
|
|
// println("HERE: f32 format")
|
|
// println(data.d.d_f32)
|
|
if data.d.d_f32 < 0 {
|
|
bf.positive = false
|
|
}
|
|
mut f := strconv.format_fl(data.d.d_f32, bf)
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
}
|
|
}
|
|
.si_f64 {
|
|
// println("HERE: f64")
|
|
if use_default_str {
|
|
mut f := data.d.d_f64.str()
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
} else {
|
|
if data.d.d_f64 < 0 {
|
|
bf.positive = false
|
|
}
|
|
f_union := strconv.Float64u{
|
|
f: data.d.d_f64
|
|
}
|
|
if f_union.u == strconv.double_minus_zero {
|
|
bf.positive = false
|
|
}
|
|
|
|
mut f := strconv.format_fl(data.d.d_f64, bf)
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
}
|
|
}
|
|
.si_g32 {
|
|
// println("HERE: g32")
|
|
if use_default_str {
|
|
mut f := data.d.d_f32.strg()
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
} else {
|
|
if data.d.d_f32 < 0 {
|
|
bf.positive = false
|
|
}
|
|
d := fabs32(data.d.d_f32)
|
|
if d < 999_999.0 && d >= 0.00001 {
|
|
mut f := strconv.format_fl(data.d.d_f32, bf)
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
return
|
|
}
|
|
mut f := strconv.format_es(data.d.d_f32, bf)
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
}
|
|
}
|
|
.si_g64 {
|
|
// println("HERE: g64")
|
|
if use_default_str {
|
|
mut f := data.d.d_f64.strg()
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
} else {
|
|
if data.d.d_f64 < 0 {
|
|
bf.positive = false
|
|
}
|
|
d := fabs64(data.d.d_f64)
|
|
if d < 999_999.0 && d >= 0.00001 {
|
|
mut f := strconv.format_fl(data.d.d_f64, bf)
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
return
|
|
}
|
|
mut f := strconv.format_es(data.d.d_f64, bf)
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
}
|
|
}
|
|
.si_e32 {
|
|
// println("HERE: e32")
|
|
bf.len1 = 6
|
|
if use_default_str {
|
|
mut f := data.d.d_f32.str()
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
} else {
|
|
if data.d.d_f32 < 0 {
|
|
bf.positive = false
|
|
}
|
|
mut f := strconv.format_es(data.d.d_f32, bf)
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
}
|
|
}
|
|
.si_e64 {
|
|
// println("HERE: e64")
|
|
bf.len1 = 6
|
|
if use_default_str {
|
|
mut f := data.d.d_f64.str()
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
} else {
|
|
if data.d.d_f64 < 0 {
|
|
bf.positive = false
|
|
}
|
|
mut f := strconv.format_es(data.d.d_f64, bf)
|
|
if upper_case {
|
|
tmp := f
|
|
f = f.to_upper()
|
|
tmp.free()
|
|
}
|
|
sb.write_string(f)
|
|
f.free()
|
|
}
|
|
}
|
|
// runes
|
|
.si_c {
|
|
sb.write_string(utf32_to_str(data.d.d_c))
|
|
}
|
|
// v pointers
|
|
.si_vp {
|
|
sb.write_string(u64(data.d.d_vp).hex())
|
|
}
|
|
else {
|
|
sb.write_string('***ERROR!***')
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//====================================================================================
|
|
|
|
// storing struct used by cgen
|
|
pub struct StrIntpCgenData {
|
|
pub:
|
|
str string
|
|
fmt string
|
|
d string
|
|
}
|
|
|
|
// NOTE: LOW LEVEL struct
|
|
// storing struct passed to V in the C code
|
|
pub struct StrIntpData {
|
|
pub:
|
|
str string
|
|
// fmt u64 // expanded version for future use, 64 bit
|
|
fmt u32
|
|
d StrIntpMem
|
|
}
|
|
|
|
// interpolation function
|
|
[manualfree]
|
|
pub fn str_intp(data_len int, in_data voidptr) string {
|
|
mut res := strings.new_builder(256)
|
|
unsafe {
|
|
mut i := 0
|
|
for i < data_len {
|
|
data := &StrIntpData(&byte(in_data) + (int(sizeof(StrIntpData)) * i))
|
|
// avoid empty strings
|
|
if data.str.len != 0 {
|
|
res.write_string(data.str)
|
|
}
|
|
// skip empty data
|
|
if data.fmt != 0 {
|
|
data.get_fmt_format(mut &res)
|
|
}
|
|
i++
|
|
}
|
|
}
|
|
ret := res.str()
|
|
unsafe { res.free() }
|
|
return ret
|
|
}
|
|
|
|
//====================================================================================
|
|
// Utility for the compiler "auto_str_methods.v"
|
|
//====================================================================================
|
|
|
|
// substitute old _STR calls
|
|
|
|
pub const (
|
|
// BUG: this const is not released from the memory! use a const for now
|
|
// si_s_code = "0x" + int(StrIntpType.si_s).hex() // code for a simple string
|
|
si_s_code = '0xfe10'
|
|
si_g32_code = '0xfe0e'
|
|
si_g64_code = '0xfe0f'
|
|
)
|
|
|
|
[inline]
|
|
pub fn str_intp_sq(in_str string) string {
|
|
return 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("\'"), $si_s_code, {.d_s = $in_str}},{_SLIT("\'"), 0, {.d_c = 0 }}}))'
|
|
}
|
|
|
|
[inline]
|
|
pub fn str_intp_rune(in_str string) string {
|
|
return 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("\`"), $si_s_code, {.d_s = $in_str}},{_SLIT("\`"), 0, {.d_c = 0 }}}))'
|
|
}
|
|
|
|
[inline]
|
|
pub fn str_intp_g32(in_str string) string {
|
|
return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT(""), $si_g32_code, {.d_f32 = $in_str }}}))'
|
|
}
|
|
|
|
[inline]
|
|
pub fn str_intp_g64(in_str string) string {
|
|
return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT(""), $si_g64_code, {.d_f64 = $in_str }}}))'
|
|
}
|
|
|
|
// replace %% with the in_str
|
|
[manualfree]
|
|
pub fn str_intp_sub(base_str string, in_str string) string {
|
|
index := base_str.index('%%') or {
|
|
eprintln('No strin interpolation %% parameteres')
|
|
exit(1)
|
|
}
|
|
// return base_str[..index] + in_str + base_str[index+2..]
|
|
|
|
unsafe {
|
|
st_str := base_str[..index]
|
|
if index + 2 < base_str.len {
|
|
en_str := base_str[index + 2..]
|
|
res_str := 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("$st_str"), $si_s_code, {.d_s = $in_str }},{_SLIT("$en_str"), 0, {.d_c = 0}}}))'
|
|
st_str.free()
|
|
en_str.free()
|
|
return res_str
|
|
}
|
|
res2_str := 'str_intp(1, _MOV((StrIntpData[]){{_SLIT("$st_str"), $si_s_code, {.d_s = $in_str }}}))'
|
|
st_str.free()
|
|
return res2_str
|
|
}
|
|
}
|