v / vlib / strconv / vprintf.c.v
815 lines · 773 sloc · 16.27 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1/*=============================================================================
2Copyright (c) 2019-2024 Dario Deledda. All rights reserved.
3Use of this source code is governed by an MIT license
4that can be found in the LICENSE file.
5
6This file contains string interpolation V functions
7=============================================================================*/
8module strconv
9
10import strings
11
12enum Char_parse_state {
13 start
14 norm_char
15 field_char
16 pad_ch
17 len_set_start
18 len_set_in
19 check_type
20 check_float
21 check_float_in
22 reset_params
23}
24
25// v_printf prints a sprintf-like formatted `string` to the terminal.
26// The format string `str` can be constructed at runtime.
27// Note, that this function is unsafe.
28// In most cases, you are better off using V's string interpolation,
29// when your format string is known at compile time.
30@[unsafe]
31pub fn v_printf(str string, pt ...voidptr) {
32 print(unsafe { v_sprintf(str, ...pt) })
33}
34
35// v_sprintf returns a sprintf-like formatted `string`.
36// The format string `str` can be constructed at runtime.
37// Note, that this function is unsafe.
38// In most cases, you are better off using V's string interpolation,
39// when your format string is known at compile time.
40// Small integer and `f32` arguments follow C-style default promotions.
41// Example:
42// ```v
43// x := 3.141516
44// assert unsafe{strconv.v_sprintf('aaa %G', x)} == 'aaa 3.141516'
45// ```
46@[direct_array_access; manualfree; unsafe]
47pub fn v_sprintf(str string, pt ...voidptr) string {
48 mut res := strings.new_builder(pt.len * 16)
49 defer {
50 unsafe { res.free() }
51 }
52
53 mut i := 0 // main string index
54 mut p_index := 0 // parameter index
55 mut sign := false // sign flag
56 mut align := Align_text.right
57 mut len0 := -1 // forced length, if -1 free length
58 mut len1 := -1 // decimal part for floats
59 def_len1 := 6 // default value for len1
60 mut pad_ch := u8(` `) // pad char
61
62 // prefix chars for Length field
63 mut ch1 := `0` // +1 char if present else `0`
64 mut ch2 := `0` // +2 char if present else `0`
65
66 mut status := Char_parse_state.norm_char
67 for i < str.len {
68 if status == .reset_params {
69 sign = false
70 align = .right
71 len0 = -1
72 len1 = -1
73 pad_ch = ` `
74 status = .norm_char
75 ch1 = `0`
76 ch2 = `0`
77 continue
78 }
79
80 ch := str[i]
81 if ch != `%` && status == .norm_char {
82 res.write_u8(ch)
83 i++
84 continue
85 }
86 if ch == `%` && status == .field_char {
87 status = .norm_char
88 res.write_u8(ch)
89 i++
90 continue
91 }
92 if ch == `%` && status == .norm_char {
93 status = .field_char
94 i++
95 continue
96 }
97
98 // single char, manage it here
99 if ch == `c` && status == .field_char {
100 v_sprintf_panic(p_index, pt.len)
101 d1 := u8(unsafe { *(&int(pt[p_index])) })
102 res.write_u8(d1)
103 status = .reset_params
104 p_index++
105 i++
106 continue
107 }
108
109 // pointer, manage it here
110 if ch == `p` && status == .field_char {
111 v_sprintf_panic(p_index, pt.len)
112 res.write_string('0x')
113 res.write_string(ptr_str(unsafe { pt[p_index] }))
114 status = .reset_params
115 p_index++
116 i++
117 continue
118 }
119
120 if status == .field_char {
121 mut fc_ch1 := `0`
122 mut fc_ch2 := `0`
123 if (i + 1) < str.len {
124 fc_ch1 = str[i + 1]
125 if (i + 2) < str.len {
126 fc_ch2 = str[i + 2]
127 }
128 }
129 if ch == `+` {
130 sign = true
131 i++
132 continue
133 } else if ch == `-` {
134 align = .left
135 i++
136 continue
137 } else if ch in [`0`, ` `] {
138 if align == .right {
139 pad_ch = ch
140 }
141 i++
142 continue
143 } else if ch == `'` {
144 i++
145 continue
146 } else if ch == `.` && fc_ch1 >= `1` && fc_ch1 <= `9` {
147 status = .check_float
148 i++
149 continue
150 }
151 // manage "%.*s" precision field
152 else if ch == `.` && fc_ch1 == `*` && fc_ch2 == `s` {
153 v_sprintf_panic(p_index, pt.len)
154 len := unsafe { *(&int(pt[p_index])) }
155 p_index++
156 v_sprintf_panic(p_index, pt.len)
157 mut s := unsafe { *(&string(pt[p_index])) }
158 s = s[..len]
159 p_index++
160 res.write_string(s)
161 status = .reset_params
162 i += 3
163 continue
164 }
165 status = .len_set_start
166 continue
167 }
168
169 if status == .len_set_start {
170 if ch >= `1` && ch <= `9` {
171 len0 = int(ch - `0`)
172 status = .len_set_in
173 i++
174 continue
175 }
176 if ch == `.` {
177 status = .check_float
178 i++
179 continue
180 }
181 status = .check_type
182 continue
183 }
184
185 if status == .len_set_in {
186 if ch >= `0` && ch <= `9` {
187 len0 *= 10
188 len0 += int(ch - `0`)
189 i++
190 continue
191 }
192 if ch == `.` {
193 status = .check_float
194 i++
195 continue
196 }
197 status = .check_type
198 continue
199 }
200
201 if status == .check_float {
202 if ch >= `0` && ch <= `9` {
203 len1 = int(ch - `0`)
204 status = .check_float_in
205 i++
206 continue
207 }
208 status = .check_type
209 continue
210 }
211
212 if status == .check_float_in {
213 if ch >= `0` && ch <= `9` {
214 len1 *= 10
215 len1 += int(ch - `0`)
216 i++
217 continue
218 }
219 status = .check_type
220 continue
221 }
222
223 if status == .check_type {
224 if ch == `l` {
225 if ch1 == `0` {
226 ch1 = `l`
227 i++
228 continue
229 } else {
230 ch2 = `l`
231 i++
232 continue
233 }
234 } else if ch == `h` {
235 if ch1 == `0` {
236 ch1 = `h`
237 i++
238 continue
239 } else {
240 ch2 = `h`
241 i++
242 continue
243 }
244 }
245 // signed integer
246 else if ch in [`d`, `i`] {
247 mut d1 := u64(0)
248 mut positive := true
249
250 // println("${ch1} ${ch2}")
251 match ch1 {
252 // h for 16 bit int
253 // hh for 8 bit int
254 `h` {
255 v_sprintf_panic(p_index, pt.len)
256 x := unsafe { *(&int(pt[p_index])) }
257 if ch2 == `h` {
258 sx := i8(x)
259 positive = if sx >= 0 { true } else { false }
260 d1 = if positive { u64(sx) } else { u64(-sx) }
261 } else {
262 sx := i16(x)
263 positive = if sx >= 0 { true } else { false }
264 d1 = if positive { u64(sx) } else { u64(-sx) }
265 }
266 }
267 // l i64
268 // ll i64 for now
269 `l` {
270 // placeholder for future 128bit integer code
271 /*
272 if ch2 == `l` {
273 v_sprintf_panic(p_index, pt.len)
274 x := *(&i128(pt[p_index]))
275 positive = if x >= 0 { true } else { false }
276 d1 = if positive { u128(x) } else { u128(-x) }
277 } else {
278 v_sprintf_panic(p_index, pt.len)
279 x := *(&i64(pt[p_index]))
280 positive = if x >= 0 { true } else { false }
281 d1 = if positive { u64(x) } else { u64(-x) }
282 }
283 */
284 v_sprintf_panic(p_index, pt.len)
285 x := unsafe { *(&i64(pt[p_index])) }
286 positive = if x >= 0 { true } else { false }
287 d1 = if positive { u64(x) } else { u64(-x) }
288 }
289 // default int
290 else {
291 v_sprintf_panic(p_index, pt.len)
292 x := unsafe { *(&int(pt[p_index])) }
293 positive = if x >= 0 { true } else { false }
294 d1 = if positive { u64(x) } else { u64(-x) }
295 }
296 }
297
298 tmp := format_dec_old(d1,
299 pad_ch: pad_ch
300 len0: len0
301 len1: 0
302 positive: positive
303 sign_flag: sign
304 align: align
305 )
306 res.write_string(tmp)
307 unsafe { tmp.free() }
308 status = .reset_params
309 p_index++
310 i++
311 ch1 = `0`
312 ch2 = `0`
313 continue
314 }
315 // unsigned integer
316 else if ch == `u` {
317 mut d1 := u64(0)
318 positive := true
319 v_sprintf_panic(p_index, pt.len)
320 match ch1 {
321 // h for 16 bit unsigned int
322 // hh for 8 bit unsigned int
323 `h` {
324 x := unsafe { *(&int(pt[p_index])) }
325 if ch2 == `h` {
326 d1 = u64(u8(x))
327 } else {
328 d1 = u64(u16(x))
329 }
330 }
331 // l u64
332 // ll u64 for now
333 `l` {
334 // placeholder for future 128bit integer code
335 /*
336 if ch2 == `l` {
337 d1 = u128(*(&u128(pt[p_index])))
338 } else {
339 d1 = u64(*(&u64(pt[p_index])))
340 }
341 */
342 d1 = u64(unsafe { *(&u64(pt[p_index])) })
343 }
344 // default int
345 else {
346 d1 = u64(u32(unsafe { *(&int(pt[p_index])) }))
347 }
348 }
349
350 tmp := format_dec_old(d1,
351 pad_ch: pad_ch
352 len0: len0
353 len1: 0
354 positive: positive
355 sign_flag: sign
356 align: align
357 )
358 res.write_string(tmp)
359 unsafe { tmp.free() }
360 status = .reset_params
361 p_index++
362 i++
363 continue
364 }
365 // hex
366 else if ch in [`x`, `X`] {
367 v_sprintf_panic(p_index, pt.len)
368 mut s := ''
369 match ch1 {
370 // h for 16 bit int
371 // hh fot 8 bit int
372 `h` {
373 x := unsafe { *(&int(pt[p_index])) }
374 if ch2 == `h` {
375 s = i8(x).hex()
376 } else {
377 s = i16(x).hex()
378 }
379 }
380 // l i64
381 // ll i64 for now
382 `l` {
383 // placeholder for future 128bit integer code
384 /*
385 if ch2 == `l` {
386 x := *(&i128(pt[p_index]))
387 s = x.hex()
388 } else {
389 x := *(&i64(pt[p_index]))
390 s = x.hex()
391 }
392 */
393 x := unsafe { *(&i64(pt[p_index])) }
394 s = x.hex()
395 }
396 else {
397 x := unsafe { *(&int(pt[p_index])) }
398 s = x.hex()
399 }
400 }
401
402 if ch == `X` {
403 tmp := s
404 s = s.to_upper()
405 unsafe { tmp.free() }
406 }
407
408 tmp := format_str(s,
409 pad_ch: pad_ch
410 len0: len0
411 len1: 0
412 positive: true
413 sign_flag: false
414 align: align
415 )
416 res.write_string(tmp)
417 unsafe { tmp.free() }
418 unsafe { s.free() }
419 status = .reset_params
420 p_index++
421 i++
422 continue
423 }
424
425 // float and double
426 if ch in [`f`, `F`] {
427 $if !nofloat ? {
428 v_sprintf_panic(p_index, pt.len)
429 x := unsafe { *(&f64(pt[p_index])) }
430 positive := x >= f64(0.0)
431 len1 = if len1 >= 0 { len1 } else { def_len1 }
432 s := format_fl_old(f64(x),
433 pad_ch: pad_ch
434 len0: len0
435 len1: len1
436 positive: positive
437 sign_flag: sign
438 align: align
439 )
440 if ch == `F` {
441 tmp := s.to_upper()
442 res.write_string(tmp)
443 unsafe { tmp.free() }
444 } else {
445 res.write_string(s)
446 }
447 unsafe { s.free() }
448 }
449 status = .reset_params
450 p_index++
451 i++
452 continue
453 } else if ch in [`e`, `E`] {
454 $if !nofloat ? {
455 v_sprintf_panic(p_index, pt.len)
456 x := unsafe { *(&f64(pt[p_index])) }
457 positive := x >= f64(0.0)
458 len1 = if len1 >= 0 { len1 } else { def_len1 }
459 s := format_es_old(f64(x),
460 pad_ch: pad_ch
461 len0: len0
462 len1: len1
463 positive: positive
464 sign_flag: sign
465 align: align
466 )
467 if ch == `E` {
468 tmp := s.to_upper()
469 res.write_string(tmp)
470 unsafe { tmp.free() }
471 } else {
472 res.write_string(s)
473 }
474 unsafe { s.free() }
475 }
476 status = .reset_params
477 p_index++
478 i++
479 continue
480 } else if ch in [`g`, `G`] {
481 $if !nofloat ? {
482 v_sprintf_panic(p_index, pt.len)
483 x := unsafe { *(&f64(pt[p_index])) }
484 positive := x >= f64(0.0)
485 mut s := ''
486 tx := fabs(x)
487 if tx < 999_999.0 && tx >= 0.00001 {
488 // println("Here g format_fl [${tx}]")
489 len1 = if len1 >= 0 { len1 + 1 } else { def_len1 }
490 tmp := s
491 s = format_fl_old(x,
492 pad_ch: pad_ch
493 len0: len0
494 len1: len1
495 positive: positive
496 sign_flag: sign
497 align: align
498 rm_tail_zero: true
499 )
500 unsafe { tmp.free() }
501 } else {
502 len1 = if len1 >= 0 { len1 + 1 } else { def_len1 }
503 tmp := s
504 s = format_es_old(x,
505 pad_ch: pad_ch
506 len0: len0
507 len1: len1
508 positive: positive
509 sign_flag: sign
510 align: align
511 rm_tail_zero: true
512 )
513 unsafe { tmp.free() }
514 }
515 if ch == `G` {
516 tmp := s.to_upper()
517 res.write_string(tmp)
518 unsafe { tmp.free() }
519 } else {
520 res.write_string(s)
521 }
522 unsafe { s.free() }
523 }
524 status = .reset_params
525 p_index++
526 i++
527 continue
528 }
529 // string
530 else if ch == `s` {
531 v_sprintf_panic(p_index, pt.len)
532 s1 := unsafe { *(&string(pt[p_index])) }
533 pad_ch = ` `
534 tmp := format_str(s1,
535 pad_ch: pad_ch
536 len0: len0
537 len1: 0
538 positive: true
539 sign_flag: false
540 align: align
541 )
542 res.write_string(tmp)
543 unsafe { tmp.free() }
544 status = .reset_params
545 p_index++
546 i++
547 continue
548 }
549 }
550
551 status = .reset_params
552 p_index++
553 i++
554 }
555
556 if p_index != pt.len {
557 panic_n2('% conversion specifiers number mismatch (expected %, given args)', p_index,
558 pt.len)
559 }
560
561 return res.str()
562}
563
564@[inline]
565fn v_sprintf_panic(idx int, len int) {
566 if idx >= len {
567 panic_n2('% conversion specifiers number mismatch (expected %, given args)', idx + 1, len)
568 }
569}
570
571fn fabs(x f64) f64 {
572 if x < 0.0 {
573 return -x
574 }
575 return x
576}
577
578// strings.Builder version of format_fl
579@[direct_array_access; manualfree]
580pub fn format_fl_old(f f64, p BF_param) string {
581 unsafe {
582 mut s := ''
583 // mut fs := "1.2343"
584 mut fs := f64_to_str_lnd1(if f >= 0.0 { f } else { -f }, p.len1)
585 // println("Dario")
586 // println(fs)
587
588 // error!!
589 if fs[0] == `[` {
590 s.free()
591 return fs
592 }
593
594 if p.rm_tail_zero {
595 tmp := fs
596 fs = remove_tail_zeros_old(fs)
597 tmp.free()
598 }
599 mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len })
600 defer {
601 res.free()
602 }
603
604 mut sign_len_diff := 0
605 if p.pad_ch == `0` {
606 if p.positive {
607 if p.sign_flag {
608 res.write_u8(`+`)
609 sign_len_diff = -1
610 }
611 } else {
612 res.write_u8(`-`)
613 sign_len_diff = -1
614 }
615 tmp := s
616 s = fs.clone()
617 tmp.free()
618 } else {
619 if p.positive {
620 if p.sign_flag {
621 tmp := s
622 s = '+' + fs
623 tmp.free()
624 } else {
625 tmp := s
626 s = fs.clone()
627 tmp.free()
628 }
629 } else {
630 tmp := s
631 s = '-' + fs
632 tmp.free()
633 }
634 }
635
636 dif := p.len0 - s.len + sign_len_diff
637
638 if p.align == .right {
639 for i1 := 0; i1 < dif; i1++ {
640 res.write_u8(p.pad_ch)
641 }
642 }
643 res.write_string(s)
644 if p.align == .left {
645 for i1 := 0; i1 < dif; i1++ {
646 res.write_u8(p.pad_ch)
647 }
648 }
649
650 s.free()
651 fs.free()
652 return res.str()
653 }
654}
655
656@[manualfree]
657fn format_es_old(f f64, p BF_param) string {
658 unsafe {
659 mut s := ''
660 mut fs := f64_to_str_pad(if f > 0 { f } else { -f }, p.len1)
661 if p.rm_tail_zero {
662 tmp := fs
663 fs = remove_tail_zeros_old(fs)
664 tmp.free()
665 }
666 mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len })
667 defer {
668 res.free()
669 fs.free()
670 s.free()
671 }
672
673 mut sign_len_diff := 0
674 if p.pad_ch == `0` {
675 if p.positive {
676 if p.sign_flag {
677 res.write_u8(`+`)
678 sign_len_diff = -1
679 }
680 } else {
681 res.write_u8(`-`)
682 sign_len_diff = -1
683 }
684 tmp := s
685 s = fs.clone()
686 tmp.free()
687 } else {
688 if p.positive {
689 if p.sign_flag {
690 tmp := s
691 s = '+' + fs
692 tmp.free()
693 } else {
694 tmp := s
695 s = fs.clone()
696 tmp.free()
697 }
698 } else {
699 tmp := s
700 s = '-' + fs
701 tmp.free()
702 }
703 }
704
705 dif := p.len0 - s.len + sign_len_diff
706 if p.align == .right {
707 for i1 := 0; i1 < dif; i1++ {
708 res.write_u8(p.pad_ch)
709 }
710 }
711 res.write_string(s)
712 if p.align == .left {
713 for i1 := 0; i1 < dif; i1++ {
714 res.write_u8(p.pad_ch)
715 }
716 }
717 return res.str()
718 }
719}
720
721fn remove_tail_zeros_old(s string) string {
722 mut i := 0
723 mut last_zero_start := -1
724 mut dot_pos := -1
725 mut in_decimal := false
726 mut prev_ch := u8(0)
727 for i < s.len {
728 ch := unsafe { s.str[i] }
729 if ch == `.` {
730 in_decimal = true
731 dot_pos = i
732 } else if in_decimal {
733 if ch == `0` && prev_ch != `0` {
734 last_zero_start = i
735 } else if ch >= `1` && ch <= `9` {
736 last_zero_start = -1
737 } else if ch == `e` {
738 break
739 }
740 }
741 prev_ch = ch
742 i++
743 }
744
745 mut tmp := ''
746 if last_zero_start > 0 {
747 if last_zero_start == dot_pos + 1 {
748 tmp = s[..dot_pos] + s[i..]
749 } else {
750 tmp = s[..last_zero_start] + s[i..]
751 }
752 } else {
753 tmp = s.clone()
754 }
755 if unsafe { tmp.str[tmp.len - 1] } == `.` {
756 return tmp[..tmp.len - 1]
757 }
758 return tmp
759}
760
761// max int64 9223372036854775807
762@[manualfree]
763pub fn format_dec_old(d u64, p BF_param) string {
764 mut s := ''
765 mut res := strings.new_builder(20)
766 defer {
767 unsafe { res.free() }
768 unsafe { s.free() }
769 }
770 mut sign_len_diff := 0
771 if p.pad_ch == `0` {
772 if p.positive {
773 if p.sign_flag {
774 res.write_u8(`+`)
775 sign_len_diff = -1
776 }
777 } else {
778 res.write_u8(`-`)
779 sign_len_diff = -1
780 }
781 tmp := s
782 s = d.str()
783 unsafe { tmp.free() }
784 } else {
785 if p.positive {
786 if p.sign_flag {
787 tmp := s
788 s = '+' + d.str()
789 unsafe { tmp.free() }
790 } else {
791 tmp := s
792 s = d.str()
793 unsafe { tmp.free() }
794 }
795 } else {
796 tmp := s
797 s = '-' + d.str()
798 unsafe { tmp.free() }
799 }
800 }
801 dif := p.len0 - s.len + sign_len_diff
802
803 if p.align == .right {
804 for i1 := 0; i1 < dif; i1++ {
805 res.write_u8(p.pad_ch)
806 }
807 }
808 res.write_string(s)
809 if p.align == .left {
810 for i1 := 0; i1 < dif; i1++ {
811 res.write_u8(p.pad_ch)
812 }
813 }
814 return res.str()
815}
816