v2 / vlib / v / gen / js / auto_str_methods.v
877 lines · 818 sloc · 32.04 KB · 3c0c27933ec8efde92625344dbfb286f4e7407bc
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 js
4
5import v.ast
6import v.util
7import strings
8
9struct StrType {
10 styp string
11mut:
12 typ ast.Type
13}
14
15fn (mut g JsGen) ensure_autostr_helpers() {
16 if g.generated_autostr_helpers {
17 return
18 }
19 g.generated_autostr_helpers = true
20 g.definitions.writeln('const builtin__autostr_type_stack_max_depth = 64;')
21 g.definitions.writeln('let builtin__g_autostr_type_stack = Array(builtin__autostr_type_stack_max_depth).fill(0);')
22 g.definitions.writeln('let builtin__g_autostr_type_stack_len = 0;')
23 g.definitions.writeln('function builtin__autostr_type_in_stack(typ) {')
24 g.definitions.writeln('\tfor (let i = 0; i < builtin__g_autostr_type_stack_len; ++i) {')
25 g.definitions.writeln('\t\tif (builtin__g_autostr_type_stack[i] === typ) return true;')
26 g.definitions.writeln('\t}')
27 g.definitions.writeln('\treturn false;')
28 g.definitions.writeln('}')
29 g.definitions.writeln('function builtin__autostr_type_push(typ) {')
30 g.definitions.writeln('\tif (builtin__g_autostr_type_stack_len >= builtin__autostr_type_stack_max_depth) return;')
31 g.definitions.writeln('\tbuiltin__g_autostr_type_stack[builtin__g_autostr_type_stack_len++] = typ;')
32 g.definitions.writeln('}')
33 g.definitions.writeln('function builtin__autostr_type_pop() {')
34 g.definitions.writeln('\tif (builtin__g_autostr_type_stack_len > 0) builtin__g_autostr_type_stack_len--;')
35 g.definitions.writeln('}')
36}
37
38fn (mut g JsGen) get_str_fn(typ ast.Type) string {
39 mut unwrapped := g.unwrap_generic(typ).set_nr_muls(0).clear_flag(.variadic)
40 if g.pref.nofloat {
41 if typ == ast.f32_type {
42 unwrapped = ast.u32_type
43 } else if typ == ast.f64_type {
44 unwrapped = ast.u64_type
45 }
46 }
47
48 if typ.has_flag(.option) {
49 unwrapped.set_flag(.option)
50 }
51 styp := g.styp(unwrapped)
52 mut sym := g.table.sym(unwrapped)
53 mut str_fn_name := styp_to_str_fn_name(styp)
54 if mut sym.info is ast.Alias {
55 if sym.info.is_import {
56 sym = g.table.sym(sym.info.parent_type)
57 str_fn_name = styp_to_str_fn_name(sym.name)
58 }
59 }
60 g.str_types << StrType{
61 typ: unwrapped
62 styp: styp
63 }
64 return str_fn_name
65}
66
67fn (mut g JsGen) final_gen_str(typ StrType) {
68 if typ in g.generated_str_fns {
69 return
70 }
71 g.generated_str_fns << typ
72 sym := g.table.sym(typ.typ)
73 if sym.has_method('str') && !typ.typ.has_flag(.option) {
74 return
75 }
76 styp := typ.styp
77 if styp == 'any' {
78 return
79 }
80 str_fn_name := styp_to_str_fn_name(styp)
81 if typ.typ.has_flag(.option) {
82 g.gen_str_for_option(typ.typ, styp, str_fn_name)
83 return
84 }
85 match sym.info {
86 ast.Alias {
87 if sym.info.is_import {
88 g.gen_str_default(sym, styp, str_fn_name)
89 } else {
90 g.gen_str_for_alias(sym.info, styp, str_fn_name)
91 }
92 }
93 ast.Array {
94 g.gen_str_for_array(sym.info, styp, str_fn_name)
95 }
96 ast.ArrayFixed {
97 g.gen_str_for_array_fixed(sym.info, styp, str_fn_name)
98 }
99 ast.Enum {
100 g.gen_str_for_enum(sym.info, styp, str_fn_name)
101 }
102 ast.FnType {
103 g.gen_str_for_fn_type(sym.info, styp, str_fn_name)
104 }
105 ast.Struct {
106 g.gen_str_for_struct(sym.info, styp, str_fn_name, sym.idx)
107 }
108 ast.Map {
109 g.gen_str_for_map(sym.info, styp, str_fn_name)
110 }
111 ast.MultiReturn {
112 g.gen_str_for_multi_return(sym.info, styp, str_fn_name)
113 }
114 ast.SumType {
115 g.gen_str_for_union_sum_type(sym.info, styp, str_fn_name)
116 }
117 ast.Interface {
118 g.gen_str_for_interface(sym.info, styp, str_fn_name)
119 }
120 ast.Chan {
121 g.gen_str_for_chan(sym.info, styp, str_fn_name)
122 }
123 ast.Thread {
124 g.gen_str_for_thread(sym.info, styp, str_fn_name)
125 }
126 else {
127 verror("could not generate string method ${str_fn_name} for type '${styp}'")
128 }
129 }
130}
131
132pub enum StrIntpType {
133 si_no_str = 0 // no parameter to print only fix string
134 si_c
135 si_u8
136 si_i8
137 si_u16
138 si_i16
139 si_u32
140 si_i32
141 si_u64
142 si_i64
143 si_e32
144 si_e64
145 si_f32
146 si_f64
147 si_g32
148 si_g64
149 si_s
150 si_p
151 si_r
152 si_vp
153}
154
155pub fn type_to_str(x StrIntpType) string {
156 match x {
157 .si_no_str { return 'no_str' }
158 .si_c { return 'c' }
159 .si_u8 { return 'u8' }
160 .si_i8 { return 'i8' }
161 .si_u16 { return 'u16' }
162 .si_i16 { return 'i16' }
163 .si_u32 { return 'u32' }
164 .si_i32 { return 'i32' }
165 .si_u64 { return 'u64' }
166 .si_i64 { return 'i64' }
167 .si_f32 { return 'f32' }
168 .si_f64 { return 'f64' }
169 .si_g32 { return 'f32' } // g32 format use f32 data
170 .si_g64 { return 'f64' } // g64 format use f64 data
171 .si_e32 { return 'f32' } // e32 format use f32 data
172 .si_e64 { return 'f64' } // e64 format use f64 data
173 .si_s { return 's' }
174 .si_p { return 'p' }
175 .si_r { return 'r' } // repeat string
176 .si_vp { return 'vp' }
177 }
178}
179
180pub fn data_str(x StrIntpType) string {
181 match x {
182 .si_no_str { return 'no_str' }
183 .si_c { return 'd_c' }
184 .si_u8 { return 'd_u8' }
185 .si_i8 { return 'd_i8' }
186 .si_u16 { return 'd_u16' }
187 .si_i16 { return 'd_i16' }
188 .si_u32 { return 'd_u32' }
189 .si_i32 { return 'd_i32' }
190 .si_u64 { return 'd_u64' }
191 .si_i64 { return 'd_i64' }
192 .si_f32 { return 'd_f32' }
193 .si_f64 { return 'd_f64' }
194 .si_g32 { return 'd_f32' } // g32 format use f32 data
195 .si_g64 { return 'd_f64' } // g64 format use f64 data
196 .si_e32 { return 'd_f32' } // e32 format use f32 data
197 .si_e64 { return 'd_f64' } // e64 format use f64 data
198 .si_s { return 'd_s' }
199 .si_p { return 'd_p' }
200 .si_r { return 'd_r' } // repeat string
201 .si_vp { return 'd_vp' }
202 }
203}
204
205// BUG: this const is not released from the memory! use a const for now
206// si_s_code = "0x" + int(StrIntpType.si_s).hex() // code for a simple string
207const si_s_code = '0xfe10'
208
209fn should_use_indent_func(kind ast.Kind) bool {
210 return kind in [.struct, .alias, .array, .array_fixed, .map, .sum_type, .interface]
211}
212
213fn (mut g JsGen) gen_str_default(sym ast.TypeSymbol, styp string, str_fn_name string) {
214 mut convertor := ''
215 mut typename_ := ''
216 if sym.parent_idx in ast.integer_type_idxs {
217 convertor = 'int'
218 typename_ = 'int'
219 } else if sym.parent_idx == ast.f32_type_idx {
220 convertor = 'float'
221 typename_ = 'f32'
222 } else if sym.parent_idx == ast.f64_type_idx {
223 convertor = 'double'
224 typename_ = 'f64'
225 } else if sym.parent_idx == ast.bool_type_idx {
226 convertor = 'bool'
227 typename_ = 'bool'
228 } else {
229 panic("could not generate string method for type '${styp}'")
230 }
231
232 g.definitions.writeln('function ${str_fn_name}(it) {')
233 if convertor == 'bool' {
234 g.definitions.writeln('\tlet tmp1 = string__plus(new string("${styp}("), it.valueOf()? new string("true") : new string("false"));')
235 } else {
236 g.definitions.writeln('\tlet tmp1 = string__plus(new string("${styp}("), new string(${typename_}_str((${convertor})it).str));')
237 }
238 g.definitions.writeln('\tstring tmp2 = string__plus(tmp1, new string(")"));')
239 g.definitions.writeln('\treturn tmp2;')
240 g.definitions.writeln('}')
241}
242
243fn (mut g JsGen) gen_str_for_option(typ ast.Type, _styp string, str_fn_name string) {
244 parent_type := typ.clear_flag(.option)
245 sym := g.table.sym(parent_type)
246 sym_has_str_method, _, _ := sym.str_method_info()
247 parent_str_fn_name := g.get_str_fn(parent_type)
248
249 g.definitions.writeln('function ${str_fn_name}(it) { return indent_${str_fn_name}(it, 0); }')
250 g.definitions.writeln('function indent_${str_fn_name}(it, indent_count) {')
251 g.definitions.writeln('\tlet res;')
252 g.definitions.writeln('\tif (it.state.val == 0) {')
253 if sym.kind == .string {
254 tmp_res := '${parent_str_fn_name}(it.data)'
255 g.definitions.writeln('\t\tres = ${str_intp_sq(tmp_res)};')
256 } else if should_use_indent_func(sym.kind) && !sym_has_str_method {
257 g.definitions.writeln('\t\tres = indent_${parent_str_fn_name}(it.data, indent_count);')
258 } else {
259 g.definitions.writeln('\t\tres = ${parent_str_fn_name}(it.data);')
260 }
261 g.definitions.writeln('\t} else {')
262
263 tmp_str := str_intp_sub('error: %%', 'IError_str(it.err)')
264 g.definitions.writeln('\t\tres = ${tmp_str};')
265 g.definitions.writeln('\t}')
266
267 g.definitions.writeln('\treturn ${str_intp_sub('Option(%%)', 'res')};')
268 g.definitions.writeln('}')
269}
270
271fn (mut g JsGen) gen_str_for_alias(info ast.Alias, _styp string, str_fn_name string) {
272 parent_str_fn_name := g.get_str_fn(info.parent_type)
273 g.definitions.writeln('function ${str_fn_name}(it) { return indent_${str_fn_name}(it, 0); }')
274
275 g.definitions.writeln('function indent_${str_fn_name}(it, indent_count) {')
276 g.definitions.writeln('\tlet indents = string_repeat(new string(" "), indent_count);')
277 g.definitions.writeln('\tlet tmp_ds = ${parent_str_fn_name}(it);')
278 g.definitions.writeln('\tlet res = new string("TODO");')
279 g.definitions.writeln('\treturn res;')
280 g.definitions.writeln('}')
281}
282
283fn (mut g JsGen) gen_str_for_multi_return(info ast.MultiReturn, _styp string, str_fn_name string) {
284 mut fn_builder := strings.new_builder(512)
285 fn_builder.writeln('function ${str_fn_name}(a) {')
286 fn_builder.writeln('\tlet sb = strings__new_builder(${info.types.len} * 10);')
287 fn_builder.writeln('\tstrings__Builder_write_string(sb, new string("("));')
288 for i, typ in info.types {
289 sym := g.table.sym(typ)
290 is_arg_ptr := typ.is_ptr()
291 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
292 arg_str_fn_name := g.get_str_fn(typ)
293
294 if should_use_indent_func(sym.kind) && !sym_has_str_method {
295 fn_builder.writeln('\tstrings__Builder_write_string(sb, ${arg_str_fn_name}(a[${i}]));')
296 } else if sym.kind in [.f32, .f64] {
297 if sym.kind == .f32 {
298 tmp_val := str_intp_g32('a[${i}]')
299 fn_builder.writeln('\tstrings__Builder_write_string(sb, ${tmp_val});')
300 } else {
301 tmp_val := str_intp_g64('a[${i}]')
302 fn_builder.writeln('\tstrings__Builder_write_string(sb, ${tmp_val});')
303 }
304 } else if sym.kind == .string {
305 tmp_str := str_intp_sq('a[${i}]')
306 fn_builder.writeln('\tstrings__Builder_write_string(sb, ${tmp_str});')
307 } else if sym.kind == .function {
308 fn_builder.writeln('\tstrings__Builder_write_string(sb, ${arg_str_fn_name}());')
309 } else {
310 deref, deref_label := deref_kind(str_method_expects_ptr, is_arg_ptr, typ)
311 fn_builder.writeln('\t\tstrings__Builder_write_string(sb, new string("${deref_label}"));')
312 fn_builder.writeln('\tstrings__Builder_write_string(sb, ${arg_str_fn_name}( a[${i}] ${deref} ));')
313 }
314 if i != info.types.len - 1 {
315 fn_builder.writeln('\tstrings__Builder_write_string(sb, new string(", "));')
316 }
317 }
318 fn_builder.writeln('\tstrings__Builder_write_string(sb, new string(")"));')
319 fn_builder.writeln('\tlet res = strings__Builder_str(sb);')
320 fn_builder.writeln('\treturn res;')
321 fn_builder.writeln('}')
322 g.definitions.writeln(fn_builder.str())
323}
324
325fn (mut g JsGen) gen_str_for_enum(info ast.Enum, styp string, str_fn_name string) {
326 s := util.no_dots(styp)
327
328 g.definitions.writeln('function ${str_fn_name}(it) { /* gen_str_for_enum */')
329 // Enums tagged with `@[flag]` are special in that they can be a combination of enum values
330 if info.is_flag {
331 clean_name := util.strip_main_name(styp.replace('__', '.'))
332 g.definitions.writeln('\tlet ret = new string("${clean_name}{");')
333 g.definitions.writeln('\tlet first = 1;')
334 for i, val in info.vals {
335 g.definitions.writeln('\tif (it & (1 << ${i})) {if (!first) {ret = string__plus(ret, new string(" | "));} ret = string__plus(ret, new string(".${val}")); first = 0;}')
336 }
337 g.definitions.writeln('\tret = string__plus(ret, new string("}"));')
338 g.definitions.writeln('\treturn ret;')
339 } else {
340 g.definitions.writeln('\tswitch(it) {')
341 // Only use the first multi value on the lookup
342 mut seen := []string{len: info.vals.len}
343 for val in info.vals {
344 if info.is_multi_allowed && val in seen {
345 continue
346 } else if info.is_multi_allowed {
347 seen << val
348 }
349 g.definitions.writeln('\t\tcase ${s}.${val}: return new string("${val}");')
350 }
351 g.definitions.writeln('\t\tdefault: return new string("unknown enum value");')
352 g.definitions.writeln('\t}')
353 }
354 g.definitions.writeln('}')
355}
356
357fn (mut g JsGen) gen_str_for_interface(info ast.Interface, styp string, str_fn_name string) {
358 // _str() functions should have a single argument, the indenting ones take 2:
359
360 g.definitions.writeln('function ${str_fn_name}(x) { return indent_${str_fn_name}(x, 0); }')
361
362 mut fn_builder := strings.new_builder(512)
363 mut clean_interface_v_type_name := styp.replace('__', '.')
364 if styp.ends_with('*') {
365 clean_interface_v_type_name = '&' + clean_interface_v_type_name.replace('*', '')
366 }
367 if clean_interface_v_type_name.contains('_T_') {
368 clean_interface_v_type_name =
369 clean_interface_v_type_name.replace('Array_', '[]').replace('_T_', '[').replace('_', ', ') +
370 ']'
371 }
372 clean_interface_v_type_name = util.strip_main_name(clean_interface_v_type_name)
373 fn_builder.writeln('function indent_${str_fn_name}(x,indent_count) { /* gen_str_for_interface */')
374 for typ in info.types {
375 subtype := g.table.sym(typ)
376 mut func_name := g.get_str_fn(typ)
377 sym_has_str_method, str_method_expects_ptr, _ := subtype.str_method_info()
378 if should_use_indent_func(subtype.kind) && !sym_has_str_method {
379 func_name = 'indent_${func_name}'
380 }
381 deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '.valueOf()' }
382 // str_intp
383
384 if typ == ast.string_type {
385 fn_builder.write_string('\tif (x.val instanceof string)')
386 fn_builder.write_string(' return "new string(${clean_interface_v_type_name}(" + x.val.str + ")");')
387 } else {
388 mut val := '${func_name}(x ${deref}'
389 if should_use_indent_func(subtype.kind) && !sym_has_str_method {
390 val += ', indent_count'
391 }
392 val += ')'
393
394 fn_builder.write_string('\tif (x.val instanceof ${subtype.cname})')
395 fn_builder.write_string(' return new string("${clean_interface_v_type_name}(" + ${val}.str + ")");\n')
396 }
397 }
398 fn_builder.writeln('\treturn new string("unknown interface value");')
399 fn_builder.writeln('}')
400 g.definitions.writeln(fn_builder.str())
401}
402
403fn (mut g JsGen) gen_str_for_union_sum_type(info ast.SumType, _styp string, str_fn_name string) {
404 g.definitions.writeln('function ${str_fn_name}(x) { return indent_${str_fn_name}(x, 0); }')
405 mut fn_builder := strings.new_builder(512)
406 fn_builder.writeln('function indent_${str_fn_name}(x, indent_count) {')
407 for typ in info.variants {
408 typ_str := g.styp(typ)
409 mut func_name := g.get_str_fn(typ)
410 sym := g.table.sym(typ)
411 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
412 deref := if sym_has_str_method && str_method_expects_ptr {
413 ' '
414 } else {
415 if typ.is_ptr() { '.valueOf()' } else { ' ' }
416 }
417 if should_use_indent_func(sym.kind) && !sym_has_str_method {
418 func_name = 'indent_${func_name}'
419 }
420 fn_builder.writeln('if (x instanceof ${typ_str}) { return ${func_name}(x${deref}); }')
421 }
422 fn_builder.writeln('builtin__panic(new string("unknown sum type value"));\n}')
423 g.definitions.writeln(fn_builder.str())
424}
425
426fn (mut g JsGen) fn_decl_str(info ast.FnType) string {
427 mut fn_str := 'fn ('
428 for i, arg in info.func.params {
429 if arg.is_mut {
430 fn_str += 'mut '
431 }
432 if i > 0 {
433 fn_str += ', '
434 }
435 if arg.typ.has_flag(.option) {
436 fn_str += '?'
437 }
438 fn_str += util.strip_main_name(g.table.get_type_name(g.unwrap_generic(arg.typ)))
439 }
440 fn_str += ')'
441 if info.func.return_type == ast.ovoid_type {
442 fn_str += ' ?'
443 } else if info.func.return_type == ast.rvoid_type {
444 fn_str += ' !'
445 } else if info.func.return_type != ast.void_type {
446 x := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.func.return_type)))
447 if info.func.return_type.has_flag(.option) {
448 fn_str += ' ?${x}'
449 } else {
450 fn_str += ' ${x}'
451 }
452 }
453 return fn_str
454}
455
456fn (mut g JsGen) gen_str_for_fn_type(info ast.FnType, _styp string, str_fn_name string) {
457 g.definitions.writeln('function ${str_fn_name}() { return new string("${g.fn_decl_str(info)}");}')
458}
459
460fn (mut g JsGen) gen_str_for_chan(info ast.Chan, _styp string, str_fn_name string) {
461 elem_type_name := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.elem_type)))
462
463 g.definitions.writeln('function ${str_fn_name}(x) { return sync__Channel_auto_str(x, new string("${elem_type_name}")); }')
464}
465
466fn (mut g JsGen) gen_str_for_thread(info ast.Thread, _styp string, str_fn_name string) {
467 ret_type_name := util.strip_main_name(g.table.get_type_name(info.return_type))
468
469 g.definitions.writeln('function ${str_fn_name}(_) { return new string("thread(${ret_type_name})");}')
470}
471
472@[inline]
473fn styp_to_str_fn_name(styp string) string {
474 return styp.replace_each(['*', '', '.', '__', ' ', '__']) + '_str'
475}
476
477fn deref_kind(str_method_expects_ptr bool, is_elem_ptr bool, typ ast.Type) (string, string) {
478 if str_method_expects_ptr != is_elem_ptr {
479 if is_elem_ptr {
480 return '.val'.repeat(typ.nr_muls()), 'new \$ref('.repeat(typ.nr_muls())
481 } else {
482 return 'new \$ref', ''
483 }
484 }
485 return '', ''
486}
487
488fn (mut g JsGen) gen_str_for_array(info ast.Array, _styp string, str_fn_name string) {
489 mut typ := info.elem_type
490 mut sym := g.table.sym(info.elem_type)
491 if mut sym.info is ast.Alias {
492 typ = sym.info.parent_type
493 sym = g.table.sym(typ)
494 }
495 is_elem_ptr := typ.is_ptr()
496 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
497 mut elem_str_fn_name := g.get_str_fn(typ)
498 if sym.kind == .u8 {
499 elem_str_fn_name = elem_str_fn_name + '_escaped'
500 }
501
502 g.definitions.writeln('function ${str_fn_name}(a) { return indent_${str_fn_name}(a, 0);}')
503 g.definitions.writeln('function indent_${str_fn_name}(a, indent_count) {')
504 g.definitions.writeln('\tlet sb = strings__new_builder(a.len * 10);')
505 g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("["));')
506 g.definitions.writeln('\tfor (let i = 0; i < a.len; ++i) {')
507 if sym.kind == .function {
508 g.definitions.writeln('\t\tlet it = ${elem_str_fn_name}();')
509 } else {
510 g.definitions.writeln('\t\tlet it = a.arr.get(new int(i));')
511
512 if should_use_indent_func(sym.kind) && !sym_has_str_method {
513 if is_elem_ptr {
514 g.definitions.writeln('\t\tlet x = indent_${elem_str_fn_name}(it.val, indent_count);')
515 } else {
516 g.definitions.writeln('\t\tlet x = indent_${elem_str_fn_name}(it, indent_count);')
517 }
518 } else if sym.kind in [.f32, .f64] {
519 g.definitions.writeln('\t\tlet x = new string( it.val + "");')
520 } else if sym.kind == .rune {
521 g.definitions.writeln('\t\tlet x = new string("\`" + String.fromCharCode(it.val) + "\`");')
522 // Rune are managed at this level as strings
523 // g.definitions.writeln('\t\tstring x = builtin__str_intp(2, _MOV((StrIntpData[]){{new string("\`"), ${c.si_s_code}, {.d_s = ${elem_str_fn_name}(it) }}, {new string("\`"), 0, {.d_c = 0 }}}));\n')
524 } else if sym.kind == .string {
525 g.definitions.writeln('\t\tlet x = new string(it);')
526 // g.definitions.writeln('\t\tstring x = builtin__str_intp(2, _MOV((StrIntpData[]){{new string("\'"), ${c.si_s_code}, {.d_s = it }}, {new string("\'"), 0, {.d_c = 0 }}}));\n')
527 } else {
528 // There is a custom .str() method, so use it.
529 // Note: we need to take account of whether the user has defined
530 // `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly
531 deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
532 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string("${deref_label}"));')
533 g.definitions.writeln('\t\tlet x = ${elem_str_fn_name}( ${deref} it);')
534 }
535 }
536 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, x);')
537
538 g.definitions.writeln('\t\tif (i < a.len-1) {')
539 g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, new string(", "));')
540 g.definitions.writeln('\t\t}')
541 g.definitions.writeln('\t}')
542 g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("]"));')
543 g.definitions.writeln('\tlet res = strings__Builder_str(sb);')
544 g.definitions.writeln('\treturn res;')
545 g.definitions.writeln('}')
546}
547
548fn (mut g JsGen) gen_str_for_array_fixed(info ast.ArrayFixed, _styp string, str_fn_name string) {
549 mut typ := info.elem_type
550 mut sym := g.table.sym(info.elem_type)
551 if mut sym.info is ast.Alias {
552 typ = sym.info.parent_type
553 sym = g.table.sym(typ)
554 }
555 is_elem_ptr := typ.is_ptr()
556 sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
557 elem_str_fn_name := g.get_str_fn(typ)
558
559 g.definitions.writeln('function ${str_fn_name}(a) { return indent_${str_fn_name}(a, 0);}')
560
561 g.definitions.writeln('function indent_${str_fn_name}(a, indent_count) {')
562 g.definitions.writeln('\tlet sb = strings__new_builder(${info.size} * 10);')
563 g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("["));')
564 g.definitions.writeln('\tfor (let i = 0; i < ${info.size}; ++i) {')
565 if sym.kind == .function {
566 g.definitions.writeln('\t\tstring x = ${elem_str_fn_name}();')
567 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, x);')
568 } else {
569 deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
570 if should_use_indent_func(sym.kind) && !sym_has_str_method {
571 if is_elem_ptr {
572 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string("${deref_label}"));')
573 g.definitions.writeln('\t\tif ( 0 == a.arr.get(new int(i)) ) {')
574 g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, new string("0"));')
575 g.definitions.writeln('\t\t}else{')
576 g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr.get(new int(i)) ${deref}) );')
577 g.definitions.writeln('\t\t}')
578 } else {
579 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr.get(new int(i))) );')
580 }
581 } else if sym.kind in [.f32, .f64] {
582 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string(a.arr.get(new int(i)).val.toString()) );')
583 } else if sym.kind == .string {
584 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, a.arr.get(new int(i)));')
585 } else if sym.kind == .rune {
586 g.definitions.writeln('\t\tlet x = new string("\`" + String.fromCharCode(a.arr.get(new int(i)).val) + "\`");')
587 g.definitions.writeln('\t\tstrings__Builder_write_string(sb,x);')
588 } else {
589 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr.get(new int(i)) ${deref}));')
590 }
591 }
592 g.definitions.writeln('\t\tif (i < ${info.size - 1}) {')
593 g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, new string(", "));')
594 g.definitions.writeln('\t\t}')
595 g.definitions.writeln('\t}')
596 g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("]"));')
597 g.definitions.writeln('\tlet res = strings__Builder_str(sb);')
598 g.definitions.writeln('\treturn res;')
599 g.definitions.writeln('}')
600}
601
602fn (mut g JsGen) gen_str_for_map(info ast.Map, _styp string, str_fn_name string) {
603 mut key_typ := info.key_type
604 mut key_sym := g.table.sym(key_typ)
605 if mut key_sym.info is ast.Alias {
606 key_typ = key_sym.info.parent_type
607 key_sym = g.table.sym(key_typ)
608 }
609 key_styp := g.styp(key_typ)
610 key_str_fn_name := key_styp.replace('*', '') + '_str'
611 if !key_sym.has_method('str') {
612 g.get_str_fn(key_typ)
613 }
614
615 mut val_typ := info.value_type
616 mut val_sym := g.table.sym(val_typ)
617 if mut val_sym.info is ast.Alias {
618 val_typ = val_sym.info.parent_type
619 val_sym = g.table.sym(val_typ)
620 }
621 val_styp := g.styp(val_typ)
622 elem_str_fn_name := val_styp.replace('*', '') + '_str'
623 if !val_sym.has_method('str') {
624 g.get_str_fn(val_typ)
625 }
626
627 g.definitions.writeln('function ${str_fn_name}(m) { return indent_${str_fn_name}(m, 0);}')
628
629 g.definitions.writeln('function indent_${str_fn_name}(m, indent_count) { /* gen_str_for_map */')
630 g.definitions.writeln('\tlet sb = strings__new_builder(m.map.length * 10);')
631 g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("{"));')
632 g.definitions.writeln('\tlet i = 0;')
633 g.definitions.writeln('\tlet keys = Object.keys(m.map);')
634 g.definitions.writeln('\tfor (let j = 0; j < keys.length;j++) {')
635 g.definitions.writeln('\t\tlet key = keys[j];')
636 g.definitions.writeln('\t\tlet value = m.map[key].val;')
637 if key_sym.kind == .enum {
638 g.definitions.writeln('\t\tkey = +key;')
639 } else {
640 g.definitions.writeln('\t\tkey = new ${key_styp}(key);')
641 }
642 if key_sym.kind == .string {
643 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string("\'" + key.str + "\'"));')
644 } else if key_sym.kind == .rune {
645 g.definitions.writeln('\t\tlet x = new string("\`" + String.fromCharCode(key.val) + "\`");')
646 g.definitions.writeln('\t\tstrings__Builder_write_string(sb,x);')
647 // g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${tmp_str});')
648 } else {
649 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${key_str_fn_name}(key));')
650 }
651 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string(": "));')
652 if val_sym.kind == .function {
653 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}());')
654 } else if val_sym.kind == .string {
655 // tmp_str := str_intp_sq('*(${val_styp}*)DenseArray_value(&m.key_values, i)')
656 g.definitions.writeln('\t\tstrings__Builder_write_string(sb,new string("\'" + value.str + "\'"));')
657 } else if should_use_indent_func(val_sym.kind) && !val_sym.has_method('str') {
658 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, indent_${elem_str_fn_name}(value, indent_count));')
659 } else if val_sym.kind in [.f32, .f64] {
660 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string(value.val + ""));')
661 } else if val_sym.kind == .rune {
662 g.definitions.writeln('\t\tlet x = new string("\`" + String.fromCharCode(value.val) + "\`");')
663 g.definitions.writeln('\t\tstrings__Builder_write_string(sb,x);')
664 } else {
665 g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(value));')
666 }
667 g.definitions.writeln('\t\tif (i != keys.length-1) {')
668 g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, new string(", "));')
669 g.definitions.writeln('\t\t}')
670 g.definitions.writeln('\t\ti++;')
671 g.definitions.writeln('\t}')
672 g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("}"));')
673 g.definitions.writeln('\tlet res = strings__Builder_str(sb);')
674 g.definitions.writeln('\treturn res;')
675 g.definitions.writeln('}')
676}
677
678fn (g &JsGen) type_to_fmt(typ ast.Type) StrIntpType {
679 if typ == ast.u8_type_idx {
680 return .si_u8
681 }
682 if typ == ast.char_type_idx {
683 return .si_c
684 }
685 if typ in ast.voidptr_types || typ == ast.nil_type || typ in ast.byteptr_types {
686 return .si_p
687 }
688 if typ in ast.charptr_types {
689 // return '%C\\000' // a C string
690 return .si_s
691 }
692 sym := g.table.sym(typ)
693 if typ.is_int_valptr() || typ.is_float_valptr() {
694 return .si_s
695 } else if sym.kind in [.struct, .array, .array_fixed, .map, .bool, .enum, .interface, .sum_type,
696 .function, .alias, .chan] {
697 return .si_s
698 } else if sym.kind == .string {
699 return .si_s
700 // return "'%.*s\\000'"
701 } else if sym.kind in [.f32, .f64] {
702 if sym.kind == .f32 {
703 return .si_g32
704 }
705 return .si_g64
706 } else if sym.kind == .int {
707 return .si_i32
708 } else if sym.kind == .u32 {
709 return .si_u32
710 } else if sym.kind == .u64 {
711 return .si_u64
712 } else if sym.kind == .i64 {
713 return .si_i64
714 } else if sym.kind == .usize {
715 return .si_u64
716 } else if sym.kind == .isize {
717 return .si_i64
718 }
719 return .si_i32
720}
721
722fn (mut g JsGen) gen_str_for_struct(info ast.Struct, styp string, str_fn_name string, type_idx int) {
723 // _str() functions should have a single argument, the indenting ones take 2:
724
725 g.definitions.writeln('function ${str_fn_name}(it) { return indent_${str_fn_name}(it, 0);}')
726
727 mut fn_builder := strings.new_builder(512)
728 defer {
729 g.definitions.writeln(fn_builder.str())
730 }
731 fn_builder.writeln('function indent_${str_fn_name}(it, indent_count) {')
732 mut clean_struct_v_type_name := styp.replace('__', '.')
733 if clean_struct_v_type_name.contains('_T_') {
734 // TODO: this is a bit hacky. styp shouldn't be even parsed with _T_
735 // use something different than g.typ for styp
736 clean_struct_v_type_name =
737 clean_struct_v_type_name.replace('Array_', '[]').replace('_T_', '[').replace('_', ', ') +
738 ']'
739 }
740 clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name)
741 // generate ident / indent length = 4 spaces
742 if info.fields.len == 0 {
743 fn_builder.writeln('\treturn new string("${clean_struct_v_type_name}{}");')
744 fn_builder.writeln('}')
745 return
746 }
747 allow_circular := info.attrs.any(it.name == 'autostr' && it.arg == 'allowrecurse')
748 if !allow_circular {
749 g.ensure_autostr_helpers()
750 fn_builder.writeln('\tif (builtin__autostr_type_in_stack(${type_idx})) {')
751 fn_builder.writeln('\t\treturn new string("<circular>")')
752 fn_builder.writeln('\t}')
753 fn_builder.writeln('\tbuiltin__autostr_type_push(${type_idx})')
754 }
755
756 fn_builder.writeln('\tlet res = /*struct name*/new string("${clean_struct_v_type_name}{\\n")')
757 for i, field in info.fields {
758 mut ptr_amp := if field.typ.is_ptr() { '&' } else { '' }
759 mut prefix := ''
760 // manage prefix and quote symbol for the filed
761 /*
762 mut quote_str := ''
763
764
765 if sym.kind == .string {
766 quote_str = "'"
767 } else if field.typ in ast.charptr_types {
768 quote_str = '\\"'
769 prefix = 'C'
770 }
771 quote_str = quote_str
772 */
773 sym := g.table.sym(g.unwrap_generic(field.typ))
774 // first fields doesn't need \n
775 if i == 0 {
776 fn_builder.write_string('res.str += " ${field.name}: ${ptr_amp}${prefix}" + ')
777 } else {
778 fn_builder.write_string('res.str += "\\n ${field.name}: ${ptr_amp}${prefix}" + ')
779 }
780
781 // custom methods management
782 has_custom_str := sym.has_method('str')
783 mut field_styp := g.styp(field.typ).replace('*', '')
784 field_styp_fn_name := if has_custom_str {
785 '${field_styp}_str'
786 } else {
787 g.get_str_fn(field.typ)
788 }
789
790 mut func := struct_auto_str_func(mut g, sym, field.typ, field_styp_fn_name, field.name)
791 if field.typ in ast.cptr_types {
792 func = '(voidptr) it.${field.name}'
793 } else if field.typ.is_ptr() {
794 // reference types can be "nil"
795 fn_builder.write_string('builtin__isnil(it.${g.js_name(field.name)})')
796 fn_builder.write_string(' ? new string("nil") : ')
797 // struct, floats and ints have a special case through the _str function
798 if sym.kind != .struct && !field.typ.is_int_valptr() && !field.typ.is_float_valptr() {
799 fn_builder.write_string('*')
800 }
801 }
802 // handle circular ref type of struct to the struct itself
803 if styp == field_styp && !allow_circular {
804 fn_builder.write_string('res.str += new string("<circular>")')
805 } else {
806 // manage C charptr
807 if field.typ in ast.charptr_types {
808 fn_builder.write_string('builtin__tos4((byteptr)${func})')
809 } else {
810 if field.typ.is_ptr() && sym.kind == .struct {
811 fn_builder.write_string('(indent_count > 25)? new string("<probably circular>") : ')
812 }
813 fn_builder.write_string(func)
814 }
815 }
816
817 fn_builder.writeln('')
818 }
819 fn_builder.writeln('res.str += "\\n}"')
820 if !allow_circular {
821 fn_builder.writeln('\tbuiltin__autostr_type_pop()')
822 }
823 // fn_builder.writeln('\t\t{new string("\\n"), ${c.si_s_code}, {.d_s=indents}}, {new string("}"), 0, {.d_c=0}},')
824 fn_builder.writeln('\treturn res;')
825 fn_builder.writeln('}')
826}
827
828fn struct_auto_str_func(mut g JsGen, sym &ast.TypeSymbol, field_type ast.Type, fn_name string, field_name string) string {
829 has_custom_str, expects_ptr, _ := sym.str_method_info()
830 if sym.kind == .enum {
831 return '${fn_name}(it.${g.js_name(field_name)})'
832 } else if should_use_indent_func(sym.kind) {
833 mut obj := 'it.${g.js_name(field_name)}'
834 if field_type.is_ptr() && !expects_ptr {
835 obj = '*${obj}'
836 }
837 if has_custom_str {
838 return '${fn_name}(${obj})'
839 }
840 return 'indent_${fn_name}(${obj}, indent_count + 1)'
841 } else if sym.kind in [.array, .array_fixed, .map, .sum_type] {
842 if has_custom_str {
843 return '${fn_name}(it.${g.js_name(field_name)})'
844 }
845 return 'indent_${fn_name}(it.${g.js_name(field_name)}, indent_count + 1)'
846 } else if sym.kind == .function {
847 return '${fn_name}()'
848 } else {
849 if sym.kind == .chan {
850 return '${fn_name}(it.${g.js_name(field_name)})'
851 }
852 mut method_str := 'it.${g.js_name(field_name)}'
853 if sym.kind == .bool {
854 method_str += ' ? new string("true") : new string("false")'
855 } else if (field_type.is_int_valptr() || field_type.is_float_valptr()) && !expects_ptr {
856 // ptr int can be "nil", so this needs to be casted to a string
857 if sym.kind == .f32 {
858 return 'builtin__str_intp(1, _MOV((StrIntpData[]){
859 {_SLIT0, ${si_g32_code}, {.d_f32 = *${method_str} }}
860 }))'
861 } else if sym.kind == .f64 {
862 return 'builtin__str_intp(1, _MOV((StrIntpData[]){
863 {_SLIT0, ${si_g64_code}, {.d_f64 = *${method_str} }}
864 }))'
865 } else if sym.kind in [.u64, .usize] {
866 fmt_type := StrIntpType.si_u64
867 return 'builtin__str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_u64 = *${method_str} }}}))'
868 } else if sym.kind in [.i64, .isize] {
869 fmt_type := StrIntpType.si_u64
870 return 'builtin__str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_i64 = *${method_str} }}}))'
871 }
872 fmt_type := StrIntpType.si_i32
873 return 'builtin__str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_i32 = *${method_str} }}}))'
874 }
875 return method_str
876 }
877}
878