diff --git a/cmd/tools/vtest-cleancode.v b/cmd/tools/vtest-cleancode.v
index 58208e8193..4e1225e330 100644
--- a/cmd/tools/vtest-cleancode.v
+++ b/cmd/tools/vtest-cleancode.v
@@ -24,6 +24,7 @@ const (
 		'examples/sokol/03_march_tracing_glsl/rt_glsl.v',
 		'examples/sokol/04_multi_shader_glsl/rt_glsl.v',
 		'examples/sokol/05_instancing_glsl/rt_glsl.v',
+		'examples/sokol/06_obj_viewer/show_obj.v',
 		'vlib/gg/m4/graphic.v',
 		'vlib/gg/m4/m4_test.v',
 		'vlib/gg/m4/matrix.v',
diff --git a/examples/sokol/06_obj_viewer/show_obj.v b/examples/sokol/06_obj_viewer/show_obj.v
index dbf95ac7bf..18f0258a60 100644
--- a/examples/sokol/06_obj_viewer/show_obj.v
+++ b/examples/sokol/06_obj_viewer/show_obj.v
@@ -336,4 +336,4 @@ fn main() {
 
 	app.ticks = time.ticks()
 	app.gg.run()
-}
+}
\ No newline at end of file
diff --git a/vlib/builtin/float.v b/vlib/builtin/float.v
index 8d64478a05..99fe8b8c09 100644
--- a/vlib/builtin/float.v
+++ b/vlib/builtin/float.v
@@ -12,6 +12,17 @@ import strconv
 // str return a `f64` as `string` in suitable notation.
 [inline]
 pub fn (x f64) str() string {
+	unsafe {
+		f := strconv.Float64u{
+			f: x
+		}
+		if f.u == strconv.double_minus_zero {
+			return '-0'
+		}
+		if f.u == strconv.double_plus_zero {
+			return '0'
+		}
+	}
 	abs_x := f64_abs(x)
 	if abs_x >= 0.0001 && abs_x < 1.0e6 {
 		return strconv.f64_to_str_l(x)
@@ -20,6 +31,20 @@ pub fn (x f64) str() string {
 	}
 }
 
+// strg return a `f64` as `string` in "g" printf format
+[inline]
+pub fn (x f64) strg() string {
+	if x == 0 {
+		return '0'
+	}
+	abs_x := f64_abs(x)
+	if abs_x >= 0.0001 && abs_x < 1.0e6 {
+		return strconv.f64_to_str_l_no_dot(x)
+	} else {
+		return strconv.ftoa_64(x)
+	}
+}
+
 // str returns the value of the `float_literal` as a `string`.
 [inline]
 pub fn (d float_literal) str() string {
@@ -53,6 +78,17 @@ pub fn (x f64) strlong() string {
 // str returns a `f32` as `string` in suitable notation.
 [inline]
 pub fn (x f32) str() string {
+	unsafe {
+		f := strconv.Float32u{
+			f: x
+		}
+		if f.u == strconv.single_minus_zero {
+			return '-0'
+		}
+		if f.u == strconv.single_plus_zero {
+			return '0'
+		}
+	}
 	abs_x := f32_abs(x)
 	if abs_x >= 0.0001 && abs_x < 1.0e6 {
 		return strconv.f32_to_str_l(x)
@@ -61,6 +97,20 @@ pub fn (x f32) str() string {
 	}
 }
 
+// strg return a `f32` as `string` in "g" printf format
+[inline]
+pub fn (x f32) strg() string {
+	if x == 0 {
+		return '0'
+	}
+	abs_x := f32_abs(x)
+	if abs_x >= 0.0001 && abs_x < 1.0e6 {
+		return strconv.f32_to_str_l_no_dot(x)
+	} else {
+		return strconv.ftoa_32(x)
+	}
+}
+
 // strsci returns the `f32` as a `string` in scientific notation with `digit_num` deciamals displayed, max 8 digits.
 // Example: assert f32(1.234).strsci(3) == '1.234e+00'
 [inline]
diff --git a/vlib/builtin/string_interpolation.v b/vlib/builtin/string_interpolation.v
new file mode 100644
index 0000000000..208a5fa883
--- /dev/null
+++ b/vlib/builtin/string_interpolation.v
@@ -0,0 +1,674 @@
+/*=============================================================================
+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'
+)
+
+// replace _STR("\'%.*s\\000\'", 2, in_str)
+[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 }}}))'
+}
+
+// replace _STR("\`%.*s\\000\`", 2, in_str)
+[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
+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..]
+	if index + 2 < base_str.len {
+		return 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("${base_str[..index]}"), $si_s_code, {.d_s = $in_str }},{_SLIT("${base_str[
+			index + 2..]}"), 0, {.d_c = 0}}}))'
+	}
+	return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT("${base_str[..index]}"), $si_s_code, {.d_s = $in_str }}}))'
+}
diff --git a/vlib/math/complex/complex.v b/vlib/math/complex/complex.v
index f5917f1698..9fd7cc8cb9 100644
--- a/vlib/math/complex/complex.v
+++ b/vlib/math/complex/complex.v
@@ -18,8 +18,8 @@ pub fn complex(re f64, im f64) Complex {
 
 // To String method
 pub fn (c Complex) str() string {
-	mut out := '${c.re:f}'
-	out += if c.im >= 0 { '+${c.im:f}' } else { '${c.im:f}' }
+	mut out := '${c.re:.6f}'
+	out += if c.im >= 0 { '+${c.im:.6f}' } else { '${c.im:.6f}' }
 	out += 'i'
 	return out
 }
diff --git a/vlib/strconv/atof.v b/vlib/strconv/atof.v
index a8179f81d8..e641adbcf5 100644
--- a/vlib/strconv/atof.v
+++ b/vlib/strconv/atof.v
@@ -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{}
diff --git a/vlib/strconv/f32_str.v b/vlib/strconv/f32_str.v
index 290887d2fa..ad629741f3 100644
--- a/vlib/strconv/f32_str.v
+++ b/vlib/strconv/f32_str.v
@@ -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 {
diff --git a/vlib/strconv/format.v b/vlib/strconv/format.v
index 7608c78ce3..876a92eab7 100644
--- a/vlib/strconv/format.v
+++ b/vlib/strconv/format.v
@@ -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
 
 /*
diff --git a/vlib/strconv/format_mem.v b/vlib/strconv/format_mem.v
new file mode 100644
index 0000000000..6948cc70cf
--- /dev/null
+++ b/vlib/strconv/format_mem.v
@@ -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)
+	}
+}
diff --git a/vlib/strconv/number_to_base.v b/vlib/strconv/number_to_base.v
index 3284df0f70..8881d08689 100644
--- a/vlib/strconv/number_to_base.v
+++ b/vlib/strconv/number_to_base.v
@@ -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
 }
diff --git a/vlib/strconv/structs.v b/vlib/strconv/structs.v
index ebc45712e2..1f7abbf2ec 100644
--- a/vlib/strconv/structs.v
+++ b/vlib/strconv/structs.v
@@ -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
+}
\ No newline at end of file
diff --git a/vlib/strconv/utilities.v b/vlib/strconv/utilities.v
index c0aa43940d..c87863c376 100644
--- a/vlib/strconv/utilities.v
+++ b/vlib/strconv/utilities.v
@@ -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) }
 }
diff --git a/vlib/strconv/vprintf.v b/vlib/strconv/vprintf.v
index 35ab0f74de..6123fe2454 100644
--- a/vlib/strconv/vprintf.v
+++ b/vlib/strconv/vprintf.v
@@ -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 {
diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v
index dfcfa7aff1..0ecb6ac3ce 100644
--- a/vlib/v/ast/str.v
+++ b/vlib/v/ast/str.v
@@ -493,7 +493,8 @@ pub fn (node Stmt) str() string {
 }
 
 fn field_to_string(f ConstField) string {
-	return '${f.name.trim_prefix(f.mod + '.')} = $f.expr'
+	x := f.name.trim_prefix(f.mod + '.')
+	return '$x = $f.expr'
 }
 
 pub fn (e CompForKind) str() string {
diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v
index 43c565aea1..6ed4293302 100644
--- a/vlib/v/checker/check_types.v
+++ b/vlib/v/checker/check_types.v
@@ -443,7 +443,9 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Typ
 		typ := c.table.unalias_num_type(ftyp)
 		mut fmt := node.fmts[i]
 		// analyze and validate format specifier
-		if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`, `_`] {
+		if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `S`, `p`,
+			`_`,
+		] {
 			c.error('unknown format specifier `${fmt:c}`', node.fmt_poss[i])
 		}
 		if fmt == `_` { // set default representation for type if none has been given
@@ -469,7 +471,7 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Typ
 				|| (typ.is_int_literal() && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`])
 				|| (typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`])
 				|| (typ.is_pointer() && fmt !in [`p`, `x`, `X`])
-				|| (typ.is_string() && fmt != `s`)
+				|| (typ.is_string() && fmt !in [`s`, `S`])
 				|| (typ.idx() in [ast.i64_type_idx, ast.f64_type_idx] && fmt == `c`) {
 				c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`',
 					node.fmt_poss[i])
diff --git a/vlib/v/gen/c/auto_str_array.v b/vlib/v/gen/c/auto_str_array.v
new file mode 100644
index 0000000000..6cda13f1b4
--- /dev/null
+++ b/vlib/v/gen/c/auto_str_array.v
@@ -0,0 +1,170 @@
+module c
+
+// Copyright (c) 2019-2021 Alexander Medvednikov. 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.
+import v.ast
+
+fn (mut g Gen) gen_str_for_array(info ast.Array, styp string, str_fn_name string) {
+	mut typ := info.elem_type
+	mut sym := g.table.get_type_symbol(info.elem_type)
+	if mut sym.info is ast.Alias {
+		typ = sym.info.parent_type
+		sym = g.table.get_type_symbol(typ)
+	}
+	field_styp := g.typ(typ)
+	is_elem_ptr := typ.is_ptr()
+	sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
+	mut elem_str_fn_name := ''
+	if sym_has_str_method {
+		elem_str_fn_name = if is_elem_ptr {
+			field_styp.replace('*', '') + '_str'
+		} else {
+			field_styp + '_str'
+		}
+		if sym.kind == .byte {
+			elem_str_fn_name = elem_str_fn_name + '_escaped'
+		}
+	} else {
+		elem_str_fn_name = styp_to_str_fn_name(field_styp)
+	}
+	if !sym_has_str_method {
+		g.gen_str_for_type(typ)
+	}
+	g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto')
+	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}')
+	g.type_definitions.writeln('static string indent_${str_fn_name}($styp a, int indent_count); // auto')
+	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp a, int indent_count) {')
+	g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(a.len * 10);')
+	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("["));')
+	g.auto_str_funcs.writeln('\tfor (int i = 0; i < a.len; ++i) {')
+	if sym.kind == .function {
+		g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
+	} else {
+		if sym.kind == .array_fixed {
+			g.auto_str_funcs.writeln('\t\t$field_styp it;')
+			g.auto_str_funcs.writeln('\t\tmemcpy(*($field_styp*)it, (byte*)array_get(a, i), sizeof($field_styp));')
+		} else {
+			g.auto_str_funcs.writeln('\t\t$field_styp it = *($field_styp*)array_get(a, i);')
+		}
+		if should_use_indent_func(sym.kind) && !sym_has_str_method {
+			if is_elem_ptr {
+				g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(*it, indent_count);')
+			} else {
+				g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(it, indent_count);')
+			}
+		} else if sym.kind in [.f32, .f64] {
+			if sym.kind == .f32 {
+				g.auto_str_funcs.writeln('\t\tstring x = ${str_intp_g32('it')};')
+			} else {
+				g.auto_str_funcs.writeln('\t\tstring x = ${str_intp_g64('it')};')
+			}
+
+			//	g.auto_str_funcs.writeln('\t\tstring x = _STR("%g", 1, it);')
+		} else if sym.kind == .rune {
+			// Rune are managed at this level as strings
+			g.auto_str_funcs.writeln('\t\tstring x = str_intp(2, _MOV((StrIntpData[]){{_SLIT("\`"), $si_s_code, {.d_s = ${elem_str_fn_name}(it) }}, {_SLIT("\`"), 0, {.d_c = 0 }}}));\n')
+
+			//	g.auto_str_funcs.writeln('\t\tstring x = _STR("`%.*s\\000`", 2, ${elem_str_fn_name}(it));')
+		} else if sym.kind == .string {
+			g.auto_str_funcs.writeln('\t\tstring x = str_intp(2, _MOV((StrIntpData[]){{_SLIT("\'"), $si_s_code, {.d_s = it }}, {_SLIT("\'"), 0, {.d_c = 0 }}}));\n')
+
+			//	g.auto_str_funcs.writeln('\t\tstring x = _STR("\'%.*s\\000\'", 2, it);')
+		} else {
+			// There is a custom .str() method, so use it.
+			// NB: we need to take account of whether the user has defined
+			// `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly
+			deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
+			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));')
+			g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}( $deref it);')
+		}
+	}
+	g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, x);')
+	if g.is_autofree && typ != ast.bool_type {
+		// no need to free "true"/"false" literals
+		g.auto_str_funcs.writeln('\t\tstring_free(&x);')
+	}
+	g.auto_str_funcs.writeln('\t\tif (i < a.len-1) {')
+	g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));')
+	g.auto_str_funcs.writeln('\t\t}')
+	g.auto_str_funcs.writeln('\t}')
+	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("]"));')
+	g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
+	g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
+	g.auto_str_funcs.writeln('\treturn res;')
+	g.auto_str_funcs.writeln('}')
+}
+
+fn (mut g Gen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_name string) {
+	mut typ := info.elem_type
+	mut sym := g.table.get_type_symbol(info.elem_type)
+	if mut sym.info is ast.Alias {
+		typ = sym.info.parent_type
+		sym = g.table.get_type_symbol(typ)
+	}
+	field_styp := g.typ(typ)
+	is_elem_ptr := typ.is_ptr()
+	sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
+	mut elem_str_fn_name := ''
+	if sym_has_str_method {
+		elem_str_fn_name = if is_elem_ptr {
+			field_styp.replace('*', '') + '_str'
+		} else {
+			field_styp + '_str'
+		}
+	} else {
+		elem_str_fn_name = styp_to_str_fn_name(field_styp)
+	}
+	if !sym.has_method('str') {
+		elem_str_fn_name = g.gen_str_for_type(typ)
+	}
+	g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto')
+	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}')
+	g.type_definitions.writeln('static string indent_${str_fn_name}($styp a, int indent_count); // auto')
+	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp a, int indent_count) {')
+	g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder($info.size * 10);')
+	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("["));')
+	if sym.kind == .function {
+		g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
+	} else {
+		deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
+		g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; ++i) {')
+		if should_use_indent_func(sym.kind) && !sym_has_str_method {
+			if is_elem_ptr {
+				g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));')
+				g.auto_str_funcs.writeln('\t\tif ( 0 == a[i] ) {')
+				g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT("0"));')
+				g.auto_str_funcs.writeln('\t\t}else{')
+				g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]) );')
+				g.auto_str_funcs.writeln('\t\t}')
+			} else {
+				g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]) );')
+			}
+		} else if sym.kind in [.f32, .f64] {
+			if sym.kind == .f32 {
+				g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g32('a[i]')} );')
+			} else {
+				g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g64('a[i]')} );')
+			}
+			// g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("%g", 1, a[i]));')
+		} else if sym.kind == .string {
+			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_sq('a[i]')});')
+			// g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, a[i]));')
+		} else if sym.kind == .rune {
+			tmp_str := str_intp_rune('${elem_str_fn_name}(  $deref a[i])')
+			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);')
+			// g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("`%.*s\\000`", 2, ${elem_str_fn_name}(a[i])));')
+		} else {
+			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]));')
+		}
+	}
+	g.auto_str_funcs.writeln('\t\tif (i < ${info.size - 1}) {')
+	g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));')
+	g.auto_str_funcs.writeln('\t\t}')
+	g.auto_str_funcs.writeln('\t}')
+	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("]"));')
+	g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
+	g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
+	g.auto_str_funcs.writeln('\treturn res;')
+	g.auto_str_funcs.writeln('}')
+}
diff --git a/vlib/v/gen/c/auto_str_map.v b/vlib/v/gen/c/auto_str_map.v
new file mode 100644
index 0000000000..25935b8a80
--- /dev/null
+++ b/vlib/v/gen/c/auto_str_map.v
@@ -0,0 +1,90 @@
+module c
+
+// Copyright (c) 2019-2021 Alexander Medvednikov. 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.
+import v.ast
+
+fn (mut g Gen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) {
+	mut key_typ := info.key_type
+	mut key_sym := g.table.get_type_symbol(key_typ)
+	if mut key_sym.info is ast.Alias {
+		key_typ = key_sym.info.parent_type
+		key_sym = g.table.get_type_symbol(key_typ)
+	}
+	key_styp := g.typ(key_typ)
+	key_str_fn_name := key_styp.replace('*', '') + '_str'
+	if !key_sym.has_method('str') {
+		g.gen_str_for_type(key_typ)
+	}
+
+	mut val_typ := info.value_type
+	mut val_sym := g.table.get_type_symbol(val_typ)
+	if mut val_sym.info is ast.Alias {
+		val_typ = val_sym.info.parent_type
+		val_sym = g.table.get_type_symbol(val_typ)
+	}
+	val_styp := g.typ(val_typ)
+	elem_str_fn_name := val_styp.replace('*', '') + '_str'
+	if !val_sym.has_method('str') {
+		g.gen_str_for_type(val_typ)
+	}
+
+	g.type_definitions.writeln('static string ${str_fn_name}($styp m); // auto')
+	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp m) { return indent_${str_fn_name}(m, 0);}')
+	g.type_definitions.writeln('static string indent_${str_fn_name}($styp m, int indent_count); // auto')
+	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp m, int indent_count) { /* gen_str_for_map */')
+	g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(m.key_values.len*10);')
+	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("{"));')
+	g.auto_str_funcs.writeln('\tfor (int i = 0; i < m.key_values.len; ++i) {')
+	g.auto_str_funcs.writeln('\t\tif (!DenseArray_has_index(&m.key_values, i)) { continue; }')
+
+	if key_sym.kind == .string {
+		g.auto_str_funcs.writeln('\t\tstring key = *(string*)DenseArray_key(&m.key_values, i);')
+	} else {
+		g.auto_str_funcs.writeln('\t\t$key_styp key = *($key_styp*)DenseArray_key(&m.key_values, i);')
+	}
+	if key_sym.kind == .string {
+		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_sq('key')});')
+		// g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, key));')
+	} else if key_sym.kind == .rune {
+		tmp_str := str_intp_rune('${key_str_fn_name}(key)')
+		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);')
+		// g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("`%.*s\\000`", 2, ${key_str_fn_name}(key)));')
+	} else {
+		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${key_str_fn_name}(key));')
+	}
+	g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT(": "));')
+	if val_sym.kind == .function {
+		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}());')
+	} else if val_sym.kind == .string {
+		tmp_str := str_intp_sq('*($val_styp*)DenseArray_value(&m.key_values, i)')
+		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);')
+		// g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, *($val_styp*)DenseArray_value(&m.key_values, i)));')
+	} else if should_use_indent_func(val_sym.kind) && !val_sym.has_method('str') {
+		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, indent_${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i), indent_count));')
+	} else if val_sym.kind in [.f32, .f64] {
+		tmp_val := '*($val_styp*)DenseArray_value(&m.key_values, i)'
+		if val_sym.kind == .f32 {
+			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g32(tmp_val)});')
+		} else {
+			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g64(tmp_val)});')
+		}
+		// g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("%g", 1, *($val_styp*)DenseArray_value(&m.key_values, i)));')
+	} else if val_sym.kind == .rune {
+		tmp_str := str_intp_rune('${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i))')
+		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);')
+		// g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("`%.*s\\000`", 2, ${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i))));')
+	} else {
+		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i)));')
+	}
+	g.auto_str_funcs.writeln('\t\tif (i != m.key_values.len-1) {')
+	g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));')
+	g.auto_str_funcs.writeln('\t\t}')
+	g.auto_str_funcs.writeln('\t}')
+	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("}"));')
+	g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
+	g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
+	g.auto_str_funcs.writeln('\treturn res;')
+	g.auto_str_funcs.writeln('}')
+}
diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v
index 06cae0665c..c93605434a 100644
--- a/vlib/v/gen/c/auto_str_methods.v
+++ b/vlib/v/gen/c/auto_str_methods.v
@@ -5,6 +5,82 @@ module c
 import v.ast
 import v.util
 
