vxx2 / vlib / v / ast / str.v
977 lines · 945 sloc · 23.03 KB · 16e9dff060e9e7db1a6fdd8d171127734d6a6849
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4@[has_globals]
5module ast
6
7import v.token
8import v.util
9import strings
10import sync.stdatomic
11
12// get_name returns the real name for the function declaration
13pub fn (f &FnDecl) get_name() string {
14 if f.is_static_type_method {
15 return f.name.all_after_last('__static__')
16 } else {
17 return f.name
18 }
19}
20
21// get_anon_fn_name returns the unique anonymous function name, based on the prefix, the func signature and its position in the source code
22pub fn (table &Table) get_anon_fn_name(prefix string, func &Fn, pos token.Pos) string {
23 return 'anon_fn_${prefix}_${pos.file_idx}_${table.fn_type_signature(func)}_${pos.pos}'
24}
25
26// get_name returns the real name for the function calling
27pub fn (f &CallExpr) get_name() string {
28 if f.is_static_method {
29 return f.name.replace('__static__', '.')
30 } else {
31 return f.name
32 }
33}
34
35pub fn (node &FnDecl) modname() string {
36 if node.mod != '' {
37 return node.mod
38 }
39 mut pamod := node.name.all_before_last('.')
40 if pamod == node.name.after('.') {
41 pamod = if node.is_builtin { 'builtin' } else { 'main' }
42 }
43 return pamod
44}
45
46// fkey returns a unique name of the function/method.
47// it is used in table.used_fns and v.markused.
48pub fn (node &FnDecl) fkey() string {
49 if node.is_method {
50 return fkey_from_receiver_type(node.receiver.typ, node.name)
51 }
52 return node.name
53}
54
55// sfkey returns a unique name of the struct field.
56// it is used in v.markused.
57pub fn (node &StructField) sfkey() string {
58 return '${int(node.container_typ)}.${node.name}}'
59}
60
61pub fn (node &Fn) fkey() string {
62 if node.is_method {
63 return fkey_from_receiver_type(node.receiver_type, node.name)
64 }
65 return node.name
66}
67
68pub fn (node &CallExpr) fkey() string {
69 if node.is_method {
70 return fkey_from_receiver_type(node.receiver_type, node.name)
71 }
72 return node.name
73}
74
75fn fkey_from_receiver_type(receiver_type Type, name string) string {
76 mut builder := strings.new_builder(name.len + 24)
77 builder.write_string(int(receiver_type).str())
78 builder.write_u8(`.`)
79 builder.write_string(name)
80 return builder.str()
81}
82
83// These methods are used only by vfmt, vdoc, and for debugging.
84pub fn (t &Table) stringify_anon_decl(node &AnonFn, cur_mod string, m2a map[string]string) string {
85 mut f := strings.new_builder(30)
86 f.write_string('fn ')
87 if node.inherited_vars.len > 0 {
88 f.write_string('[')
89 for i, var in node.inherited_vars {
90 if i > 0 {
91 f.write_string(', ')
92 }
93 if var.is_shared {
94 f.write_string('shared ')
95 } else if var.is_atomic {
96 f.write_string('atomic ')
97 } else if var.is_mut {
98 f.write_string('mut ')
99 }
100 f.write_string(var.name)
101 }
102 f.write_string('] ')
103 }
104 t.stringify_fn_after_name(node.decl, mut f, cur_mod, m2a)
105 return f.str()
106}
107
108pub fn (t &Table) stringify_fn_decl(node &FnDecl, cur_mod string, m2a map[string]string, needs_wrap bool) string {
109 mut f := strings.new_builder(30)
110 if node.is_pub {
111 f.write_string('pub ')
112 }
113 f.write_string('fn ')
114 pre_comments := node.comments.filter(it.pos.pos < node.name_pos.pos)
115 if pre_comments.len > 0 {
116 write_comments(pre_comments, mut f)
117 if !f.last_n(1)[0].is_space() {
118 f.write_string(' ')
119 }
120 }
121 if node.is_method {
122 f.write_string('(')
123 mut styp := util.no_cur_mod(t.type_to_code(node.receiver.typ.clear_flag(.shared_f)),
124 cur_mod)
125 if node.rec_mut {
126 f.write_string(node.receiver.typ.share().str() + ' ')
127 styp = styp[1..] // remove &
128 }
129 f.write_string(node.receiver.name + ' ')
130 styp = util.no_cur_mod(styp, cur_mod)
131 if t.new_int_fmt_fix && styp == 'int' {
132 styp = 'i32'
133 }
134 f.write_string(styp + ') ')
135 } else if node.is_static_type_method {
136 mut styp := util.no_cur_mod(t.type_to_code(node.receiver.typ.clear_flag(.shared_f)),
137 cur_mod)
138 f.write_string(styp + '.')
139 }
140 mut name := if !node.is_method && node.language == .v {
141 node.name.all_after_last('.')
142 } else {
143 node.name
144 }
145 if node.is_static_type_method {
146 name = name.after('__static__')
147 }
148 f.write_string(name)
149 if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<=', '[]', '[]='] {
150 f.write_string(' ')
151 }
152 t.stringify_fn_after_name(node, mut f, cur_mod, m2a)
153 return f.str()
154}
155
156fn (t &Table) stringify_fn_after_name(node &FnDecl, mut f strings.Builder, cur_mod string, m2a map[string]string) {
157 mut add_para_types := true
158 if node.generic_names.len > 0 {
159 if node.is_method {
160 sym := t.sym(node.params[0].typ)
161 if sym.info is Struct {
162 generic_names := sym.info.generic_types.map(t.sym(it).name)
163 if generic_names == node.generic_names {
164 add_para_types = false
165 }
166 }
167 }
168 if add_para_types {
169 f.write_string('[')
170 for i, gname in node.generic_names {
171 f.write_string(gname)
172 if i != node.generic_names.len - 1 {
173 f.write_string(', ')
174 }
175 }
176 f.write_string(']')
177 }
178 }
179 f.write_string('(')
180 mut old_pline := node.pos.line_nr
181 mut pline := node.pos.line_nr
182 mut nparams_on_pline := 0
183 for i, param in node.params {
184 // skip receiver
185 if node.is_method && i == 0 {
186 continue
187 }
188 if param.is_hidden {
189 continue
190 }
191 param_typ := if t.new_int_fmt_fix && param.typ == int_type { i32_type } else { param.typ }
192 is_last_param := i == node.params.len - 1
193 is_type_only := param.name == ''
194 if param.on_newline {
195 f.write_string('\n\t')
196 pline++
197 nparams_on_pline = 0
198 }
199 if pline == old_pline && nparams_on_pline > 0 {
200 f.write_string(' ')
201 }
202 if param.is_mut {
203 f.write_string(param_typ.share().str() + ' ')
204 }
205 f.write_string(param.name)
206 param_sym := t.sym(param_typ)
207 if param_sym.info is Struct && param_sym.info.is_anon {
208 if param_typ.has_flag(.option) {
209 f.write_string(' ?')
210 } else {
211 f.write_string(' ')
212 }
213 f.write_string('struct {')
214 for field in param_sym.info.fields {
215 field_typ := if t.new_int_fmt_fix && field.typ == int_type {
216 i32_type
217 } else {
218 field.typ
219 }
220 f.write_string(' ${field.name} ${t.type_to_str(field_typ)}')
221 if field.has_default_expr {
222 f.write_string(' = ${field.default_expr}')
223 }
224 }
225 if param_sym.info.fields.len > 0 {
226 f.write_string(' ')
227 }
228 f.write_string('}')
229 } else {
230 mut s := t.type_to_str(param_typ.clear_flag(.shared_f))
231 if param.is_mut {
232 if s.starts_with('&') && ((!param_sym.is_number() && param_sym.kind != .bool)
233 || node.language != .v
234 || (param_typ.is_ptr() && param_sym.kind == .struct)) {
235 s = s[1..]
236 } else if param_typ.is_ptr() && param_sym.kind == .struct && !s.contains('[') {
237 s = t.type_to_str(param_typ.clear_flag(.shared_f).deref())
238 }
239 }
240 s = shorten_full_name_based_on_cur_mod(s, cur_mod)
241 s = shorten_full_name_based_on_aliases(s, m2a)
242 if !is_type_only {
243 f.write_string(' ')
244 }
245 if node.is_variadic && is_last_param && !node.is_c_variadic {
246 f.write_string('...')
247 }
248 f.write_string(s)
249 }
250 if !is_last_param {
251 f.write_string(',')
252 } else if node.is_c_variadic {
253 f.write_string(', ...')
254 }
255 nparams_on_pline++
256 old_pline = pline
257 }
258 f.write_string(')')
259 if node.return_type != void_type {
260 return_type := if t.new_int_fmt_fix && node.return_type == int_type {
261 i32_type
262 } else {
263 node.return_type
264 }
265 sreturn_type := shorten_full_name_based_on_cur_mod(t.type_to_str(return_type), cur_mod)
266 short_sreturn_type := shorten_full_name_based_on_aliases(sreturn_type, m2a)
267 f.write_string(' ${short_sreturn_type}')
268 }
269}
270
271fn write_comments(comments []Comment, mut f strings.Builder) {
272 for i, c in comments {
273 write_comment(c, mut f)
274 if i < comments.len - 1 {
275 f.writeln('')
276 }
277 }
278}
279
280fn write_comment(node Comment, mut f strings.Builder) {
281 if node.is_multi {
282 x := node.text.trim_left('\x01').trim_space()
283 f.writeln('/*')
284 f.writeln(x)
285 f.write_string('*/')
286 } else {
287 mut s := node.text.trim_left('\x01').trim_right(' ')
288 mut out_s := '//'
289 if s != '' {
290 if s[0].is_letter() || s[0].is_digit() {
291 out_s += ' '
292 }
293 out_s += s
294 }
295 f.writeln(out_s)
296 }
297}
298
299struct StringifyModReplacement {
300 mod string
301 alias string
302 weight int
303}
304
305fn is_qualified_name_boundary(c u8) bool {
306 return !(c.is_letter() || c.is_digit() || c == `_` || c == `.`)
307}
308
309fn replace_qualified_name_based_on_alias(input string, mod string, alias string) string {
310 if mod.len == 0 || !input.contains(mod) {
311 return input
312 }
313 mut start := 0
314 mut changed := false
315 mut sb := strings.new_builder(input.len)
316 for {
317 idx := input.index_after(mod, start) or { break }
318 end := idx + mod.len
319 before_ok := idx == 0 || is_qualified_name_boundary(input[idx - 1])
320 after_ok := end == input.len || input[end] == `.` || is_qualified_name_boundary(input[end])
321 if before_ok && after_ok {
322 sb.write_string(input[start..idx])
323 sb.write_string(alias)
324 start = end
325 changed = true
326 continue
327 }
328 sb.write_string(input[start..end])
329 start = end
330 }
331 if !changed {
332 return input
333 }
334 sb.write_string(input[start..])
335 return sb.str()
336}
337
338fn shorten_full_name_based_on_aliases(input string, m2a map[string]string) string {
339 if m2a.len == 0 || -1 == input.index_u8(`.`) {
340 // a simple typename, like `string` or `[]bool`; no module aliasings apply,
341 // (or there just are not any mappings)
342 return input
343 }
344 // Shorten the full names to their aliases, but replace the longer mods first, so that:
345 // `import user.project`
346 // `import user.project.routes`
347 // will lead to replacing `user.project.routes` first to `routes`, NOT `user.project.routes` to `project.routes`.
348 // Also take into account the nesting level, so `a.e.c.d` will be shortened before `a.xyz.b`, even though they are the same length.
349 mut replacements := []StringifyModReplacement{cap: m2a.len}
350 for mod, alias in m2a {
351 if mod == alias {
352 // for vlib modules like `import strings` -> mod: `strings` | alias: `strings`
353 // ... which is the same, so no replacements are needed
354 continue
355 }
356 if !input.contains(mod) {
357 continue
358 }
359 replacements << StringifyModReplacement{
360 mod: mod
361 alias: alias
362 weight: mod.count('.') * 100 + mod.len
363 }
364 }
365 if replacements.len == 0 {
366 return input
367 }
368
369 mut res := input
370 if replacements.len > 1 {
371 replacements.sort(a.weight > b.weight)
372 }
373 for r in replacements {
374 if -1 == res.index_u8(`.`) {
375 // there are no remaining module parts left in the type name, it is a local one after all
376 break
377 }
378 if !res.contains(r.mod) {
379 // nothing to replace as well (just minimises modifications and string clonings)
380 continue
381 }
382 // r.mod: `v.token` | r.alias: `xyz` | res: `v.token.Abc` -> `xyz.Abc`
383 // r.mod: `v.ast` | r.alias: `ast` | res: `v.ast.AliasTypeDecl` -> `ast.AliasTypeDecl`
384 // r.mod: `v.ast` | r.alias: `ast` | res: `[]v.ast.InterfaceEmbedding` -> `[]ast.InterfaceEmbedding`
385 res = replace_qualified_name_based_on_alias(res, r.mod, r.alias)
386 }
387 return res
388}
389
390fn shorten_full_name_based_on_cur_mod(input string, cur_mod string) string {
391 if cur_mod == '' || !input.contains('${cur_mod}.') {
392 return input
393 }
394 pattern := '${cur_mod}.'
395 mut out := strings.new_builder(input.len)
396 for i := 0; i < input.len; i++ {
397 if input[i..].starts_with(pattern)
398 && (i == 0 || input[i - 1] in [` `, `(`, `[`, `,`, `|`, `&`, `?`, `!`, `:`]) {
399 i += pattern.len - 1
400 continue
401 }
402 out.write_u8(input[i])
403 }
404 return out.str()
405}
406
407// This method creates the format specifier (including the colon) or an empty
408// string if none is needed. For example, '${z:8.3f} ${a:-20} ${a>b+2}'
409pub fn (lit &StringInterLiteral) get_fspec(i int) string {
410 mut res := []string{}
411 has_dynamic_width := i < lit.fwidth_exprs.len && lit.fwidth_exprs[i] !is EmptyExpr
412 has_dynamic_precision := i < lit.precision_exprs.len && lit.precision_exprs[i] !is EmptyExpr
413 needs_fspec := lit.need_fmts[i] || lit.pluss[i]
414 || (lit.fills[i] && (lit.fwidths[i] >= 0 || has_dynamic_width))
415 || lit.fwidths[i] != 0 || lit.precisions[i] != 987698 || has_dynamic_width
416 || has_dynamic_precision
417 if needs_fspec {
418 res << ':'
419 if lit.pluss[i] {
420 res << '+'
421 }
422 if lit.fills[i] && (lit.fwidths[i] >= 0 || has_dynamic_width) {
423 res << '0'
424 }
425 if has_dynamic_width {
426 res << '(${lit.fwidth_exprs[i].str()})'
427 } else if lit.fwidths[i] != 0 {
428 res << '${lit.fwidths[i]}'
429 }
430 if has_dynamic_precision {
431 res << '.(${lit.precision_exprs[i].str()})'
432 } else if lit.precisions[i] != 987698 {
433 res << '.${lit.precisions[i]}'
434 }
435 if lit.need_fmts[i] {
436 res << '${lit.fmts[i]:c}'
437 }
438 }
439 return res.join('')
440}
441
442__global nested_expr_str_calls = i64(0)
443// too big values, risk stack overflow in `${expr}` (which uses `expr.str()`) calls
444const max_nested_expr_str_calls = 300
445
446// string representation of expr
447pub fn (x Expr) str() string {
448 str_calls := stdatomic.add_i64(&nested_expr_str_calls, 1)
449 if str_calls > max_nested_expr_str_calls {
450 $if panic_on_deeply_nested_expr_str_calls ? {
451 eprintln('${@LOCATION}: too many nested Expr.str() calls: ${str_calls}, expr type: ${x.type_name()}')
452 exit(1)
453 }
454 return '{expression too deep}'
455 }
456 defer {
457 stdatomic.sub_i64(&nested_expr_str_calls, 1)
458 }
459 match x {
460 AnonFn {
461 return 'anon_fn'
462 }
463 ComptimeType {
464 return x.str()
465 }
466 DumpExpr {
467 return 'dump(${x.expr.str()})'
468 }
469 ArrayInit {
470 mut fields := []string{}
471 if x.has_len {
472 fields << 'len: ${x.len_expr.str()}'
473 }
474 if x.has_cap {
475 fields << 'cap: ${x.cap_expr.str()}'
476 }
477 if x.has_init {
478 fields << 'init: ${x.init_expr.str()}'
479 }
480 typ_str := if x.elem_type_expr !is EmptyExpr {
481 x.elem_type_expr.str()
482 } else {
483 global_table.type_to_str(x.elem_type)
484 }
485 if fields.len > 0 {
486 if x.is_fixed {
487 return '${x.exprs.str()}${typ_str}{${fields.join(', ')}}'
488 } else {
489 return '[]${typ_str}{${fields.join(', ')}}'
490 }
491 } else {
492 if x.is_fixed {
493 return '${x.exprs.str()}${typ_str}{}'
494 } else if x.exprs.len == 0 && typ_str != '' {
495 return '[]${typ_str}{}'
496 } else if x.has_update_expr {
497 if x.exprs.len == 0 {
498 return '[...${x.update_expr}]'
499 }
500 return '[...${x.update_expr}, ${x.exprs.map(it.str()).join(', ')}]'
501 } else {
502 return x.exprs.str()
503 }
504 }
505 }
506 AsCast {
507 return '${x.expr.str()} as ${global_table.type_to_str(x.typ)}'
508 }
509 AtExpr {
510 return '${x.name}'
511 }
512 CTempVar {
513 return x.orig.str()
514 }
515 BoolLiteral {
516 return x.val.str()
517 }
518 CastExpr {
519 type_name := util.strip_main_name(global_table.type_to_str(x.typ))
520 return '${type_name}(${x.expr.str()})'
521 }
522 CallExpr {
523 sargs := args2str(x.args)
524 propagate_suffix := if x.or_block.kind == .propagate_option {
525 '?'
526 } else if x.or_block.kind == .propagate_result {
527 '!'
528 } else {
529 ''
530 }
531 if x.is_method {
532 return '${x.left.str()}.${x.name}(${sargs})${propagate_suffix}'
533 }
534 if x.name.starts_with('${x.mod}.') {
535 return util.strip_main_name('${x.get_name()}(${sargs})${propagate_suffix}')
536 }
537 if x.mod == '' && x.name == '' {
538 return x.left.str() + '(${sargs})${propagate_suffix}'
539 }
540 if x.name.contains('.') {
541 return '${x.get_name()}(${sargs})${propagate_suffix}'
542 }
543 if x.is_static_method {
544 return '${x.mod}.${x.get_name()}(${sargs})${propagate_suffix}'
545 }
546 if x.mod == 'main' {
547 return '${x.get_name()}(${sargs})${propagate_suffix}'
548 } else {
549 return '${x.mod}.${x.get_name()}(${sargs})${propagate_suffix}'
550 }
551 }
552 CharLiteral {
553 return '`${x.val}`'
554 }
555 Comment {
556 if x.is_multi {
557 lines := x.text.split_into_lines()
558 return '/* ${lines.len} lines comment */'
559 } else {
560 text := x.text.trim('\x01').trim_space()
561 return '´// ${text}´'
562 }
563 }
564 ComptimeSelector {
565 return '${x.left}.$(${x.field_expr})'
566 }
567 ConcatExpr {
568 return x.vals.map(it.str()).join(',')
569 }
570 EnumVal {
571 return '.${x.val}'
572 }
573 FloatLiteral, IntegerLiteral {
574 return x.val.clone()
575 }
576 GoExpr {
577 return 'go ${x.call_expr}'
578 }
579 SpawnExpr {
580 return 'spawn ${x.call_expr}'
581 }
582 Ident {
583 if x.cached_name == '' {
584 unsafe {
585 x.cached_name = util.strip_main_name(x.name)
586 }
587 }
588 // This clone may freed by auto str()
589 return x.cached_name.clone()
590 }
591 IfExpr {
592 mut parts := []string{}
593 dollar := if x.is_comptime { '$' } else { '' }
594 for i, branch in x.branches {
595 if i != 0 {
596 parts << ' } ${dollar}else '
597 }
598 if i < x.branches.len - 1 || !x.has_else {
599 parts << ' ${dollar}if ' + branch.cond.str() + ' { '
600 } else if x.has_else && i == x.branches.len - 1 {
601 parts << '{ '
602 }
603 for stmt in branch.stmts {
604 parts << stmt.str()
605 }
606 }
607 parts << ' }'
608 return parts.join('')
609 }
610 IndexExpr {
611 parts := if x.indices.len > 0 { x.indices } else { [x.index] }
612 return '${x.left.str()}[${parts.map(it.str()).join(', ')}]'
613 }
614 InfixExpr {
615 return '${x.left.str()} ${x.op.str()} ${x.right.str()}'
616 }
617 MapInit {
618 mut pairs := []string{}
619 for ik, kv in x.keys {
620 mv := x.vals[ik].str()
621 pairs << '${kv}: ${mv}'
622 }
623 if x.has_update_expr {
624 return 'map{ ...${x.update_expr} ${pairs.join(' ')} }'
625 }
626 return 'map{ ${pairs.join(' ')} }'
627 }
628 Nil {
629 return 'nil'
630 }
631 ParExpr {
632 return '(${x.expr})'
633 }
634 PostfixExpr {
635 if x.op == .question {
636 return '${x.expr} ?'
637 }
638 return '${x.expr}${x.op}'
639 }
640 PrefixExpr {
641 return x.op.str() + x.right.str()
642 }
643 RangeExpr {
644 mut s := '..'
645 if x.has_low {
646 s = '${x.low} ' + s
647 }
648 if x.has_high {
649 s = s + ' ${x.high}'
650 }
651 return s
652 }
653 SelectExpr {
654 return 'ast.SelectExpr'
655 }
656 SelectorExpr {
657 propagate_suffix := if x.or_block.kind == .propagate_option {
658 '?'
659 } else if x.or_block.kind == .propagate_result {
660 '!'
661 } else {
662 ''
663 }
664 return '${x.expr.str()}.${x.field_name}${propagate_suffix}'
665 }
666 SizeOf {
667 if x.is_type {
668 return 'sizeof(${global_table.type_to_str(x.typ)})'
669 }
670 return 'sizeof(${x.expr.str()})'
671 }
672 OffsetOf {
673 return '__offsetof(${global_table.type_to_str(x.struct_type)}, ${x.field})'
674 }
675 StringInterLiteral {
676 mut res := strings.new_builder(50)
677 res.write_string("'")
678 for i, val in x.vals {
679 res.write_string(val)
680 if i >= x.exprs.len {
681 break
682 }
683 res.write_string('$')
684 fspec_str := x.get_fspec(i)
685
686 res.write_string('{')
687 res.write_string(x.exprs[i].str())
688 res.write_string(fspec_str)
689 res.write_string('}')
690 }
691 res.write_string("'")
692 return res.str()
693 }
694 StringLiteral {
695 return "'${x.val}'"
696 }
697 TypeNode {
698 opt_prefix := if x.typ.has_flag(.option) { '?' } else { '' }
699 return 'TypeNode(${opt_prefix}${global_table.type_str(x.typ)})'
700 }
701 TypeOf {
702 if x.is_type {
703 return 'typeof[${global_table.type_to_str(x.typ)}]()'
704 }
705 return 'typeof(${x.expr.str()})'
706 }
707 LambdaExpr {
708 ilist := x.params.map(it.name).join(', ')
709 return '|${ilist}| ${x.expr.str()}'
710 }
711 Likely {
712 return '_likely_(${x.expr.str()})'
713 }
714 UnsafeExpr {
715 return 'unsafe { ${x.expr} }'
716 }
717 None {
718 return 'none'
719 }
720 IsRefType {
721 if x.is_type {
722 return 'isreftype(${global_table.type_to_str(x.typ)})'
723 }
724 return 'isreftype(${x.expr.str()})'
725 }
726 IfGuardExpr {
727 mut s := ''
728 for i, var in x.vars {
729 s += var.name
730 if i != x.vars.len - 1 {
731 s += ', '
732 }
733 }
734 return s + ' := ' + x.expr.str()
735 }
736 StructInit {
737 sname := if x.typ_expr !is EmptyExpr && x.typ == 0 {
738 x.typ_expr.str()
739 } else {
740 idx := x.typ.idx()
741 if idx > 0 && idx < global_table.type_symbols.len {
742 global_table.type_symbols[idx].name
743 } else {
744 'unknown'
745 }
746 }
747 return '${sname}{....}'
748 }
749 ArrayDecompose {
750 return 'ast.ArrayDecompose'
751 }
752 Assoc {
753 return 'ast.Assoc'
754 }
755 ChanInit {
756 return 'ast.ChanInit'
757 }
758 ComptimeCall {
759 return x.expr_str()
760 }
761 EmptyExpr {
762 return 'ast.EmptyExpr'
763 }
764 LockExpr {
765 return 'ast.LockExpr'
766 }
767 MatchExpr {
768 return 'ast.MatchExpr'
769 }
770 NodeError {
771 return 'ast.NodeError'
772 }
773 OrExpr {
774 return 'ast.OrExpr'
775 }
776 SqlExpr {
777 return 'ast.SqlExpr'
778 }
779 SqlQueryDataExpr {
780 return 'ast.SqlQueryDataExpr'
781 }
782 }
783
784 return '[unhandled expr type ${x.type_name()}]'
785}
786
787pub fn (a CallArg) str() string {
788 if a.is_mut {
789 return 'mut ${a.expr.str()}'
790 }
791 return '${a.expr.str()}'
792}
793
794pub fn args2str(args []CallArg) string {
795 mut res := []string{}
796 for a in args {
797 res << a.str()
798 }
799 return res.join(', ')
800}
801
802pub fn (node &BranchStmt) str() string {
803 mut s := '${node.kind}'
804 if node.label.len > 0 {
805 s += ' ${node.label}'
806 }
807 return s
808}
809
810pub fn (node Stmt) str() string {
811 match node {
812 AssertStmt {
813 return 'assert ${node.expr}'
814 }
815 AssignStmt {
816 mut out := ''
817 for i, left in node.left {
818 if left is Ident {
819 var_info := left.var_info()
820 if var_info.is_mut {
821 out += 'mut '
822 }
823 }
824 out += left.str()
825 if i < node.left.len - 1 {
826 out += ','
827 }
828 }
829 out += ' ${node.op.str()} '
830 for i, val in node.right {
831 out += val.str()
832 if i < node.right.len - 1 {
833 out += ','
834 }
835 }
836 return out
837 }
838 Block {
839 mut res := ''
840 res += '{'
841 for s in node.stmts {
842 res += s.str()
843 res += ';'
844 }
845 res += '}'
846 return res
847 }
848 BranchStmt {
849 return node.str()
850 }
851 ConstDecl {
852 return node.fields.map(field_to_string).join('')
853 }
854 DeferStmt {
855 mut res := ''
856 res += 'defer {'
857 for s in node.stmts {
858 res += s.str()
859 res += ';'
860 }
861 res += '}'
862 return res
863 }
864 EnumDecl {
865 return 'enum ${node.name} { ${node.fields.len} fields }'
866 }
867 ExprStmt {
868 return node.expr.str()
869 }
870 FnDecl {
871 return 'fn ${node.name}( ${node.params.len} params ) { ${node.stmts.len} stmts }'
872 }
873 ForInStmt {
874 mut res := ''
875 if node.label.len > 0 {
876 res += '${node.label}: '
877 }
878 res += 'for '
879 if node.key_var != '' {
880 res += node.key_var
881 }
882 if node.key_var != '' && node.val_var != '' {
883 res += ', '
884 }
885 if node.val_var != '' {
886 if node.val_is_mut {
887 res += 'mut '
888 }
889 res += node.val_var
890 }
891 res += ' in '
892 res += node.cond.str()
893 if node.is_range {
894 res += ' .. '
895 res += node.high.str()
896 }
897 res += ' {'
898 return res
899 }
900 ForStmt {
901 if node.is_inf {
902 return 'for {'
903 }
904 return 'for ${node.cond} {'
905 }
906 GlobalDecl {
907 mut res := ''
908 if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line {
909 return '__global ()'
910 }
911 res += '__global '
912 if node.is_block {
913 res += '( '
914 }
915 for field in node.fields {
916 if field.is_volatile {
917 res += 'volatile '
918 }
919 res += field.name
920 res += ' '
921 if field.has_expr {
922 res += '= '
923 res += field.expr.str()
924 } else {
925 res += global_table.type_to_str(field.typ)
926 }
927 res += ';'
928 }
929 if node.is_block {
930 res += ' )'
931 }
932 return res
933 }
934 Import {
935 mut out := 'import ${node.mod}'
936 if node.alias.len > 0 {
937 out += ' as ${node.alias}'
938 }
939 return out
940 }
941 Module {
942 return 'module ${node.name}'
943 }
944 Return {
945 mut out := 'return'
946 for i, val in node.exprs {
947 out += ' ${val}'
948 if i < node.exprs.len - 1 {
949 out += ','
950 }
951 }
952 return out
953 }
954 StructDecl {
955 return 'struct ${node.name} { ${node.fields.len} fields }'
956 }
957 else {
958 return '[unhandled stmt str type: ${node.type_name()} ]'
959 }
960 }
961}
962
963fn field_to_string(f ConstField) string {
964 x := f.name.trim_string_left(f.mod + '.')
965 return 'const ${x} = ${f.expr};'
966}
967
968pub fn (e ComptimeForKind) str() string {
969 match e {
970 .methods { return 'methods' }
971 .fields { return 'fields' }
972 .attributes { return 'attributes' }
973 .values { return 'values' }
974 .variants { return 'variants' }
975 .params { return 'params' }
976 }
977}
978