vxx / vlib / v2_toberemoved / gen / v / gen.v
1122 lines · 1099 sloc · 21.7 KB · c0624b274a458fe3e487d89ae9554c2e8c25bdb6
Raw
1// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module v
5
6import v2.ast
7import v2.pref
8import strings
9import time
10
11struct Gen {
12 pref &pref.Preferences
13mut:
14 file ast.File
15 out strings.Builder
16 indent int
17 on_newline bool
18 in_init bool
19}
20
21pub fn new_gen(prefs &pref.Preferences) &Gen {
22 unsafe {
23 return &Gen{
24 pref: prefs
25 out: strings.new_builder(1000)
26 indent: -1
27 }
28 }
29}
30
31pub fn (mut g Gen) reset() {
32 g.out.go_back_to(0)
33 g.indent = -1
34 g.on_newline = false
35}
36
37pub fn (mut g Gen) gen(file ast.File) {
38 // clear in case we are reusing gen instance
39 if g.out.len > 1 {
40 g.reset()
41 }
42 if !g.pref.verbose {
43 unsafe {
44 goto start_no_time
45 }
46 }
47 mut sw := time.new_stopwatch()
48 start_no_time:
49 g.file = file
50 g.stmt_list(g.file.stmts)
51 if g.pref.verbose {
52 gen_time := sw.elapsed()
53 println('gen (v) ${file.name}: ${gen_time.milliseconds()}ms (${gen_time.microseconds()}µs)')
54 }
55}
56
57fn (mut g Gen) stmt_list(stmts []ast.Stmt) {
58 for stmt in stmts {
59 g.indent++
60 g.stmt(stmt)
61 g.indent--
62 }
63}
64
65fn (mut g Gen) stmt(stmt ast.Stmt) {
66 match stmt {
67 ast.AsmStmt {
68 g.writeln('asm ${stmt.arch} {')
69 g.writeln('}')
70 }
71 ast.AssertStmt {
72 g.write('assert ')
73 g.expr(stmt.expr)
74 if stmt.extra !is ast.EmptyExpr {
75 g.write(', ')
76 g.expr(stmt.extra)
77 }
78 g.writeln('')
79 }
80 ast.AssignStmt {
81 g.expr_list(stmt.lhs, ', ')
82 g.write(' ${stmt.op} ')
83 g.expr_list(stmt.rhs, ', ')
84 if !g.in_init {
85 g.writeln('')
86 }
87 }
88 []ast.Attribute {
89 g.attributes(stmt)
90 }
91 ast.BlockStmt {
92 g.writeln('{')
93 g.stmt_list(stmt.stmts)
94 g.writeln('}')
95 }
96 ast.ConstDecl {
97 is_v_header := g.file.name.ends_with('.vh')
98 for field in stmt.fields {
99 if stmt.is_public {
100 g.write('pub ')
101 }
102 g.write('const ')
103 g.write(field.name)
104 if is_v_header && is_header_const_type_expr(field.value) {
105 g.write(' ')
106 g.expr(field.value)
107 g.writeln('')
108 continue
109 }
110 g.write(' = ')
111 g.expr(field.value)
112 g.writeln('')
113 }
114 }
115 ast.ComptimeStmt {
116 g.write('$')
117 g.stmt(stmt.stmt)
118 }
119 ast.DeferStmt {
120 g.write('defer {')
121 g.stmt_list(stmt.stmts)
122 g.writeln('}')
123 }
124 ast.Directive {
125 g.write('#')
126 g.write(stmt.name)
127 g.write(' ')
128 if stmt.ct_cond.len > 0 {
129 g.write(stmt.ct_cond)
130 g.write(' ')
131 }
132 g.writeln(stmt.value)
133 }
134 ast.EmptyStmt {}
135 ast.EnumDecl {
136 if stmt.attributes.len > 0 {
137 g.attributes(stmt.attributes)
138 g.writeln('')
139 }
140 if stmt.is_public {
141 g.write('pub ')
142 }
143 g.write('enum ')
144 g.write(stmt.name)
145 if stmt.as_type !is ast.EmptyExpr {
146 g.write(' as ')
147 g.expr(stmt.as_type)
148 }
149 g.writeln(' {')
150 g.indent++
151 for field in stmt.fields {
152 g.write('${field.name}')
153 g.expr(field.typ)
154 if field.value !is ast.EmptyExpr {
155 g.write(' = ')
156 g.expr(field.value)
157 }
158 if field.attributes.len > 0 {
159 g.write(' ')
160 g.attributes(field.attributes)
161 }
162 g.writeln('')
163 }
164 g.indent--
165 g.writeln('}')
166 }
167 ast.ExprStmt {
168 g.expr(stmt.expr)
169 if !g.in_init {
170 g.writeln('')
171 }
172 }
173 ast.FlowControlStmt {
174 if stmt.label.len > 0 {
175 g.write(stmt.op.str())
176 g.write(' ')
177 g.writeln(stmt.label)
178 } else {
179 g.writeln(stmt.op.str())
180 }
181 }
182 ast.FnDecl {
183 if stmt.attributes.len > 0 {
184 g.attributes(stmt.attributes)
185 g.writeln('')
186 }
187 if stmt.is_public {
188 g.write('pub ')
189 }
190 g.write('fn ')
191 if stmt.is_method {
192 if !stmt.is_static {
193 g.write('(')
194 if stmt.receiver.is_mut {
195 g.write('mut ')
196 }
197 g.write(stmt.receiver.name)
198 g.write(' ')
199 g.expr(stmt.receiver.typ)
200 g.write(') ')
201 } else {
202 g.expr(stmt.receiver.typ)
203 g.write('.')
204 }
205 }
206 if stmt.language != .v {
207 g.write(stmt.language.str())
208 g.write('.')
209 }
210 g.write(stmt.name)
211 g.fn_type(stmt.typ)
212 // C fn definition |
213 // v fns with compiler implementations eg. `pub fn (a array) filter(predicate fn (voidptr) bool) array`
214 // NOTE: can we use generics for these fns, also make sure we parser error for normal fns without a body
215 // TODO: is it the correct way to handle those cases (the fn definitions, not this code)?
216 // if stmt.language == .c && stmt.stmts.len == 0 {
217 if stmt.stmts.len == 0 {
218 g.writeln('')
219 }
220 // normal v function
221 else {
222 g.writeln(' {')
223 g.stmt_list(stmt.stmts)
224 g.writeln('}')
225 }
226 }
227 ast.ForStmt {
228 g.write('for ')
229 in_init := g.in_init
230 g.in_init = true
231 mut is_plain := true
232 mut has_init := stmt.init !is ast.EmptyStmt
233 if has_init && stmt.init is ast.ForInStmt {
234 is_plain = false
235 g.write('/* ForIn */')
236 g.stmt(stmt.init)
237 } else {
238 mut has_post := stmt.post !is ast.EmptyStmt
239 if has_init {
240 is_plain = false
241 g.stmt(stmt.init)
242 g.write('; ')
243 }
244 if stmt.cond !is ast.EmptyExpr {
245 is_plain = false
246 g.write('/* cond: ${stmt.cond.name()} */')
247 g.expr(stmt.cond)
248 }
249 if has_init || has_post {
250 g.write('; ')
251 }
252 if has_post {
253 is_plain = false
254 g.stmt(stmt.post)
255 }
256 }
257 g.in_init = in_init
258 g.writeln(if is_plain { '{' } else { ' {' })
259 g.stmt_list(stmt.stmts)
260 g.writeln('}')
261 }
262 ast.ForInStmt {
263 // if stmt.key.len > 0 {
264 // g.write(stmt.key)
265 // g.write(', ')
266 // }
267 // if stmt.value_is_mut {
268 // g.write('mut ')
269 // }
270 // g.write(stmt.value)
271 if stmt.key !is ast.EmptyExpr {
272 g.expr(stmt.key)
273 g.write(', ')
274 }
275 g.expr(stmt.value)
276 g.write(' in ')
277 g.expr(stmt.expr)
278 }
279 ast.GlobalDecl {
280 if stmt.attributes.len > 0 {
281 g.attributes(stmt.attributes)
282 g.writeln('')
283 }
284 if stmt.is_public {
285 g.write('pub ')
286 }
287 g.writeln('__global (')
288 g.indent++
289 for field in stmt.fields {
290 // TODO
291 if field.is_public && !stmt.is_public {
292 g.write('pub ')
293 }
294 if field.is_mut {
295 g.write('mut ')
296 }
297 g.write(field.name)
298 // if field.value != none {
299 if field.value !is ast.EmptyExpr {
300 g.write(' = ')
301 g.expr(field.value)
302 } else {
303 g.write(' ')
304 g.expr(field.typ)
305 }
306 g.writeln('')
307 }
308 g.indent--
309 g.writeln(')')
310 }
311 ast.InterfaceDecl {
312 if stmt.is_public {
313 g.write('pub ')
314 }
315 g.write('interface ')
316 g.write(stmt.name)
317 if stmt.generic_params.len > 0 {
318 g.generic_list(stmt.generic_params)
319 }
320 g.writeln(' {')
321 g.indent++
322 for embed in stmt.embedded {
323 g.expr(embed)
324 g.writeln('')
325 }
326 mut field_is_mut := false
327 for field in stmt.fields {
328 if field.is_mut != field_is_mut {
329 if field.is_mut {
330 g.writeln('mut:')
331 }
332 field_is_mut = field.is_mut
333 }
334 g.write(field.name)
335 if field.is_interface_method && field.typ is ast.Type && field.typ is ast.FnType {
336 g.fn_type(field.typ as ast.FnType)
337 } else {
338 g.write(' ')
339 g.expr(field.typ)
340 }
341 g.writeln('')
342 }
343 g.indent--
344 g.writeln('}')
345 }
346 ast.ImportStmt {
347 g.write('import ')
348 g.write(stmt.name)
349 if stmt.is_aliased {
350 g.write(' as ')
351 g.write(stmt.alias)
352 }
353 if stmt.symbols.len > 0 {
354 g.write(' { ')
355 g.expr_list(stmt.symbols, ', ')
356 g.write(' }')
357 }
358 g.writeln('')
359 }
360 ast.LabelStmt {
361 g.write(stmt.name)
362 g.writeln(':')
363 if stmt.stmt !is ast.EmptyStmt {
364 g.stmt(stmt.stmt)
365 }
366 }
367 ast.ModuleStmt {
368 g.write('module ')
369 g.writeln(stmt.name)
370 }
371 ast.ReturnStmt {
372 g.write('return ')
373 g.expr_list(stmt.exprs, ', ')
374 g.writeln('')
375 }
376 ast.StructDecl {
377 g.struct_decl(stmt)
378 }
379 ast.TypeDecl {
380 if stmt.is_public {
381 g.write('pub ')
382 }
383 g.write('type ')
384 if stmt.language != .v {
385 g.write(stmt.language.str())
386 g.write('.')
387 }
388 g.write(stmt.name)
389 if stmt.generic_params.len > 0 {
390 g.generic_list(stmt.generic_params)
391 }
392 if stmt.variants.len > 0 {
393 g.write(' = ')
394 g.expr_list(stmt.variants, ' | ')
395 } else if stmt.base_type !is ast.EmptyExpr {
396 g.write(' = ')
397 g.expr(stmt.base_type)
398 }
399 g.writeln('')
400 }
401 }
402
403 // g.writeln('')
404}
405
406fn (mut g Gen) expr(expr ast.Expr) {
407 match expr {
408 ast.ArrayInitExpr {
409 if expr.update_expr !is ast.EmptyExpr {
410 g.write('[...')
411 g.expr(expr.update_expr)
412 if expr.exprs.len > 0 {
413 g.write(', ')
414 g.expr_list(expr.exprs, ', ')
415 }
416 g.write(']')
417 } else if expr.exprs.len > 0 {
418 g.write('[')
419 g.expr_list(expr.exprs, ', ')
420 g.write(']')
421 // TODO: better way to handle this
422 if expr.len !is ast.EmptyExpr {
423 g.write('!')
424 }
425 } else {
426 has_init := expr.init !is ast.EmptyExpr
427 has_len := expr.len !is ast.EmptyExpr
428 has_cap := expr.cap !is ast.EmptyExpr
429 g.expr(expr.typ)
430 g.write('{')
431 // if expr.init != none {
432 if has_init {
433 g.write('init: ')
434 g.expr(expr.init)
435 if has_len || has_cap {
436 g.write(', ')
437 }
438 }
439 // if expr.len != none {
440 if has_len {
441 g.write('len: ')
442 g.expr(expr.len)
443 if has_cap {
444 g.write(', ')
445 }
446 }
447 // if expr.cap != none {
448 if has_cap {
449 g.write('cap: ')
450 g.expr(expr.cap)
451 }
452 g.write('}')
453 }
454 }
455 ast.AsCastExpr {
456 g.expr(expr.expr)
457 g.write(' as ')
458 g.expr(expr.typ)
459 }
460 ast.AssocExpr {
461 g.expr(expr.typ)
462 g.writeln('{')
463 g.indent++
464 g.write('...')
465 g.expr(expr.expr)
466 g.writeln('')
467 for field in expr.fields {
468 g.write(field.name)
469 g.write(': ')
470 g.expr(field.value)
471 g.writeln('')
472 }
473 g.indent--
474 g.write('}')
475 }
476 ast.BasicLiteral {
477 if expr.kind == .char {
478 g.write('`')
479 g.write(expr.value)
480 g.write('`')
481 } else {
482 g.write(expr.value)
483 }
484 }
485 ast.CallExpr {
486 g.expr(expr.lhs)
487 g.write('(')
488 g.expr_list(expr.args, ', ')
489 g.write(')')
490 }
491 ast.CallOrCastExpr {
492 g.expr(expr.lhs)
493 g.write('(')
494 g.expr(expr.expr)
495 g.write(')')
496 }
497 ast.CastExpr {
498 g.expr(expr.typ)
499 g.write('(')
500 g.expr(expr.expr)
501 g.write(')')
502 }
503 ast.ComptimeExpr {
504 g.write('$')
505 g.expr(expr.expr)
506 }
507 ast.EmptyExpr {}
508 // TODO: should this be handled like this
509 ast.FieldInit {
510 g.write(expr.name)
511 g.write(': ')
512 g.expr(expr.value)
513 }
514 ast.FnLiteral {
515 g.write('fn')
516 if expr.captured_vars.len > 0 {
517 g.write(' [')
518 g.expr_list(expr.captured_vars, ', ')
519 g.write('] ')
520 }
521 g.fn_type(expr.typ)
522 g.writeln(' {')
523 g.stmt_list(expr.stmts)
524 g.write('}')
525 }
526 ast.GenericArgs {
527 // g.write('/* ast.GenericArgs */')
528 g.expr(expr.lhs)
529 g.generic_list(expr.args)
530 }
531 ast.GenericArgOrIndexExpr {
532 // g.write('/* ast.GenericArgOrIndexExpr */')
533 g.expr(expr.lhs)
534 mut generic_args := []ast.Expr{cap: 1}
535 generic_args << expr.expr
536 g.generic_list(generic_args)
537 }
538 ast.Ident {
539 g.write(expr.name)
540 }
541 ast.IfExpr {
542 g.if_expr(expr)
543 }
544 ast.IfGuardExpr {
545 g.stmt(expr.stmt)
546 }
547 ast.IndexExpr {
548 g.expr(expr.lhs)
549 g.write('[')
550 g.expr(expr.expr)
551 g.write(']')
552 }
553 ast.InfixExpr {
554 g.expr(expr.lhs)
555 g.write(' ')
556 g.write(expr.op.str())
557 g.write(' ')
558 g.expr(expr.rhs)
559 }
560 ast.InitExpr {
561 g.expr(expr.typ)
562 // with field names
563 if expr.fields.len > 0 && expr.fields[0].name != '' {
564 g.writeln('{')
565 in_init := g.in_init
566 g.in_init = true
567 g.indent++
568 for i, field in expr.fields {
569 g.write(field.name)
570 g.write(': ')
571 g.expr(field.value)
572 if i < expr.fields.len - 1 {
573 g.writeln(',')
574 } else {
575 g.writeln('')
576 }
577 }
578 g.indent--
579 g.in_init = in_init
580 }
581 // without field names, or empty init `Struct{}`
582 else {
583 g.write('{')
584 for i, field in expr.fields {
585 g.expr(field.value)
586 if i < expr.fields.len - 1 {
587 g.write(', ')
588 }
589 }
590 }
591 g.write('}')
592 }
593 ast.Keyword {
594 g.write(expr.tok.str())
595 }
596 ast.KeywordOperator {
597 g.write(expr.op.str())
598 if expr.op in [.key_go, .key_spawn] {
599 g.expr(expr.exprs[0])
600 } else {
601 g.write('(')
602 g.expr_list(expr.exprs, ', ')
603 g.write(')')
604 }
605 }
606 ast.LifetimeExpr {
607 g.write('^')
608 g.write(expr.name)
609 }
610 ast.LambdaExpr {
611 g.write('|')
612 for i, arg in expr.args {
613 g.write(arg.name)
614 if i < expr.args.len - 1 {
615 g.write(', ')
616 }
617 }
618 g.write('| ')
619 g.expr(expr.expr)
620 }
621 ast.LockExpr {
622 has_lock_exprs := expr.lock_exprs.len > 0
623 has_rlock_exprs := expr.rlock_exprs.len > 0
624 if !has_lock_exprs && !has_rlock_exprs {
625 g.write('lock')
626 } else {
627 if has_lock_exprs {
628 g.write('lock ')
629 g.expr_list(expr.lock_exprs, ', ')
630 if has_rlock_exprs {
631 g.write('; ')
632 }
633 }
634 if has_rlock_exprs {
635 g.write('rlock ')
636 g.expr_list(expr.rlock_exprs, ', ')
637 }
638 }
639 g.writeln(' {')
640 g.stmt_list(expr.stmts)
641 g.writeln('}')
642 }
643 ast.MapInitExpr {
644 // long syntax
645 if expr.typ !is ast.EmptyExpr {
646 g.expr(expr.typ)
647 g.write('{}')
648 }
649 // shorthand syntax
650 else if expr.keys.len > 0 {
651 g.write('{')
652 for i, key in expr.keys {
653 val := expr.vals[i]
654 g.expr(key)
655 g.write(': ')
656 g.expr(val)
657 if i < expr.keys.len - 1 {
658 g.write(', ')
659 }
660 }
661 g.write('}')
662 }
663 // empty {}
664 else {
665 g.write('{}')
666 }
667 }
668 ast.MatchExpr {
669 g.write('match ')
670 g.expr(expr.expr)
671 g.writeln(' {')
672 g.indent++
673 for branch in expr.branches {
674 if branch.cond.len > 0 {
675 g.expr_list(branch.cond, ', ')
676 } else {
677 g.write('else')
678 }
679 g.writeln(' {')
680 g.stmt_list(branch.stmts)
681 g.writeln('}')
682 }
683 g.indent--
684 g.write('}')
685 // g.writeln(' ==== DeSugared MatchExpr ==== ')
686 // g.expr(expr.desugar())
687 }
688 ast.ModifierExpr {
689 g.write(expr.kind.str())
690 g.write(' ')
691 g.expr(expr.expr)
692 }
693 ast.OrExpr {
694 g.expr(expr.expr)
695 g.writeln(' or {')
696 g.stmt_list(expr.stmts)
697 g.writeln('}')
698 // g.writeln('==== DeSugared OrExpr ====')
699 // g.expr(expr.desugar())
700 }
701 ast.ParenExpr {
702 g.write('(')
703 g.expr(expr.expr)
704 g.write(')')
705 }
706 ast.PostfixExpr {
707 g.expr(expr.expr)
708 g.write(expr.op.str())
709 }
710 ast.PrefixExpr {
711 g.write(expr.op.str())
712 g.expr(expr.expr)
713 }
714 ast.RangeExpr {
715 g.write('RangeExpr[ ')
716 g.expr(expr.start)
717 // g.write(' ')
718 g.write(expr.op.str())
719 // g.write(' ')
720 g.expr(expr.end)
721 g.write(' ]')
722 }
723 ast.SelectExpr {
724 g.writeln('select {')
725 g.indent++
726 g.select_expr(expr)
727 g.indent--
728 g.write('}')
729 }
730 ast.SelectorExpr {
731 g.expr(expr.lhs)
732 g.write('.')
733 g.expr(expr.rhs)
734 }
735 ast.StringInterLiteral {
736 if expr.kind != .v {
737 g.write(expr.kind.str())
738 }
739 // TODO: smart quote
740 // quote_str := "'"
741 // g.write(quote_str)
742 for i, value in expr.values {
743 g.write(value)
744 if i < expr.inters.len {
745 inter := expr.inters[i]
746 g.write('\${')
747 g.expr(inter.expr)
748 if inter.format != .unformatted {
749 g.write(':')
750 g.expr(inter.format_expr)
751 g.write(inter.format.str())
752 }
753 g.write('}')
754 }
755 }
756 // g.write(quote_str)
757 }
758 ast.StringLiteral {
759 if expr.kind != .v {
760 g.write(expr.kind.str())
761 }
762 // TODO: smart quote
763 // quote_str := "'"
764 // g.write(quote_str)
765 g.write(expr.value)
766 // g.write(quote_str)
767 }
768 ast.SqlExpr {
769 g.write('sql ')
770 g.expr(expr.expr)
771 g.writeln(' {')
772 g.write('}')
773 }
774 ast.Tuple {
775 g.expr_list(expr.exprs, ', ')
776 }
777 ast.UnsafeExpr {
778 one_liner := g.in_init || expr.stmts.len == 1
779 if one_liner {
780 g.write('unsafe { ')
781 } else {
782 g.writeln('unsafe {')
783 }
784 in_init := g.in_init
785 g.in_init = one_liner
786 g.stmt_list(expr.stmts)
787 g.in_init = in_init
788 if one_liner {
789 g.write(' }')
790 } else {
791 g.write('}')
792 }
793 }
794 // Type Nodes
795 // TODO: I really would like to allow matching the nested sumtypes like TS
796 ast.Type {
797 match expr {
798 ast.AnonStructType {
799 g.write('struct')
800 if expr.generic_params.len > 0 {
801 g.generic_list(expr.generic_params)
802 }
803 g.struct_decl_fields(expr.embedded, expr.fields)
804 }
805 ast.ArrayType {
806 g.write('[]')
807 g.expr(expr.elem_type)
808 }
809 ast.ArrayFixedType {
810 g.write('[')
811 if expr.len !is ast.EmptyExpr {
812 g.expr(expr.len)
813 }
814 g.write(']')
815 g.expr(expr.elem_type)
816 }
817 ast.ChannelType {
818 g.write('chan ')
819 g.expr(expr.elem_type)
820 }
821 ast.FnType {
822 g.write('fn')
823 g.fn_type(expr)
824 }
825 ast.GenericType {
826 g.expr(expr.name)
827 g.generic_list(expr.params)
828 }
829 ast.MapType {
830 g.write('map[')
831 g.expr(expr.key_type)
832 g.write(']')
833 g.expr(expr.value_type)
834 }
835 ast.NilType {
836 g.write('nil')
837 }
838 ast.NoneType {
839 g.write('none')
840 }
841 ast.OptionType {
842 g.write('?')
843 if expr.base_type !is ast.EmptyExpr {
844 g.expr(expr.base_type)
845 }
846 }
847 ast.PointerType {
848 g.write('&')
849 if expr.lifetime != '' {
850 g.write('^')
851 g.write(expr.lifetime)
852 g.write(' ')
853 }
854 g.expr(expr.base_type)
855 }
856 ast.ResultType {
857 g.write('!')
858 if expr.base_type !is ast.EmptyExpr {
859 g.expr(expr.base_type)
860 }
861 }
862 ast.ThreadType {
863 g.write('thread')
864 if expr.elem_type !is ast.EmptyExpr {
865 g.write(' ')
866 g.expr(expr.elem_type)
867 }
868 }
869 ast.TupleType {
870 g.write('(')
871 g.expr_list(expr.types, ', ')
872 g.write(')')
873 }
874 // TODO: v bug since all variants are accounted for
875 // this should not be required?
876 // ast.Type {}
877 }
878 }
879 }
880}
881
882// NOTE: I purposefully left out `$` from every branch of comptime if
883// Hopefully this will be removed from the syntax. if not ill add it.
884fn (mut g Gen) if_expr(expr ast.IfExpr) {
885 if expr.cond !is ast.EmptyExpr {
886 g.write('if ')
887 in_init := g.in_init
888 g.in_init = true
889 g.expr(expr.cond)
890 g.in_init = in_init
891 g.write(' ')
892 }
893 g.writeln('{')
894 g.stmt_list(expr.stmts)
895 g.write('}')
896 if expr.else_expr is ast.IfExpr {
897 g.write(' else ')
898 g.if_expr(expr.else_expr)
899 }
900}
901
902fn (mut g Gen) select_expr(expr ast.SelectExpr) {
903 // TODO: fix missing writeln after block due to `p.in_init`
904 in_init := g.in_init
905 g.in_init = true
906 g.stmt(expr.stmt)
907 g.in_init = in_init
908 if expr.stmts.len > 0 {
909 g.writeln(' {')
910 g.stmt_list(expr.stmts)
911 g.write('}')
912 }
913 g.writeln('')
914 if expr.next is ast.SelectExpr {
915 g.select_expr(expr.next)
916 }
917}
918
919fn (mut g Gen) attributes(attributes []ast.Attribute) {
920 g.write('@[')
921 for i, attribute in attributes {
922 if attribute.comptime_cond !is ast.EmptyExpr {
923 g.write('if ')
924 g.expr(attribute.comptime_cond)
925 } else {
926 if attribute.name != '' {
927 g.write(attribute.name)
928 g.write(': ')
929 }
930 g.expr(attribute.value)
931 if i < attributes.len - 1 {
932 g.write('; ')
933 }
934 }
935 }
936 g.write(']')
937}
938
939fn (mut g Gen) fn_type(typ ast.FnType) {
940 if typ.generic_params.len > 0 {
941 g.generic_list(typ.generic_params)
942 }
943 g.write('(')
944 for i, param in typ.params {
945 if param.is_mut {
946 g.write('mut ')
947 }
948 if param.name != '' {
949 g.write(param.name)
950 g.write(' ')
951 }
952 g.expr(param.typ)
953 if i < typ.params.len - 1 {
954 g.write(', ')
955 }
956 }
957 g.write(')')
958 if typ.return_type !is ast.EmptyExpr {
959 g.write(' ')
960 g.expr(typ.return_type)
961 }
962}
963
964fn (mut g Gen) struct_decl(stmt ast.StructDecl) {
965 if stmt.attributes.len > 0 {
966 g.attributes(stmt.attributes)
967 g.writeln('')
968 }
969 if stmt.is_public {
970 g.write('pub ')
971 }
972 g.write('struct ')
973 if stmt.language != .v {
974 g.write(stmt.language.str())
975 g.write('.')
976 }
977 g.write(stmt.name)
978 if stmt.generic_params.len > 0 {
979 g.generic_list(stmt.generic_params)
980 }
981 if stmt.implements.len > 0 {
982 g.write(' implements ')
983 for i, expr in stmt.implements {
984 if i > 0 {
985 g.write(', ')
986 }
987 g.expr(expr)
988 }
989 }
990 g.struct_decl_fields(stmt.embedded, stmt.fields)
991}
992
993fn (mut g Gen) struct_decl_fields(embedded []ast.Expr, fields []ast.FieldDecl) {
994 if fields.len > 0 {
995 g.writeln(' {')
996 } else {
997 g.write(' {')
998 }
999 g.indent++
1000 for embed in embedded {
1001 g.expr(embed)
1002 g.writeln('')
1003 }
1004 mut field_access := ''
1005 for field in fields {
1006 new_access := if field.is_module_mut {
1007 'pub module_mut'
1008 } else if field.is_public && field.is_mut {
1009 'pub mut'
1010 } else if field.is_public {
1011 'pub'
1012 } else if field.is_mut {
1013 'mut'
1014 } else {
1015 ''
1016 }
1017 if new_access != field_access {
1018 match new_access {
1019 'pub module_mut' {
1020 g.writeln('pub module_mut:')
1021 }
1022 'pub mut' {
1023 g.writeln('pub mut:')
1024 }
1025 'pub' {
1026 g.writeln('pub:')
1027 }
1028 'mut' {
1029 g.writeln('mut:')
1030 }
1031 else {}
1032 }
1033
1034 field_access = new_access
1035 }
1036 g.write(field.name)
1037 g.write(' ')
1038 g.expr(field.typ)
1039 // if field.value != none {
1040 if field.value !is ast.EmptyExpr {
1041 g.write(' = ')
1042 g.expr(field.value)
1043 }
1044 if field.attributes.len > 0 {
1045 g.write(' ')
1046 g.attributes(field.attributes)
1047 }
1048 g.writeln('')
1049 }
1050 g.indent--
1051 g.writeln('}')
1052}
1053
1054@[inline]
1055fn (mut g Gen) expr_list(exprs []ast.Expr, separator string) {
1056 for i, expr in exprs {
1057 g.expr(expr)
1058 if i < exprs.len - 1 {
1059 g.write(separator)
1060 }
1061 }
1062}
1063
1064@[inline]
1065fn (mut g Gen) generic_list(exprs []ast.Expr) {
1066 g.write('[')
1067 g.expr_list(exprs, ', ')
1068 g.write(']')
1069}
1070
1071@[inline]
1072fn (mut g Gen) write(str string) {
1073 if g.on_newline {
1074 if g.indent > 0 {
1075 for _ in 0 .. g.indent {
1076 g.out.write_u8(`\t`)
1077 }
1078 }
1079 }
1080 g.out.write_string(str)
1081 g.on_newline = false
1082}
1083
1084@[inline]
1085fn (mut g Gen) writeln(str string) {
1086 if g.on_newline {
1087 if g.indent > 0 {
1088 for _ in 0 .. g.indent {
1089 g.out.write_u8(`\t`)
1090 }
1091 }
1092 }
1093 g.out.writeln(str)
1094 g.on_newline = true
1095}
1096
1097fn is_header_const_type_expr(expr ast.Expr) bool {
1098 return match expr {
1099 ast.Type, ast.SelectorExpr {
1100 true
1101 }
1102 ast.Ident {
1103 name := expr.name
1104 name in ['bool', 'byte', 'char', 'f32', 'f64', 'i8', 'i16', 'i32', 'int', 'i64', 'isize', 'rune', 'string', 'u8', 'u16', 'u32', 'u64', 'usize', 'void', 'voidptr', 'byteptr', 'charptr']
1105 || name.starts_with('&') || name.starts_with('[]') || name.starts_with('?')
1106 || name.starts_with('!') || name.contains('[') || name.contains('__')
1107 || name.contains('.')
1108 }
1109 else {
1110 false
1111 }
1112 }
1113}
1114
1115pub fn (mut g Gen) print_output() {
1116 println(g.out.str())
1117}
1118
1119// output_string returns the generated V source code.
1120pub fn (mut g Gen) output_string() string {
1121 return g.out.str()
1122}
1123