v / vlib / builtin
Raw file | 729 loc (688 sloc) | 16.94 KB | Latest commit hash 868908b80
1module builtin
2
3import strconv
4import strings
5
6/*=============================================================================
7Copyright (c) 2019-2023 Dario Deledda. All rights reserved.
8Use of this source code is governed by an MIT license
9that can be found in the LICENSE file.
10
11This file contains string interpolation V functions
12=============================================================================*/
13
14/*============================================================================
15Enum format types max 0x1F => 32 types
16=============================================================================*/
17pub enum StrIntpType {
18 si_no_str = 0 // no parameter to print only fix string
19 si_c
20 si_u8
21 si_i8
22 si_u16
23 si_i16
24 si_u32
25 si_i32
26 si_u64
27 si_i64
28 si_e32
29 si_e64
30 si_f32
31 si_f64
32 si_g32
33 si_g64
34 si_s
35 si_p
36 si_vp
37}
38
39pub fn (x StrIntpType) str() string {
40 return match x {
41 .si_no_str { 'no_str' }
42 .si_c { 'c' }
43 .si_u8 { 'u8' }
44 .si_i8 { 'i8' }
45 .si_u16 { 'u16' }
46 .si_i16 { 'i16' }
47 .si_u32 { 'u32' }
48 .si_i32 { 'i32' }
49 .si_u64 { 'u64' }
50 .si_i64 { 'i64' }
51 .si_f32 { 'f32' }
52 .si_f64 { 'f64' }
53 .si_g32 { 'f32' } // g32 format use f32 data
54 .si_g64 { 'f64' } // g64 format use f64 data
55 .si_e32 { 'f32' } // e32 format use f32 data
56 .si_e64 { 'f64' } // e64 format use f64 data
57 .si_s { 's' }
58 .si_p { 'p' }
59 .si_vp { 'vp' }
60 }
61}
62
63// Union data used by StrIntpData
64pub union StrIntpMem {
65pub mut:
66 d_c u32
67 d_u8 byte
68 d_i8 i8
69 d_u16 u16
70 d_i16 i16
71 d_u32 u32
72 d_i32 int
73 d_u64 u64
74 d_i64 i64
75 d_f32 f32
76 d_f64 f64
77 d_s string
78 d_p voidptr
79 d_vp voidptr
80}
81
82[inline]
83fn fabs32(x f32) f32 {
84 return if x < 0 { -x } else { x }
85}
86
87[inline]
88fn fabs64(x f64) f64 {
89 return if x < 0 { -x } else { x }
90}
91
92[inline]
93fn abs64(x i64) u64 {
94 return if x < 0 { u64(-x) } else { u64(x) }
95}
96
97// u32/u64 bit compact format
98//___ 32 24 16 8
99//___ | | | |
100//_3333333333222222222211111111110000000000
101//_9876543210987654321098765432109876543210
102//_nPPPPPPPPBBBBWWWWWWWWWWTDDDDDDDSUAA=====
103// = data type 5 bit max 32 data type
104// A allign 2 bit Note: for now only 1 used!
105// U uppercase 1 bit 0 do nothing, 1 do to_upper()
106// S sign 1 bit show the sign if positive
107// D decimals 7 bit number of decimals digit to show
108// T tail zeros 1 bit 1 remove tail zeros, 0 do nothing
109// W Width 10 bit number of char for padding and indentation
110// B num base 4 bit start from 2, 0 for base 10
111// P pad char 1/8 bit padding char (in u32 format reduced to 1 bit as flag for `0` padding)
112// --------------
113// TOTAL: 39/32 bit
114//---------------------------------------
115
116// convert from data format to compact u64
117pub 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 byte, in_base int, in_upper_case bool) u64 {
118 width := if in_width != 0 { abs64(in_width) } else { u64(0) }
119 allign := if in_width > 0 { u64(1 << 5) } else { u64(0) } // two bit 0 .left 1 .rigth, for now we use only one
120 upper_case := if in_upper_case { u64(1 << 7) } else { u64(0) }
121 sign := if in_sign { u64(1 << 8) } else { u64(0) }
122 precision := if in_precision != 987698 {
123 (u64(in_precision & 0x7F) << 9)
124 } else {
125 u64(0x7F) << 9
126 }
127 tail_zeros := if in_tail_zeros { u32(1) << 16 } else { u32(0) }
128 base := u64(u32(in_base & 0xf) << 27)
129 res := u64((u64(fmt_type) & 0x1F) | allign | upper_case | sign | precision | tail_zeros | (u64(width & 0x3FF) << 17) | base | (u64(in_pad_ch) << 31))
130 return res
131}
132
133// convert from data format to compact u32
134pub 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 byte, in_base int, in_upper_case bool) u32 {
135 width := if in_width != 0 { abs64(in_width) } else { u32(0) }
136 allign := if in_width > 0 { u32(1 << 5) } else { u32(0) } // two bit 0 .left 1 .rigth, for now we use only one
137 upper_case := if in_upper_case { u32(1 << 7) } else { u32(0) }
138 sign := if in_sign { u32(1 << 8) } else { u32(0) }
139 precision := if in_precision != 987698 {
140 (u32(in_precision & 0x7F) << 9)
141 } else {
142 u32(0x7F) << 9
143 }
144 tail_zeros := if in_tail_zeros { u32(1) << 16 } else { u32(0) }
145 base := u32(u32(in_base & 0xf) << 27)
146 res := u32((u32(fmt_type) & 0x1F) | allign | upper_case | sign | precision | tail_zeros | (u32(width & 0x3FF) << 17) | base | (u32(in_pad_ch & 1) << 31))
147 return res
148}
149
150// convert from struct to formated string
151[manualfree]
152fn (data &StrIntpData) process_str_intp_data(mut sb strings.Builder) {
153 x := data.fmt
154 typ := unsafe { StrIntpType(x & 0x1F) }
155 allign := int((x >> 5) & 0x01)
156 upper_case := ((x >> 7) & 0x01) > 0
157 sign := int((x >> 8) & 0x01)
158 precision := int((x >> 9) & 0x7F)
159 tail_zeros := ((x >> 16) & 0x01) > 0
160 width := int(i16((x >> 17) & 0x3FF))
161 mut base := int(x >> 27) & 0xF
162 fmt_pad_ch := u8((x >> 31) & 0xFF)
163
164 // no string interpolation is needed, return empty string
165 if typ == .si_no_str {
166 return
167 }
168
169 // if width > 0 { println("${x.hex()} Type: ${x & 0x7F} Width: ${width} Precision: ${precision} allign:${allign}") }
170
171 // manage base if any
172 if base > 0 {
173 base += 2 // we start from 2, 0 == base 10
174 }
175
176 // mange pad char, for now only 0 allowed
177 mut pad_ch := u8(` `)
178 if fmt_pad_ch > 0 {
179 // pad_ch = fmt_pad_ch
180 pad_ch = `0`
181 }
182
183 len0_set := if width > 0 { width } else { -1 }
184 len1_set := if precision == 0x7F { -1 } else { precision }
185 sign_set := sign == 1
186
187 mut bf := strconv.BF_param{
188 pad_ch: pad_ch // padding char
189 len0: len0_set // default len for whole the number or string
190 len1: len1_set // number of decimal digits, if needed
191 positive: true // mandatory: the sign of the number passed
192 sign_flag: sign_set // flag for print sign as prefix in padding
193 allign: .left // alignment of the string
194 rm_tail_zero: tail_zeros // false // remove the tail zeros from floats
195 }
196
197 // allign
198 if fmt_pad_ch == 0 {
199 match allign {
200 0 { bf.allign = .left }
201 1 { bf.allign = .right }
202 // 2 { bf.allign = .center }
203 else { bf.allign = .left }
204 }
205 } else {
206 bf.allign = .right
207 }
208
209 unsafe {
210 // strings
211 if typ == .si_s {
212 mut s := ''
213 if upper_case {
214 s = data.d.d_s.to_upper()
215 } else {
216 s = data.d.d_s.clone()
217 }
218 if width == 0 {
219 sb.write_string(s)
220 } else {
221 strconv.format_str_sb(s, bf, mut sb)
222 }
223 s.free()
224 return
225 }
226
227 // signed int
228 if typ in [.si_i8, .si_i16, .si_i32, .si_i64] {
229 mut d := data.d.d_i64
230 if typ == .si_i8 {
231 d = i64(data.d.d_i8)
232 } else if typ == .si_i16 {
233 d = i64(data.d.d_i16)
234 } else if typ == .si_i32 {
235 d = i64(data.d.d_i32)
236 }
237
238 if base == 0 {
239 if width == 0 {
240 d_str := d.str()
241 sb.write_string(d_str)
242 d_str.free()
243 return
244 }
245 if d < 0 {
246 bf.positive = false
247 }
248 strconv.format_dec_sb(abs64(d), bf, mut sb)
249 } else {
250 // binary, we use 3 for binary
251 if base == 3 {
252 base = 2
253 }
254 mut absd, mut write_minus := d, false
255 if d < 0 && pad_ch != ` ` {
256 absd = -d
257 write_minus = true
258 }
259 mut hx := strconv.format_int(absd, base)
260 if upper_case {
261 tmp := hx
262 hx = hx.to_upper()
263 tmp.free()
264 }
265 if write_minus {
266 sb.write_u8(`-`)
267 bf.len0-- // compensate for the `-` above
268 }
269 if width == 0 {
270 sb.write_string(hx)
271 } else {
272 strconv.format_str_sb(hx, bf, mut sb)
273 }
274 hx.free()
275 }
276 return
277 }
278
279 // unsigned int and pointers
280 if typ in [.si_u8, .si_u16, .si_u32, .si_u64] {
281 mut d := data.d.d_u64
282 if typ == .si_u8 {
283 d = u64(data.d.d_u8)
284 } else if typ == .si_u16 {
285 d = u64(data.d.d_u16)
286 } else if typ == .si_u32 {
287 d = u64(data.d.d_u32)
288 }
289 if base == 0 {
290 if width == 0 {
291 d_str := d.str()
292 sb.write_string(d_str)
293 d_str.free()
294 return
295 }
296 strconv.format_dec_sb(d, bf, mut sb)
297 } else {
298 // binary, we use 3 for binary
299 if base == 3 {
300 base = 2
301 }
302 mut hx := strconv.format_uint(d, base)
303 if upper_case {
304 tmp := hx
305 hx = hx.to_upper()
306 tmp.free()
307 }
308 if width == 0 {
309 sb.write_string(hx)
310 } else {
311 strconv.format_str_sb(hx, bf, mut sb)
312 }
313 hx.free()
314 }
315 return
316 }
317
318 // pointers
319 if typ == .si_p {
320 mut d := data.d.d_u64
321 base = 16 // TODO: **** decide the behaviour of this flag! ****
322 if base == 0 {
323 if width == 0 {
324 d_str := d.str()
325 sb.write_string(d_str)
326 d_str.free()
327 return
328 }
329 strconv.format_dec_sb(d, bf, mut sb)
330 } else {
331 mut hx := strconv.format_uint(d, base)
332 if upper_case {
333 tmp := hx
334 hx = hx.to_upper()
335 tmp.free()
336 }
337 if width == 0 {
338 sb.write_string(hx)
339 } else {
340 strconv.format_str_sb(hx, bf, mut sb)
341 }
342 hx.free()
343 }
344 return
345 }
346
347 // default settings for floats
348 mut use_default_str := false
349 if width == 0 && precision == 0x7F {
350 bf.len1 = 3
351 use_default_str = true
352 }
353 if bf.len1 < 0 {
354 bf.len1 = 3
355 }
356
357 match typ {
358 // floating point
359 .si_f32 {
360 $if !nofloat ? {
361 // println("HERE: f32")
362 if use_default_str {
363 mut f := data.d.d_f32.str()
364 if upper_case {
365 tmp := f
366 f = f.to_upper()
367 tmp.free()
368 }
369 sb.write_string(f)
370 f.free()
371 } else {
372 // println("HERE: f32 format")
373 // println(data.d.d_f32)
374 if data.d.d_f32 < 0 {
375 bf.positive = false
376 }
377 mut f := strconv.format_fl(data.d.d_f32, bf)
378 if upper_case {
379 tmp := f
380 f = f.to_upper()
381 tmp.free()
382 }
383 sb.write_string(f)
384 f.free()
385 }
386 }
387 }
388 .si_f64 {
389 $if !nofloat ? {
390 // println("HERE: f64")
391 if use_default_str {
392 mut f := data.d.d_f64.str()
393 if upper_case {
394 tmp := f
395 f = f.to_upper()
396 tmp.free()
397 }
398 sb.write_string(f)
399 f.free()
400 } else {
401 if data.d.d_f64 < 0 {
402 bf.positive = false
403 }
404 f_union := strconv.Float64u{
405 f: data.d.d_f64
406 }
407 if f_union.u == strconv.double_minus_zero {
408 bf.positive = false
409 }
410
411 mut f := strconv.format_fl(data.d.d_f64, bf)
412 if upper_case {
413 tmp := f
414 f = f.to_upper()
415 tmp.free()
416 }
417 sb.write_string(f)
418 f.free()
419 }
420 }
421 }
422 .si_g32 {
423 // println("HERE: g32")
424 if use_default_str {
425 $if !nofloat ? {
426 mut f := data.d.d_f32.strg()
427 if upper_case {
428 tmp := f
429 f = f.to_upper()
430 tmp.free()
431 }
432 sb.write_string(f)
433 f.free()
434 }
435 } else {
436 // Manage +/-0
437 if data.d.d_f32 == strconv.single_plus_zero {
438 tmp_str := '0'
439 strconv.format_str_sb(tmp_str, bf, mut sb)
440 tmp_str.free()
441 return
442 }
443 if data.d.d_f32 == strconv.single_minus_zero {
444 tmp_str := '-0'
445 strconv.format_str_sb(tmp_str, bf, mut sb)
446 tmp_str.free()
447 return
448 }
449 // Manage +/-INF
450 if data.d.d_f32 == strconv.single_plus_infinity {
451 mut tmp_str := '+inf'
452 if upper_case {
453 tmp_str = '+INF'
454 }
455 strconv.format_str_sb(tmp_str, bf, mut sb)
456 tmp_str.free()
457 }
458 if data.d.d_f32 == strconv.single_minus_infinity {
459 mut tmp_str := '-inf'
460 if upper_case {
461 tmp_str = '-INF'
462 }
463 strconv.format_str_sb(tmp_str, bf, mut sb)
464 tmp_str.free()
465 }
466
467 if data.d.d_f32 < 0 {
468 bf.positive = false
469 }
470 d := fabs32(data.d.d_f32)
471 if d < 999_999.0 && d >= 0.00001 {
472 mut f := strconv.format_fl(data.d.d_f32, bf)
473 if upper_case {
474 tmp := f
475 f = f.to_upper()
476 tmp.free()
477 }
478 sb.write_string(f)
479 f.free()
480 return
481 }
482 mut f := strconv.format_es(data.d.d_f32, bf)
483 if upper_case {
484 tmp := f
485 f = f.to_upper()
486 tmp.free()
487 }
488 sb.write_string(f)
489 f.free()
490 }
491 }
492 .si_g64 {
493 // println("HERE: g64")
494 if use_default_str {
495 $if !nofloat ? {
496 mut f := data.d.d_f64.strg()
497 if upper_case {
498 tmp := f
499 f = f.to_upper()
500 tmp.free()
501 }
502 sb.write_string(f)
503 f.free()
504 }
505 } else {
506 // Manage +/-0
507 if data.d.d_f64 == strconv.double_plus_zero {
508 tmp_str := '0'
509 strconv.format_str_sb(tmp_str, bf, mut sb)
510 tmp_str.free()
511 return
512 }
513 if data.d.d_f64 == strconv.double_minus_zero {
514 tmp_str := '-0'
515 strconv.format_str_sb(tmp_str, bf, mut sb)
516 tmp_str.free()
517 return
518 }
519 // Manage +/-INF
520 if data.d.d_f64 == strconv.double_plus_infinity {
521 mut tmp_str := '+inf'
522 if upper_case {
523 tmp_str = '+INF'
524 }
525 strconv.format_str_sb(tmp_str, bf, mut sb)
526 tmp_str.free()
527 }
528 if data.d.d_f64 == strconv.double_minus_infinity {
529 mut tmp_str := '-inf'
530 if upper_case {
531 tmp_str = '-INF'
532 }
533 strconv.format_str_sb(tmp_str, bf, mut sb)
534 tmp_str.free()
535 }
536
537 if data.d.d_f64 < 0 {
538 bf.positive = false
539 }
540 d := fabs64(data.d.d_f64)
541 if d < 999_999.0 && d >= 0.00001 {
542 mut f := strconv.format_fl(data.d.d_f64, bf)
543 if upper_case {
544 tmp := f
545 f = f.to_upper()
546 tmp.free()
547 }
548 sb.write_string(f)
549 f.free()
550 return
551 }
552 mut f := strconv.format_es(data.d.d_f64, bf)
553 if upper_case {
554 tmp := f
555 f = f.to_upper()
556 tmp.free()
557 }
558 sb.write_string(f)
559 f.free()
560 }
561 }
562 .si_e32 {
563 $if !nofloat ? {
564 // println("HERE: e32")
565 bf.len1 = 6
566 if use_default_str {
567 mut f := data.d.d_f32.str()
568 if upper_case {
569 tmp := f
570 f = f.to_upper()
571 tmp.free()
572 }
573 sb.write_string(f)
574 f.free()
575 } else {
576 if data.d.d_f32 < 0 {
577 bf.positive = false
578 }
579 mut f := strconv.format_es(data.d.d_f32, bf)
580 if upper_case {
581 tmp := f
582 f = f.to_upper()
583 tmp.free()
584 }
585 sb.write_string(f)
586 f.free()
587 }
588 }
589 }
590 .si_e64 {
591 $if !nofloat ? {
592 // println("HERE: e64")
593 bf.len1 = 6
594 if use_default_str {
595 mut f := data.d.d_f64.str()
596 if upper_case {
597 tmp := f
598 f = f.to_upper()
599 tmp.free()
600 }
601 sb.write_string(f)
602 f.free()
603 } else {
604 if data.d.d_f64 < 0 {
605 bf.positive = false
606 }
607 mut f := strconv.format_es(data.d.d_f64, bf)
608 if upper_case {
609 tmp := f
610 f = f.to_upper()
611 tmp.free()
612 }
613 sb.write_string(f)
614 f.free()
615 }
616 }
617 }
618 // runes
619 .si_c {
620 ss := utf32_to_str(data.d.d_c)
621 sb.write_string(ss)
622 ss.free()
623 }
624 // v pointers
625 .si_vp {
626 ss := u64(data.d.d_vp).hex()
627 sb.write_string(ss)
628 ss.free()
629 }
630 else {
631 sb.write_string('***ERROR!***')
632 }
633 }
634 }
635}
636
637//--------------------------------------------------
638
639// storing struct used by cgen
640pub struct StrIntpCgenData {
641pub:
642 str string
643 fmt string
644 d string
645}
646
647// NOTE: LOW LEVEL struct
648// storing struct passed to V in the C code
649pub struct StrIntpData {
650pub:
651 str string
652 // fmt u64 // expanded version for future use, 64 bit
653 fmt u32
654 d StrIntpMem
655}
656
657// interpolation function
658[direct_array_access; manualfree]
659pub fn str_intp(data_len int, input_base &StrIntpData) string {
660 mut res := strings.new_builder(256)
661 for i := 0; i < data_len; i++ {
662 data := unsafe { &input_base[i] }
663 // avoid empty strings
664 if data.str.len != 0 {
665 res.write_string(data.str)
666 }
667 // skip empty data
668 if data.fmt != 0 {
669 data.process_str_intp_data(mut res)
670 }
671 }
672 ret := res.str()
673 unsafe { res.free() }
674 return ret
675}
676
677// The consts here are utilities for the compiler's "auto_str_methods.v".
678// They are used to substitute old _STR calls.
679// FIXME: this const is not released from memory => use a precalculated string const for now.
680// si_s_code = "0x" + int(StrIntpType.si_s).hex() // code for a simple string.
681pub const (
682 si_s_code = '0xfe10'
683 si_g32_code = '0xfe0e'
684 si_g64_code = '0xfe0f'
685)
686
687[inline]
688pub fn str_intp_sq(in_str string) string {
689 return 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("\'"), ${si_s_code}, {.d_s = ${in_str}}},{_SLIT("\'"), 0, {.d_c = 0 }}}))'
690}
691
692[inline]
693pub fn str_intp_rune(in_str string) string {
694 return 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("\`"), ${si_s_code}, {.d_s = ${in_str}}},{_SLIT("\`"), 0, {.d_c = 0 }}}))'
695}
696
697[inline]
698pub fn str_intp_g32(in_str string) string {
699 return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${si_g32_code}, {.d_f32 = ${in_str} }}}))'
700}
701
702[inline]
703pub fn str_intp_g64(in_str string) string {
704 return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${si_g64_code}, {.d_f64 = ${in_str} }}}))'
705}
706
707// replace %% with the in_str
708[manualfree]
709pub fn str_intp_sub(base_str string, in_str string) string {
710 index := base_str.index('%%') or {
711 eprintln('No strin interpolation %% parameteres')
712 exit(1)
713 }
714 // return base_str[..index] + in_str + base_str[index+2..]
715
716 unsafe {
717 st_str := base_str[..index]
718 if index + 2 < base_str.len {
719 en_str := base_str[index + 2..]
720 res_str := 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("${st_str}"), ${si_s_code}, {.d_s = ${in_str} }},{_SLIT("${en_str}"), 0, {.d_c = 0}}}))'
721 st_str.free()
722 en_str.free()
723 return res_str
724 }
725 res2_str := 'str_intp(1, _MOV((StrIntpData[]){{_SLIT("${st_str}"), ${si_s_code}, {.d_s = ${in_str} }}}))'
726 st_str.free()
727 return res2_str
728 }
729}