+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 type_to_str(x StrIntpType) 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 data_str(x StrIntpType) 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' }
+	}
+}
+
+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'
+)
+
 fn should_use_indent_func(kind ast.Kind) bool {
 	return kind in [.struct_, .alias, .array, .array_fixed, .map, .sum_type, .interface_]
 }
@@ -40,41 +116,6 @@ fn (mut g Gen) gen_str_default(sym ast.TypeSymbol, styp string, str_fn_name stri
 	g.auto_str_funcs.writeln('}')
 }
 
-fn (g &Gen) type_to_fmt(typ ast.Type) string {
-	if typ == ast.byte_type_idx {
-		return '%hhx\\000'
-	}
-	if typ == ast.char_type_idx {
-		return '%c\\000'
-	}
-	if typ == ast.voidptr_type_idx || typ in ast.byteptr_types {
-		return '%p\\000'
-	}
-	if typ in ast.charptr_types {
-		return '%C\\000' // a C string
-	}
-	sym := g.table.get_type_symbol(typ)
-	if typ.is_ptr() && (typ.is_int_valptr() || typ.is_float_valptr()) {
-		return '%.*s\\000'
-	} else if sym.kind in [.struct_, .array, .array_fixed, .map, .bool, .enum_, .interface_,
-		.sum_type, .function, .alias] {
-		return '%.*s\\000'
-	} else if sym.kind == .string {
-		return "'%.*s\\000'"
-	} else if sym.kind in [.f32, .f64] {
-		return '%g\\000' // g removes trailing zeros unlike %f
-	} else if sym.kind == .int {
-		return '%d\\000'
-	} else if sym.kind == .u32 {
-		return '%u\\000'
-	} else if sym.kind == .u64 {
-		return '%llu\\000'
-	} else if sym.kind == .i64 {
-		return '%lld\\000'
-	}
-	return '%d\\000'
-}
-
 fn (mut g Gen) gen_str_for_type(typ ast.Type) string {
 	styp := g.typ(typ).replace('*', '')
 	mut sym := g.table.get_type_symbol(g.unwrap_generic(typ))
@@ -158,16 +199,24 @@ fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string)
 	g.auto_str_funcs.writeln('\tstring res;')
 	g.auto_str_funcs.writeln('\tif (it.state == 0) {')
 	if sym.kind == .string {
-		g.auto_str_funcs.writeln('\t\tres = _STR("\'%.*s\\000\'", 2, ${parent_str_fn_name}(*($sym.cname*)it.data));')
+		tmp_res := '${parent_str_fn_name}(*($sym.cname*)it.data)'
+		g.auto_str_funcs.writeln('\t\tres = ${str_intp_sq(tmp_res)};')
+		// g.auto_str_funcs.writeln('\t\tres = _STR("\'%.*s\\000\'", 2, ${parent_str_fn_name}(*($sym.cname*)it.data));')
 	} else if should_use_indent_func(sym.kind) && !sym_has_str_method {
 		g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(*($sym.cname*)it.data, indent_count);')
 	} else {
 		g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*($sym.cname*)it.data);')
 	}
 	g.auto_str_funcs.writeln('\t} else {')
