fn test_simple_string_interpolation() { a := 'Hello' b := 'World' res := '${a} ${b}' assert res == 'Hello World' } fn test_mixed_string_interpolation() { num := 7 str := 'abc' s1 := 'number=${num}' assert s1 == 'number=7' s2 := 'string=${str}' assert s2 == 'string=abc' s3 := 'a: ${num} | b: ${str}' assert s3 == 'a: 7 | b: abc' } fn test_formatted_string_interpolation() { x := 'abc' axb := 'a:${x}:b' assert axb == 'a:abc:b' x_10 := 'a:${x:10s}:b' x10_ := 'a:${x:-10s}:b' assert x_10 == 'a: abc:b' assert x10_ == 'a:abc :b' i := 23 si_right := '${i:10d}' si__left := '${i:-10d}' assert si_right == ' 23' assert si__left == '23 ' } fn test_dynamic_format_widths() { width := 10 left_width := -10 zero_width := 5 sign_width := 6 name := 'abc' num := 42 assert '>${name:(width)}<' == '> abc<' assert '>${name:(left_width)}<' == '>abc <' assert '>${name:(-width)}<' == '>abc <' assert '${num:0(zero_width)d}' == '00042' assert '${num:+(sign_width)d}' == ' +42' } fn test_dynamic_format_precision() { width := 8 precision := 3 value := 12.34567 assert '>${value:(width).(precision)f}<' == '> 12.346<' } fn test_escape_dollar_in_string() { i := 42 assert '(${i})' == '(42)' assert '(\$i)'.contains('i') && !'(\$i)'.contains('42') assert !'(\\${i})'.contains('i') && '(\\${i})'.contains('42') && '(\\${i})'.contains('\\') assert '(\\\$i)'.contains('i') && !'(\\\$i)'.contains('42') && '(\\${i})'.contains('\\') assert !'(\\\\${i})'.contains('i') && '(\\\\${i})'.contains('42') && '(\\\\${i})'.contains('\\\\') assert '(${i})' == '(42)' assert '(\${i})'.contains('i') && !'(\${i})'.contains('42') assert !'(\\${i})'.contains('i') && '(\\${i})'.contains('42') && '(\\${i})'.contains('\\') assert '(\\\${i})'.contains('i') && !'(\\\${i})'.contains('42') && '(\\${i})'.contains('\\') assert !'(\\\\${i})'.contains('i') && '(\\\\${i})'.contains('42') && '(\\\\${i})'.contains('\\\\') assert i == 42 } fn test_dollar_sign_is_literal_without_braces() { b := 10 text := 'a$b' assert text == 'a$b' assert b == 10 } fn test_implicit_str() { i := 42 assert 'int ${i}' == 'int 42' assert '${i}' == '42' check := '${i}' == '42' assert check text := '${i}' + '42' assert text == '4242' } fn test_string_interpolation_percent_escaping() { test := 'hello' hello := 'world' x := '%.*s${hello}${test} |${hello:-30s}|' assert x == '%.*sworldhello |world |' } fn test_string_interpolation_string_prefix() { // `r`, `c` and `js` are also used as a string prefix. r := 'r' rr := '${r}${r}' assert rr == 'rr' c := 'c' cc := '${c}${c}' assert cc == 'cc' js := 'js' jsjs := '${js}${js}' assert jsjs == 'jsjs' } fn test_interpolation_string_prefix_expr() { r := 1 c := 2 js := 1 assert '>${3 + r}<' == '>4<' assert '${r == js} ${js}' == 'true 1' assert '>${js + c} ${js + r == c}<' == '>3 true<' } fn test_inttypes_string_interpolation() { c := i8(-103) // -0x67 uc := u8(217) // 0xD9 uc2 := u8(13) // 0x0D s := i16(-23456) // -0x5BA0 us := u16(54321) // 0xD431 i := -1622999040 // -0x60BD 0000 ui := u32(3421958087) // 0xCBF6 EFC7 vp := voidptr(ui) mut bp := &u8(unsafe { nil }) unsafe { $if x64 { bp = &u8(15541149836) // 0x3 9E53 208C } $else { bp = &u8(3541149836) // 0xD311 A88C } } l := i64(-7694555558525237396) ul := u64(17234006112912956370) assert '${s} ${us}' == '-23456 54321' assert '${ui} ${i}' == '3421958087 -1622999040' assert '${l} ${ul}' == '-7694555558525237396 17234006112912956370' 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}' == '-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 { assert '${vp:p}:${bp}' == '0xcbf6efc7:0x39e53208c' || '${vp:p}:${bp}' == 'CBF6EFC7:39E53208C' || '${vp:p}:${bp}' == 'cbf6efc7:39e53208c' || '${vp:p}:${bp}' == '00000000CBF6EFC7:000000039E53208C' } $else { assert '${vp:p}:${bp}' == 'CBF6EFC7:D311A88C' || '${vp:p}:${bp}' == 'cbf6efc7:d311a88c' || '${vp:p}:${bp}' == '0xcbf6efc7:0xd311a88c' } } fn int_ref_mut_string(mut n &int) string { return '${n}' } fn int_ref_string(n &int) string { return '${n}' } fn test_int_ref_string_interpolation() { mut count := 10 count_ref := &count assert int_ref_mut_string(mut &count) == '10' assert int_ref_string(&count) == '10' assert '${count_ref}' == '10' assert '${&count}' == '10' assert '${count_ref:x}' == 'a' } fn test_utf8_string_interpolation() { a := 'à-côté' st := 'Sträßle' m := '10€' assert '${a} ${st} ${m}' == 'à-côté Sträßle 10€' zz := '>${a:10}< >${st:-8}< >${m:5}<-' zz_expected := '> à-côté< >Sträßle < > 10€<-' eprintln(' zz: ${zz}') eprintln('zz_expected: ${zz_expected}') assert zz == zz_expected // e := '\u20AC' // Eurosign doesn' work with MSVC and tcc e := '€' assert '100.00 ${e}' == '100.00 €' m2 := 'Москва́' // cyrillic а́: combination of U+0430 and U+0301, UTF-8: d0 b0 cc 81 d := 'Antonín Dvořák' // latin á: U+00E1, UTF-8: c3 a1 assert ':${m2:7}:${d:-15}:' == ': Москва́:Antonín Dvořák :' g := 'Πελοπόννησος' assert '>${g:-13}<' == '>Πελοπόννησος <' } fn test_utf8_string_interpolation_uses_grapheme_clusters() { assert '>${'\u006E\u0303':10}<' == '> ñ<' assert '>${'\U0001F3F3\uFE0F\u200D\U0001F308':10}<' == '> 🏳️‍🌈<' assert '>${'ห์':10}<' == '> ห์<' assert '>${'ปีเตอร์':10}<' == '> ปีเตอร์<' assert '>${'👩🏽‍💻':10}<' == '> 👩🏽‍💻<' } struct Sss { v1 int v2 f64 } fn (s Sss) str() string { return '[${s.v1}, ${s.v2:.3f}]' } fn test_string_interpolation_str_evaluation() { mut x := Sss{17, 13.455893} assert '${x}' == '[17, 13.456]' } fn test_string_interpolation_with_negative_format_width_should_compile_and_run_without_segfaulting() { // discovered during debugging VLS i := 3 input := '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' eprintln('---------------------------------------------------------------------------------------------') eprintln('+60 ${i:10} | input.len: ${input.len:10} | ${input.hex():60} | ${input}') eprintln('-60 ${i:10} | input.len: ${input.len:10} | ${input.hex():-60} | ${input}') eprintln('---------------------------------------------------------------------------------------------') assert true } struct Aa { a int } struct Bb { b Aa } fn (x Bb) f() Aa { return x.b } fn test_method_interpolation() { y := Bb{ b: Aa{ a: 2 } } assert '>${y.f().a}<' == '>2<' assert '>${y.f().a}<' == '>2<' } fn f(i int) int { return i } fn test_call() { s := '${f(4)}' assert s == '4' } // for issue: 19048 struct Foo { } fn (mut f Foo) intp_pointer() string { return '${f:p}' } fn test_intp_pointer_specifier_p() { mut foo := Foo{} str1 := foo.intp_pointer() str2 := '${&foo:p}' assert str1 == str2 } // for issue: 20199 // string contains the zero character: `\0` fn test_contains_zero_characters() { mut str := 'abc' str = '${str}\x00${str}' assert str.len == 7 assert str == 'abc\0abc' } // for issue: 20432 // non-ascii escape was lost fn test_interpo_non_ascii_characters() { hello := '你好' assert '${hello},世界!' == '你好,世界!' } fn test_float_exponent_sign() { a := 1234567.0123456e03 assert '${a:6.1e}' == '1.2e+09' assert '${a:6.2e}' == '1.23e+09' assert '${a:6.5e}' == '1.23457e+09' }