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