-	g.auto_str_funcs.writeln('\t\tres = _STR("error: %.*s\\000", 2, IError_str(it.err));')
+
+	tmp_str := str_intp_sub('error: %%', 'IError_str(it.err)')
+	g.auto_str_funcs.writeln('\t\tres = $tmp_str;')
+	// g.auto_str_funcs.writeln('\t\tres = _STR("error: %.*s\\000", 2, IError_str(it.err));')
+
 	g.auto_str_funcs.writeln('\t}')
-	g.auto_str_funcs.writeln('\treturn _STR("Option(%.*s\\000)", 2, res);')
+
+	g.auto_str_funcs.writeln('\treturn ${str_intp_sub('Option(%%)', 'res')};')
+	// g.auto_str_funcs.writeln('\treturn _STR("Option(%.*s\\000)", 2, res);')
 	g.auto_str_funcs.writeln('}')
 }
 
@@ -187,223 +236,15 @@ fn (mut g Gen) gen_str_for_alias(info ast.Alias, styp string, str_fn_name string
 	g.auto_str_funcs.writeln('\tfor (int i = 0; i < indent_count; ++i) {')
 	g.auto_str_funcs.writeln('\t\tindents = string_add(indents, _SLIT("    "));')
 	g.auto_str_funcs.writeln('\t}')
-	g.auto_str_funcs.writeln('\treturn _STR("%.*s\\000${clean_type_v_type_name}(%.*s\\000)", 3, indents, ${parent_str_fn_name}(it));')
-	g.auto_str_funcs.writeln('}')
-}
 
-fn (mut g Gen) gen_str_for_array(info ast.Array, styp string, str_fn_name string) {
-	mut typ := info.elem_type
-	mut sym := g.table.get_type_symbol(info.elem_type)
-	if mut sym.info is ast.Alias {
-		typ = sym.info.parent_type
-		sym = g.table.get_type_symbol(typ)
-	}
-	field_styp := g.typ(typ)
-	is_elem_ptr := typ.is_ptr()
-	sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
-	mut elem_str_fn_name := ''
-	if sym_has_str_method {
-		elem_str_fn_name = if is_elem_ptr {
-			field_styp.replace('*', '') + '_str'
-		} else {
-			field_styp + '_str'
-		}
-		if sym.kind == .byte {
-			elem_str_fn_name = elem_str_fn_name + '_escaped'
-		}
-	} else {
-		elem_str_fn_name = styp_to_str_fn_name(field_styp)
-	}
-	if !sym_has_str_method {
-		g.gen_str_for_type(typ)
-	}
-	g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto')
-	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}')
-	g.type_definitions.writeln('static string indent_${str_fn_name}($styp a, int indent_count); // auto')
-	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp a, int indent_count) {')
-	g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(a.len * 10);')
-	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("["));')
-	g.auto_str_funcs.writeln('\tfor (int i = 0; i < a.len; ++i) {')
-	if sym.kind == .function {
-		g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
-	} else {
-		if sym.kind == .array_fixed {
-			g.auto_str_funcs.writeln('\t\t$field_styp it;')
-			g.auto_str_funcs.writeln('\t\tmemcpy(*($field_styp*)it, (byte*)array_get(a, i), sizeof($field_styp));')
-		} else {
-			g.auto_str_funcs.writeln('\t\t$field_styp it = *($field_styp*)array_get(a, i);')
-		}
-		if should_use_indent_func(sym.kind) && !sym_has_str_method {
-			if is_elem_ptr {
-				g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(*it, indent_count);')
-			} else {
-				g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(it, indent_count);')
-			}
-		} else if sym.kind in [.f32, .f64] {
-			g.auto_str_funcs.writeln('\t\tstring x = _STR("%g", 1, it);')
-		} else if sym.kind == .rune {
-			g.auto_str_funcs.writeln('\t\tstring x = _STR("`%.*s\\000`", 2, ${elem_str_fn_name}(it));')
-		} else if sym.kind == .string {
-			g.auto_str_funcs.writeln('\t\tstring x = _STR("\'%.*s\\000\'", 2, it);')
-		} else {
-			// There is a custom .str() method, so use it.
-			// NB: we need to take account of whether the user has defined
-			// `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly
-			deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
-			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));')
-			g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}( $deref it);')
-		}
-	}
-	g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, x);')
-	if g.is_autofree && typ != ast.bool_type {
-		// no need to free "true"/"false" literals
-		g.auto_str_funcs.writeln('\t\tstring_free(&x);')
-	}
-	g.auto_str_funcs.writeln('\t\tif (i < a.len-1) {')
-	g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));')
-	g.auto_str_funcs.writeln('\t\t}')
-	g.auto_str_funcs.writeln('\t}')
-	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("]"));')
-	g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
-	g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
-	g.auto_str_funcs.writeln('\treturn res;')
-	g.auto_str_funcs.writeln('}')
-}
+	g.auto_str_funcs.writeln('\treturn str_intp(3, _MOV((StrIntpData[]){
+		{_SLIT0, $c.si_s_code, {.d_s = indents }},
+		{_SLIT("${clean_type_v_type_name}("), $c.si_s_code, {.d_s = ${parent_str_fn_name}(it) }},
+		{_SLIT(")"), 0, {.d_c = 0 }} 
+	}));\n')
 
-fn (mut g Gen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_name string) {
-	mut typ := info.elem_type
-	mut sym := g.table.get_type_symbol(info.elem_type)
-	if mut sym.info is ast.Alias {
-		typ = sym.info.parent_type
-		sym = g.table.get_type_symbol(typ)
-	}
-	field_styp := g.typ(typ)
-	is_elem_ptr := typ.is_ptr()
-	sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
-	mut elem_str_fn_name := ''
-	if sym_has_str_method {
-		elem_str_fn_name = if is_elem_ptr {
-			field_styp.replace('*', '') + '_str'
-		} else {
-			field_styp + '_str'
-		}
-	} else {
-		elem_str_fn_name = styp_to_str_fn_name(field_styp)
-	}
-	if !sym.has_method('str') {
-		elem_str_fn_name = g.gen_str_for_type(typ)
-	}
-	g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto')
-	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}')
-	g.type_definitions.writeln('static string indent_${str_fn_name}($styp a, int indent_count); // auto')
-	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp a, int indent_count) {')
-	g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder($info.size * 10);')
-	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("["));')
-	if sym.kind == .function {
-		g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
-	} else {
-		deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
-		g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; ++i) {')
-		if should_use_indent_func(sym.kind) && !sym_has_str_method {
-			if is_elem_ptr {
-				g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));')
-				g.auto_str_funcs.writeln('\t\tif ( 0 == a[i] ) {')
-				g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT("0"));')
-				g.auto_str_funcs.writeln('\t\t}else{')
-				g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]) );')
-				g.auto_str_funcs.writeln('\t\t}')
-			} else {
-				g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]) );')
-			}
-		} else if sym.kind in [.f32, .f64] {
-			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("%g", 1, a[i]));')
-		} else if sym.kind == .string {
-			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, a[i]));')
-		} else if sym.kind == .rune {
-			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("`%.*s\\000`", 2, ${elem_str_fn_name}( $deref a[i])));')
-		} else {
-			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]));')
-		}
-	}
-	g.auto_str_funcs.writeln('\t\tif (i < ${info.size - 1}) {')
-	g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));')
-	g.auto_str_funcs.writeln('\t\t}')
-	g.auto_str_funcs.writeln('\t}')
-	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("]"));')
-	g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
-	g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
-	g.auto_str_funcs.writeln('\treturn res;')
-	g.auto_str_funcs.writeln('}')
-}
+	// g.auto_str_funcs.writeln('\treturn _STR("%.*s\\000${clean_type_v_type_name}(%.*s\\000)", 3, indents, ${parent_str_fn_name}(it));')
 
-fn (mut g Gen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) {
-	mut key_typ := info.key_type
-	mut key_sym := g.table.get_type_symbol(key_typ)
-	if mut key_sym.info is ast.Alias {
-		key_typ = key_sym.info.parent_type
-		key_sym = g.table.get_type_symbol(key_typ)
-	}
-	key_styp := g.typ(key_typ)
-	key_str_fn_name := key_styp.replace('*', '') + '_str'
-	if !key_sym.has_method('str') {
-		g.gen_str_for_type(key_typ)
-	}
-
-	mut val_typ := info.value_type
-	mut val_sym := g.table.get_type_symbol(val_typ)
-	if mut val_sym.info is ast.Alias {
-		val_typ = val_sym.info.parent_type
-		val_sym = g.table.get_type_symbol(val_typ)
-	}
-	val_styp := g.typ(val_typ)
-	elem_str_fn_name := val_styp.replace('*', '') + '_str'
-	if !val_sym.has_method('str') {
-		g.gen_str_for_type(val_typ)
-	}
-
-	g.type_definitions.writeln('static string ${str_fn_name}($styp m); // auto')
-	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp m) { return indent_${str_fn_name}(m, 0);}')
-	g.type_definitions.writeln('static string indent_${str_fn_name}($styp m, int indent_count); // auto')
-	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp m, int indent_count) { /* gen_str_for_map */')
-	g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(m.key_values.len*10);')
-	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("{"));')
-	g.auto_str_funcs.writeln('\tfor (int i = 0; i < m.key_values.len; ++i) {')
-	g.auto_str_funcs.writeln('\t\tif (!DenseArray_has_index(&m.key_values, i)) { continue; }')
-
-	if key_sym.kind == .string {
-		g.auto_str_funcs.writeln('\t\tstring key = *(string*)DenseArray_key(&m.key_values, i);')
-	} else {
-		g.auto_str_funcs.writeln('\t\t$key_styp key = *($key_styp*)DenseArray_key(&m.key_values, i);')
-	}
-	if key_sym.kind == .string {
-		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, key));')
-	} else if key_sym.kind == .rune {
-		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("`%.*s\\000`", 2, ${key_str_fn_name}(key)));')
-	} else {
-		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${key_str_fn_name}(key));')
-	}
-	g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT(": "));')
-	if val_sym.kind == .function {
-		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}());')
-	} else if val_sym.kind == .string {
-		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, *($val_styp*)DenseArray_value(&m.key_values, i)));')
-	} else if should_use_indent_func(val_sym.kind) && !val_sym.has_method('str') {
-		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, indent_${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i), indent_count));')
-	} else if val_sym.kind in [.f32, .f64] {
-		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("%g", 1, *($val_styp*)DenseArray_value(&m.key_values, i)));')
-	} else if val_sym.kind == .rune {
-		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("`%.*s\\000`", 2, ${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i))));')
-	} else {
-		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i)));')
-	}
-	g.auto_str_funcs.writeln('\t\tif (i != m.key_values.len-1) {')
-	g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));')
-	g.auto_str_funcs.writeln('\t\t}')
-	g.auto_str_funcs.writeln('\t}')
-	g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("}"));')
-	g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
-	g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
-	g.auto_str_funcs.writeln('\treturn res;')
 	g.auto_str_funcs.writeln('}')
 }
 
