v2 / vlib / v / ast / str.v
972 lines · 940 sloc · 22.86 KB · a75b9622292f36a85c5da0926c1f8a1e1623e4ae
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 {
497 return x.exprs.str()
498 }
499 }
500 }
501 AsCast {
502 return '${x.expr.str()} as ${global_table.type_to_str(x.typ)}'
503 }
504 AtExpr {
505 return '${x.name}'
506 }
507 CTempVar {
508 return x.orig.str()
509 }
510 BoolLiteral {
511 return x.val.str()
512 }
513 CastExpr {
514 type_name := util.strip_main_name(global_table.type_to_str(x.typ))
515 return '${type_name}(${x.expr.str()})'
516 }
517 CallExpr {
518 sargs := args2str(x.args)
519 propagate_suffix := if x.or_block.kind == .propagate_option {
520 '?'
521 } else if x.or_block.kind == .propagate_result {
522 '!'
523 } else {
524 ''
525 }
526 if x.is_method {
527 return '${x.left.str()}.${x.name}(${sargs})${propagate_suffix}'
528 }
529 if x.name.starts_with('${x.mod}.') {
530 return util.strip_main_name('${x.get_name()}(${sargs})${propagate_suffix}')
531 }
532 if x.mod == '' && x.name == '' {
533 return x.left.str() + '(${sargs})${propagate_suffix}'
534 }
535 if x.name.contains('.') {
536 return '${x.get_name()}(${sargs})${propagate_suffix}'
537 }
538 if x.is_static_method {
539 return '${x.mod}.${x.get_name()}(${sargs})${propagate_suffix}'
540 }
541 if x.mod == 'main' {
542 return '${x.get_name()}(${sargs})${propagate_suffix}'
543 } else {
544 return '${x.mod}.${x.get_name()}(${sargs})${propagate_suffix}'
545 }
546 }
547 CharLiteral {
548 return '`${x.val}`'
549 }
550 Comment {
551 if x.is_multi {
552 lines := x.text.split_into_lines()
553 return '/* ${lines.len} lines comment */'
554 } else {
555 text := x.text.trim('\x01').trim_space()
556 return '´// ${text}´'
557 }
558 }
559 ComptimeSelector {
560 return '${x.left}.$(${x.field_expr})'
561 }
562 ConcatExpr {
563 return x.vals.map(it.str()).join(',')
564 }
565 EnumVal {
566 return '.${x.val}'
567 }
568 FloatLiteral, IntegerLiteral {
569 return x.val.clone()
570 }
571 GoExpr {
572 return 'go ${x.call_expr}'
573 }
574 SpawnExpr {
575 return 'spawn ${x.call_expr}'
576 }
577 Ident {
578 if x.cached_name == '' {
579 unsafe {
580 x.cached_name = util.strip_main_name(x.name)
581 }
582 }
583 // This clone may freed by auto str()
584 return x.cached_name.clone()
585 }
586 IfExpr {
587 mut parts := []string{}
588 dollar := if x.is_comptime { '$' } else { '' }
589 for i, branch in x.branches {
590 if i != 0 {
591 parts << ' } ${dollar}else '
592 }
593 if i < x.branches.len - 1 || !x.has_else {
594 parts << ' ${dollar}if ' + branch.cond.str() + ' { '
595 } else if x.has_else && i == x.branches.len - 1 {
596 parts << '{ '
597 }
598 for stmt in branch.stmts {
599 parts << stmt.str()
600 }
601 }
602 parts << ' }'
603 return parts.join('')
604 }
605 IndexExpr {
606 parts := if x.indices.len > 0 { x.indices } else { [x.index] }
607 return '${x.left.str()}[${parts.map(it.str()).join(', ')}]'
608 }
609 InfixExpr {
610 return '${x.left.str()} ${x.op.str()} ${x.right.str()}'
611 }
612 MapInit {
613 mut pairs := []string{}
614 for ik, kv in x.keys {
615 mv := x.vals[ik].str()
616 pairs << '${kv}: ${mv}'
617 }
618 if x.has_update_expr {
619 return 'map{ ...${x.update_expr} ${pairs.join(' ')} }'
620 }
621 return 'map{ ${pairs.join(' ')} }'
622 }
623 Nil {
624 return 'nil'
625 }
626 ParExpr {
627 return '(${x.expr})'
628 }
629 PostfixExpr {
630 if x.op == .question {
631 return '${x.expr} ?'
632 }
633 return '${x.expr}${x.op}'
634 }
635 PrefixExpr {
636 return x.op.str() + x.right.str()
637 }
638 RangeExpr {
639 mut s := '..'
640 if x.has_low {
641 s = '${x.low} ' + s
642 }
643 if x.has_high {
644 s = s + ' ${x.high}'
645 }
646 return s
647 }
648 SelectExpr {
649 return 'ast.SelectExpr'
650 }
651 SelectorExpr {
652 propagate_suffix := if x.or_block.kind == .propagate_option {
653 '?'
654 } else if x.or_block.kind == .propagate_result {
655 '!'
656 } else {
657 ''
658 }
659 return '${x.expr.str()}.${x.field_name}${propagate_suffix}'
660 }
661 SizeOf {
662 if x.is_type {
663 return 'sizeof(${global_table.type_to_str(x.typ)})'
664 }
665 return 'sizeof(${x.expr.str()})'
666 }
667 OffsetOf {
668 return '__offsetof(${global_table.type_to_str(x.struct_type)}, ${x.field})'
669 }
670 StringInterLiteral {
671 mut res := strings.new_builder(50)
672 res.write_string("'")
673 for i, val in x.vals {
674 res.write_string(val)
675 if i >= x.exprs.len {
676 break
677 }
678 res.write_string('$')
679 fspec_str := x.get_fspec(i)
680
681 res.write_string('{')
682 res.write_string(x.exprs[i].str())
683 res.write_string(fspec_str)
684 res.write_string('}')
685 }
686 res.write_string("'")
687 return res.str()
688 }
689 StringLiteral {
690 return "'${x.val}'"
691 }
692 TypeNode {
693 opt_prefix := if x.typ.has_flag(.option) { '?' } else { '' }
694 return 'TypeNode(${opt_prefix}${global_table.type_str(x.typ)})'
695 }
696 TypeOf {
697 if x.is_type {
698 return 'typeof[${global_table.type_to_str(x.typ)}]()'
699 }
700 return 'typeof(${x.expr.str()})'
701 }
702 LambdaExpr {
703 ilist := x.params.map(it.name).join(', ')
704 return '|${ilist}| ${x.expr.str()}'
705 }
706 Likely {
707 return '_likely_(${x.expr.str()})'
708 }
709 UnsafeExpr {
710 return 'unsafe { ${x.expr} }'
711 }
712 None {
713 return 'none'
714 }
715 IsRefType {
716 if x.is_type {
717 return 'isreftype(${global_table.type_to_str(x.typ)})'
718 }
719 return 'isreftype(${x.expr.str()})'
720 }
721 IfGuardExpr {
722 mut s := ''
723 for i, var in x.vars {
724 s += var.name
725 if i != x.vars.len - 1 {
726 s += ', '
727 }
728 }
729 return s + ' := ' + x.expr.str()
730 }
731 StructInit {
732 sname := if x.typ_expr !is EmptyExpr && x.typ == 0 {
733 x.typ_expr.str()
734 } else {
735 idx := x.typ.idx()
736 if idx > 0 && idx < global_table.type_symbols.len {
737 global_table.type_symbols[idx].name
738 } else {
739 'unknown'
740 }
741 }
742 return '${sname}{....}'
743 }
744 ArrayDecompose {
745 return 'ast.ArrayDecompose'
746 }
747 Assoc {
748 return 'ast.Assoc'
749 }
750 ChanInit {
751 return 'ast.ChanInit'
752 }
753 ComptimeCall {
754 return x.expr_str()
755 }
756 EmptyExpr {
757 return 'ast.EmptyExpr'
758 }
759 LockExpr {
760 return 'ast.LockExpr'
761 }
762 MatchExpr {
763 return 'ast.MatchExpr'
764 }
765 NodeError {
766 return 'ast.NodeError'
767 }
768 OrExpr {
769 return 'ast.OrExpr'
770 }
771 SqlExpr {
772 return 'ast.SqlExpr'
773 }
774 SqlQueryDataExpr {
775 return 'ast.SqlQueryDataExpr'
776 }
777 }
778
779 return '[unhandled expr type ${x.type_name()}]'
780}
781
782pub fn (a CallArg) str() string {
783 if a.is_mut {
784 return 'mut ${a.expr.str()}'
785 }
786 return '${a.expr.str()}'
787}
788
789pub fn args2str(args []CallArg) string {
790 mut res := []string{}
791 for a in args {
792 res << a.str()
793 }
794 return res.join(', ')
795}
796
797pub fn (node &BranchStmt) str() string {
798 mut s := '${node.kind}'
799 if node.label.len > 0 {
800 s += ' ${node.label}'
801 }
802 return s
803}
804
805pub fn (node Stmt) str() string {
806 match node {
807 AssertStmt {
808 return 'assert ${node.expr}'
809 }
810 AssignStmt {
811 mut out := ''
812 for i, left in node.left {
813 if left is Ident {
814 var_info := left.var_info()
815 if var_info.is_mut {
816 out += 'mut '
817 }
818 }
819 out += left.str()
820 if i < node.left.len - 1 {
821 out += ','
822 }
823 }
824 out += ' ${node.op.str()} '
825 for i, val in node.right {
826 out += val.str()
827 if i < node.right.len - 1 {
828 out += ','
829 }
830 }
831 return out
832 }
833 Block {
834 mut res := ''
835 res += '{'
836 for s in node.stmts {
837 res += s.str()
838 res += ';'
839 }
840 res += '}'
841 return res
842 }
843 BranchStmt {
844 return node.str()
845 }
846 ConstDecl {
847 return node.fields.map(field_to_string).join('')
848 }
849 DeferStmt {
850 mut res := ''
851 res += 'defer {'
852 for s in node.stmts {
853 res += s.str()
854 res += ';'
855 }
856 res += '}'
857 return res
858 }
859 EnumDecl {
860 return 'enum ${node.name} { ${node.fields.len} fields }'
861 }
862 ExprStmt {
863 return node.expr.str()
864 }
865 FnDecl {
866 return 'fn ${node.name}( ${node.params.len} params ) { ${node.stmts.len} stmts }'
867 }
868 ForInStmt {
869 mut res := ''
870 if node.label.len > 0 {
871 res += '${node.label}: '
872 }
873 res += 'for '
874 if node.key_var != '' {
875 res += node.key_var
876 }
877 if node.key_var != '' && node.val_var != '' {
878 res += ', '
879 }
880 if node.val_var != '' {
881 if node.val_is_mut {
882 res += 'mut '
883 }
884 res += node.val_var
885 }
886 res += ' in '
887 res += node.cond.str()
888 if node.is_range {
889 res += ' .. '
890 res += node.high.str()
891 }
892 res += ' {'
893 return res
894 }
895 ForStmt {
896 if node.is_inf {
897 return 'for {'
898 }
899 return 'for ${node.cond} {'
900 }
901 GlobalDecl {
902 mut res := ''
903 if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line {
904 return '__global ()'
905 }
906 res += '__global '
907 if node.is_block {
908 res += '( '
909 }
910 for field in node.fields {
911 if field.is_volatile {
912 res += 'volatile '
913 }
914 res += field.name
915 res += ' '
916 if field.has_expr {
917 res += '= '
918 res += field.expr.str()
919 } else {
920 res += global_table.type_to_str(field.typ)
921 }
922 res += ';'
923 }
924 if node.is_block {
925 res += ' )'
926 }
927 return res
928 }
929 Import {
930 mut out := 'import ${node.mod}'
931 if node.alias.len > 0 {
932 out += ' as ${node.alias}'
933 }
934 return out
935 }
936 Module {
937 return 'module ${node.name}'
938 }
939 Return {
940 mut out := 'return'
941 for i, val in node.exprs {
942 out += ' ${val}'
943 if i < node.exprs.len - 1 {
944 out += ','
945 }
946 }
947 return out
948 }
949 StructDecl {
950 return 'struct ${node.name} { ${node.fields.len} fields }'
951 }
952 else {
953 return '[unhandled stmt str type: ${node.type_name()} ]'
954 }
955 }
956}
957
958fn field_to_string(f ConstField) string {
959 x := f.name.trim_string_left(f.mod + '.')
960 return 'const ${x} = ${f.expr};'
961}
962
963pub fn (e ComptimeForKind) str() string {
964 match e {
965 .methods { return 'methods' }
966 .fields { return 'fields' }
967 .attributes { return 'attributes' }
968 .values { return 'values' }
969 .variants { return 'variants' }
970 .params { return 'params' }
971 }
972}
973