| 1 | fn test_simple_string_interpolation() { |
| 2 | a := 'Hello' |
| 3 | b := 'World' |
| 4 | res := '${a} ${b}' |
| 5 | assert res == 'Hello World' |
| 6 | } |
| 7 | |
| 8 | fn test_mixed_string_interpolation() { |
| 9 | num := 7 |
| 10 | str := 'abc' |
| 11 | s1 := 'number=${num}' |
| 12 | assert s1 == 'number=7' |
| 13 | s2 := 'string=${str}' |
| 14 | assert s2 == 'string=abc' |
| 15 | s3 := 'a: ${num} | b: ${str}' |
| 16 | assert s3 == 'a: 7 | b: abc' |
| 17 | } |
| 18 | |
| 19 | fn test_formatted_string_interpolation() { |
| 20 | x := 'abc' |
| 21 | axb := 'a:${x}:b' |
| 22 | assert axb == 'a:abc:b' |
| 23 | x_10 := 'a:${x:10s}:b' |
| 24 | x10_ := 'a:${x:-10s}:b' |
| 25 | assert x_10 == 'a: abc:b' |
| 26 | assert x10_ == 'a:abc :b' |
| 27 | i := 23 |
| 28 | si_right := '${i:10d}' |
| 29 | si__left := '${i:-10d}' |
| 30 | assert si_right == ' 23' |
| 31 | assert si__left == '23 ' |
| 32 | } |
| 33 | |
| 34 | fn test_dynamic_format_widths() { |
| 35 | width := 10 |
| 36 | left_width := -10 |
| 37 | zero_width := 5 |
| 38 | sign_width := 6 |
| 39 | name := 'abc' |
| 40 | num := 42 |
| 41 | assert '>${name:(width)}<' == '> abc<' |
| 42 | assert '>${name:(left_width)}<' == '>abc <' |
| 43 | assert '>${name:(-width)}<' == '>abc <' |
| 44 | assert '${num:0(zero_width)d}' == '00042' |
| 45 | assert '${num:+(sign_width)d}' == ' +42' |
| 46 | } |
| 47 | |
| 48 | fn test_dynamic_format_precision() { |
| 49 | width := 8 |
| 50 | precision := 3 |
| 51 | value := 12.34567 |
| 52 | assert '>${value:(width).(precision)f}<' == '> 12.346<' |
| 53 | } |
| 54 | |
| 55 | fn test_escape_dollar_in_string() { |
| 56 | i := 42 |
| 57 | assert '(${i})' == '(42)' |
| 58 | assert '(\$i)'.contains('i') && !'(\$i)'.contains('42') |
| 59 | assert !'(\\${i})'.contains('i') && '(\\${i})'.contains('42') && '(\\${i})'.contains('\\') |
| 60 | assert '(\\\$i)'.contains('i') && !'(\\\$i)'.contains('42') && '(\\${i})'.contains('\\') |
| 61 | assert !'(\\\\${i})'.contains('i') && '(\\\\${i})'.contains('42') |
| 62 | && '(\\\\${i})'.contains('\\\\') |
| 63 | assert '(${i})' == '(42)' |
| 64 | assert '(\${i})'.contains('i') && !'(\${i})'.contains('42') |
| 65 | assert !'(\\${i})'.contains('i') && '(\\${i})'.contains('42') && '(\\${i})'.contains('\\') |
| 66 | assert '(\\\${i})'.contains('i') && !'(\\\${i})'.contains('42') && '(\\${i})'.contains('\\') |
| 67 | assert !'(\\\\${i})'.contains('i') && '(\\\\${i})'.contains('42') |
| 68 | && '(\\\\${i})'.contains('\\\\') |
| 69 | assert i == 42 |
| 70 | } |
| 71 | |
| 72 | fn test_dollar_sign_is_literal_without_braces() { |
| 73 | b := 10 |
| 74 | text := 'a$b' |
| 75 | assert text == 'a$b' |
| 76 | assert b == 10 |
| 77 | } |
| 78 | |
| 79 | fn test_implicit_str() { |
| 80 | i := 42 |
| 81 | assert 'int ${i}' == 'int 42' |
| 82 | assert '${i}' == '42' |
| 83 | check := '${i}' == '42' |
| 84 | assert check |
| 85 | text := '${i}' + '42' |
| 86 | assert text == '4242' |
| 87 | } |
| 88 | |
| 89 | fn test_string_interpolation_percent_escaping() { |
| 90 | test := 'hello' |
| 91 | hello := 'world' |
| 92 | x := '%.*s${hello}${test} |${hello:-30s}|' |
| 93 | assert x == '%.*sworldhello |world |' |
| 94 | } |
| 95 | |
| 96 | fn test_string_interpolation_string_prefix() { |
| 97 | // `r`, `c` and `js` are also used as a string prefix. |
| 98 | r := 'r' |
| 99 | rr := '${r}${r}' |
| 100 | assert rr == 'rr' |
| 101 | c := 'c' |
| 102 | cc := '${c}${c}' |
| 103 | assert cc == 'cc' |
| 104 | js := 'js' |
| 105 | jsjs := '${js}${js}' |
| 106 | assert jsjs == 'jsjs' |
| 107 | } |
| 108 | |
| 109 | fn test_interpolation_string_prefix_expr() { |
| 110 | r := 1 |
| 111 | c := 2 |
| 112 | js := 1 |
| 113 | assert '>${3 + r}<' == '>4<' |
| 114 | assert '${r == js} ${js}' == 'true 1' |
| 115 | assert '>${js + c} ${js + r == c}<' == '>3 true<' |
| 116 | } |
| 117 | |
| 118 | fn test_inttypes_string_interpolation() { |
| 119 | c := i8(-103) // -0x67 |
| 120 | uc := u8(217) // 0xD9 |
| 121 | uc2 := u8(13) // 0x0D |
| 122 | s := i16(-23456) // -0x5BA0 |
| 123 | us := u16(54321) // 0xD431 |
| 124 | i := -1622999040 // -0x60BD 0000 |
| 125 | ui := u32(3421958087) // 0xCBF6 EFC7 |
| 126 | vp := voidptr(ui) |
| 127 | mut bp := &u8(unsafe { nil }) |
| 128 | unsafe { |
| 129 | $if x64 { |
| 130 | bp = &u8(15541149836) // 0x3 9E53 208C |
| 131 | } $else { |
| 132 | bp = &u8(3541149836) // 0xD311 A88C |
| 133 | } |
| 134 | } |
| 135 | l := i64(-7694555558525237396) |
| 136 | ul := u64(17234006112912956370) |
| 137 | assert '${s} ${us}' == '-23456 54321' |
| 138 | assert '${ui} ${i}' == '3421958087 -1622999040' |
| 139 | assert '${l} ${ul}' == '-7694555558525237396 17234006112912956370' |
| 140 | assert '>${s:11}:${us:-13}<' == '> -23456:54321 <' |
| 141 | assert '0x${ul:-19x}:${l:22d}' == '0xef2b7d4001165bd2 : -7694555558525237396' |
| 142 | assert '${c:5}${uc:-7}x' == ' -103217 x' |
| 143 | assert '${c:x}:${uc:x}:${uc2:02X}' == '-67:d9:0D' |
| 144 | assert '${s:X}:${us:x}:${u16(uc):04x}' == '-5BA0:d431:00d9' |
| 145 | assert '${i:x}:${ui:X}:${int(s):x}' == '-60bd0000:CBF6EFC7:-5ba0' |
| 146 | assert '${l:x}:${ul:X}' == '-6ac88d8352677894:EF2B7D4001165BD2' |
| 147 | // default pointer format is platform dependent, so try a few |
| 148 | eprintln("platform pointer format: '${vp:p}:${bp}'") |
| 149 | $if x64 { |
| 150 | assert '${vp:p}:${bp}' == '0xcbf6efc7:0x39e53208c' |
| 151 | || '${vp:p}:${bp}' == 'CBF6EFC7:39E53208C' || '${vp:p}:${bp}' == 'cbf6efc7:39e53208c' |
| 152 | || '${vp:p}:${bp}' == '00000000CBF6EFC7:000000039E53208C' |
| 153 | } $else { |
| 154 | assert '${vp:p}:${bp}' == 'CBF6EFC7:D311A88C' || '${vp:p}:${bp}' == 'cbf6efc7:d311a88c' |
| 155 | || '${vp:p}:${bp}' == '0xcbf6efc7:0xd311a88c' |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | fn int_ref_mut_string(mut n &int) string { |
| 160 | return '${n}' |
| 161 | } |
| 162 | |
| 163 | fn int_ref_string(n &int) string { |
| 164 | return '${n}' |
| 165 | } |
| 166 | |
| 167 | fn test_int_ref_string_interpolation() { |
| 168 | mut count := 10 |
| 169 | count_ref := &count |
| 170 | assert int_ref_mut_string(mut &count) == '10' |
| 171 | assert int_ref_string(&count) == '10' |
| 172 | assert '${count_ref}' == '10' |
| 173 | assert '${&count}' == '10' |
| 174 | assert '${count_ref:x}' == 'a' |
| 175 | } |
| 176 | |
| 177 | fn test_utf8_string_interpolation() { |
| 178 | a := 'à-côté' |
| 179 | st := 'Sträßle' |
| 180 | m := '10€' |
| 181 | assert '${a} ${st} ${m}' == 'à-côté Sträßle 10€' |
| 182 | zz := '>${a:10}< >${st:-8}< >${m:5}<-' |
| 183 | zz_expected := '> à-côté< >Sträßle < > 10€<-' |
| 184 | eprintln(' zz: ${zz}') |
| 185 | eprintln('zz_expected: ${zz_expected}') |
| 186 | assert zz == zz_expected |
| 187 | // e := '\u20AC' // Eurosign doesn' work with MSVC and tcc |
| 188 | e := '€' |
| 189 | assert '100.00 ${e}' == '100.00 €' |
| 190 | m2 := 'Москва́' // cyrillic а́: combination of U+0430 and U+0301, UTF-8: d0 b0 cc 81 |
| 191 | d := 'Antonín Dvořák' // latin á: U+00E1, UTF-8: c3 a1 |
| 192 | assert ':${m2:7}:${d:-15}:' == ': Москва́:Antonín Dvořák :' |
| 193 | g := 'Πελοπόννησος' |
| 194 | assert '>${g:-13}<' == '>Πελοπόννησος <' |
| 195 | } |
| 196 | |
| 197 | fn test_utf8_string_interpolation_uses_grapheme_clusters() { |
| 198 | assert '>${'\u006E\u0303':10}<' == '> ñ<' |
| 199 | assert '>${'\U0001F3F3\uFE0F\u200D\U0001F308':10}<' == '> 🏳️🌈<' |
| 200 | assert '>${'ห์':10}<' == '> ห์<' |
| 201 | assert '>${'ปีเตอร์':10}<' == '> ปีเตอร์<' |
| 202 | assert '>${'👩🏽💻':10}<' == '> 👩🏽💻<' |
| 203 | } |
| 204 | |
| 205 | struct Sss { |
| 206 | v1 int |
| 207 | v2 f64 |
| 208 | } |
| 209 | |
| 210 | fn (s Sss) str() string { |
| 211 | return '[${s.v1}, ${s.v2:.3f}]' |
| 212 | } |
| 213 | |
| 214 | fn test_string_interpolation_str_evaluation() { |
| 215 | mut x := Sss{17, 13.455893} |
| 216 | assert '${x}' == '[17, 13.456]' |
| 217 | } |
| 218 | |
| 219 | fn test_string_interpolation_with_negative_format_width_should_compile_and_run_without_segfaulting() { |
| 220 | // discovered during debugging VLS |
| 221 | i := 3 |
| 222 | input := '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' |
| 223 | eprintln('---------------------------------------------------------------------------------------------') |
| 224 | eprintln('+60 ${i:10} | input.len: ${input.len:10} | ${input.hex():60} | ${input}') |
| 225 | eprintln('-60 ${i:10} | input.len: ${input.len:10} | ${input.hex():-60} | ${input}') |
| 226 | eprintln('---------------------------------------------------------------------------------------------') |
| 227 | assert true |
| 228 | } |
| 229 | |
| 230 | struct Aa { |
| 231 | a int |
| 232 | } |
| 233 | |
| 234 | struct Bb { |
| 235 | b Aa |
| 236 | } |
| 237 | |
| 238 | fn (x Bb) f() Aa { |
| 239 | return x.b |
| 240 | } |
| 241 | |
| 242 | fn test_method_interpolation() { |
| 243 | y := Bb{ |
| 244 | b: Aa{ |
| 245 | a: 2 |
| 246 | } |
| 247 | } |
| 248 | assert '>${y.f().a}<' == '>2<' |
| 249 | assert '>${y.f().a}<' == '>2<' |
| 250 | } |
| 251 | |
| 252 | fn f(i int) int { |
| 253 | return i |
| 254 | } |
| 255 | |
| 256 | fn test_call() { |
| 257 | s := '${f(4)}' |
| 258 | assert s == '4' |
| 259 | } |
| 260 | |
| 261 | // for issue: 19048 |
| 262 | struct Foo { |
| 263 | } |
| 264 | |
| 265 | fn (mut f Foo) intp_pointer() string { |
| 266 | return '${f:p}' |
| 267 | } |
| 268 | |
| 269 | fn test_intp_pointer_specifier_p() { |
| 270 | mut foo := Foo{} |
| 271 | str1 := foo.intp_pointer() |
| 272 | |
| 273 | str2 := '${&foo:p}' |
| 274 | assert str1 == str2 |
| 275 | } |
| 276 | |
| 277 | // for issue: 20199 |
| 278 | // string contains the zero character: `\0` |
| 279 | fn test_contains_zero_characters() { |
| 280 | mut str := 'abc' |
| 281 | str = '${str}\x00${str}' |
| 282 | assert str.len == 7 |
| 283 | assert str == 'abc\0abc' |
| 284 | } |
| 285 | |
| 286 | // for issue: 20432 |
| 287 | // non-ascii escape was lost |
| 288 | fn test_interpo_non_ascii_characters() { |
| 289 | hello := '你好' |
| 290 | assert '${hello},世界!' == '你好,世界!' |
| 291 | } |
| 292 | |
| 293 | fn test_float_exponent_sign() { |
| 294 | a := 1234567.0123456e03 |
| 295 | assert '${a:6.1e}' == '1.2e+09' |
| 296 | assert '${a:6.2e}' == '1.23e+09' |
| 297 | assert '${a:6.5e}' == '1.23457e+09' |
| 298 | } |
| 299 | |