v2 / vlib / v / gen / c / str.v
400 lines · 391 sloc · 11.8 KB · a937b1f0e5d451eea99065a4a134849a82e6b057
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
3module c
4
5import v.ast
6import v.util
7
8fn (mut g Gen) string_literal(node ast.StringLiteral) {
9 escaped_val := cescape_nonascii(util.smart_quote(node.val, node.is_raw))
10 if node.language == .c {
11 g.write(cescaped_string_literal(escaped_val))
12 } else {
13 g.write('_S(${cescaped_string_literal(escaped_val)})')
14 }
15}
16
17// optimize string interpolation in string builders:
18// `sb.writeln('a=${a}')` =>
19// `sb.writeln('a='); sb.writeln(a.str())`
20fn (mut g Gen) string_inter_literal_sb_optimized(call_expr ast.CallExpr) {
21 node := call_expr.args[0].expr as ast.StringInterLiteral
22 g.writeln('// sb inter opt')
23 is_nl := call_expr.name == 'writeln'
24 for i, val in node.vals {
25 escaped_val := cescape_nonascii(util.smart_quote(val, false))
26 g.write('strings__Builder_write_string(&')
27 g.expr(call_expr.left)
28 g.write2(', _S("', escaped_val)
29 g.writeln('"));')
30 if i >= node.exprs.len {
31 break
32 }
33 if is_nl && i == node.exprs.len - 1 {
34 g.write('strings__Builder_writeln(&')
35 } else {
36 g.write('strings__Builder_write_string(&')
37 }
38 g.expr(call_expr.left)
39 g.write(', ')
40 typ := node.expr_types[i]
41 g.write2(g.styp(typ), '_str(')
42 sym := g.table.sym(typ)
43 if sym.kind != .function {
44 g.expr(node.exprs[i])
45 }
46 g.writeln('));')
47 }
48 g.writeln('')
49 return
50}
51
52fn (g &Gen) option_mut_param_surface_type(expr ast.Expr) ast.Type {
53 ident := match expr {
54 ast.Ident { expr }
55 else { return 0 }
56 }
57
58 mut typ := ast.Type(0)
59 if ident.obj is ast.Var {
60 typ = g.option_mut_param_surface_type_from_var(ident.name, ident.obj)
61 }
62 if scope_var := ident.scope.find_var(ident.name) {
63 scope_typ := g.option_mut_param_surface_type_from_var(ident.name, scope_var)
64 if scope_typ != 0 {
65 typ = scope_typ
66 }
67 }
68 if typ == 0 && ident.obj is ast.Var {
69 if ident.obj.is_arg && ident.obj.orig_type.has_flag(.option) {
70 typ = ident.obj.orig_type
71 }
72 }
73 if typ == 0 || !typ.has_flag(.option) {
74 return 0
75 }
76 return typ
77}
78
79fn (g &Gen) option_mut_param_surface_type_from_var(name string, var ast.Var) ast.Type {
80 if !var.is_arg || var.is_unwrapped || !var.typ.has_flag(.option_mut_param_t) {
81 return 0
82 }
83 mut typ := var.typ.clear_flag(.option_mut_param_t)
84 if g.mut_option_param_assigned_directly(name) {
85 inner := typ.clear_option_and_result()
86 if inner.is_ptr() {
87 typ = inner.deref().set_flag(.option)
88 }
89 }
90 return typ
91}
92
93fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
94 old_inside_opt_or_res := g.inside_opt_or_res
95 g.inside_opt_or_res = true
96 g.expected_fixed_arr = true
97 defer {
98 g.inside_opt_or_res = old_inside_opt_or_res
99 g.expected_fixed_arr = false
100 }
101 mut expr_type := etype
102 if expr is ast.Ident && g.resolved_ident_is_by_value_auto_deref_capture(expr) {
103 resolved_scope_type := g.resolved_scope_var_type(expr)
104 if resolved_scope_type != 0 {
105 expr_type = resolved_scope_type
106 }
107 }
108 is_shared := expr_type.has_flag(.shared_f)
109 mut typ := expr_type
110 if is_shared {
111 typ = typ.clear_flag(.shared_f).set_nr_muls(0)
112 }
113 if expr is ast.Ident && g.cur_fn != unsafe { nil }
114 && g.mut_option_param_assigned_directly(expr.name) {
115 for param in g.cur_fn.params {
116 if param.name == expr.name && param.typ.has_flag(.option_mut_param_t) {
117 mut opt_typ := param.typ.clear_flag(.option_mut_param_t)
118 if opt_typ.is_ptr() {
119 opt_typ = opt_typ.deref()
120 }
121 g.write('${g.get_str_fn(opt_typ)}(*')
122 g.expr(expr)
123 g.write(')')
124 return
125 }
126 }
127 }
128 mut_arg_option_type := g.option_mut_param_surface_type(expr)
129 if mut_arg_option_type != 0 {
130 typ = mut_arg_option_type
131 }
132 if mut_arg_option_type != 0 && expr is ast.Ident
133 && g.mut_option_param_assigned_directly(expr.name) {
134 g.write('${g.get_str_fn(mut_arg_option_type)}(*')
135 g.expr(expr)
136 g.write(')')
137 return
138 }
139 if mut_arg_option_type == 0 && expr is ast.Ident && g.expr_is_auto_deref_var(expr)
140 && typ.has_flag(.option) {
141 g.write('${g.get_str_fn(typ)}(*')
142 g.expr(expr)
143 g.write(')')
144 return
145 }
146 if expr is ast.Ident && expr.obj is ast.Var && expr.obj.is_inherited {
147 inherited_typ := g.resolved_scope_var_type(expr)
148 if inherited_typ != 0 {
149 typ = inherited_typ
150 }
151 }
152 // `mut ?T` params are passed by pointer in C, but should still stringify as
153 // option values rather than as raw `&...` pointers.
154 is_ptr := typ.is_ptr() || (typ.has_flag(.option_mut_param_t) && !typ.has_flag(.option))
155 mut sym := g.table.sym(typ)
156 // when type is non-option alias and doesn't has `str()`, print the aliased value
157 if mut sym.info is ast.Alias && !sym.has_method('str') && !expr_type.has_flag(.option) {
158 parent_sym := g.table.sym(sym.info.parent_type)
159 if parent_sym.has_method('str') {
160 typ = sym.info.parent_type
161 sym = unsafe { parent_sym }
162 }
163 }
164 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
165 // When interface smartcast expr produces a pointer in C but type was already dereffed,
166 // we need to dereference the generated expression.
167 is_interface_smartcast_to_nonptr := !is_ptr && expr is ast.Ident && expr.obj is ast.Var
168 && (expr.obj as ast.Var).smartcasts.len > 0
169 && (expr.obj as ast.Var).smartcasts.last().is_ptr()
170 && g.table.final_sym(g.unwrap_generic((expr.obj as ast.Var).orig_type)).kind == .interface
171 && g.table.final_sym(g.unwrap_generic((expr.obj as ast.Var).smartcasts.last())).kind != .interface
172 use_raw_interface_smartcast_expr := is_ptr && expr is ast.Ident && expr.obj is ast.Var
173 && (expr.obj as ast.Var).smartcasts.len > 0
174 && (expr.obj as ast.Var).smartcasts.last().is_ptr()
175 && (g.table.final_sym(g.unwrap_generic((expr.obj as ast.Var).typ)).kind == .interface
176 || ((expr.obj as ast.Var).orig_type != 0
177 && g.table.final_sym(g.unwrap_generic((expr.obj as ast.Var).orig_type)).kind == .interface))
178 if typ.has_flag(.variadic) {
179 str_fn_name := g.get_str_fn(typ)
180 g.write('${str_fn_name}(')
181 g.expr(expr)
182 g.write(')')
183 } else if typ == ast.string_type {
184 if expr_type.is_ptr() {
185 g.write('*')
186 }
187 g.expr(expr)
188 } else if typ == ast.bool_type {
189 g.write('(')
190 g.expr(expr)
191 g.write(' ? _S("true") : _S("false"))')
192 } else if sym.kind == .none || typ == ast.void_type.set_flag(.option) {
193 if expr is ast.CallExpr {
194 stmt_str := g.go_before_last_stmt()
195 g.expr(expr)
196 g.writeln(';')
197 g.write(stmt_str)
198 }
199 g.write('_S("<none>")')
200 } else if sym.kind == .enum {
201 if expr !is ast.EnumVal || sym.has_method('str') {
202 str_fn_name := g.get_str_fn(typ)
203 g.write('${str_fn_name}(')
204 if typ.nr_muls() > 0 {
205 g.write('*'.repeat(typ.nr_muls()))
206 }
207 if expr is ast.EnumVal {
208 g.write2(sym.cname, '__')
209 }
210 g.enum_expr(expr)
211 g.write(')')
212 } else {
213 g.write('_S("')
214 g.enum_expr(expr)
215 g.write('")')
216 }
217 } else if sym_has_str_method
218 || sym.kind in [.array, .array_fixed, .map, .struct, .multi_return, .sum_type, .interface] {
219 unwrap_opt_or_res := match expr {
220 ast.CallExpr, ast.ComptimeCall, ast.ComptimeSelector, ast.InfixExpr, ast.PrefixExpr,
221 ast.SelectorExpr {
222 expr.or_block.kind != .absent
223 }
224 ast.Ident, ast.IndexExpr {
225 expr.or_expr.kind != .absent
226 }
227 else {
228 false
229 }
230 }
231
232 exp_typ := if unwrap_opt_or_res { typ.clear_option_and_result() } else { typ }
233 if unwrap_opt_or_res {
234 typ = exp_typ
235 }
236 is_dump_expr := expr is ast.DumpExpr
237 is_var_mut := g.expr_is_auto_deref_var(expr) && !typ.has_flag(.option)
238 str_fn_name := if mut_arg_option_type != 0 {
239 g.get_str_fn(mut_arg_option_type)
240 } else {
241 g.get_str_fn(exp_typ)
242 }
243 temp_var_needed := expr is ast.CallExpr
244 && (expr.return_type.is_ptr() || g.table.sym(expr.return_type).is_c_struct())
245 mut tmp_var := ''
246 if temp_var_needed {
247 tmp_var = g.new_tmp_var()
248 ret_typ := g.styp(exp_typ)
249 line := g.go_before_last_stmt().trim_space()
250 g.empty_line = true
251 g.write('${ret_typ} ${tmp_var} = ')
252 g.expr(expr)
253 g.writeln(';')
254 g.write(line)
255 }
256 if is_ptr && !is_var_mut {
257 ref_str := '&'.repeat(typ.nr_muls())
258 g.write('builtin__str_intp(1, _MOV((StrIntpData[]){{_S("${ref_str}"), ${si_s_code}, {.d_s = builtin__isnil(')
259 if typ.has_flag(.option) || mut_arg_option_type != 0 {
260 if mut_arg_option_type != 0 {
261 if temp_var_needed {
262 g.write(tmp_var)
263 } else {
264 g.expr(expr)
265 }
266 g.write(') ? _S("nil") : ')
267 } else {
268 g.write('*(${g.base_type(exp_typ)}*)&')
269 if temp_var_needed {
270 g.write(tmp_var)
271 } else {
272 g.expr(expr)
273 }
274 g.write('.data) ? _S("Option(&nil)") : ')
275 }
276 } else {
277 inside_interface_deref_old := g.inside_interface_deref
278 g.inside_interface_deref = false
279 defer(fn) {
280 g.inside_interface_deref = inside_interface_deref_old
281 }
282 if temp_var_needed {
283 g.write(tmp_var)
284 } else if use_raw_interface_smartcast_expr {
285 old_inside_selector_lhs := g.inside_selector_lhs
286 g.inside_selector_lhs = true
287 g.expr(expr)
288 g.inside_selector_lhs = old_inside_selector_lhs
289 } else {
290 g.expr(expr)
291 }
292 g.write(') ? _S("nil") : ')
293 }
294 }
295 g.write2(str_fn_name, '(')
296 if str_method_expects_ptr && !is_ptr {
297 if is_dump_expr || (g.pref.ccompiler_type != .tinyc && expr is ast.CallExpr) {
298 g.write('ADDR(${g.styp(typ)}, ')
299 defer(fn) {
300 g.write(')')
301 }
302 } else {
303 g.write('&')
304 }
305 } else if mut_arg_option_type != 0 {
306 g.write('*')
307 } else if is_ptr && typ.has_flag(.option) {
308 if typ.has_flag(.option_mut_param_t) {
309 g.write('*')
310 } else {
311 g.write('*(${g.styp(typ)}*)&')
312 }
313 } else if !str_method_expects_ptr && !is_shared && (is_ptr || is_var_mut) {
314 if sym.is_c_struct() {
315 g.write(c_struct_ptr(sym, typ, str_method_expects_ptr))
316 } else {
317 g.write('*'.repeat(expr_type.nr_muls()))
318 }
319 } else if !str_method_expects_ptr && is_interface_smartcast_to_nonptr {
320 g.write('*')
321 } else if sym.is_c_struct() {
322 g.write(c_struct_ptr(sym, typ, str_method_expects_ptr))
323 }
324 if expr is ast.ArrayInit {
325 if expr.is_fixed {
326 s := g.styp(expr.typ)
327 if !expr.has_index {
328 g.write('(${s})')
329 }
330 }
331 }
332 if unwrap_opt_or_res {
333 g.expr(expr)
334 } else {
335 if temp_var_needed {
336 g.write(tmp_var)
337 } else if use_raw_interface_smartcast_expr {
338 old_inside_selector_lhs := g.inside_selector_lhs
339 g.inside_selector_lhs = true
340 g.expr_with_cast(expr, typ, typ)
341 g.inside_selector_lhs = old_inside_selector_lhs
342 } else {
343 g.expr_with_cast(expr, typ, typ)
344 }
345 }
346
347 if is_shared {
348 g.write('->val')
349 }
350 g.write(')')
351 if is_ptr && !is_var_mut {
352 g.write('}, 0, 0, 0}}))')
353 }
354 } else {
355 is_var_mut := g.expr_is_auto_deref_var(expr) && !typ.has_flag(.option)
356 str_fn_name := g.get_str_fn(typ)
357 g.write('${str_fn_name}(')
358 if sym.kind != .function {
359 unwrap_option := expr is ast.Ident && expr.or_expr.kind == .propagate_option
360 exp_typ := if unwrap_option { typ.clear_flag(.option) } else { typ }
361 temp_var_needed := expr is ast.CallExpr
362 && (expr.return_type.is_ptr() || g.table.sym(expr.return_type).is_c_struct())
363 mut tmp_var := ''
364 if temp_var_needed {
365 tmp_var = g.new_tmp_var()
366 ret_typ := g.styp(exp_typ)
367 line := g.go_before_last_stmt().trim_space()
368 g.empty_line = true
369 g.write('${ret_typ} ${tmp_var} = ')
370 g.expr(expr)
371 g.writeln(';')
372 g.write(line)
373 }
374 if str_method_expects_ptr && !is_ptr && !typ.has_flag(.option) {
375 g.write('&')
376 } else if typ.has_flag(.option_mut_param_t) {
377 g.write('*')
378 } else if (!str_method_expects_ptr && is_ptr && !is_shared) || is_var_mut {
379 g.write('*'.repeat(typ.nr_muls()))
380 } else {
381 if sym.is_c_struct() {
382 g.write(c_struct_ptr(sym, typ, str_method_expects_ptr))
383 }
384 }
385 if temp_var_needed {
386 g.write(tmp_var)
387 } else {
388 if expr is ast.StructInit && g.table.final_sym(expr.typ).is_primitive_fixed_array() {
389 s := g.styp(expr.typ)
390 g.write('(${s})')
391 }
392 g.expr_with_cast(expr, typ, typ)
393 }
394 } else if typ.has_flag(.option) {
395 // only Option fn receive argument
396 g.expr_with_cast(expr, typ, typ)
397 }
398 g.write(')')
399 }
400}
401