@@ -436,9 +277,18 @@ fn (mut g Gen) gen_str_for_multi_return(info ast.MultiReturn, styp string, str_f
 		if should_use_indent_func(sym.kind) && !sym_has_str_method {
 			g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}(a.arg$i));')
 		} else if sym.kind in [.f32, .f64] {
-			g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _STR("%g", 1, a.arg$i));')
+			if sym.kind == .f32 {
+				tmp_val := str_intp_g32('a.arg$i')
+				g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, $tmp_val);')
+			} else {
+				tmp_val := str_intp_g64('a.arg$i')
+				g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, $tmp_val);')
+			}
+			// g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _STR("%g", 1, a.arg$i));')
 		} else if sym.kind == .string {
-			g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, a.arg$i));')
+			tmp_str := str_intp_sq('a.arg$i')
+			g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, $tmp_str);')
+			// g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, a.arg$i));')
 		} else if sym.kind == .function {
 			g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}());')
 		} else {
@@ -457,136 +307,6 @@ fn (mut g Gen) gen_str_for_multi_return(info ast.MultiReturn, styp string, str_f
 	g.auto_str_funcs.writeln('}')
 }
 
-fn (mut g Gen) gen_str_for_struct(info ast.Struct, styp string, str_fn_name string) {
-	// TODO: short it if possible
-	// generates all definitions of substructs
-	mut fnames2strfunc := map{
-		'': ''
-	}
-	for field in info.fields {
-		sym := g.table.get_type_symbol(field.typ)
-		if !sym.has_method('str') {
-			mut typ := field.typ
-			if typ.is_ptr() {
-				typ = typ.deref()
-			}
-			field_styp := g.typ(typ)
-			field_fn_name := g.gen_str_for_type(field.typ)
-			fnames2strfunc[field_styp] = field_fn_name
-		}
-	}
-	// _str() functions should have a single argument, the indenting ones take 2:
-	g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
-	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0);}')
-	g.type_definitions.writeln('static string indent_${str_fn_name}($styp it, int indent_count); // auto')
-	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp it, int indent_count) {')
-	mut clean_struct_v_type_name := styp.replace('__', '.')
-	if clean_struct_v_type_name.contains('_T_') {
-		// TODO: this is a bit hacky. styp shouldn't be even parsed with _T_
-		// use something different than g.typ for styp
-		clean_struct_v_type_name =
-			clean_struct_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
-			'>'
-	}
-	clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name)
-	// generate ident / indent length = 4 spaces
-	g.auto_str_funcs.writeln('\tstring indents = _SLIT("");')
-	g.auto_str_funcs.writeln('\tfor (int i = 0; i < indent_count; ++i) {')
-	g.auto_str_funcs.writeln('\t\tindents = string_add(indents, _SLIT("    "));')
-	g.auto_str_funcs.writeln('\t}')
-	if info.fields.len == 0 {
-		g.auto_str_funcs.write_string('\treturn _SLIT("$clean_struct_v_type_name{}");')
-	} else {
-		g.auto_str_funcs.write_string('\treturn _STR("$clean_struct_v_type_name{\\n"')
-		for field in info.fields {
-			mut fmt := if field.typ.is_ptr() { '&' } else { '' }
-			fmt += g.type_to_fmt(field.typ)
-			g.auto_str_funcs.writeln('\t\t"%.*s\\000    $field.name: $fmt\\n"')
-		}
-		g.auto_str_funcs.write_string('\t\t"%.*s\\000}", ${2 * (info.fields.len + 1)}')
-		if info.fields.len > 0 {
-			g.auto_str_funcs.write_string(',\n\t\t')
-			for i, field in info.fields {
-				sym := g.table.get_type_symbol(field.typ)
-				has_custom_str := sym.has_method('str')
-				mut field_styp := g.typ(field.typ).replace('*', '')
-				field_styp_fn_name := if has_custom_str {
-					'${field_styp}_str'
-				} else {
-					fnames2strfunc[field_styp]
-				}
-				g.auto_str_funcs.write_string('indents, ')
-				mut func := struct_auto_str_func(sym, field.typ, field_styp_fn_name, field.name)
-				// reference types can be "nil"
-				if field.typ.is_ptr() && !(field.typ in ast.charptr_types
-					|| field.typ in ast.byteptr_types
-					|| field.typ == ast.voidptr_type_idx) {
-					g.auto_str_funcs.write_string('isnil(it.${c_name(field.name)})')
-					g.auto_str_funcs.write_string(' ? _SLIT("nil") : ')
-					// struct, floats and ints have a special case through the _str function
-					if sym.kind != .struct_ && !field.typ.is_int_valptr()
-						&& !field.typ.is_float_valptr() {
-						g.auto_str_funcs.write_string('*')
-					}
-				}
-				// handle circular ref type of struct to the struct itself
-				if styp == field_styp {
-					g.auto_str_funcs.write_string('_SLIT("<circular>")')
-				} else {
-					g.auto_str_funcs.write_string(func)
-				}
-
-				if i < info.fields.len - 1 {
-					g.auto_str_funcs.write_string(',\n\t\t')
-				}
-			}
-		}
-		g.auto_str_funcs.writeln(',')
-		g.auto_str_funcs.writeln('\t\tindents);')
-	}
-	g.auto_str_funcs.writeln('}')
-}
-
-fn struct_auto_str_func(sym &ast.TypeSymbol, field_type ast.Type, fn_name string, field_name string) string {
-	has_custom_str, expects_ptr, _ := sym.str_method_info()
-	if sym.kind == .enum_ {
-		return '${fn_name}(it.${c_name(field_name)})'
-	} else if should_use_indent_func(sym.kind) {
-		mut obj := 'it.${c_name(field_name)}'
-		if field_type.is_ptr() && !expects_ptr {
-			obj = '*$obj'
-		}
-		if has_custom_str {
-			return '${fn_name}($obj)'
-		}
-		return 'indent_${fn_name}($obj, indent_count + 1)'
-	} else if sym.kind in [.array, .array_fixed, .map, .sum_type] {
-		if has_custom_str {
-			return '${fn_name}(it.${c_name(field_name)})'
-		}
-		return 'indent_${fn_name}(it.${c_name(field_name)}, indent_count + 1)'
-	} else if sym.kind == .function {
-		return '${fn_name}()'
-	} else {
-		mut method_str := 'it.${c_name(field_name)}'
-		if sym.kind == .bool {
-			method_str += ' ? _SLIT("true") : _SLIT("false")'
-		} else if (field_type.is_int_valptr() || field_type.is_float_valptr())
-			&& field_type.is_ptr() && !expects_ptr {
-			// ptr int can be "nil", so this needs to be castet to a string
-			fmt := if sym.kind in [.f32, .f64] {
-				'%g\\000'
-			} else if sym.kind == .u64 {
-				'%lld\\000'
-			} else {
-				'%d\\000'
-			}
-			method_str = '_STR("$fmt", 2, *$method_str)'
-		}
-		return method_str
-	}
-}
-
 fn (mut g Gen) gen_str_for_enum(info ast.Enum, styp string, str_fn_name string) {
 	s := util.no_dots(styp)
 	g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
@@ -653,6 +373,38 @@ fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, str_fn_nam
 		if should_use_indent_func(subtype.kind) && !sym_has_str_method {
 			func_name = 'indent_$func_name'
 		}
+
+		//------------------------------------------
+		// str_intp
+		deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
+		if typ == ast.string_type {
+			mut val := '${func_name}(${deref}($subtype.cname*)x._$subtype.cname'
+			if should_use_indent_func(subtype.kind) && !sym_has_str_method {
+				val += ', indent_count'
+			}
+			val += ')'
+			res := 'str_intp(2, _MOV((StrIntpData[]){
+				{_SLIT("${clean_interface_v_type_name}(\'"), $c.si_s_code, {.d_s = $val}},
+				{_SLIT("\')"), 0, {.d_c = 0 }}
+			}))'
+			g.auto_str_funcs.write_string('\tif (x._typ == _${styp}_${subtype.cname}_index)')
+			g.auto_str_funcs.write_string(' return $res;')
+		} else {
+			mut val := '${func_name}(${deref}($subtype.cname*)x._$subtype.cname'
+			if should_use_indent_func(subtype.kind) && !sym_has_str_method {
+				val += ', indent_count'
+			}
+			val += ')'
+			res := 'str_intp(2, _MOV((StrIntpData[]){
+				{_SLIT("${clean_interface_v_type_name}("), $c.si_s_code, {.d_s = $val}},
+				{_SLIT(")"), 0, {.d_c = 0 }}
+			}))'
+			g.auto_str_funcs.write_string('\tif (x._typ == _${styp}_${subtype.cname}_index)')
+			g.auto_str_funcs.write_string(' return $res;\n')
+		}
+
+		//------------------------------------------
+		/*
 		deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
 		value_fmt := if typ == ast.string_type { "'%.*s\\000'" } else { '%.*s\\000' }
 
@@ -662,7 +414,9 @@ fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, str_fn_nam
 		if should_use_indent_func(subtype.kind) && !sym_has_str_method {
 			g.auto_str_funcs.write_string(', indent_count')
 		}
-		g.auto_str_funcs.writeln('));')
+		g.auto_str_funcs.writeln('));\n')
+		*/
+		//------------------------------------------
 	}
 	g.auto_str_funcs.writeln('\treturn _SLIT("unknown interface value");')
 	g.auto_str_funcs.writeln('}')
