v2 / vlib / v / gen / c / auto_str_methods.v
1478 lines · 1421 sloc · 60.65 KB · e35325885cca6c2fbd76257048cfdd9e1de249de
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
7import strings
8
9// BUG: this const is not released from the memory! use a const for now
10// si_s_code = "0x" + int(StrIntpType.si_s).hex() // code for a simple string
11const si_s_code = '0xfe10'
12
13fn (mut g Gen) gen_str_default(sym ast.TypeSymbol, styp string, str_fn_name string) {
14 $if trace_autostr ? {
15 eprintln('> gen_str_default: ${sym.name} | ${styp} | str_fn_name')
16 }
17 mut convertor := ''
18 mut typename_ := ''
19 if sym.parent_idx in ast.integer_type_idxs {
20 convertor = 'int'
21 typename_ = 'int'
22 } else if sym.parent_idx == ast.f32_type_idx {
23 convertor = 'float'
24 typename_ = 'f32'
25 } else if sym.parent_idx == ast.f64_type_idx {
26 convertor = 'double'
27 typename_ = 'f64'
28 } else if sym.parent_idx == ast.bool_type_idx {
29 convertor = 'bool'
30 typename_ = 'bool'
31 } else {
32 verror('could not generate string method for type `${styp}`')
33 }
34 g.definitions.writeln('string ${str_fn_name}(${styp} it);')
35 g.auto_str_funcs.writeln('string ${str_fn_name}(${styp} it) {')
36 if convertor == 'bool' {
37 g.auto_str_funcs.writeln('\tstring tmp1 = builtin__string__plus(_S("${styp}("), ((${convertor})it ? _S("true") : _S("false")));')
38 } else {
39 g.auto_str_funcs.writeln('\tstring tmp1 = builtin__string__plus(_S("${styp}("), builtin__tos3(_string__plus(_S("${typename_}("), _string__plus(_S(")"), _S(")"))).str));')
40 }
41 g.auto_str_funcs.writeln('\tstring tmp2 = builtin__string__plus(tmp1, _S(")"));')
42 g.auto_str_funcs.writeln('\tbuiltin__string_free(&tmp1);')
43 g.auto_str_funcs.writeln('\treturn tmp2;')
44 g.auto_str_funcs.writeln('}')
45}
46
47struct StrType {
48 styp string
49mut:
50 typ ast.Type
51}
52
53fn (mut g Gen) get_str_fn(typ ast.Type) string {
54 $if trace_autostr ? {
55 eprintln('> get_str_fn: ${typ.debug()}')
56 }
57 mut unwrapped := if typ.has_flag(.option) {
58 g.unwrap_generic(typ).clear_flag(.variadic)
59 } else {
60 g.unwrap_generic(typ).set_nr_muls(0).clear_flag(.variadic)
61 }
62 if g.pref.nofloat {
63 if typ == ast.f32_type {
64 unwrapped = ast.u32_type
65 } else if typ == ast.f64_type {
66 unwrapped = ast.u64_type
67 }
68 }
69 // Promote literal types (int_literal -> int, float_literal -> f64) in array/map element types.
70 // These are never emitted as C typedefs (e.g., `Array_int_literal` doesn't exist).
71 unwrapped_sym := g.table.sym(unwrapped)
72 if unwrapped_sym.info is ast.Array {
73 promoted_elem := ast.mktyp(unwrapped_sym.info.elem_type)
74 if promoted_elem != unwrapped_sym.info.elem_type {
75 idx := g.table.find_or_register_array(promoted_elem)
76 if idx > 0 {
77 unwrapped = ast.new_type(idx).derive(unwrapped)
78 }
79 }
80 }
81 styp := g.styp(unwrapped)
82 mut sym := g.table.sym(unwrapped)
83 mut str_fn_name := styp_to_str_fn_name(styp)
84 if mut sym.info is ast.Alias && !sym.has_method('str') {
85 if sym.info.is_import {
86 sym = g.table.sym(sym.info.parent_type)
87 str_fn_name = styp_to_str_fn_name(sym.name)
88 }
89 }
90 if sym.is_builtin() && !str_fn_name.starts_with('builtin__') {
91 str_fn_name = 'builtin__${str_fn_name}'
92 }
93 if sym.has_method_with_generic_parent('str') && !g.pref.new_generic_solver {
94 match mut sym.info {
95 ast.Struct, ast.SumType, ast.Interface {
96 str_fn_name = g.generic_fn_name(sym.info.concrete_types, str_fn_name)
97 }
98 else {}
99 }
100 }
101 if sym.language == .c && !typ.has_flag(.option) && sym.has_method('str') {
102 str_fn_name = util.no_dots(g.cc_type(unwrapped, false)) + '_str'
103 }
104 g.str_types << StrType{
105 typ: unwrapped
106 styp: styp
107 }
108 return str_fn_name
109}
110
111fn (mut g Gen) final_gen_str(typ StrType) {
112 if typ in g.generated_str_fns {
113 return
114 }
115 $if trace_autostr ? {
116 eprintln('> final_gen_str: ${typ}')
117 }
118 g.generated_str_fns << typ
119 sym := g.table.sym(typ.typ)
120 if sym.has_method_with_generic_parent('str') && !(typ.typ.has_flag(.option)
121 || typ.typ.has_flag(.result)) {
122 return
123 }
124 // Skip str generation for unresolved generic parameter types (e.g. T)
125 if sym.kind == .any && !sym.is_builtin() {
126 return
127 }
128 styp := typ.styp
129 str_fn_name := g.get_str_fn(typ.typ)
130 lock g.str_fn_names {
131 if str_fn_name in g.str_fn_names {
132 return
133 }
134 g.str_fn_names << str_fn_name
135 }
136 if typ.typ.has_flag(.option) {
137 opt_typ := if typ.typ.has_flag(.option_mut_param_t) { styp.replace('*', '') } else { styp }
138 // Check if this is a type alias to an option type
139 mut type_name := 'Option'
140 mut unwrapped_typ := g.table.unaliased_type(typ.typ)
141 if unwrapped_typ != typ.typ {
142 // This is a type alias, check if it's an alias to an option type
143 alias_sym := g.table.sym(typ.typ)
144 if alias_sym.kind == .alias && alias_sym.info is ast.Alias {
145 // Check if the parent type has the option flag
146 if alias_sym.info.parent_type.has_flag(.option) {
147 // This is an alias to an option type (e.g., type MyOpt = ?MyStruct)
148 // Use the alias name instead of "Option"
149 mut alias_name := alias_sym.name
150 if alias_name.contains('.') {
151 alias_name = alias_name.all_after_last('.')
152 }
153 type_name = '?${alias_name}'
154 }
155 }
156 }
157 g.gen_str_for_option(typ.typ, opt_typ, str_fn_name, type_name)
158 return
159 }
160 if typ.typ.has_flag(.result) {
161 g.gen_str_for_result(typ.typ, styp, str_fn_name)
162 return
163 }
164 match sym.info {
165 ast.Alias {
166 if sym.info.is_import {
167 g.gen_str_default(sym, styp, str_fn_name)
168 } else {
169 g.gen_str_for_alias(sym.info, styp, str_fn_name)
170 }
171 }
172 ast.Array {
173 g.gen_str_for_array(sym.info, styp, str_fn_name)
174 }
175 ast.ArrayFixed {
176 g.gen_str_for_array_fixed(sym.info, styp, str_fn_name)
177 }
178 ast.Enum {
179 g.gen_str_for_enum(sym.info, styp, str_fn_name)
180 }
181 ast.FnType {
182 g.gen_str_for_fn_type(sym.info, styp, str_fn_name)
183 }
184 ast.Struct {
185 g.gen_str_for_struct(sym.info, sym.language, styp, g.table.type_to_str(typ.typ),
186 str_fn_name)
187 }
188 ast.Map {
189 g.gen_str_for_map(sym.info, styp, str_fn_name)
190 }
191 ast.MultiReturn {
192 g.gen_str_for_multi_return(sym.info, styp, str_fn_name)
193 }
194 ast.SumType {
195 g.gen_str_for_union_sum_type(sym.info, styp, g.table.type_to_str(typ.typ), str_fn_name)
196 }
197 ast.Interface {
198 g.gen_str_for_interface(sym.info, styp, g.table.type_to_str(typ.typ), str_fn_name)
199 }
200 ast.Chan {
201 g.gen_str_for_chan(sym.info, styp, str_fn_name)
202 }
203 ast.Thread {
204 g.gen_str_for_thread(sym.info, styp, str_fn_name)
205 }
206 else {
207 if sym.name != 'nil' {
208 verror('could not generate string method `${str_fn_name}` for type `${styp}`')
209 }
210 }
211 }
212}
213
214fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string, type_name string) {
215 $if trace_autostr ? {
216 eprintln('> gen_str_for_option: ${typ.debug()} | ${styp} | ${str_fn_name}')
217 }
218 parent_type := typ.clear_flag(.option)
219 sym := g.table.sym(parent_type)
220 sym_has_str_method, expects_ptr, _ := sym.str_method_info()
221 parent_str_fn_name := g.get_str_fn(parent_type)
222 parent_cast_type := g.auto_str_storage_cast_type(parent_type)
223
224 g.definitions.writeln('string ${str_fn_name}(${styp} it);')
225 g.auto_str_funcs.writeln('string ${str_fn_name}(${styp} it) { return indent_${str_fn_name}(it, 0); }')
226 g.definitions.writeln('string indent_${str_fn_name}(${styp} it, ${ast.int_type_name} indent_count);')
227 g.auto_str_funcs.writeln('string indent_${str_fn_name}(${styp} it, ${ast.int_type_name} indent_count) {')
228 g.auto_str_funcs.writeln('\tstring res;')
229 g.auto_str_funcs.writeln('\tif (it.state == 0) {')
230 deref := if typ.is_ptr() && !typ.has_flag(.option_mut_param_t) {
231 dot := if expects_ptr { '*'.repeat(typ.nr_muls()) } else { '*'.repeat(typ.nr_muls() + 1) }
232 '${dot}(${parent_cast_type}**)&'
233 } else if typ.has_flag(.option_mut_param_t) {
234 '*(${parent_cast_type}*)'
235 } else if expects_ptr {
236 '(${parent_cast_type}*)'
237 } else {
238 '*(${parent_cast_type}*)'
239 }
240 if sym.kind == .string {
241 if typ.nr_muls() > 1 {
242 g.auto_str_funcs.writeln('\t\tres = builtin__ptr_str(*(${parent_cast_type}**)&it.data);')
243 } else {
244 tmp_res := '${parent_str_fn_name}(${deref}it.data)'
245 g.auto_str_funcs.writeln('\t\tres = ${str_intp_sq(tmp_res)};')
246 }
247 } else if should_use_indent_func(sym.kind) && !sym_has_str_method {
248 g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(${deref}it.data, indent_count);')
249 } else if sym.kind == .function {
250 g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}();')
251 } else {
252 if typ.nr_muls() > 1 {
253 g.auto_str_funcs.writeln('\t\tres = builtin__ptr_str(*(${parent_cast_type}**)&it.data);')
254 } else {
255 g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(${deref}it.data);')
256 }
257 }
258 g.auto_str_funcs.writeln('\t\treturn ${str_intp_sub('${type_name}(%%)', 'res')};')
259 g.auto_str_funcs.writeln('\t}')
260 g.auto_str_funcs.writeln('\treturn _S("${type_name}(none)");')
261 g.auto_str_funcs.writeln('}')
262}
263
264fn (mut g Gen) gen_str_for_result(typ ast.Type, styp string, str_fn_name string) {
265 $if trace_autostr ? {
266 eprintln('> gen_str_for_result: ${typ.debug()} | ${styp} | ${str_fn_name}')
267 }
268 parent_type := typ.clear_flag(.result)
269 sym := g.table.sym(parent_type)
270 sym_has_str_method, _, _ := sym.str_method_info()
271 parent_str_fn_name := g.get_str_fn(parent_type)
272 parent_cast_type := g.auto_str_storage_cast_type(parent_type)
273
274 g.definitions.writeln('string ${str_fn_name}(${styp} it);')
275 g.auto_str_funcs.writeln('string ${str_fn_name}(${styp} it) { return indent_${str_fn_name}(it, 0); }')
276 g.definitions.writeln('string indent_${str_fn_name}(${styp} it, ${ast.int_type_name} indent_count);')
277 g.auto_str_funcs.writeln('string indent_${str_fn_name}(${styp} it, ${ast.int_type_name} indent_count) {')
278 g.auto_str_funcs.writeln('\tstring res;')
279 g.auto_str_funcs.writeln('\tif (!it.is_error) {')
280 if sym.kind == .string {
281 tmp_res := '${parent_str_fn_name}(*(${parent_cast_type}*)it.data)'
282 g.auto_str_funcs.writeln('\t\tres = ${str_intp_sq(tmp_res)};')
283 } else if should_use_indent_func(sym.kind) && !sym_has_str_method {
284 g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(*(${parent_cast_type}*)it.data, indent_count);')
285 } else {
286 g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*(${parent_cast_type}*)it.data);')
287 }
288 g.auto_str_funcs.writeln('\t} else {')
289
290 tmp_str := str_intp_sub('error: %%', 'builtin__IError_str(it.err)')
291 g.auto_str_funcs.writeln('\t\tres = ${tmp_str};')
292 g.auto_str_funcs.writeln('\t}')
293
294 g.auto_str_funcs.writeln('\treturn ${str_intp_sub('Result(%%)', 'res')};')
295 g.auto_str_funcs.writeln('}')
296}
297
298@[inline]
299fn (mut g Gen) auto_str_storage_cast_type(typ ast.Type) string {
300 return g.base_type(typ).trim('*')
301}
302
303fn (mut g Gen) gen_str_for_alias(info ast.Alias, styp string, str_fn_name string) {
304 parent_str_fn_name := g.get_str_fn(info.parent_type)
305 parent_sym := g.table.sym(info.parent_type)
306 _, str_method_expects_ptr, _ := parent_sym.str_method_info()
307
308 $if trace_autostr ? {
309 eprintln('> gen_str_for_alias: ${parent_str_fn_name} | ${styp} | ${str_fn_name}')
310 }
311 mut clean_type_v_type_name := util.strip_main_name(styp.replace('__', '.'))
312
313 is_c_struct := parent_sym.is_c_struct() && str_method_expects_ptr
314 arg_def := if is_c_struct { '${styp}* it' } else { '${styp} it' }
315
316 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${arg_def});')
317 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${arg_def}) { return indent_${str_fn_name}(it, 0); }')
318 g.definitions.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${arg_def}, ${ast.int_type_name} indent_count);')
319 g.auto_str_funcs.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${arg_def}, ${ast.int_type_name} indent_count) {')
320 old := g.reset_tmp_count()
321 defer { g.tmp_count = old }
322 if str_method_expects_ptr {
323 it_arg := if is_c_struct { 'it' } else { '&it' }
324 g.auto_str_funcs.writeln('\tstring tmp_ds = ${parent_str_fn_name}(${it_arg});')
325 } else {
326 deref, _ := deref_kind(str_method_expects_ptr, info.parent_type.is_ptr(), info.parent_type)
327 g.auto_str_funcs.writeln('\tstring tmp_ds = ${parent_str_fn_name}(${deref}it);')
328 }
329 g.auto_str_funcs.writeln('\tstring res = builtin__str_intp(2, _MOV((StrIntpData[]){
330 {_S("${clean_type_v_type_name}("), ${si_s_code}, {.d_s = tmp_ds }, 0, 0, 0},
331 {_S(")"), 0, {0}, 0, 0, 0}
332 }));')
333 g.auto_str_funcs.writeln('\tbuiltin__string_free(&tmp_ds);')
334 g.auto_str_funcs.writeln('\treturn res;')
335 g.auto_str_funcs.writeln('}')
336}
337
338fn (mut g Gen) gen_str_for_multi_return(info ast.MultiReturn, styp string, str_fn_name string) {
339 $if trace_autostr ? {
340 eprintln('> gen_str_for_multi_return: ${info.types} | ${styp} | ${str_fn_name}')
341 }
342 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} a);')
343 mut fn_builder := strings.new_builder(512)
344 fn_builder.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} a) {')
345 fn_builder.writeln('\tstrings__Builder sb = strings__new_builder(2 + ${info.types.len} * 10);')
346 fn_builder.writeln('\tstrings__Builder_write_string(&sb, _S("("));')
347 for i, typ in info.types {
348 sym := g.table.sym(typ)
349 is_arg_ptr := typ.is_ptr()
350 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
351 arg_str_fn_name := g.get_str_fn(typ)
352
353 if should_use_indent_func(sym.kind) && !sym_has_str_method {
354 fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}(a.arg${i}));')
355 } else if sym.kind in [.f32, .f64] {
356 if sym.kind == .f32 {
357 tmp_val := str_intp_g32('a.arg${i}')
358 fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${tmp_val});')
359 } else {
360 tmp_val := str_intp_g64('a.arg${i}')
361 fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${tmp_val});')
362 }
363 } else if sym.kind == .string {
364 tmp_str := str_intp_sq('a.arg${i}')
365 fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${tmp_str});')
366 } else if sym.kind == .function {
367 fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}());')
368 } else {
369 deref, deref_label := deref_kind(str_method_expects_ptr, is_arg_ptr, typ)
370 fn_builder.writeln('\t\tstrings__Builder_write_string(&sb, _S("${deref_label}"));')
371 fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}( ${deref} a.arg${i}));')
372 }
373 if i != info.types.len - 1 {
374 fn_builder.writeln('\tstrings__Builder_write_string(&sb, _S(", "));')
375 }
376 }
377 fn_builder.writeln('\tstrings__Builder_write_string(&sb, _S(")"));')
378 fn_builder.writeln('\tstring res = strings__Builder_str(&sb);')
379 fn_builder.writeln('\tstrings__Builder_free(&sb);')
380 fn_builder.writeln('\treturn res;')
381 fn_builder.writeln('}')
382 g.auto_fn_definitions << fn_builder.str()
383}
384
385fn (mut g Gen) gen_str_for_enum(info ast.Enum, styp string, str_fn_name string) {
386 $if trace_autostr ? {
387 eprintln('> gen_str_for_enum: ${info} | ${styp} | ${str_fn_name}')
388 }
389 s := util.no_dots(styp)
390 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} it);')
391 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} it) { /* gen_str_for_enum */')
392 // Enums tagged with `@[flag]` are special in that they can be a combination of enum values
393 if info.is_flag {
394 clean_name := util.strip_main_name(styp.replace('__', '.'))
395 g.auto_str_funcs.writeln('\tstring ret = _S("${clean_name}{");')
396 g.auto_str_funcs.writeln('\t${ast.int_type_name} first = 1;')
397 g.auto_str_funcs.writeln('\tu64 zit = (u64)it;')
398 for i, val in info.vals {
399 mask := u64(1) << i
400 g.auto_str_funcs.writeln('\tif (zit & 0x${mask:016x}U) {if (!first) {ret = builtin__string__plus(ret, _S(" | "));} ret = builtin__string__plus(ret, _S(".${val}")); first = 0;}')
401 }
402 g.auto_str_funcs.writeln('\tret = builtin__string__plus(ret, _S("}"));')
403 g.auto_str_funcs.writeln('\treturn ret;')
404 } else if info.uses_exprs {
405 // The enum values could be C macros, expanded later to duplicate values, and we do not know if that is the case, so we can not use a switch here.
406 // Instead we generate multiple if statements, which is slower, but is guaranteed to work in the presence of duplicates.
407 for val in info.vals {
408 g.auto_str_funcs.writeln('\t\tif(it == ${s}__${val}){ return _S("${val}"); }')
409 }
410 g.auto_str_funcs.writeln('\t\treturn _S("unknown enum value");')
411 } else {
412 g.auto_str_funcs.writeln('\tswitch(it) {')
413 // Only use the first multi value on the lookup
414 mut seen := []string{len: info.vals.len}
415 for val in info.vals {
416 if info.is_multi_allowed && val in seen {
417 continue
418 } else if info.is_multi_allowed {
419 seen << val
420 }
421 g.auto_str_funcs.writeln('\t\tcase ${s}__${val}: return _S("${val}");')
422 }
423 g.auto_str_funcs.writeln('\t\tdefault: return _S("unknown enum value");')
424 g.auto_str_funcs.writeln('\t}')
425 }
426 g.auto_str_funcs.writeln('}')
427}
428
429fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, typ_str string, str_fn_name string) {
430 $if trace_autostr ? {
431 eprintln('> gen_str_for_interface: ${info.types} | ${styp} | ${str_fn_name}')
432 }
433 // _str() functions should have a single argument, the indenting ones take 2:
434 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} x);')
435 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} x) { return indent_${str_fn_name}(x, 0); }')
436 g.definitions.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} x, ${ast.int_type_name} indent_count);')
437 mut fn_builder := strings.new_builder(512)
438 clean_interface_v_type_name := util.strip_main_name(typ_str)
439 fn_builder.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} x, ${ast.int_type_name} indent_count) { /* gen_str_for_interface */')
440 fn_builder.writeln('\tif (x._typ == 0 && x._object == NULL) return _S("nil");')
441 for typ in info.types {
442 sub_sym := g.table.sym(ast.mktyp(typ))
443 if g.pref.skip_unused && sub_sym.idx !in g.table.used_features.used_syms {
444 continue
445 }
446 mut func_name := g.get_str_fn(typ)
447 sym_has_str_method, str_method_expects_ptr, _ := sub_sym.str_method_info()
448 if should_use_indent_func(sub_sym.kind) && !sym_has_str_method {
449 func_name = 'indent_${func_name}'
450 }
451
452 // str_intp
453 if sub_sym.kind == .function {
454 res := 'builtin__str_intp(2, _MOV((StrIntpData[]){
455 {_S("${clean_interface_v_type_name}("), ${si_s_code}, {.d_s = ${func_name}()}, 0, 0, 0},
456 {_S(")"), 0, {0}, 0, 0, 0}
457 }))'
458 fn_builder.write_string2('\tif (x._typ == _${styp}_${sub_sym.cname}_index)',
459 ' return ${res};\n')
460 continue
461 }
462 deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
463 if typ == ast.string_type {
464 mut val := '${func_name}(${deref}(${sub_sym.cname}*)x._${sub_sym.cname}'
465 if should_use_indent_func(sub_sym.kind) && !sym_has_str_method {
466 val += ', indent_count'
467 }
468 val += ')'
469 res := 'builtin__str_intp(2, _MOV((StrIntpData[]){
470 {_S("${clean_interface_v_type_name}(\'"), ${si_s_code}, {.d_s = ${val}}, 0, 0, 0},
471 {_S("\')"), 0, {0}, 0, 0, 0}
472 }))'
473 fn_builder.write_string2('\tif (x._typ == _${styp}_${sub_sym.cname}_index)',
474 ' return ${res};')
475 } else {
476 if !(sub_sym.kind == .array && g.table.sym(g.table.value_type(typ)).cname == styp) {
477 mut val := '${func_name}(${deref}(${sub_sym.cname}*)x._${sub_sym.cname}'
478 if should_use_indent_func(sub_sym.kind) && !sym_has_str_method {
479 val += ', indent_count'
480 }
481 val += ')'
482 res := 'builtin__str_intp(2, _MOV((StrIntpData[]){
483 {_S("${clean_interface_v_type_name}("), ${si_s_code}, {.d_s = ${val}}, 0, 0, 0},
484 {_S(")"), 0, {0}, 0, 0, 0}
485 }))'
486 if should_use_indent_func(sub_sym.kind) {
487 tmpvar := g.new_tmp_var()
488 fn_builder.writeln('\tif (x._typ == _${styp}_${sub_sym.cname}_index) {')
489 fn_builder.writeln('\t\tif (builtin__isnil(x._object) || builtin__autostr_addr_in_stack(x._object)) {')
490 fn_builder.writeln('\t\t\treturn builtin__isnil(x._object) ? _S("nil") : _S("<circular>");')
491 fn_builder.writeln('\t\t}')
492 fn_builder.writeln('\t\tbuiltin__autostr_addr_push(x._object);')
493 fn_builder.writeln('\t\tstring ${tmpvar} = ${res};')
494 fn_builder.writeln('\t\tbuiltin__autostr_addr_pop();')
495 fn_builder.writeln('\t\treturn ${tmpvar};')
496 fn_builder.writeln('\t}')
497 } else {
498 fn_builder.write_string2('\tif (x._typ == _${styp}_${sub_sym.cname}_index)',
499 ' return ${res};\n')
500 }
501 } else {
502 fn_builder.write_string2('\tif (x._typ == _${styp}_${sub_sym.cname}_index)',
503 ' return _S("<circular>");\n')
504 }
505 }
506 }
507 fn_builder.writeln('\treturn _S("unknown interface value");')
508 fn_builder.writeln('}')
509 g.auto_fn_definitions << fn_builder.str()
510}
511
512fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, typ_str string, str_fn_name string) {
513 $if trace_autostr ? {
514 eprintln('> gen_str_for_union_sum_type: ${info.variants} | ${styp} | ${str_fn_name}')
515 }
516 // _str() functions should have a single argument, the indenting ones take 2:
517 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} x);')
518 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} x) { return indent_${str_fn_name}(x, 0); }')
519 g.definitions.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} x, ${ast.int_type_name} indent_count);')
520 mut fn_builder := strings.new_builder(512)
521 fn_builder.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} x, ${ast.int_type_name} indent_count) {')
522 mut clean_sum_type_v_type_name := ''
523 if info.is_anon {
524 variant_names := info.variants.map(util.strip_main_name(g.table.sym(it).name))
525 clean_sum_type_v_type_name = '${variant_names.join('|')}'
526 } else {
527 clean_sum_type_v_type_name = util.strip_main_name(typ_str)
528 }
529 fn_builder.writeln('\tswitch(x._typ) {')
530 mut idxs := []int{}
531 for typ in info.variants {
532 if typ in idxs {
533 continue
534 }
535 idxs << typ
536 typ_name := g.styp(typ)
537 mut func_name := g.get_str_fn(typ)
538 sym := g.table.sym(typ)
539 is_c_struct := sym.is_c_struct()
540 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
541 deref := if is_c_struct || (sym_has_str_method && str_method_expects_ptr) {
542 ' '
543 } else {
544 '*'
545 }
546 if should_use_indent_func(sym.kind) && !sym_has_str_method {
547 func_name = 'indent_${func_name}'
548 }
549
550 // str_intp
551 if typ == ast.string_type {
552 mut val := '${func_name}(${deref}(${typ_name}*)x._${sym.cname}'
553 if should_use_indent_func(sym.kind) && !sym_has_str_method {
554 val += ', indent_count'
555 }
556 val += ')'
557 res := 'builtin__str_intp(2, _MOV((StrIntpData[]){
558 {_S("${clean_sum_type_v_type_name}(\'"), ${si_s_code}, {.d_s = ${val}}, 0, 0, 0},
559 {_S("\')"), 0, {0}, 0, 0, 0}
560 }))'
561 fn_builder.write_string('\t\tcase ${int(typ)}: return ${res};\n')
562 } else {
563 mut val := '${func_name}(${deref}(${typ_name}*)x._${g.get_sumtype_variant_name(typ, sym)}'
564 if should_use_indent_func(sym.kind) && !sym_has_str_method {
565 val += ', indent_count'
566 }
567 val += ')'
568 res := 'builtin__str_intp(2, _MOV((StrIntpData[]){
569 {_S("${clean_sum_type_v_type_name}("), ${si_s_code}, {.d_s = ${val}}, 0, 0, 0},
570 {_S(")"), 0, {0}, 0, 0, 0}
571 }))'
572 fn_builder.write_string('\t\tcase ${int(typ)}: return ${res};\n')
573 }
574 }
575 fn_builder.writeln('\t\tdefault: return _S("unknown sum type value");')
576 fn_builder.writeln('\t}')
577 fn_builder.writeln('}')
578 g.auto_fn_definitions << fn_builder.str()
579}
580
581fn (mut g Gen) fn_decl_str(info ast.FnType) string {
582 mut fn_str := 'fn ('
583 for i, arg in info.func.params {
584 if arg.is_mut {
585 fn_str += 'mut '
586 }
587 if i > 0 {
588 fn_str += ', '
589 }
590 if arg.typ.has_flag(.option) {
591 fn_str += '?'
592 }
593 fn_str += util.strip_main_name(g.table.get_type_name(g.unwrap_generic(arg.typ)))
594 }
595 fn_str += ')'
596 if info.func.return_type == ast.ovoid_type {
597 fn_str += ' ?'
598 } else if info.func.return_type == ast.rvoid_type {
599 fn_str += ' !'
600 } else if info.func.return_type != ast.void_type {
601 x := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.func.return_type)))
602 if info.func.return_type.has_flag(.option) {
603 fn_str += ' ?${x}'
604 } else if info.func.return_type.has_flag(.result) {
605 fn_str += ' !${x}'
606 } else {
607 fn_str += ' ${x}'
608 }
609 }
610 return fn_str
611}
612
613fn (mut g Gen) gen_str_for_fn_type(info ast.FnType, styp string, str_fn_name string) {
614 $if trace_autostr ? {
615 eprintln('> gen_str_for_fn_type: ${info.func.name} | ${styp} | ${str_fn_name}')
616 }
617 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}();')
618 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}() { return _S("${g.fn_decl_str(info)}");}')
619}
620
621fn (mut g Gen) gen_str_for_chan(info ast.Chan, styp string, str_fn_name string) {
622 $if trace_autostr ? {
623 eprintln('> gen_str_for_chan: ${info.elem_type.debug()} | ${styp} | ${str_fn_name}')
624 }
625 elem_type_name := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.elem_type)))
626 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} x);')
627 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} x) { return indent_${str_fn_name}(x, 0);}')
628 g.definitions.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} x, ${ast.int_type_name} indent_count);')
629 g.auto_str_funcs.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} x, ${ast.int_type_name} indent_count) {')
630 g.auto_str_funcs.writeln('\tstring indents = builtin__string_repeat(_S(" "), indent_count);')
631 g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(64);')
632 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("chan "));')
633 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("${elem_type_name}"));')
634 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("{\\n"));')
635 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, indents);')
636 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S(" cap: "));')
637 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, builtin__int_str(x->cap));')
638 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S(", closed: "));')
639 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, (x->closed != 0 ? _S("true") : _S("false")));')
640 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("\\n"));')
641 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, indents);')
642 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("}"));')
643 g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
644 g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
645 g.auto_str_funcs.writeln('\tbuiltin__string_free(&indents);')
646 g.auto_str_funcs.writeln('\treturn res;')
647 g.auto_str_funcs.writeln('}')
648}
649
650fn (mut g Gen) gen_str_for_thread(info ast.Thread, styp string, str_fn_name string) {
651 $if trace_autostr ? {
652 eprintln('> gen_str_for_thread: ${info.return_type.debug()} | ${styp} | ${str_fn_name}')
653 }
654 ret_type_name := util.strip_main_name(g.table.get_type_name(info.return_type))
655 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} _); // auto}')
656 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} _) { return _S("thread(${ret_type_name})");}')
657}
658
659@[inline]
660fn styp_to_str_fn_name(styp string) string {
661 return styp.replace_each(['*', '', '.', '__', ' ', '__']) + '_str'
662}
663
664// deref_kind returns deref, deref_label
665fn deref_kind(str_method_expects_ptr bool, is_elem_ptr bool, typ ast.Type) (string, string) {
666 if typ.has_flag(.option) {
667 return '', ''
668 }
669 if str_method_expects_ptr != is_elem_ptr {
670 if is_elem_ptr {
671 return '*'.repeat(typ.nr_muls()), '&'.repeat(typ.nr_muls())
672 } else {
673 return '&', ''
674 }
675 }
676 return '', ''
677}
678
679fn (mut g Gen) gen_str_for_array(info ast.Array, styp string, str_fn_name string) {
680 $if trace_autostr ? {
681 eprintln('> gen_str_for_array: ${info.elem_type.debug()} | ${styp} | ${str_fn_name}')
682 }
683 mut typ := info.elem_type
684 mut sym := g.table.sym(info.elem_type)
685 is_option := typ.has_flag(.option)
686 if !is_option && mut sym.info is ast.Alias {
687 // Preserve pointer depth when arrays contain alias pointer types.
688 typ = g.unalias_type_keep_muls(typ)
689 sym = g.table.sym(typ)
690 }
691 field_styp := g.styp(typ)
692 is_elem_ptr := typ.is_ptr()
693 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
694 elem_str_fn_name := g.get_str_fn(typ)
695
696 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} a);')
697 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} a) { return indent_${str_fn_name}(a, 0);}')
698 g.definitions.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} a, ${ast.int_type_name} indent_count);')
699 g.auto_str_funcs.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} a, ${ast.int_type_name} indent_count) {')
700 g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(2 + a.len * 10);')
701 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("["));')
702 g.auto_str_funcs.writeln('\tfor (${ast.int_type_name} i = 0; i < a.len; ++i) {')
703 if sym.kind == .function {
704 g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
705 } else {
706 if !typ.has_flag(.option) && sym.kind == .array_fixed {
707 g.auto_str_funcs.writeln('\t\t${field_styp} it;')
708 g.auto_str_funcs.writeln('\t\tmemcpy(*(${field_styp}*)it, (byte*)builtin__array_get(a, i), sizeof(${field_styp}));')
709 } else {
710 g.auto_str_funcs.writeln('\t\t${field_styp} it = *(${field_styp}*)builtin__array_get(a, i);')
711 }
712 if should_use_indent_func(sym.kind) && !sym_has_str_method {
713 if is_elem_ptr {
714 deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
715 g.auto_str_funcs.writeln('\t\tstring x = _S("nil");')
716 if !typ.has_flag(.option) {
717 g.auto_str_funcs.writeln('\t\tif (it != 0) {')
718 }
719 g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _S("${deref_label}"));')
720 g.auto_str_funcs.writeln('\t\t\tx = ${elem_str_fn_name}(${deref}it);')
721 if !typ.has_flag(.option) {
722 g.auto_str_funcs.writeln('\t\t}')
723 }
724 } else {
725 prefix := if !is_option && sym.is_c_struct() && str_method_expects_ptr {
726 '&'
727 } else {
728 ''
729 }
730 g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(${prefix}it, indent_count);')
731 }
732 } else if sym.kind in [.f32, .f64] {
733 if sym.kind == .f32 {
734 g.auto_str_funcs.writeln('\t\tstring x = ${str_intp_g32('it')};')
735 } else {
736 g.auto_str_funcs.writeln('\t\tstring x = ${str_intp_g64('it')};')
737 }
738 } else if sym.kind == .rune {
739 // Rune are managed at this level as strings
740 g.auto_str_funcs.writeln('\t\tstring x = builtin__str_intp(2, _MOV((StrIntpData[]){{_S("\`"), ${si_s_code}, {.d_s = ${elem_str_fn_name}(it) }, 0, 0, 0}, {_S("\`"), 0, {0}, 0, 0, 0}}));\n')
741 } else if sym.kind == .string {
742 if typ.has_flag(.option) {
743 func := g.get_str_fn(typ)
744 g.auto_str_funcs.writeln('\t\tstring x = ${func}(it);\n')
745 } else if is_elem_ptr {
746 g.auto_str_funcs.writeln('\t\tstring x = builtin__str_intp(2, _MOV((StrIntpData[]){{_S("&\'"), ${si_s_code}, {.d_s = *it }, 0, 0, 0}, {_S("\'"), 0, {0}, 0, 0, 0}}));\n')
747 } else {
748 g.auto_str_funcs.writeln('\t\tstring x = builtin__str_intp(2, _MOV((StrIntpData[]){{_S("\'"), ${si_s_code}, {.d_s = it }, 0, 0, 0}, {_S("\'"), 0, {0}, 0, 0, 0}}));\n')
749 }
750 } else {
751 // There is a custom .str() method, so use it.
752 // Note: we need to take account of whether the user has defined
753 // `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly
754 deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
755 if is_elem_ptr {
756 g.auto_str_funcs.writeln('\t\tstring x = _S("nil");')
757 if !typ.has_flag(.option) {
758 g.auto_str_funcs.writeln('\t\tif (it != 0) {')
759 }
760 g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _S("${deref_label}"));')
761 g.auto_str_funcs.writeln('\t\t\tx = ${elem_str_fn_name}(${deref}it);')
762 if !typ.has_flag(.option) {
763 g.auto_str_funcs.writeln('\t\t}')
764 }
765 } else {
766 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _S("${deref_label}"));')
767 g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(${deref}it);')
768 }
769 }
770 }
771 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, x);')
772 if g.is_autofree && typ != ast.bool_type {
773 // no need to free "true"/"false" literals
774 g.auto_str_funcs.writeln('\t\tbuiltin__string_free(&x);')
775 }
776 g.auto_str_funcs.writeln('\t\tif (i < a.len-1) {')
777 g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _S(", "));')
778 g.auto_str_funcs.writeln('\t\t}')
779 g.auto_str_funcs.writeln('\t}')
780 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("]"));')
781 g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
782 g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
783 g.auto_str_funcs.writeln('\treturn res;')
784 g.auto_str_funcs.writeln('}')
785}
786
787fn (mut g Gen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_name string) {
788 $if trace_autostr ? {
789 eprintln('> gen_str_for_array_fixed: ${info.elem_type.debug()} | ${styp} | ${str_fn_name}')
790 }
791 mut typ := info.elem_type
792 mut sym := g.table.sym(info.elem_type)
793 if mut sym.info is ast.Alias {
794 typ = g.unalias_type_keep_muls(typ)
795 sym = g.table.sym(typ)
796 }
797 is_elem_ptr := typ.is_ptr()
798 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
799 elem_str_fn_name := g.get_str_fn(typ)
800 def_arg := if info.is_fn_ret { '${g.styp(typ)} a[${info.size}]' } else { '${styp} a' }
801
802 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${def_arg});')
803 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${def_arg}) { return indent_${str_fn_name}(a, 0);}')
804 g.definitions.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${def_arg}, ${ast.int_type_name} indent_count);')
805 g.auto_str_funcs.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${def_arg}, ${ast.int_type_name} indent_count) {')
806 g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(2 + ${info.size} * 10);')
807 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("["));')
808 g.auto_str_funcs.writeln('\tfor (${ast.int_type_name} i = 0; i < ${info.size}; ++i) {')
809 if sym.kind == .function {
810 g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
811 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, x);')
812 } else {
813 deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
814 if should_use_indent_func(sym.kind) && !sym_has_str_method {
815 if is_elem_ptr {
816 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _S("${deref_label}"));')
817 g.auto_str_funcs.writeln('\t\tif ( 0 == a[i] ) {')
818 g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _S("0"));')
819 g.auto_str_funcs.writeln('\t\t}else{')
820 g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(${deref}a[i]));')
821 g.auto_str_funcs.writeln('\t\t}')
822 } else {
823 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(${deref}a[i]));')
824 }
825 } else if sym.kind in [.f32, .f64] && !typ.has_flag(.option) {
826 if sym.kind == .f32 {
827 field_str := str_intp_g32('a[i]')
828 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${field_str});')
829 } else {
830 field_str := str_intp_g64('a[i]')
831 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${field_str});')
832 }
833 } else if sym.kind == .string && !typ.has_flag(.option) {
834 field_str := str_intp_sq('a[i]')
835 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${field_str});')
836 } else if sym.kind == .rune && !typ.has_flag(.option) {
837 tmp_str := str_intp_rune('${elem_str_fn_name}(${deref}a[i])')
838 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${tmp_str});')
839 } else {
840 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(${deref}a[i]));')
841 }
842 }
843 g.auto_str_funcs.writeln('\t\tif (i < ${info.size - 1}) {')
844 g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _S(", "));')
845 g.auto_str_funcs.writeln('\t\t}')
846 g.auto_str_funcs.writeln('\t}')
847 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("]"));')
848 g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
849 g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
850 g.auto_str_funcs.writeln('\treturn res;')
851 g.auto_str_funcs.writeln('}')
852}
853
854fn (mut g Gen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) {
855 $if trace_autostr ? {
856 eprintln('> gen_str_for_map: ${info.key_type.debug()} -> ${info.value_type.debug()} | ${styp} | ${str_fn_name}')
857 }
858 mut key_typ := info.key_type
859 mut key_sym := g.table.sym(key_typ)
860 if mut key_sym.info is ast.Alias {
861 key_typ = key_sym.info.parent_type
862 key_sym = g.table.sym(key_typ)
863 }
864 key_styp := g.styp(key_typ)
865 key_str_fn_name := g.get_str_fn(key_typ)
866 if !key_sym.has_method('str') {
867 g.get_str_fn(key_typ)
868 }
869
870 mut val_typ := info.value_type
871 mut val_sym := g.table.sym(val_typ)
872 is_option := val_typ.has_flag(.option)
873 if !is_option && mut val_sym.info is ast.Alias {
874 val_typ = val_sym.info.parent_type
875 val_sym = g.table.sym(val_typ)
876 }
877 val_styp := g.styp(val_typ)
878 mut elem_str_fn_name := g.get_str_fn(val_typ)
879
880 mut receiver_is_ptr := false
881 fn_str := val_sym.find_method_with_generic_parent('str') or { ast.Fn{} }
882
883 if fn_str.name == 'str' {
884 receiver_is_ptr = fn_str.receiver_type.is_ptr()
885 } else {
886 g.get_str_fn(val_typ)
887 }
888
889 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} m);')
890 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${styp} m) { return indent_${str_fn_name}(m, 0);}')
891 g.definitions.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} m, ${ast.int_type_name} indent_count);')
892 g.auto_str_funcs.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} m, ${ast.int_type_name} indent_count) { /* gen_str_for_map */')
893 g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(2 + m.key_values.len * 10);')
894 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("{"));')
895 g.auto_str_funcs.writeln('\tbool is_first = true;')
896 g.auto_str_funcs.writeln('\tfor (${ast.int_type_name} i = 0; i < m.key_values.len; ++i) {')
897 g.auto_str_funcs.writeln('\t\tif (!builtin__DenseArray_has_index(&m.key_values, i)) { continue; }')
898 g.auto_str_funcs.writeln('\t\telse if (!is_first) { strings__Builder_write_string(&sb, _S(", ")); }')
899
900 if key_sym.kind == .string {
901 g.auto_str_funcs.writeln('\t\tstring key = *(string*)builtin__DenseArray_key(&m.key_values, i);')
902 } else if key_sym.kind == .array_fixed {
903 g.auto_str_funcs.writeln('\t\t${key_styp} key;')
904 g.auto_str_funcs.writeln('\t\tmemcpy(key, builtin__DenseArray_key(&m.key_values, i), sizeof(${key_styp}));')
905 } else {
906 g.auto_str_funcs.writeln('\t\t${key_styp} key = *(${key_styp}*)builtin__DenseArray_key(&m.key_values, i);')
907 }
908 if key_sym.kind == .string {
909 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_sq('key')});')
910 } else if key_sym.kind == .rune {
911 tmp_str := str_intp_rune('${key_str_fn_name}(key)')
912 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${tmp_str});')
913 } else {
914 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${key_str_fn_name}(key));')
915 }
916 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _S(": "));')
917 _, str_method_expects_ptr, _ := val_sym.str_method_info()
918 _, deref_label := deref_kind(str_method_expects_ptr, val_typ.is_ptr(), val_typ)
919 if deref_label != '' {
920 g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _S("${deref_label}"));')
921 }
922 if val_sym.kind == .function {
923 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}());')
924 } else if val_sym.kind == .string {
925 if val_typ.has_flag(.option) {
926 func := g.get_str_fn(val_typ)
927 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${func}(*(${val_styp}*)builtin__DenseArray_value(&m.key_values, i)));')
928 } else {
929 tmp_str := str_intp_sq('*(${val_styp}*)builtin__DenseArray_value(&m.key_values, i)')
930 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${tmp_str});')
931 }
932 } else if should_use_indent_func(val_sym.kind) && fn_str.name != 'str' {
933 ptr_str := if !is_option && val_sym.is_c_struct() && str_method_expects_ptr {
934 ''
935 } else {
936 '*'.repeat(val_typ.nr_muls() + 1)
937 }
938 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, indent_${elem_str_fn_name}(${ptr_str}(${val_styp}*)builtin__DenseArray_value(&m.key_values, i), indent_count));')
939 } else if val_sym.kind in [.f32, .f64] {
940 tmp_val := '*(${val_styp}*)builtin__DenseArray_value(&m.key_values, i)'
941 if val_typ.has_flag(.option) {
942 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${g.get_str_fn(val_typ)}(*(${val_styp}*)builtin__DenseArray_value(&m.key_values, i)));')
943 } else {
944 if val_sym.kind == .f32 {
945 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g32(tmp_val)});')
946 } else {
947 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g64(tmp_val)});')
948 }
949 }
950 } else if val_sym.kind == .rune {
951 tmp_str :=
952 str_intp_rune('${elem_str_fn_name}(*(${val_styp}*)builtin__DenseArray_value(&m.key_values, i))')
953 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${tmp_str});')
954 } else {
955 ptr_str := '*'.repeat(val_typ.nr_muls())
956 if val_typ.has_flag(.option) {
957 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${g.get_str_fn(val_typ)}(*${ptr_str}(${val_styp}*)builtin__DenseArray_value(&m.key_values, i)));')
958 } else if receiver_is_ptr {
959 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(${ptr_str}(${val_styp}*)builtin__DenseArray_value(&m.key_values, i)));')
960 } else {
961 g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(*${ptr_str}(${val_styp}*)builtin__DenseArray_value(&m.key_values, i)));')
962 }
963 }
964 g.auto_str_funcs.writeln('\t\tis_first = false;')
965 g.auto_str_funcs.writeln('\t}')
966 g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _S("}"));')
967 g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
968 g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
969 g.auto_str_funcs.writeln('\treturn res;')
970 g.auto_str_funcs.writeln('}')
971}
972
973fn (g &Gen) type_to_fmt(typ ast.Type) StrIntpType {
974 if typ == ast.u8_type_idx {
975 return .si_u8
976 }
977 if typ == ast.char_type_idx {
978 return .si_c
979 }
980 if typ in ast.voidptr_types || typ == ast.nil_type || typ in ast.byteptr_types {
981 return .si_p
982 }
983 if typ in ast.charptr_types {
984 // return '%C\\000' // a C string
985 return .si_s
986 }
987 typ_nr_muls := typ.nr_muls()
988 if typ_nr_muls > 1 {
989 return .si_p
990 }
991 sym := g.table.sym(typ)
992 if typ.is_int_valptr() || typ.is_float_valptr() {
993 return .si_s
994 } else if sym.kind in [.struct, .array, .array_fixed, .map, .bool, .enum, .interface, .sum_type,
995 .function, .alias, .chan, .thread] {
996 return .si_s
997 } else if sym.kind == .string {
998 return .si_s
999 // return "'%.*s\\000'"
1000 } else if sym.kind in [.f32, .f64] {
1001 if sym.kind == .f32 {
1002 return .si_g32
1003 }
1004 return .si_g64
1005 } else if sym.kind == .int {
1006 $if new_int ? && x64 {
1007 return .si_i64
1008 } $else {
1009 return .si_i32
1010 }
1011 } else if sym.kind == .u32 {
1012 return .si_u32
1013 } else if sym.kind == .u64 {
1014 return .si_u64
1015 } else if sym.kind == .i64 {
1016 return .si_i64
1017 } else if sym.kind == .usize {
1018 return .si_u64
1019 } else if sym.kind == .isize {
1020 return .si_i64
1021 }
1022 return .si_i32
1023}
1024
1025fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp string, typ_str string, str_fn_name string) {
1026 $if trace_autostr ? {
1027 eprintln('> gen_str_for_struct: ${info.parent_type.debug()} | ${styp} | ${str_fn_name}')
1028 }
1029 // _str() functions should have a single argument, the indenting ones take 2:
1030 is_c_struct := lang == .c
1031 arg_def := if is_c_struct { '${styp}* it' } else { '${styp} it' }
1032 g.definitions.writeln('${g.static_non_parallel}string ${str_fn_name}(${arg_def});')
1033 g.auto_str_funcs.writeln('${g.static_non_parallel}string ${str_fn_name}(${arg_def}) { return indent_${str_fn_name}(it, 0);}')
1034 g.definitions.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${arg_def}, ${ast.int_type_name} indent_count);')
1035 mut fn_builder := strings.new_builder(512)
1036 defer {
1037 g.auto_fn_definitions << fn_builder.str()
1038 }
1039 fn_builder.writeln('string indent_${str_fn_name}(${arg_def}, ${ast.int_type_name} indent_count) {')
1040 old := g.reset_tmp_count()
1041 defer { g.tmp_count = old }
1042 clean_struct_v_type_name := if info.is_anon { 'struct ' } else { util.strip_main_name(typ_str) }
1043 // generate ident / indent length = 4 spaces
1044 if info.fields.len == 0 {
1045 fn_builder.writeln('\treturn _S("${clean_struct_v_type_name}{}");')
1046 fn_builder.writeln('}')
1047 return
1048 }
1049 allow_circular := info.attrs.any(it.name == 'autostr' && it.arg == 'allowrecurse')
1050 if g.pref.hide_auto_str {
1051 fn_builder.writeln('\tstring res = { .str ="str() used with -hide-auto-str", .len=30 };')
1052 fn_builder.writeln('\treturn res;')
1053 fn_builder.writeln('}')
1054 return
1055 }
1056 if !allow_circular {
1057 fn_builder.writeln('\tif (indent_count > 20) {')
1058 fn_builder.writeln('\t\treturn _S("<circular>");')
1059 fn_builder.writeln('\t}')
1060 }
1061 fn_builder.writeln('\tstring indents = builtin__string_repeat(_S(" "), indent_count);')
1062
1063 mut fn_body_surrounder := util.new_surrounder(info.fields.len)
1064 mut fn_body := strings.new_builder(info.fields.len * 256)
1065 defer {
1066 fn_body_surrounder.builder_write_befores(mut fn_builder)
1067 fn_builder << fn_body
1068 fn_body_surrounder.builder_write_afters(mut fn_builder)
1069 fn_builder.writeln('\tbuiltin__string_free(&indents);')
1070 fn_builder.writeln('\treturn res;')
1071 fn_builder.writeln('}')
1072 }
1073 // find `[str: skip]` fields
1074 mut field_skips := []int{}
1075 for i, field in info.fields {
1076 if attr := field.attrs.find_first('str') {
1077 if attr.arg == 'skip' {
1078 field_skips << i
1079 }
1080 }
1081 }
1082 // For @[typedef] C structs, V's declared field type may differ from the
1083 // actual C struct field type (e.g. V declares a field as `voidptr` while
1084 // the C header has it as a value struct like `FT_BBox`). Casting a struct
1085 // to voidptr in the auto-generated str() would be a C compile error, so
1086 // skip voidptr fields entirely for @[typedef] structs — users can write
1087 // their own str() method if they need to print these opaque fields.
1088 if is_c_struct && info.is_typedef {
1089 for i, field in info.fields {
1090 if i in field_skips {
1091 continue
1092 }
1093 if field.typ in ast.cptr_types {
1094 field_skips << i
1095 }
1096 }
1097 }
1098 fn_body.writeln('\tstring res = builtin__str_intp( ${(info.fields.len - field_skips.len) * 4 + 3}, _MOV((StrIntpData[]){')
1099 fn_body.writeln('\t\t{_S("${clean_struct_v_type_name}{\\n"), 0, {0}, 0, 0, 0},')
1100
1101 mut is_first := true
1102 for i, field in info.fields {
1103 // Skip `str:skip` fields
1104 if i in field_skips {
1105 continue
1106 }
1107 ftyp_noshared := if field.typ.has_flag(.shared_f) {
1108 field.typ.deref().clear_flag(.shared_f)
1109 } else {
1110 field.typ
1111 }
1112 mut ptr_amp := if ftyp_noshared.is_ptr() { '&' } else { '' }
1113 mut base_typ := g.unwrap_generic(field.typ)
1114 if base_typ.has_flag(.shared_f) {
1115 base_typ = base_typ.clear_flag(.shared_f).deref()
1116 }
1117 base_fmt := g.type_to_fmt(base_typ)
1118 is_opt_field := field.typ.has_flag(.option)
1119
1120 // manage prefix and quote symbol for the filed
1121 mut quote_str := ''
1122 mut prefix := ''
1123 sym := g.table.sym(g.unwrap_generic(field.typ))
1124 if !is_opt_field {
1125 if sym.kind == .string {
1126 quote_str = "'"
1127 } else if field.typ in ast.charptr_types {
1128 quote_str = '\\"'
1129 prefix = 'C'
1130 }
1131 }
1132
1133 if is_first {
1134 // first field doesn't need \n
1135 fn_body.write_string('\t\t{_SLIT0, ${si_s_code}, {.d_s=indents}, 0, 0, 0}, {_S(" ${field.name}: ${ptr_amp}${prefix}"), 0, {0}, 0, 0, 0}, ')
1136 is_first = false
1137 } else {
1138 fn_body.write_string('\t\t{_S("\\n"), ${si_s_code}, {.d_s=indents}, 0, 0, 0}, {_S(" ${field.name}: ${ptr_amp}${prefix}"), 0, {0}, 0, 0, 0}, ')
1139 }
1140
1141 // custom methods management
1142 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
1143 sftyp := g.styp(ftyp_noshared)
1144 mut field_styp := sftyp.replace('*', '')
1145 field_styp_fn_name := if sym_has_str_method {
1146 mut field_fn_name := if ftyp_noshared.has_flag(.option) {
1147 g.get_str_fn(ftyp_noshared)
1148 } else {
1149 left_cc_type := g.cc_type(ftyp_noshared, false)
1150 left_fn_name := util.no_dots(left_cc_type)
1151 '${left_fn_name}_str'
1152 }
1153 if sym.info is ast.Struct && !g.pref.new_generic_solver {
1154 field_fn_name = g.generic_fn_name(sym.info.concrete_types, field_fn_name)
1155 }
1156 if sym.is_builtin() && !field_fn_name.starts_with('builtin__') {
1157 field_fn_name = 'builtin__${field_fn_name}'
1158 }
1159 field_fn_name
1160 } else {
1161 g.get_str_fn(ftyp_noshared)
1162 }
1163 // with floats we use always the g representation:
1164 if is_opt_field {
1165 fn_body.write_string('{_S("${quote_str}"), ${si_s_code}, {.d_s=')
1166 } else if sym.kind !in [.f32, .f64] {
1167 fn_body.write_string('{_S("${quote_str}"), ${int(base_fmt)}, {.${data_str(base_fmt)}=')
1168 } else {
1169 g_fmt := '0x' + (u32(base_fmt) | u32(0x7F) << 9).hex()
1170 fn_body.write_string('{_S("${quote_str}"), ${g_fmt}, {.${data_str(base_fmt)}=')
1171 }
1172
1173 mut funcprefix := ''
1174 mut func, mut caller_should_free := struct_auto_str_func(sym, lang, field.typ,
1175 field_styp_fn_name, field.name, sym_has_str_method, str_method_expects_ptr)
1176 ftyp_nr_muls := field.typ.nr_muls()
1177 field_name := if lang == .c { field.name } else { c_name(field.name) }
1178 op := if is_c_struct { '->' } else { '.' }
1179 it_field_name := 'it${op}${field_name}'
1180 if ftyp_nr_muls > 1 || field.typ in ast.cptr_types {
1181 if is_opt_field {
1182 } else {
1183 func = '(voidptr) ${it_field_name}'
1184 caller_should_free = false
1185 }
1186 } else if ftyp_noshared.is_ptr() {
1187 if ftyp_noshared.has_flag(.option) {
1188 // optional pointer types: let the option auto-str function handle the none case
1189 } else {
1190 // reference types can be "nil"
1191 funcprefix += 'builtin__isnil(${it_field_name})'
1192 funcprefix += ' ? _S("nil") : '
1193 // struct, floats and ints have a special case through the _str function
1194 if sym.kind !in [.struct, .alias, .enum, .sum_type, .map, .interface, .bool]
1195 && !field.typ.is_int_valptr() && !field.typ.is_float_valptr() {
1196 funcprefix += '*'
1197 }
1198 }
1199 }
1200 mut is_field_array := false
1201 if sym.info is ast.Array {
1202 field_styp = g.styp(sym.info.elem_type).trim('*')
1203 is_field_array = true
1204 } else if sym.info is ast.ArrayFixed {
1205 field_styp = g.styp(sym.info.elem_type).trim('*')
1206 is_field_array = true
1207 }
1208 // handle circular ref type of struct to the struct itself
1209 if styp == field_styp && !allow_circular {
1210 if is_field_array {
1211 tmpvar := g.new_tmp_var()
1212 if is_opt_field {
1213 arr_styp := g.base_type(field.typ)
1214 fn_body_surrounder.add('\tstring ${tmpvar} = ${funcprefix}builtin__autostr_array_circular(${it_field_name}.state != 2 ? (*(${arr_styp}*)${it_field_name}.data).len : 0);',
1215 '\tbuiltin__string_free(&${tmpvar});')
1216 } else {
1217 fn_body_surrounder.add('\tstring ${tmpvar} = ${funcprefix}builtin__autostr_array_circular(${it_field_name}.len);',
1218 '\tbuiltin__string_free(&${tmpvar});')
1219 }
1220 fn_body.write_string(tmpvar)
1221 } else {
1222 fn_body.write_string('${funcprefix}_S("<circular>")')
1223 }
1224 } else {
1225 // manage C charptr
1226 if field.typ in ast.charptr_types {
1227 fn_body.write_string('builtin__tos4((byteptr)${func})')
1228 } else {
1229 is_ptr_field := field.typ.is_ptr() && sym.kind in [.struct, .interface]
1230 is_opt_ptr_field := field.typ.has_flag(.option) && field.typ.is_ptr()
1231 && sym.kind in [.struct, .interface]
1232 if is_ptr_field && !field.typ.has_flag(.option) {
1233 // Use address-based circular reference detection for pointer fields.
1234 // This correctly detects actual circular references (same instance)
1235 // without false positives from different instances of the same type.
1236 tmpvar := g.new_tmp_var()
1237 mut before := '\tstring ${tmpvar};\n'
1238 before += '\tif (builtin__isnil((voidptr)${it_field_name}) || builtin__autostr_addr_in_stack((voidptr)${it_field_name})) {\n'
1239 before += '\t\t${tmpvar} = ${funcprefix}builtin__isnil((voidptr)${it_field_name}) ? _S("nil") : _S("<circular>");\n'
1240 before += '\t} else {\n'
1241 before += '\t\tbuiltin__autostr_addr_push((voidptr)${it_field_name});\n'
1242 before += '\t\t${tmpvar} = ${funcprefix}${func};\n'
1243 before += '\t\tbuiltin__autostr_addr_pop();\n'
1244 before += '\t}'
1245 mut after := ''
1246 if caller_should_free {
1247 after = '\tbuiltin__string_free(&${tmpvar});'
1248 }
1249 fn_body_surrounder.add(before, after)
1250 fn_body.write_string(tmpvar)
1251 } else if is_opt_ptr_field {
1252 // Use address-based circular reference detection for option pointer fields (?&Struct).
1253 // Extract the pointer from the option struct's data field.
1254 tmpvar := g.new_tmp_var()
1255 mut before := '\tstring ${tmpvar};\n'
1256 before += '\tif (${it_field_name}.state != 0) {\n'
1257 before += '\t\t${tmpvar} = ${funcprefix}_S("Option(none)");\n'
1258 before += '\t} else {\n'
1259 before += '\t\tvoidptr ${tmpvar}_addr = *(voidptr*)${it_field_name}.data;\n'
1260 before += '\t\tif (builtin__isnil(${tmpvar}_addr) || builtin__autostr_addr_in_stack(${tmpvar}_addr)) {\n'
1261 before += '\t\t\t${tmpvar} = ${funcprefix}builtin__isnil(${tmpvar}_addr) ? _S("Option(nil)") : _S("<circular>");\n'
1262 before += '\t\t} else {\n'
1263 before += '\t\t\tbuiltin__autostr_addr_push(${tmpvar}_addr);\n'
1264 before += '\t\t\t${tmpvar} = ${funcprefix}${func};\n'
1265 before += '\t\t\tbuiltin__autostr_addr_pop();\n'
1266 before += '\t\t}\n'
1267 before += '\t}'
1268 mut after := ''
1269 if caller_should_free {
1270 after = '\tbuiltin__string_free(&${tmpvar});'
1271 }
1272 fn_body_surrounder.add(before, after)
1273 fn_body.write_string(tmpvar)
1274 } else if caller_should_free {
1275 tmpvar := g.new_tmp_var()
1276 fn_body_surrounder.add('\tstring ${tmpvar} = ${funcprefix}${func};',
1277 '\tbuiltin__string_free(&${tmpvar});')
1278 fn_body.write_string(tmpvar)
1279 } else {
1280 fn_body.write_string2(funcprefix, func)
1281 }
1282 }
1283 }
1284
1285 fn_body.writeln('}, 0, 0, 0}, {_S("${quote_str}"), 0, {0}, 0, 0, 0},')
1286 }
1287 fn_body.writeln('\t\t{_S("\\n"), ${si_s_code}, {.d_s=indents}, 0, 0, 0}, {_S("}"), 0, {0}, 0, 0, 0},')
1288 fn_body.writeln('\t}));')
1289}
1290
1291// c_struct_ptr handles the C struct argument for .str() method
1292fn c_struct_ptr(sym &ast.TypeSymbol, typ ast.Type, expects_ptr bool) string {
1293 if sym.is_c_struct() {
1294 if typ.has_flag(.option) {
1295 return ''
1296 }
1297 if typ.nr_muls() >= 1 {
1298 if expects_ptr {
1299 return '*'.repeat(typ.nr_muls() - 1)
1300 } else {
1301 return '*'.repeat(typ.nr_muls())
1302 }
1303 }
1304 return if expects_ptr { '&' } else { '' }
1305 }
1306 return ''
1307}
1308
1309fn struct_auto_str_func(sym &ast.TypeSymbol, lang ast.Language, _field_type ast.Type, fn_name string, field_name string,
1310 has_custom_str bool, expects_ptr bool) (string, bool) {
1311 $if trace_autostr ? {
1312 eprintln('> struct_auto_str_func: ${sym.name} | field_type.debug() | ${fn_name} | ${field_name} | ${has_custom_str} | ${expects_ptr}')
1313 }
1314 field_type := if _field_type.has_flag(.shared_f) { _field_type.deref() } else { _field_type }
1315 sufix := if field_type.has_flag(.shared_f) { '->val' } else { '' }
1316 deref, _ := deref_kind(expects_ptr, field_type.is_ptr(), field_type)
1317 final_field_name := if lang == .c { field_name } else { c_name(field_name) }
1318 op := if lang == .c { '->' } else { '.' }
1319 prefix := if sym.is_c_struct() { c_struct_ptr(sym, _field_type, expects_ptr) } else { deref }
1320 if sym.kind == .enum {
1321 return '${fn_name}(${deref}(it${op}${final_field_name}))', true
1322 } else if _field_type.has_flag(.option) || should_use_indent_func(sym.kind) {
1323 obj := '${prefix}it${op}${final_field_name}${sufix}'
1324 if has_custom_str {
1325 if sym.kind == .interface && (sym.info as ast.Interface).defines_method('str') {
1326 iface_obj := '${prefix}it${op}${final_field_name}${sufix}'
1327 dot := if field_type.is_ptr() { '->' } else { '.' }
1328 return '${fn_name.trim_string_right('_str')}_name_table[${iface_obj}${dot}_typ]._method_str(${iface_obj}${dot}_object)', true
1329 }
1330 return '${fn_name}(${obj})', true
1331 }
1332 if sym.kind == .struct {
1333 if sym.info is ast.Struct && sym.info.is_anon && !_field_type.has_flag(.option)
1334 && !_field_type.has_flag(.shared_f) {
1335 typed_obj := if lang == .c {
1336 '(${sym.cname}*)&(${obj})'
1337 } else {
1338 '*(${sym.cname}*)&(${obj})'
1339 }
1340 return '${fn_name}(${typed_obj})', true
1341 }
1342 }
1343 return 'indent_${fn_name}(${obj}, indent_count + 1)', true
1344 } else if sym.kind in [.array, .array_fixed, .map, .sum_type] {
1345 obj := '${prefix}it${op}${final_field_name}${sufix}'
1346 if has_custom_str {
1347 return '${fn_name}(${obj})', true
1348 }
1349 return 'indent_${fn_name}(${obj}, indent_count + 1)', true
1350 } else if sym.kind == .function {
1351 return '${fn_name}()', true
1352 } else if sym.kind == .chan {
1353 return '${fn_name}(${deref}it${op}${final_field_name}${sufix})', true
1354 } else if sym.kind == .thread {
1355 return '${fn_name}(${deref}it${op}${final_field_name}${sufix})', false
1356 } else {
1357 mut method_str := ''
1358 if !field_type.is_ptr() && field_type.has_option_or_result() {
1359 method_str = '(*(${sym.name}*)it${op}${final_field_name}.data)'
1360 } else {
1361 method_str = 'it${op}${final_field_name}${sufix}'
1362 }
1363 if sym.kind == .bool {
1364 deref_str := if field_type.is_ptr() { '*${method_str}' } else { method_str }
1365 return '(${deref_str} ? _S("true") : _S("false"))', false
1366 } else if (field_type.is_int_valptr() || field_type.is_float_valptr()) && !expects_ptr {
1367 // ptr int can be "nil", so this needs to be casted to a string
1368 if sym.kind == .f32 {
1369 return 'builtin__str_intp(1, _MOV((StrIntpData[]){
1370 {_SLIT0, ${si_g32_code}, {.d_f32 = *${method_str} }, 0, 0, 0}
1371 }))', true
1372 } else if sym.kind == .f64 {
1373 return 'builtin__str_intp(1, _MOV((StrIntpData[]){
1374 {_SLIT0, ${si_g64_code}, {.d_f64 = *${method_str} }, 0, 0, 0}
1375 }))', true
1376 } else if sym.kind in [.u64, .usize] {
1377 fmt_type := StrIntpType.si_u64
1378 return 'builtin__str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_u64 = *${method_str} }, 0, 0, 0}}))', true
1379 } else if sym.kind in [.i64, .isize] {
1380 fmt_type := StrIntpType.si_i64
1381 return 'builtin__str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_i64 = *${method_str} }, 0, 0, 0}}))', true
1382 }
1383 fmt_type := StrIntpType.si_i32
1384 return 'builtin__str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_i32 = *${method_str} }, 0, 0, 0}}))', true
1385 }
1386 return method_str, false
1387 }
1388}
1389
1390fn data_str(x StrIntpType) string {
1391 return match x {
1392 .si_no_str { 'no_str' }
1393 .si_c { 'd_c' }
1394 .si_u8 { 'd_u8' }
1395 .si_i8 { 'd_i8' }
1396 .si_u16 { 'd_u16' }
1397 .si_i16 { 'd_i16' }
1398 .si_u32 { 'd_u32' }
1399 .si_i32 { 'd_i32' }
1400 .si_u64 { 'd_u64' }
1401 .si_i64 { 'd_i64' }
1402 .si_f32 { 'd_f32' }
1403 .si_f64 { 'd_f64' }
1404 .si_g32 { 'd_f32' } // g32 format use f32 data
1405 .si_g64 { 'd_f64' } // g64 format use f64 data
1406 .si_e32 { 'd_f32' } // e32 format use f32 data
1407 .si_e64 { 'd_f64' } // e64 format use f64 data
1408 .si_s { 'd_s' }
1409 .si_r { 'd_r' } // repeat string
1410 .si_p { 'd_p' }
1411 .si_vp { 'd_vp' }
1412 }
1413}
1414
1415fn should_use_indent_func(kind ast.Kind) bool {
1416 return kind in [.struct, .alias, .array, .array_fixed, .map, .sum_type, .interface, .chan]
1417}
1418
1419fn (mut g Gen) get_enum_type_idx_from_fn_name(fn_name string) (string, int) {
1420 enum_name := fn_name.all_before('__static__')
1421 mut mod_enum_name := if !enum_name.contains('.') {
1422 g.cur_mod.name + '.' + enum_name
1423 } else {
1424 enum_name
1425 }
1426 mut idx := g.table.type_idxs[mod_enum_name]
1427 if idx == 0 && (enum_name.contains('.') || enum_name[0].is_capital()) {
1428 // no cur mod, find from another mods.
1429 for import_sym in g.file.imports {
1430 mod_enum_name = '${import_sym.mod}.${enum_name}'
1431 idx = g.table.type_idxs[mod_enum_name]
1432 if idx > 0 {
1433 break
1434 }
1435 }
1436 }
1437 return mod_enum_name, idx
1438}
1439
1440fn (mut g Gen) gen_enum_static_from_string(fn_name string, mod_enum_name string, idx int) {
1441 enum_typ := ast.idx_to_type(idx)
1442 enum_styp := g.styp(enum_typ)
1443 option_enum_typ := enum_typ.set_flag(.option)
1444 option_enum_styp := g.styp(option_enum_typ)
1445 enum_field_names := g.table.get_enum_field_names(mod_enum_name)
1446 enum_field_vals := g.table.get_enum_field_vals(mod_enum_name)
1447
1448 mut fn_builder := strings.new_builder(512)
1449 g.definitions.writeln('${g.static_non_parallel}${option_enum_styp} ${fn_name}(string name);')
1450
1451 fn_builder.writeln('${g.static_non_parallel}${option_enum_styp} ${fn_name}(string name) {')
1452 fn_builder.writeln('\t${option_enum_styp} t1;')
1453 fn_builder.writeln('\tbool exists = false;')
1454 fn_builder.writeln('\tint inx = 0;')
1455 fn_builder.writeln('\tarray field_names = builtin____new_array_with_default(0, 0, sizeof(string), 0);')
1456 for field_name in enum_field_names {
1457 fn_builder.writeln('\tbuiltin__array_push((array*)&field_names, _MOV((string[]){ _S("${field_name}") }));')
1458 }
1459 fn_builder.writeln('\tarray field_vals = builtin____new_array_with_default(0, 0, sizeof(i64), 0);')
1460 for field_val in enum_field_vals {
1461 fn_builder.writeln('\tbuiltin__array_push((array*)&field_vals, _MOV((i64[]){ ${field_val} }));')
1462 }
1463 fn_builder.writeln('\tfor (${ast.int_type_name} i = 0; i < ${enum_field_names.len}; ++i) {')
1464 fn_builder.writeln('\t\tif (builtin__fast_string_eq(name, (*(string*)builtin__array_get(field_names, i)))) {')
1465 fn_builder.writeln('\t\t\texists = true;')
1466 fn_builder.writeln('\t\t\tinx = i;')
1467 fn_builder.writeln('\t\t\tbreak;')
1468 fn_builder.writeln('\t\t}')
1469 fn_builder.writeln('\t}')
1470 fn_builder.writeln('\tif (exists) {')
1471 fn_builder.writeln('\t\tbuiltin___option_ok(&(${enum_styp}[]){ (*(i64*)builtin__array_get(field_vals, inx)) }, (_option*)&t1, sizeof(${enum_styp}));')
1472 fn_builder.writeln('\t\treturn t1;')
1473 fn_builder.writeln('\t} else {')
1474 fn_builder.writeln('\t\treturn (${option_enum_styp}){ .state=2, .err=_const_none__, .data={E_STRUCT} };')
1475 fn_builder.writeln('\t}')
1476 fn_builder.writeln('}')
1477 g.auto_fn_definitions << fn_builder.str()
1478}
1479