v2 / vlib / v / util / d_value.v
107 lines · 102 sloc · 2.82 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// Copyright (c) 2025 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module util
5
6const d_sig = "\$d('"
7
8// resolve_d_value replaces all occurrences of `$d('ident','value')`
9// in `str` with either the default `'value'` param or a compile value passed via `-d ident=value`.
10pub fn resolve_d_value(compile_values map[string]string, str string) !string {
11 start := str.index(d_sig) or { return error('no "${d_sig}...\')" could be found in "${str}"') }
12 mut i := 0
13 mut ch := u8(`.`)
14 mut bd_ident := []u8{cap: 20}
15 mut blevel := 1
16 for i = start + d_sig.len; i < str.len && ch != `'`; i++ {
17 ch = str[i]
18 if ch == `)` {
19 blevel--
20 } else if ch == `(` {
21 blevel++
22 }
23 if ch.is_letter() || ch.is_digit() || ch == `_` {
24 bd_ident << ch
25 } else {
26 if !(ch == `'`) {
27 if ch == `$` {
28 return error('cannot use string interpolation in compile time \$d() expression')
29 }
30 return error('invalid `\$d` identifier in "${str}", invalid character `${rune(ch)}`')
31 }
32 }
33 }
34 d_ident := bd_ident.bytestr().trim_space()
35 if d_ident == '' {
36 return error('first argument of `\$d` must be a string identifier')
37 }
38
39 // At this point we should have a valid identifier in `d_ident`.
40 // Next we parse out the default string value.
41
42 // Advance past the `,` and the opening `'` in second argument, or ... eat whatever is there:
43 for ; i < str.len; i++ {
44 ch = str[i]
45 match ch {
46 ` `, `,` {
47 continue
48 }
49 `'` {
50 i++
51 }
52 else {}
53 }
54
55 break
56 }
57 // Rinse, repeat for the expected default value string
58 ch = `.`
59 dv_start := i
60 mut dv_end := i
61 for i < str.len {
62 ch = str[i]
63 dv_end++
64 i++
65 match ch {
66 `'` {
67 break
68 }
69 `(` {
70 blevel++
71 }
72 `)` {
73 blevel--
74 if blevel <= 0 {
75 break
76 }
77 }
78 `$` {
79 return error('cannot use string interpolation in compile time \$d() expression')
80 }
81 else {}
82 }
83 }
84 if dv_end - dv_start == 0 {
85 return error('second argument of `\$d` must be a pure literal')
86 }
87 for ; blevel > 0 && i < str.len; i++ {
88 if str[i] == `)` {
89 i++
90 break
91 }
92 }
93 d_default_value := str#[dv_start..dv_end - 1].trim_space() // last character is the closing `)`
94 // at this point we have the identifier and the default value.
95 // now we need to resolve which one to use from `compile_values`.
96 d_value := compile_values[d_ident] or { d_default_value }
97 original_expr_to_be_replaced := str#[start..i]
98 if original_expr_to_be_replaced[original_expr_to_be_replaced.len - 1] != `)` {
99 panic('the last character of `${original_expr_to_be_replaced}` should be `)`')
100 }
101 rep := str.replace_once(original_expr_to_be_replaced, d_value)
102 if original_expr_to_be_replaced.len > 0 && rep.contains(d_sig) {
103 // if more `$d()` calls remains, resolve those as well:
104 return resolve_d_value(compile_values, rep)
105 }
106 return rep
107}
108