@@ -690,10 +444,6 @@ fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_
 	clean_sum_type_v_type_name = util.strip_main_name(clean_sum_type_v_type_name)
 	g.auto_str_funcs.writeln('\tswitch(x._typ) {')
 	for typ in info.variants {
-		mut value_fmt := '%.*s\\000'
-		if typ == ast.string_type {
-			value_fmt = "'$value_fmt'"
-		}
 		typ_str := g.typ(typ)
 		mut func_name := if typ_str in gen_fn_names {
 			gen_fn_names[typ_str]
@@ -706,11 +456,46 @@ fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_
 		if should_use_indent_func(sym.kind) && !sym_has_str_method {
 			func_name = 'indent_$func_name'
 		}
+
+		//------------------------------------------
+		// str_intp
+		if typ == ast.string_type {
+			mut val := '${func_name}(${deref}($typ_str*)x._$sym.cname'
+			if should_use_indent_func(sym.kind) && !sym_has_str_method {
+				val += ', indent_count'
+			}
+			val += ')'
+			res := 'str_intp(2, _MOV((StrIntpData[]){
+				{_SLIT("${clean_sum_type_v_type_name}(\'"), $c.si_s_code, {.d_s = $val}},
+				{_SLIT("\')"), 0, {.d_c = 0 }}
+			}))'
+			g.auto_str_funcs.write_string('\t\tcase $typ: return $res;')
+		} else {
+			mut val := '${func_name}(${deref}($typ_str*)x._$sym.cname'
+			if should_use_indent_func(sym.kind) && !sym_has_str_method {
+				val += ', indent_count'
+			}
+			val += ')'
+			res := 'str_intp(2, _MOV((StrIntpData[]){
+				{_SLIT("${clean_sum_type_v_type_name}("), $c.si_s_code, {.d_s = $val}},
+				{_SLIT(")"), 0, {.d_c = 0 }}
+			}))'
+			g.auto_str_funcs.write_string('\t\tcase $typ: return $res;')
+		}
+
+		//------------------------------------------
+		/*
+		mut value_fmt := '%.*s\\000'
+		if typ == ast.string_type {
+			value_fmt = "'$value_fmt'"
+		}
 		g.auto_str_funcs.write_string('\t\tcase $typ: return _STR("${clean_sum_type_v_type_name}($value_fmt)", 2, ${func_name}(${deref}($typ_str*)x._$sym.cname')
 		if should_use_indent_func(sym.kind) && !sym_has_str_method {
 			g.auto_str_funcs.write_string(', indent_count')
 		}
 		g.auto_str_funcs.writeln('));')
+		*/
+		//------------------------------------------
 	}
 	g.auto_str_funcs.writeln('\t\tdefault: return _SLIT("unknown sum type value");')
 	g.auto_str_funcs.writeln('\t}')
@@ -730,7 +515,8 @@ fn (mut g Gen) fn_decl_str(info ast.FnType) string {
 	}
 	fn_str += ')'
 	if info.func.return_type != ast.void_type {
-		fn_str += ' ${util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.func.return_type)))}'
+		x := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.func.return_type)))
+		fn_str += ' $x'
 	}
 	return fn_str
 }
diff --git a/vlib/v/gen/c/auto_str_struct.v b/vlib/v/gen/c/auto_str_struct.v
new file mode 100644
index 0000000000..0fe68bce93
--- /dev/null
+++ b/vlib/v/gen/c/auto_str_struct.v
@@ -0,0 +1,387 @@
+module c
+
+// Copyright (c) 2019-2021 Alexander Medvednikov. 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.
+import v.ast
+import v.util
+
+fn (g &Gen) type_to_fmt1(typ ast.Type) StrIntpType {
+	if typ == ast.byte_type_idx {
+		return .si_u8
+	}
+	if typ == ast.char_type_idx {
+		return .si_c
+	}
+	if typ == ast.voidptr_type_idx || typ in ast.byteptr_types {
+		return .si_p
+	}
+	if typ in ast.charptr_types {
+		return .si_s
+		// return '%C\\000' // a C string
+	}
+	sym := g.table.get_type_symbol(typ)
+	if typ.is_ptr() && (typ.is_int_valptr() || typ.is_float_valptr()) {
+		return .si_s
+	} else if sym.kind in [.struct_, .array, .array_fixed, .map, .bool, .enum_, .interface_,
+		.sum_type, .function, .alias] {
+		return .si_s
+	} else if sym.kind == .string {
+		return .si_s
+		// return "'%.*s\\000'"
+	} else if sym.kind in [.f32, .f64] {
+		if sym.kind == .f32 {
+			return .si_g32
+		}
+		return .si_g64
+	} else if sym.kind == .int {
+		return .si_i32
+	} else if sym.kind == .u32 {
+		return .si_u32
+	} else if sym.kind == .u64 {
+		return .si_u64
+	} else if sym.kind == .i64 {
+		return .si_i64
+	}
+
+	return .si_i32
+}
+
+fn (mut g Gen) gen_str_for_struct(info ast.Struct, styp string, str_fn_name string) {
+	// g.gen_str_for_struct1(info, styp, str_fn_name)
+
+	// TODO: short it if possible
+	// generates all definitions of substructs
+	mut fnames2strfunc := map{
+		'': ''
+	}
+	for field in info.fields {
+		sym := g.table.get_type_symbol(field.typ)
+		if !sym.has_method('str') {
+			mut typ := field.typ
+			if typ.is_ptr() {
+				typ = typ.deref()
+			}
+			field_styp := g.typ(typ)
+			field_fn_name := g.gen_str_for_type(field.typ)
+			fnames2strfunc[field_styp] = field_fn_name
+		}
+	}
+	// _str() functions should have a single argument, the indenting ones take 2:
+	g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
+	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0);}')
+	g.type_definitions.writeln('static string indent_${str_fn_name}($styp it, int indent_count); // auto')
+	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp it, int indent_count) {')
+	mut clean_struct_v_type_name := styp.replace('__', '.')
+	if clean_struct_v_type_name.contains('_T_') {
+		// TODO: this is a bit hacky. styp shouldn't be even parsed with _T_
+		// use something different than g.typ for styp
+		clean_struct_v_type_name =
+			clean_struct_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
+			'>'
+	}
+	clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name)
+	// generate ident / indent length = 4 spaces
+	g.auto_str_funcs.writeln('\tstring indents = _SLIT("");')
+	g.auto_str_funcs.writeln('\tfor (int i = 0; i < indent_count; ++i) {')
+	g.auto_str_funcs.writeln('\t\tindents = string_add(indents, _SLIT("    "));')
+	g.auto_str_funcs.writeln('\t}')
+	if info.fields.len == 0 {
+		g.auto_str_funcs.write_string('\treturn _SLIT("$clean_struct_v_type_name{}");')
+	} else {
+		g.auto_str_funcs.write_string('\treturn str_intp( ${info.fields.len * 4 + 3}, _MOV((StrIntpData[]){\n')
+		g.auto_str_funcs.write_string('\t\t{_SLIT("$clean_struct_v_type_name{\\n"), 0, {.d_c=0}},\n')
+
+		for i, field in info.fields {
+			mut ptr_amp := if field.typ.is_ptr() { '&' } else { '' }
+			base_fmt := g.type_to_fmt1(field.typ)
+
+			// manage prefix and quote symbol for the filed
+			mut quote_str := ''
+			mut prefix := ''
+			sym := g.table.get_type_symbol(field.typ)
+			if sym.kind == .string {
+				quote_str = "'"
+			} else if field.typ in ast.charptr_types {
+				quote_str = '\\"'
+				prefix = 'C'
+			}
+
+			// first fields doesn't need \n
+			if i == 0 {
+				g.auto_str_funcs.write_string('\t\t{_SLIT0, $si_s_code, {.d_s=indents}}, {_SLIT("    $field.name: $ptr_amp$prefix"), 0, {.d_c=0}}, ')
+			} else {
+				g.auto_str_funcs.write_string('\t\t{_SLIT("\\n"), $si_s_code, {.d_s=indents}}, {_SLIT("    $field.name: $ptr_amp$prefix"), 0, {.d_c=0}}, ')
+			}
+
+			// custom methods management
+			has_custom_str := sym.has_method('str')
+			mut field_styp := g.typ(field.typ).replace('*', '')
+			field_styp_fn_name := if has_custom_str {
+				'${field_styp}_str'
+			} else {
+				fnames2strfunc[field_styp]
+			}
+
+			// manage the fact hat with float we use always the g representation
+			if sym.kind !in [.f32, .f64] {
+				g.auto_str_funcs.write_string('{_SLIT("$quote_str"), ${int(base_fmt)}, {.${data_str(base_fmt)}=')
+			} else {
+				g_fmt := '0x' + (u32(base_fmt) | u32(0x7F) << 9).hex()
+				g.auto_str_funcs.write_string('{_SLIT("$quote_str"), $g_fmt, {.${data_str(base_fmt)}=')
+			}
+
+			mut func := struct_auto_str_func1(sym, field.typ, field_styp_fn_name, field.name)
+
+			// manage reference types can be "nil"
+			if field.typ.is_ptr() && !(field.typ in ast.charptr_types
+				|| field.typ in ast.byteptr_types
+				|| field.typ == ast.voidptr_type_idx) {
+				g.auto_str_funcs.write_string('isnil(it.${c_name(field.name)})')
+				g.auto_str_funcs.write_string(' ? _SLIT("nil") : ')
+				// struct, floats and ints have a special case through the _str function
+				if sym.kind != .struct_ && !field.typ.is_int_valptr()
+					&& !field.typ.is_float_valptr() {
+					g.auto_str_funcs.write_string('*')
+				}
+			}
+			// handle circular ref type of struct to the struct itself
+			if styp == field_styp {
+				g.auto_str_funcs.write_string('_SLIT("<circular>")')
+			} else {
+				// manage C charptr
+				if field.typ in ast.charptr_types {
+					g.auto_str_funcs.write_string('tos2((byteptr)$func)')
+				} else {
+					g.auto_str_funcs.write_string(func)
+				}
+			}
+
+			g.auto_str_funcs.write_string('}}, {_SLIT("$quote_str"), 0, {.d_c=0}},\n')
+		}
+		g.auto_str_funcs.write_string('\t\t{_SLIT("\\n"), $si_s_code, {.d_s=indents}}, {_SLIT("}"), 0, {.d_c=0}},\n')
+		g.auto_str_funcs.write_string('\t}));\n')
+	}
+	g.auto_str_funcs.writeln('}')
+}
+
+fn struct_auto_str_func1(sym &ast.TypeSymbol, field_type ast.Type, fn_name string, field_name string) string {
+	has_custom_str, expects_ptr, _ := sym.str_method_info()
+	if sym.kind == .enum_ {
+		return '${fn_name}(it.${c_name(field_name)})'
+	} else if should_use_indent_func(sym.kind) {
+		mut obj := 'it.${c_name(field_name)}'
+		if field_type.is_ptr() && !expects_ptr {
+			obj = '*$obj'
+		}
+		if has_custom_str {
+			return '${fn_name}($obj)'
+		}
+		return 'indent_${fn_name}($obj, indent_count + 1)'
+	} else if sym.kind in [.array, .array_fixed, .map, .sum_type] {
+		if has_custom_str {
+			return '${fn_name}(it.${c_name(field_name)})'
+		}
+		return 'indent_${fn_name}(it.${c_name(field_name)}, indent_count + 1)'
+	} else if sym.kind == .function {
+		return '${fn_name}()'
+	} else {
+		mut method_str := 'it.${c_name(field_name)}'
+		if sym.kind == .bool {
+			method_str += ' ? _SLIT("true") : _SLIT("false")'
+		} else if (field_type.is_int_valptr() || field_type.is_float_valptr())
+			&& field_type.is_ptr() && !expects_ptr {
+			// ptr int can be "nil", so this needs to be castet to a string
+
+			if sym.kind == .f32 {
+				return 'str_intp(1, _MOV((StrIntpData[]){
+					{_SLIT0, $si_g32_code, {.d_f32 = *$method_str }}
+				}))'
+			} else if sym.kind == .f64 {
+				return 'str_intp(1, _MOV((StrIntpData[]){
+					{_SLIT0, $si_g64_code, {.d_f64 = *$method_str }}
+				}))'
+			} else if sym.kind == .u64 {
+				fmt_type := StrIntpType.si_u64
+				return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_u64 = *$method_str }}}))'
+			}
+
+			fmt_type := StrIntpType.si_i32
+			return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_i32 = *$method_str }}}))'
+		}
+		return method_str
+	}
+}
+
+//=============================================================================
+// OLD CODE
+//=============================================================================
+/*
+fn (g &Gen) type_to_fmt(typ ast.Type) string {
+	if typ == ast.byte_type_idx {
+		return '%hhx\\000'
+	}
+	if typ == ast.char_type_idx {
+		return '%c\\000'
+	}
+	if typ == ast.voidptr_type_idx || typ in ast.byteptr_types {
+		return '%p\\000'
+	}
+	if typ in ast.charptr_types {
+		return '%C\\000' // a C string
+	}
+	sym := g.table.get_type_symbol(typ)
+	if typ.is_ptr() && (typ.is_int_valptr() || typ.is_float_valptr()) {
+		return '%.*s\\000'
+	} else if sym.kind in [.struct_, .array, .array_fixed, .map, .bool, .enum_, .interface_,
+		.sum_type, .function, .alias] {
+		return '%.*s\\000'
+	} else if sym.kind == .string {
+		return "'%.*s\\000'"
+	} else if sym.kind in [.f32, .f64] {
+		return '%g\\000' // g removes trailing zeros unlike %f
+	} else if sym.kind == .int {
+		return '%d\\000'
+	} else if sym.kind == .u32 {
+		return '%u\\000'
+	} else if sym.kind == .u64 {
+		return '%llu\\000'
+	} else if sym.kind == .i64 {
+		return '%lld\\000'
+	}
+	return '%d\\000'
+}
+
+fn (mut g Gen) gen_str_for_struct(info ast.Struct, styp string, str_fn_name string) {
+
+	//g.gen_str_for_struct1(info, styp, str_fn_name)
+
+	// TODO: short it if possible
+	// generates all definitions of substructs
+	mut fnames2strfunc := map{
+		'': ''
+	}
+	for field in info.fields {
+		sym := g.table.get_type_symbol(field.typ)
+		if !sym.has_method('str') {
+			mut typ := field.typ
+			if typ.is_ptr() {
+				typ = typ.deref()
+			}
+			field_styp := g.typ(typ)
+			field_fn_name := g.gen_str_for_type(field.typ)
+			fnames2strfunc[field_styp] = field_fn_name
+		}
+	}
+	// _str() functions should have a single argument, the indenting ones take 2:
+	g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
+	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0);}')
+	g.type_definitions.writeln('static string indent_${str_fn_name}($styp it, int indent_count); // auto')
+	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp it, int indent_count) {')
+	mut clean_struct_v_type_name := styp.replace('__', '.')
+	if clean_struct_v_type_name.contains('_T_') {
+		// TODO: this is a bit hacky. styp shouldn't be even parsed with _T_
+		// use something different than g.typ for styp
+		clean_struct_v_type_name =
+			clean_struct_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
+			'>'
+	}
+	clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name)
+	// generate ident / indent length = 4 spaces
+	g.auto_str_funcs.writeln('\tstring indents = _SLIT("");')
+	g.auto_str_funcs.writeln('\tfor (int i = 0; i < indent_count; ++i) {')
+	g.auto_str_funcs.writeln('\t\tindents = string_add(indents, _SLIT("    "));')
+	g.auto_str_funcs.writeln('\t}')
+	if info.fields.len == 0 {
+		g.auto_str_funcs.write_string('\treturn _SLIT("$clean_struct_v_type_name{}");')
+	} else {
+		g.auto_str_funcs.write_string('\treturn _STR("$clean_struct_v_type_name{\\n"')
+		for field in info.fields {
+			mut fmt := if field.typ.is_ptr() { '&' } else { '' }
+			fmt += g.type_to_fmt(field.typ)
+			g.auto_str_funcs.writeln('\t\t"%.*s\\000    $field.name: $fmt\\n"')
+		}
+		g.auto_str_funcs.write_string('\t\t"%.*s\\000}", ${2 * (info.fields.len + 1)}')
+		if info.fields.len > 0 {
+			g.auto_str_funcs.write_string(',\n\t\t')
+			for i, field in info.fields {
+				sym := g.table.get_type_symbol(field.typ)
+				has_custom_str := sym.has_method('str')
+				mut field_styp := g.typ(field.typ).replace('*', '')
+				field_styp_fn_name := if has_custom_str {
+					'${field_styp}_str'
+				} else {
+					fnames2strfunc[field_styp]
+				}
+				g.auto_str_funcs.write_string('indents, ')
+				mut func := struct_auto_str_func(sym, field.typ, field_styp_fn_name, field.name)
+				// reference types can be "nil"
+				if field.typ.is_ptr() && !(field.typ in ast.charptr_types
+					|| field.typ in ast.byteptr_types
+					|| field.typ == ast.voidptr_type_idx) {
+					g.auto_str_funcs.write_string('isnil(it.${c_name(field.name)})')
+					g.auto_str_funcs.write_string(' ? _SLIT("nil") : ')
+					// struct, floats and ints have a special case through the _str function
+					if sym.kind != .struct_ && !field.typ.is_int_valptr()
+						&& !field.typ.is_float_valptr() {
+						g.auto_str_funcs.write_string('*')
+					}
+				}
+				// handle circular ref type of struct to the struct itself
+				if styp == field_styp {
+					g.auto_str_funcs.write_string('_SLIT("<circular>")')
+				} else {
+					g.auto_str_funcs.write_string(func)
+				}
+
+				if i < info.fields.len - 1 {
+					g.auto_str_funcs.write_string(',\n\t\t')
+				}
+			}
+		}
+		g.auto_str_funcs.writeln(',')
+		g.auto_str_funcs.writeln('\t\tindents);')
+	}
+	g.auto_str_funcs.writeln('}')
+}
+
+fn struct_auto_str_func(sym &ast.TypeSymbol, field_type ast.Type, fn_name string, field_name string) string {
+	has_custom_str, expects_ptr, _ := sym.str_method_info()
+	if sym.kind == .enum_ {
+		return '${fn_name}(it.${c_name(field_name)})'
+	} else if should_use_indent_func(sym.kind) {
+		mut obj := 'it.${c_name(field_name)}'
+		if field_type.is_ptr() && !expects_ptr {
+			obj = '*$obj'
+		}
+		if has_custom_str {
+			return '${fn_name}($obj)'
+		}
+		return 'indent_${fn_name}($obj, indent_count + 1)'
+	} else if sym.kind in [.array, .array_fixed, .map, .sum_type] {
+		if has_custom_str {
+			return '${fn_name}(it.${c_name(field_name)})'
+		}
+		return 'indent_${fn_name}(it.${c_name(field_name)}, indent_count + 1)'
+	} else if sym.kind == .function {
+		return '${fn_name}()'
+	} else {
+		mut method_str := 'it.${c_name(field_name)}'
+		if sym.kind == .bool {
+			method_str += ' ? _SLIT("true") : _SLIT("false")'
+		} else if (field_type.is_int_valptr() || field_type.is_float_valptr())
+			&& field_type.is_ptr() && !expects_ptr {
+			// ptr int can be "nil", so this needs to be castet to a string
+			fmt := if sym.kind in [.f32, .f64] {
+				'%g\\000'
+			} else if sym.kind == .u64 {
+				'%lld\\000'
+			} else {
+				'%d\\000'
+			}
+			method_str = '_STR("$fmt", 2, *$method_str)'
+		}
+		return method_str
+	}
+}
+*/
diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v
index 5921517bfa..4a35f6b807 100644
--- a/vlib/v/gen/c/cgen.v
+++ b/vlib/v/gen/c/cgen.v
@@ -408,8 +408,8 @@ pub fn (mut g Gen) init() {
 		} else {
 			g.cheaders.writeln(c_headers)
 		}
-		g.definitions.writeln('string _STR(const char*, int, ...);')
-		g.definitions.writeln('string _STR_TMP(const char*, ...);')
+		// g.definitions.writeln('string _STR(const char*, int, ...);')
+		// g.definitions.writeln('string _STR_TMP(const char*, ...);')
 	}
 	if g.pref.os == .ios {
 		g.cheaders.writeln('#define __TARGET_IOS__ 1')
@@ -4425,7 +4425,7 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
 		g.expr(channels[i])
 		g.write(')')
 	}
-	g.writeln('}));')
+	g.writeln('}));\n')
 	directions_array := g.new_tmp_var()
 	g.write('Array_sync__Direction $directions_array = new_array_from_c_array($n_channels, $n_channels, sizeof(sync__Direction), _MOV((sync__Direction[$n_channels]){')
 	for i in 0 .. n_channels {
@@ -4438,7 +4438,7 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
 			g.write('sync__Direction_pop')
 		}
 	}
-	g.writeln('}));')
+	g.writeln('}));\n')
 	objs_array := g.new_tmp_var()
 	g.write('Array_voidptr $objs_array = new_array_from_c_array($n_channels, $n_channels, sizeof(voidptr), _MOV((voidptr[$n_channels]){')
 	for i in 0 .. n_channels {
@@ -4453,7 +4453,7 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
 			g.write(tmp_objs[i])
 		}
 	}
-	g.writeln('}));')
+	g.writeln('}));\n')
 	select_result := g.new_tmp_var()
 	g.write('int $select_result = sync__channel_select(&/*arr*/$chan_array, $directions_array, &/*arr*/$objs_array, ')
 	if has_timeout {
@@ -6370,7 +6370,7 @@ fn (mut g Gen) as_cast(node ast.AsCast) {
 
 fn (g Gen) as_cast_name_table() string {
 	if g.as_cast_type_names.len == 0 {
-		return 'new_array_from_c_array(1, 1, sizeof(VCastTypeIndexName), _MOV((VCastTypeIndexName[1]){(VCastTypeIndexName){.tindex = 0,.tname = _SLIT("unknown")}}));'
+		return 'new_array_from_c_array(1, 1, sizeof(VCastTypeIndexName), _MOV((VCastTypeIndexName[1]){(VCastTypeIndexName){.tindex = 0,.tname = _SLIT("unknown")}}));\n'
 	}
 	mut name_ast := strings.new_builder(1024)
 	casts_len := g.as_cast_type_names.len + 1
@@ -6379,7 +6379,7 @@ fn (g Gen) as_cast_name_table() string {
 	for key, value in g.as_cast_type_names {
 		name_ast.writeln('\t\t, (VCastTypeIndexName){.tindex = $key, .tname = _SLIT("$value")}')
 	}
-	name_ast.writeln('\t}));')
+	name_ast.writeln('\t}));\n')
 	return name_ast.str()
 }
 
diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v
index 7a1a1271d7..6746e67b87 100644
--- a/vlib/v/gen/c/cheaders.v
+++ b/vlib/v/gen/c/cheaders.v
@@ -47,6 +47,8 @@ static inline void __sort_ptr(uintptr_t a[], bool b[], int l)
 	}
 }
 '
+	c_str_fn_defs   = '' // NO _STR() test
+	/*
 	c_str_fn_defs = '
 void _STR_PRINT_ARG(const char *fmt, char** refbufp, int *nbytes, int *memsize, int guess, ...) {
 	va_list args;
@@ -168,6 +170,7 @@ string _STR_TMP(const char *fmt, ...) {
 } // endof _STR_TMP
 
 '
+	*/
 	c_common_macros = '
 #define EMPTY_VARG_INITIALIZATION 0
 #define EMPTY_STRUCT_DECLARATION
@@ -450,8 +453,11 @@ static inline uint64_t wy2u0k(uint64_t r, uint64_t k){ _wymum(&r,&k); return k;
 '
 	c_helper_macros = '//============================== HELPER C MACROS =============================*/
 //#define tos4(s, slen) ((string){.str=(s), .len=(slen)})
+// _SLIT0 is used as NULL string for literal arguments
 // `"" s` is used to enforce a string literal argument
+#define _SLIT0 (string){.len=0} 
 #define _SLIT(s) ((string){.str=(byteptr)("" s), .len=(sizeof(s)-1), .is_lit=1})
+//#define _SLIT(s) ((string){.str=(byteptr)("" s), .len=(sizeof(s)-1), .is_lit=1})
 // take the address of an rvalue
 #define ADDR(type, expr) (&((type[]){expr}[0]))
 // copy something to the heap
diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v
index 461822661e..5cb1965e8f 100644
--- a/vlib/v/gen/c/comptime.v
+++ b/vlib/v/gen/c/comptime.v
@@ -418,7 +418,7 @@ fn (mut g Gen) comp_for(node ast.CompFor) {
 				attrs := cgen_attrs(method.attrs)
 				g.writeln(
 					'\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
-					attrs.join(', ') + '}));')
+					attrs.join(', ') + '}));\n')
 			}
 			if method.params.len < 2 {
 				// 0 or 1 (the receiver) args
@@ -435,7 +435,7 @@ fn (mut g Gen) comp_for(node ast.CompFor) {
 					}
 					g.comptime_var_type_map['${node.val_var}.args[$j].typ'] = typ
 				}
-				g.writeln('}));')
+				g.writeln('}));\n')
 			}
 			mut sig := 'anon_fn_'
 			// skip the first (receiver) arg
@@ -492,7 +492,7 @@ fn (mut g Gen) comp_for(node ast.CompFor) {
 					attrs := cgen_attrs(field.attrs)
 					g.writeln(
 						'\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
-						attrs.join(', ') + '}));')
+						attrs.join(', ') + '}));\n')
 				}
 				// field_sym := g.table.get_type_symbol(field.typ)
 				// g.writeln('\t${node.val_var}.typ = _SLIT("$field_sym.name");')
diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v
index b6a36680c1..cbdd41fed9 100644
--- a/vlib/v/gen/c/json.v
+++ b/vlib/v/gen/c/json.v
@@ -176,14 +176,14 @@ fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc s
 			}
 		}
 		if field_sym.kind == .enum_ {
-			enc.writeln('\tcJSON_AddItemToObject(o, "$name", json__encode_u64(val.${c_name(field.name)}));')
+			enc.writeln('\tcJSON_AddItemToObject(o, "$name", json__encode_u64(val.${c_name(field.name)}));\n')
 		} else {
 			if field_sym.name == 'time.Time' {
 				// time struct requires special treatment
 				// it has to be encoded as a unix timestamp number
 				enc.writeln('\tcJSON_AddItemToObject(o, "$name", json__encode_u64(val.${c_name(field.name)}.v_unix));')
 			} else {
-				enc.writeln('\tcJSON_AddItemToObject(o, "$name", ${enc_name}(val.${c_name(field.name)}));')
+				enc.writeln('\tcJSON_AddItemToObject(o, "$name", ${enc_name}(val.${c_name(field.name)}));\n')
 			}
 		}
 	}
diff --git a/vlib/v/gen/c/sql.v b/vlib/v/gen/c/sql.v
index 5e383b566a..fd9b73b363 100644
--- a/vlib/v/gen/c/sql.v
+++ b/vlib/v/gen/c/sql.v
@@ -368,7 +368,7 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_
 			}
 		}
 		if node.is_array {
-			g.writeln('\t array_push((array*)&${tmp}_array, _MOV(($elem_type_str[]){ $tmp }));')
+			g.writeln('\t array_push((array*)&${tmp}_array, _MOV(($elem_type_str[]){ $tmp }));\n')
 		}
 		g.writeln('}')
 		g.writeln('sqlite3_finalize($g.sql_stmt_name);')
@@ -678,7 +678,7 @@ fn (mut g Gen) mysql_select_expr(node ast.SqlExpr, sub bool, line string, typ Sq
 			}
 		}
 		if node.is_array {
-			g.writeln('\t array_push((array*)&${tmp}_array, _MOV(($elem_type_str[]) { $tmp }));')
+			g.writeln('\t array_push((array*)&${tmp}_array, _MOV(($elem_type_str[]) { $tmp }));\n')
 			g.writeln('\t $fields = mysql_fetch_row($res);')
 			g.writeln('}')
 		}
@@ -699,7 +699,9 @@ fn (mut g Gen) mysql_create_table(node ast.SqlStmtLine, typ SqlType, db_expr ast
 	g.write('Option_mysql__Result $tmp = mysql__Connection_query(&')
 	g.expr(db_expr)
 	g.writeln(', _SLIT("$create_string"));')
-	g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }')
+
+	tmp_str := 'str_intp(1, _MOV((StrIntpData[]){_SLIT("Something went wrong: "), $si_s_code ,{.d_s=IError_str(err)}}))'
+	g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln($tmp_str); }')
 }
 
 fn (mut g Gen) mysql_drop_table(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
@@ -710,7 +712,9 @@ fn (mut g Gen) mysql_drop_table(node ast.SqlStmtLine, typ SqlType, db_expr ast.E
 	g.write('Option_mysql__Result $tmp = mysql__Connection_query(&')
 	g.expr(db_expr)
 	g.writeln(', _SLIT("$drop_string"));')
-	g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }')
+
+	tmp_str := 'str_intp(1, _MOV((StrIntpData[]){_SLIT("Something went wrong: "), $si_s_code ,{.d_s=IError_str(err)}}))'
+	g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln($tmp_str); }')
 }
 
 fn (mut g Gen) mysql_bind(val string, typ ast.Type) {
@@ -857,7 +861,10 @@ fn (mut g Gen) psql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
 
 				res := g.new_tmp_var()
 				g.writeln('Option_pg__Row $res = pg__DB_exec_one($db_name, _SLIT("SELECT LASTVAL();"));')
-				g.writeln('if (${res}.state != 0) { IError err = ${res}.err; eprintln(_STR("\\000%.*s", 2, IError_str(err))); }')
+
+				tmp_str := 'str_intp(1, _MOV((StrIntpData[]){_SLIT0, $si_s_code ,{.d_s=IError_str(err)}}))'
+				g.writeln('if (${res}.state != 0) { IError err = ${res}.err; eprintln($tmp_str); }')
+
 				g.sql_buf = strings.new_builder(100)
 				g.sql_bind('string_int((*(string*)array_get((*(pg__Row*)${res}.data).vals, 0)))',
 					'', ast.int_type, typ)
@@ -897,7 +904,10 @@ fn (mut g Gen) psql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
 	if arr_stmt.len > 0 {
 		res := g.new_tmp_var()
 		g.writeln('Option_pg__Row $res = pg__DB_exec_one($db_name, _SLIT("SELECT LASTVAL();"));')
-		g.writeln('if (${res}.state != 0) { IError err = ${res}.err; eprintln(_STR("\\000%.*s", 2, IError_str(err))); }')
+
+		tmp_str := 'str_intp(1, _MOV((StrIntpData[]){_SLIT0, $si_s_code ,{.d_s=IError_str(err)}}))'
+		g.writeln('if (${res}.state != 0) { IError err = ${res}.err; eprintln($tmp_str); }')
+
 		id_name := g.new_tmp_var()
 		g.writeln('int $id_name = string_int((*(string*)array_get((*(pg__Row*)${res}.data).vals, 0)));')
 		g.sql_arr_stmt(arr_stmt, arr_fkeys, arr_field_name, id_name, db_expr)
@@ -928,7 +938,9 @@ fn (mut g Gen) psql_select_expr(node ast.SqlExpr, sub bool, line string, typ Sql
 
 	res := g.new_tmp_var()
 	g.writeln('Option_Array_pg__Row $res = pg__DB_exec($db_name, $g.sql_stmt_name);')
-	g.writeln('if (${res}.state != 0) { IError err = ${res}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }')
+
+	tmp_str := 'str_intp(1, _MOV((StrIntpData[]){_SLIT("Something went wrong: "), $si_s_code ,{.d_s=IError_str(err)}}))'
+	g.writeln('if (${res}.state != 0) { IError err = ${res}.err; eprintln($tmp_str); }')
 
 	rows := g.new_tmp_var()
 
@@ -1025,7 +1037,7 @@ fn (mut g Gen) psql_select_expr(node ast.SqlExpr, sub bool, line string, typ Sql
 			}
 		}
 		if node.is_array {
-			g.writeln('\t array_push((array*)&${tmp}_array, _MOV(($elem_type_str[]) { $tmp }));')
+			g.writeln('\t array_push((array*)&${tmp}_array, _MOV(($elem_type_str[]) { $tmp }));\n')
 			g.writeln('}')
 		}
 		g.writeln('string_free(&$g.sql_stmt_name);')
@@ -1044,7 +1056,9 @@ fn (mut g Gen) psql_create_table(node ast.SqlStmtLine, typ SqlType, db_expr ast.
 	g.write('Option_Array_pg__Row $tmp = pg__DB_exec(')
 	g.expr(db_expr)
 	g.writeln(', _SLIT("$create_string"));')
-	g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }')
+
+	tmp_str := 'str_intp(1, _MOV((StrIntpData[]){_SLIT("Something went wrong: "), $si_s_code ,{.d_s=IError_str(err)}}))'
+	g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln($tmp_str); }')
 }
 
 fn (mut g Gen) psql_drop_table(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
@@ -1056,7 +1070,9 @@ fn (mut g Gen) psql_drop_table(node ast.SqlStmtLine, typ SqlType, db_expr ast.Ex
 	g.write('Option_Array_pg__Row $tmp = pg__DB_exec(')
 	g.expr(db_expr)
 	g.writeln(', _SLIT("$drop_string"));')
-	g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }')
+
+	tmp_str := 'str_intp(1, _MOV((StrIntpData[]){_SLIT("Something went wrong: "), $si_s_code ,{.d_s=IError_str(err)}}))'
+	g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln($tmp_str); }')
 }
 
 fn (mut g Gen) psql_get_table_type(typ ast.Type) string {
diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v
index 0a8b0976e5..7d646800b3 100644
--- a/vlib/v/gen/c/str.v
+++ b/vlib/v/gen/c/str.v
@@ -74,6 +74,7 @@ fn (mut g Gen) string_inter_literal_sb_optimized(call_expr ast.CallExpr) {
 	return
 }
 
+/*
 fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
 	g.write('_STR("')
 	// Build the string with %
@@ -205,6 +206,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
 	}
 	g.write(')')
 }
+*/
 
 fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
 	is_shared := etype.has_flag(.shared_f)
@@ -255,7 +257,8 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
 		is_var_mut := expr.is_auto_deref_var()
 		str_fn_name := g.gen_str_for_type(typ)
 		if is_ptr && !is_var_mut {
-			g.write('_STR("&%.*s\\000", 2, ')
+			g.write('str_intp(1, _MOV((StrIntpData[]){_SLIT("&"), $si_s_code ,{.d_s=')
+			// g.write('_STR("&%.*s\\000", 2, ')
 		}
 		g.write('${str_fn_name}(')
 		if str_method_expects_ptr && !is_ptr {
@@ -275,7 +278,8 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
 		}
 		g.write(')')
 		if is_ptr && !is_var_mut {
-			g.write(')')
+			g.write('}}))')
+			// g.write(')')
 		}
 	} else {
 		str_fn_name := g.gen_str_for_type(typ)
diff --git a/vlib/v/gen/c/str_intp.v b/vlib/v/gen/c/str_intp.v
new file mode 100644
index 0000000000..d8b720ba80
--- /dev/null
+++ b/vlib/v/gen/c/str_intp.v
@@ -0,0 +1,239 @@
+/*
+str_intp.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 c
+
+import v.ast
+import v.util
+import strings
+
+fn (mut g Gen) str_format(node ast.StringInterLiteral, i int) (u64, string) {
+	mut base := 0 // numeric base
+	mut upper_case := false // set upercase for the result string
+	mut typ := g.unwrap_generic(node.expr_types[i])
+	sym := g.table.get_type_symbol(typ)
+	if sym.kind == .alias {
+		typ = (sym.info as ast.Alias).parent_type
+	}
+	mut remove_tail_zeros := false
+	fspec := node.fmts[i]
+	mut fmt_type := StrIntpType{}
+
+	// upper cases
+	if (fspec - `A`) <= (`Z` - `A`) {
+		upper_case = true
+	}
+
+	if fspec in [`s`, `S`] {
+		/*
+		if node.fwidths[i] == 0 {
+			fmt_type = .si_s
+		} else {
+			fmt_type = .si_s
+		}
+		*/
+		fmt_type = .si_s
+	} else if typ.is_float() {
+		if fspec in [`g`, `G`] {
+			match typ {
+				ast.f32_type { fmt_type = .si_g32 }
+				// ast.f64_type { fmt_type = .si_g64 }
+				else { fmt_type = .si_g64 }
+			}
+			remove_tail_zeros = true
+		} else if fspec in [`e`, `E`] {
+			match typ {
+				ast.f32_type { fmt_type = .si_e32 }
+				// ast.f64_type { fmt_type = .si_e64 }
+				else { fmt_type = .si_e64 }
+			}
+		} else if fspec in [`f`, `F`] {
+			match typ {
+				ast.f32_type { fmt_type = .si_f32 }
+				// ast.f64_type { fmt_type = .si_f64 }
+				else { fmt_type = .si_f64 }
+			}
+		}
+	} else if typ.is_pointer() {
+		if fspec in [`x`, `X`] {
+			base = 16 - 2 // our base start from 2
+		}
+		if fspec in [`p`, `x`, `X`] {
+			fmt_type = .si_p
+		} else {
+			fmt_type = .si_vp
+		}
+	} else if typ.is_int() {
+		if fspec in [`x`, `X`] {
+			base = 16 - 2 // our base start from 2
+		}
+		// if fspec in [`o`] {
+		if fspec == `o` {
+			base = 8 - 2 // our base start from 2
+		}
+		if fspec == `c` {
+			fmt_type = .si_c
+		} else {
+			match typ {
+				ast.i8_type { fmt_type = .si_i8 }
+				ast.byte_type { fmt_type = .si_u8 }
+				ast.i16_type { fmt_type = .si_i16 }
+				ast.u16_type { fmt_type = .si_u16 }
+				ast.i64_type { fmt_type = .si_i64 }
+				ast.u64_type { fmt_type = .si_u64 }
+				ast.u32_type { fmt_type = .si_u32 }
+				else { fmt_type = .si_i32 }
+			}
+		}
+	} else {
+		// TODO: better check this case
+		fmt_type = .si_p
+	}
+
+	/*
+	// pad filling 64bit format
+	mut pad_ch := u8(0)
+	if node.fills[i] {
+		pad_ch = u8(`0`)
+	}
+	res := get_str_intp_u64_format(fmt_type, node.fwidths[i], node.precisions[i], remove_tail_zeros, node.pluss[i], pad_ch, base, upper_case)
+	*/
+
+	// pad filling 32bit format
+	mut pad_ch := 0
+	if node.fills[i] {
+		pad_ch = 1
+	}
+	res := get_str_intp_u32_format(fmt_type, node.fwidths[i], node.precisions[i], remove_tail_zeros,
+		node.pluss[i], pad_ch, base, upper_case)
+	//
+	return res, fmt_type.str()
+}
+
+fn (mut g Gen) str_val(node ast.StringInterLiteral, i int) string {
+	tmp_out := g.out
+	g.out = strings.new_builder(8)
+
+	expr := node.exprs[i]
+
+	typ := g.unwrap_generic(node.expr_types[i])
+	if typ == ast.string_type {
+		if g.inside_vweb_tmpl {
+			g.write('vweb__filter(')
+			if expr.is_auto_deref_var() {
+				g.write('*')
+			}
+			g.expr(expr)
+			g.write(')')
+		} else {
+			if expr.is_auto_deref_var() {
+				g.write('*')
+			}
+			g.expr(expr)
+		}
+	} else if node.fmts[i] == `s` || typ.has_flag(.variadic) {
+		g.gen_expr_to_string(expr, typ)
+	} else if typ.is_number() || typ.is_pointer() || node.fmts[i] == `d` {
+		if typ.is_signed() && node.fmts[i] in [`x`, `X`, `o`] {
+			// convert to unsigned first befors C's integer propagation strikes
+			if typ == ast.i8_type {
+				g.write('(byte)(')
+			} else if typ == ast.i16_type {
+				g.write('(u16)(')
+			} else if typ == ast.int_type {
+				g.write('(u32)(')
+			} else {
+				g.write('(u64)(')
+			}
+			if expr.is_auto_deref_var() {
+				g.write('*')
+			}
+			g.expr(expr)
+			g.write(')')
+		} else {
+			if expr.is_auto_deref_var() {
+				g.write('*')
+			}
+			g.expr(expr)
+		}
+	} else {
+		if expr.is_auto_deref_var() {
+			g.write('*')
+		}
+		g.expr(expr)
+	}
+
+	/*
+	if node.fmts[i] in [`s`, `S`] && node.fwidths[i] != 0 {
+		g.write(', ${node.fwidths[i]}')
+	}
+	if i < node.exprs.len - 1 {
+		g.write(', ')
+	}
+	*/
+
+	tmp_res := g.out.str()
+	g.out = tmp_out
+
+	return tmp_res
+}
+
+fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
+	// fn (mut g Gen) str_int2(node ast.StringInterLiteral) {
+	mut data := []StrIntpCgenData{}
+	for i, val in node.vals {
+		// mut escaped_val := val.replace_each(['%', '%%'])
+		// escaped_val = util.smart_quote(escaped_val, false)
+		escaped_val := util.smart_quote(val, false)
+
+		if i >= node.exprs.len {
+			// last part of the string without data, manage it with .no_str
+			data << StrIntpCgenData{
+				str: escaped_val
+				fmt: '0' // no_str
+				d: '{ .d_c = 0 }'
+			}
+			break
+		}
+
+		ft_u64, ft_str := g.str_format(node, i)
+		ft_data := g.str_val(node, i)
+
+		// for pointers we need a void* cast
+		if unsafe { ft_str.str[0] } == `p` {
+			data << StrIntpCgenData{
+				str: escaped_val
+				fmt: '0x' + ft_u64.hex()
+				d: '{.d_$ft_str = (void*)(${ft_data.trim(' ,')})}'
+			}
+			continue
+		}
+		data << StrIntpCgenData{
+			str: escaped_val
+			fmt: '0x' + ft_u64.hex()
+			d: '{.d_$ft_str = ${ft_data.trim(' ,')}}'
+		}
+	}
+
+	// write struct
+	g.write(' str_intp($data.len, ')
+	g.write('_MOV((StrIntpData[]){')
+	for i, item in data {
+		if item.str.len > 0 {
+			g.write('{_SLIT("$item.str"), $item.fmt, $item.d}')
+		} else {
+			g.write('{_SLIT0, $item.fmt, $item.d}')
+		}
+
+		if i < (data.len - 1) {
+			g.write(', ')
+		}
+	}
+	g.write('})) ')
+}
diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v
index de8baa1164..11f82df8c2 100644
--- a/vlib/v/markused/markused.v
+++ b/vlib/v/markused/markused.v
@@ -16,6 +16,8 @@ pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.F
 	mut all_fn_root_names := [
 		'main.main',
 		'__new_array',
+		'str_intp',
+		'format_sb',
 		'__new_array_with_default',
 		'__new_array_with_array_default',
 		'v_realloc' /* needed for _STR */,
diff --git a/vlib/v/tests/string_interpolation_test.v b/vlib/v/tests/string_interpolation_test.v
index fc399d8e58..88946f8570 100644
--- a/vlib/v/tests/string_interpolation_test.v
+++ b/vlib/v/tests/string_interpolation_test.v
@@ -86,19 +86,19 @@ fn test_interpolation_string_prefix_expr() {
 }
 
 fn test_inttypes_string_interpolation() {
-	c := i8(-103)
-	uc := byte(217)
-	uc2 := byte(13)
-	s := i16(-23456)
-	us := u16(54321)
-	i := -1622999040
-	ui := u32(3421958087)
+	c := i8(-103) // -0x67
+	uc := byte(217) // 0xD9
+	uc2 := byte(13) // 0x0D
+	s := i16(-23456) // -0x5BA0
+	us := u16(54321) // 0xD431
+	i := -1622999040 // -0x60BD 0000
+	ui := u32(3421958087) // 0xCBF6 EFC7
 	vp := voidptr(ui)
 	mut bp := byteptr(0)
 	$if x64 {
-		bp = byteptr(15541149836)
+		bp = byteptr(15541149836) // 0x3 9E53 208C
 	} $else {
-		bp = byteptr(3541149836)
+		bp = byteptr(3541149836) // 0xD311 A88C
 	}
 	l := i64(-7694555558525237396)
 	ul := u64(17234006112912956370)
@@ -108,10 +108,10 @@ fn test_inttypes_string_interpolation() {
 	assert '>${s:11}:${us:-13}<' == '>     -23456:54321        <'
 	assert '0x${ul:-19x}:${l:22d}' == '0xef2b7d4001165bd2   :  -7694555558525237396'
 	assert '${c:5}${uc:-7}x' == ' -103217    x'
-	assert '${c:x}:${uc:x}:${uc2:02X}' == '99:d9:0D'
-	assert '${s:X}:${us:x}:${u16(uc):04x}' == 'A460:d431:00d9'
-	assert '${i:x}:${ui:X}:${int(s):x}' == '9f430000:CBF6EFC7:ffffa460'
-	assert '${l:x}:${ul:X}' == '9537727cad98876c:EF2B7D4001165BD2'
+	assert '${c:x}:${uc:x}:${uc2:02X}' == '-67:d9:0D'
+	assert '${s:X}:${us:x}:${u16(uc):04x}' == '-5BA0:d431:00d9'
+	assert '${i:x}:${ui:X}:${int(s):x}' == '-60bd0000:CBF6EFC7:-5ba0'
+	assert '${l:x}:${ul:X}' == '-6ac88d8352677894:EF2B7D4001165BD2'
 	// default pointer format is platform dependent, so try a few
 	eprintln("platform pointer format: '${vp:p}:$bp'")
 	$if x64 {