v2 / vlib / v / transformer / transformer.v
1515 lines · 1454 sloc · 34.33 KB · ff1f157b0654ea116dd24a27aa1af126eae5e30b
Raw
1// Copyright (c) 2019-2025 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.
4module transformer
5
6import math
7import v.pref
8import v.ast
9import v.token
10import v.util
11
12union U64F64 {
13 u u64
14 f f64
15}
16
17pub struct Transformer {
18 pref &pref.Preferences
19pub mut:
20 index &IndexState
21 table &ast.Table = unsafe { nil }
22 file &ast.File = unsafe { nil }
23 skip_array_transform bool // is the checker transformer, set by the checker
24mut:
25 is_assert bool
26 inside_dump bool
27 inside_in bool
28 inside_sql bool
29 //
30 strings_builder_type ast.Type = ast.no_type
31}
32
33fn (mut t Transformer) trace[T](fbase string, x &T) {
34 if t.file.path_base == fbase {
35 println('> t.trace | ${fbase:-10s} | ${voidptr(x):16} | ${x}')
36 }
37}
38
39pub fn new_transformer(pref_ &pref.Preferences) &Transformer {
40 return &Transformer{
41 pref: pref_
42 index: &IndexState{
43 saved_key_vals: [][]KeyVal{cap: 1000}
44 saved_disabled: []bool{cap: 1000}
45 }
46 }
47}
48
49pub fn new_transformer_with_table(table &ast.Table, pref_ &pref.Preferences) &Transformer {
50 mut transformer := new_transformer(pref_)
51 transformer.table = table
52 return transformer
53}
54
55pub fn (mut t Transformer) transform_files(ast_files []&ast.File) {
56 t.strings_builder_type = t.table.find_type('strings.Builder')
57 for i in 0 .. ast_files.len {
58 mut file := unsafe { ast_files[i] }
59 t.transform(mut file)
60 }
61}
62
63pub fn (mut t Transformer) transform(mut ast_file ast.File) {
64 t.file = ast_file
65 for mut stmt in ast_file.stmts {
66 stmt = t.stmt(mut stmt)
67 }
68}
69
70fn folded_float_literal(value f64, pos token.Pos) ast.FloatLiteral {
71 // ast.FloatLiteral stores source text, so the folded value needs a
72 // decimal/scientific form that reparses to the same bits.
73 short := value.str()
74 if transformer_f64_bits(short.f64()) == transformer_f64_bits(value) {
75 return ast.FloatLiteral{
76 val: short
77 pos: pos
78 }
79 }
80 exact := value.strsci(17)
81 return ast.FloatLiteral{
82 val: exact
83 pos: pos
84 }
85}
86
87@[inline]
88fn transformer_f64_bits(value f64) u64 {
89 return unsafe {
90 U64F64{
91 f: value
92 }.u
93 }
94}
95
96@[ignore_overflow]
97fn folded_power_i64(base i64, exponent i64) i64 {
98 mut exp := exponent
99 mut power := base
100 mut value := i64(1)
101 if exp < 0 {
102 if base == 0 {
103 return -1
104 }
105 return if base * base != 1 {
106 0
107 } else {
108 if exp & 1 > 0 {
109 base
110 } else {
111 1
112 }
113 }
114 }
115 for exp > 0 {
116 if exp & 1 > 0 {
117 value *= power
118 }
119 power *= power
120 exp >>= 1
121 }
122 return value
123}
124
125fn folded_power_f64(base f64, exponent f64) f64 {
126 return math.pow(base, exponent)
127}
128
129pub fn (mut t Transformer) find_new_range(node ast.AssignStmt) {
130 if !t.pref.is_prod {
131 return
132 }
133 // looking for, array := []type{len:int}
134 mut right := node.right[0]
135 if mut right is ast.IndexExpr {
136 mut left := node.left[0]
137 if mut left is ast.Ident {
138 // we can not analyse mut array
139 if left.is_mut {
140 t.index.safe_access(left.name, -2)
141 return
142 }
143 index := right.index
144 if index is ast.RangeExpr {
145 range_low := index.low
146 if range_low is ast.IntegerLiteral {
147 sub_left := right.left
148 if sub_left is ast.Ident {
149 safe := t.index.safe_offset(sub_left.name)
150 low := range_low.val.int()
151 if safe >= low {
152 t.index.safe_access(left.name, safe - low)
153 }
154 }
155 }
156 }
157 }
158 }
159}
160
161pub fn (mut t Transformer) find_mut_self_assign(node ast.AssignStmt) {
162 if !t.pref.is_prod {
163 return
164 }
165 // even if mutable we can be sure than `a[1] = a[2] is safe
166}
167
168pub fn (mut t Transformer) check_safe_array(mut node ast.IndexExpr) {
169 if !t.pref.is_prod {
170 return
171 }
172 if !node.is_array {
173 return
174 }
175 index := node.index
176 name := node.left
177 match index {
178 ast.IntegerLiteral {
179 is_direct := t.index.safe_access(name.str(), index.val.int())
180 node.is_direct = is_direct
181 }
182 ast.RangeExpr {
183 if index.has_high {
184 high := index.high
185 if high is ast.IntegerLiteral {
186 t.index.safe_access(name.str(), high.val.int())
187 return
188 }
189 }
190 if index.has_low {
191 low := index.low
192 if low is ast.IntegerLiteral {
193 t.index.safe_access(name.str(), low.val.int())
194 return
195 }
196 }
197 }
198 ast.CastExpr {
199 // do not deal with weird casting
200 if index.typname != 'int' {
201 return
202 }
203 index_expr := index.expr
204 if index_expr is ast.IntegerLiteral {
205 val := index_expr.val
206 node.is_direct = t.index.safe_access(name.str(), val.int())
207 }
208 }
209 ast.EnumVal {
210 debug_bounds_checking('? ${name}[.${index.val}] safe?: no-idea (yet)!')
211 }
212 ast.Ident {
213 // we may be able to track const value in simple cases
214 }
215 else {}
216 }
217}
218
219pub fn (mut t Transformer) stmt(mut node ast.Stmt) ast.Stmt {
220 $if trace_transformer ? {
221 ntype := typeof(*node).replace('v.ast.', '')
222 eprintln('transformer: ${t.file.path:-50} | pos: ${node.pos.line_str():-39} | node: ${ntype:12} | ${node}')
223 }
224 mut onode := unsafe { node }
225 match mut node {
226 ast.EmptyStmt {}
227 ast.NodeError {}
228 ast.AsmStmt {}
229 ast.DebuggerStmt {}
230 ast.AssertStmt {
231 t.assert_stmt(mut node)
232 }
233 ast.AssignStmt {
234 t.assign_stmt(mut node)
235 }
236 ast.Block {
237 t.index.indent(false)
238 for mut stmt in node.stmts {
239 stmt = t.stmt(mut stmt)
240 }
241 t.index.unindent()
242 }
243 ast.BranchStmt {
244 // break or continue:
245 // we can not rely on sequential scanning and need to cancel all index optimisation
246 t.index.disabled = true
247 }
248 ast.ComptimeFor {
249 t.comptime_for(mut node)
250 }
251 ast.ConstDecl {
252 t.const_decl(mut node)
253 }
254 ast.DeferStmt {
255 for mut stmt in node.stmts {
256 stmt = t.stmt(mut stmt)
257 }
258 }
259 ast.EnumDecl {
260 t.enum_decl(mut node)
261 }
262 ast.ExprStmt {
263 // TODO: check if this can be handled in `t.expr`
264 node.expr = match mut node.expr {
265 ast.IfExpr {
266 t.expr_stmt_if_expr(mut node.expr)
267 }
268 ast.MatchExpr {
269 t.expr_stmt_match_expr(mut node.expr)
270 }
271 else {
272 t.expr(mut node.expr)
273 }
274 }
275
276 if mut node.expr is ast.CallExpr && node.expr.is_expand_simple_interpolation {
277 t.simplify_nested_interpolation_in_sb(mut onode, mut node.expr, node.typ)
278 }
279 }
280 ast.FnDecl {
281 t.fn_decl(mut node)
282 }
283 ast.ForCStmt {
284 t.for_c_stmt(mut node)
285 }
286 ast.ForInStmt {
287 // indexes access within the for itself are not optimised (yet)
288 t.index.indent(false)
289 for mut stmt in node.stmts {
290 stmt = t.stmt(mut stmt)
291 }
292 t.index.unindent()
293 }
294 ast.ForStmt {
295 return t.for_stmt(mut node)
296 }
297 ast.GlobalDecl {
298 t.global_decl(mut node)
299 }
300 ast.GotoLabel {}
301 ast.GotoStmt {
302 // we can not rely on sequential scanning and need to cancel all index optimisation
303 t.index.disabled = true
304 }
305 ast.HashStmt {
306 for mut cond in node.ct_conds {
307 cond = t.expr(mut cond)
308 }
309 }
310 ast.Import {}
311 ast.InterfaceDecl {
312 t.interface_decl(mut node)
313 }
314 ast.Module {}
315 ast.Return {
316 for mut expr in node.exprs {
317 expr = t.expr(mut expr)
318 }
319 }
320 ast.SemicolonStmt {}
321 ast.SqlStmt {}
322 ast.StructDecl {
323 t.struct_decl(mut node)
324 }
325 ast.TypeDecl {}
326 }
327
328 return node
329}
330
331pub fn (mut t Transformer) comptime_for(mut node ast.ComptimeFor) {
332 for mut stmt in node.stmts {
333 stmt = t.stmt(mut stmt)
334 }
335}
336
337pub fn (mut t Transformer) assign_stmt(mut node ast.AssignStmt) {
338 t.find_new_array_len(node)
339 t.find_new_range(node)
340 t.find_mut_self_assign(node)
341 for mut right in node.right {
342 right = t.expr(mut right)
343 }
344 for mut left in node.left {
345 left = t.expr(mut left)
346 }
347}
348
349pub fn (mut t Transformer) const_decl(mut node ast.ConstDecl) {
350 for mut field in node.fields {
351 field.expr = t.expr(mut field.expr)
352 }
353}
354
355pub fn (mut t Transformer) enum_decl(mut node ast.EnumDecl) {
356 for mut field in node.fields {
357 if field.has_expr {
358 field.expr = t.expr(mut field.expr)
359 }
360 }
361}
362
363pub fn (mut t Transformer) global_decl(mut node ast.GlobalDecl) {
364 for mut field in node.fields {
365 field.expr = t.expr(mut field.expr)
366 }
367}
368
369pub fn (mut t Transformer) interface_decl(mut node ast.InterfaceDecl) {
370 for mut field in node.fields {
371 field.default_expr = t.expr(mut field.default_expr)
372 }
373}
374
375pub fn (mut t Transformer) struct_decl(mut node ast.StructDecl) {
376 for mut field in node.fields {
377 field.default_expr = t.expr(mut field.default_expr)
378 }
379}
380
381pub fn (mut t Transformer) assert_stmt(mut node ast.AssertStmt) {
382 t.is_assert = true
383 node.expr = t.expr(mut node.expr)
384 if !t.pref.is_prod {
385 return
386 }
387 if mut node.expr is ast.InfixExpr {
388 right := node.expr.right
389 match right {
390 ast.IntegerLiteral {
391 left := node.expr.left
392 if left is ast.SelectorExpr {
393 len := right.val.int()
394 if left.field_name == 'len' {
395 match node.expr.op {
396 .eq { // ==
397 t.index.safe_access(left.expr.str(), len - 1)
398 }
399 .ge { // >=
400 t.index.safe_access(left.expr.str(), len - 1)
401 }
402 .gt { // >
403 t.index.safe_access(left.expr.str(), len)
404 }
405 else {}
406 }
407 }
408 }
409 }
410 ast.SelectorExpr {
411 left := node.expr.left
412 if left is ast.IntegerLiteral {
413 len := left.val.int()
414 if right.field_name == 'len' {
415 match node.expr.op {
416 .eq { // ==
417 t.index.safe_access(right.expr.str(), len - 1)
418 }
419 .le { // <=
420 t.index.safe_access(right.expr.str(), len - 1)
421 }
422 .lt { // <
423 t.index.safe_access(right.expr.str(), len)
424 }
425 else {}
426 }
427 }
428 }
429 }
430 else {}
431 }
432 }
433 t.is_assert = false
434}
435
436pub fn (mut t Transformer) expr_stmt_if_expr(mut node ast.IfExpr) ast.Expr {
437 mut stop_index, mut unreachable_branches := -1, []int{cap: node.branches.len}
438 if node.is_comptime {
439 return node
440 }
441 for i, mut branch in node.branches {
442 cond := t.expr(mut branch.cond)
443 branch = ast.IfBranch{
444 ...(*branch)
445 cond: cond
446 }
447 if cond is ast.BoolLiteral {
448 if cond.val { // eliminates remaining branches when reached first bool literal `true`
449 stop_index = i
450 break
451 } else { // discard unreachable branch when reached bool literal `false`
452 unreachable_branches << i
453 }
454 }
455 t.index.indent(false)
456 for mut stmt in branch.stmts {
457 stmt = t.stmt(mut stmt)
458 }
459 t.index.unindent()
460 }
461 if stop_index != -1 {
462 unreachable_branches = unreachable_branches.filter(it < stop_index)
463 node.branches = node.branches[..stop_index + 1]
464 }
465 for unreachable_branches.len != 0 {
466 node.branches.delete(unreachable_branches.pop())
467 }
468 /*
469 FIXME: optimization causes cgen error `g.expr(): unhandled EmptyExpr`
470 if original.branches.len == 0 { // no remain branches to walk through
471 return ast.empty_expr
472 }*/
473 if node.branches.len == 1 && node.branches[0].cond.type_name() == 'unknown v.ast.Expr' {
474 node.branches[0].cond = ast.BoolLiteral{
475 val: true
476 }
477 }
478 return node
479}
480
481pub fn (mut t Transformer) expr_stmt_match_expr(mut node ast.MatchExpr) ast.Expr {
482 mut terminate := false
483 cond := t.expr(mut node.cond)
484 node.cond = cond
485 for mut branch in node.branches {
486 if branch.is_else {
487 t.index.indent(false)
488 for mut stmt in branch.stmts {
489 stmt = t.stmt(mut stmt)
490 }
491 t.index.unindent()
492 continue
493 }
494
495 for mut expr in branch.exprs {
496 expr = t.expr(mut expr)
497
498 match cond {
499 ast.BoolLiteral {
500 if mut expr is ast.BoolLiteral {
501 if cond.val == expr.val {
502 branch.exprs = [expr]
503 node.branches = [branch]
504 terminate = true
505 }
506 }
507 }
508 ast.IntegerLiteral {
509 if mut expr is ast.IntegerLiteral {
510 if cond.val.int() == expr.val.int() {
511 branch.exprs = [expr]
512 node.branches = [branch]
513 terminate = true
514 }
515 }
516 }
517 ast.FloatLiteral {
518 if mut expr is ast.FloatLiteral {
519 if cond.val.f32() == expr.val.f32() {
520 branch.exprs = [expr]
521 node.branches = [branch]
522 terminate = true
523 }
524 }
525 }
526 ast.StringLiteral {
527 if mut expr is ast.StringLiteral {
528 if cond.val == expr.val {
529 branch.exprs = [expr]
530 node.branches = [branch]
531 terminate = true
532 }
533 }
534 }
535 else {}
536 }
537 }
538
539 t.index.indent(false)
540 for mut stmt in branch.stmts {
541 stmt = t.stmt(mut stmt)
542 }
543 t.index.unindent()
544
545 if terminate {
546 break
547 }
548 }
549 return node
550}
551
552pub fn (mut t Transformer) for_c_stmt(mut node ast.ForCStmt) {
553 // TODO: we do not optimise array access for multi init
554 // for a,b := 0,1; a < 10; a,b = a+b, a {...}
555 if node.has_init && !node.is_multi {
556 node.init = t.stmt(mut node.init)
557 }
558 if node.has_cond {
559 node.cond = t.expr(mut node.cond)
560 }
561 t.index.indent(false)
562 for mut stmt in node.stmts {
563 stmt = t.stmt(mut stmt)
564 }
565 t.index.unindent()
566 if node.has_inc && !node.is_multi {
567 node.inc = t.stmt(mut node.inc)
568 }
569}
570
571pub fn (mut t Transformer) for_stmt(mut node ast.ForStmt) ast.Stmt {
572 node.cond = t.expr(mut node.cond)
573 match node.cond {
574 ast.BoolLiteral {
575 if !(node.cond as ast.BoolLiteral).val { // for false { ... } should be eliminated
576 return ast.empty_stmt
577 }
578 }
579 else {
580 if !node.is_inf {
581 t.index.indent(false)
582 for mut stmt in node.stmts {
583 stmt = t.stmt(mut stmt)
584 }
585 t.index.unindent()
586 return node
587 }
588 }
589 }
590
591 for mut stmt in node.stmts {
592 stmt = t.stmt(mut stmt)
593 }
594 return node
595}
596
597pub fn (mut t Transformer) expr(mut node ast.Expr) ast.Expr {
598 if t.inside_dump {
599 return node
600 }
601 match mut node {
602 ast.AnonFn {
603 node.decl = t.stmt(mut node.decl) as ast.FnDecl
604 }
605 ast.ArrayDecompose {
606 node.expr = t.expr(mut node.expr)
607 }
608 ast.ArrayInit {
609 return t.array_init(mut node)
610 }
611 ast.AsCast {
612 node.expr = t.expr(mut node.expr)
613 }
614 ast.CTempVar {
615 node.orig = t.expr(mut node.orig)
616 }
617 ast.CallExpr {
618 node.left = t.expr(mut node.left)
619 for mut arg in node.args {
620 arg.expr = t.expr(mut arg.expr)
621 }
622 node.or_block = t.expr(mut node.or_block) as ast.OrExpr
623 }
624 ast.CastExpr {
625 node.arg = t.expr(mut node.arg)
626 node.expr = t.expr(mut node.expr)
627 }
628 ast.ChanInit {
629 node.cap_expr = t.expr(mut node.cap_expr)
630 }
631 ast.ComptimeCall {
632 for mut arg in node.args {
633 arg.expr = t.expr(mut arg.expr)
634 }
635 }
636 ast.ComptimeSelector {
637 node.left = t.expr(mut node.left)
638 node.field_expr = t.expr(mut node.field_expr)
639 }
640 ast.ConcatExpr {
641 for mut val in node.vals {
642 val = t.expr(mut val)
643 }
644 }
645 ast.DumpExpr {
646 old_inside_dump := t.inside_dump
647 t.inside_dump = true
648 node.expr = t.expr(mut node.expr)
649 t.inside_dump = old_inside_dump
650 }
651 ast.GoExpr {
652 t.expr(mut node.call_expr)
653 }
654 ast.IfExpr {
655 return t.if_expr(mut node)
656 }
657 ast.IfGuardExpr {
658 node.expr = t.expr(mut node.expr)
659 }
660 ast.IndexExpr {
661 t.check_safe_array(mut node)
662 node.left = t.expr(mut node.left)
663 node.index = t.expr(mut node.index)
664 mut indices := []ast.Expr{cap: node.indices.len}
665 for mut index in node.indices {
666 indices << t.expr(mut index)
667 }
668 node.indices = indices
669 node.or_expr = t.expr(mut node.or_expr) as ast.OrExpr
670 }
671 ast.InfixExpr {
672 return t.infix_expr(mut node)
673 }
674 ast.IsRefType {
675 node.expr = t.expr(mut node.expr)
676 }
677 ast.Likely {
678 node.expr = t.expr(mut node.expr)
679 }
680 ast.LockExpr {
681 for mut stmt in node.stmts {
682 stmt = t.stmt(mut stmt)
683 }
684 for mut locked in node.lockeds {
685 locked = t.expr(mut locked)
686 }
687 }
688 ast.MapInit {
689 for mut key in node.keys {
690 key = t.expr(mut key)
691 }
692 for mut val in node.vals {
693 val = t.expr(mut val)
694 }
695 }
696 ast.MatchExpr {
697 return t.match_expr(mut node)
698 }
699 ast.OrExpr {
700 for mut stmt in node.stmts {
701 stmt = t.stmt(mut stmt)
702 }
703 if node.stmts.len > 0 {
704 // todo fix [] => new_array_from_c_array() now
705 mut stmt := node.stmts.last()
706 if mut stmt is ast.ExprStmt {
707 if mut stmt.expr is ast.CallExpr {
708 stmt.expr.is_return_used = true
709 }
710 }
711 }
712 }
713 ast.ParExpr {
714 mut inner_expr := t.expr(mut node.expr)
715 if inner_expr in [
716 ast.IntegerLiteral,
717 ast.FloatLiteral,
718 ast.BoolLiteral,
719 ast.StringLiteral,
720 ast.StringInterLiteral,
721 ast.CharLiteral,
722 ast.Ident,
723 ] {
724 return inner_expr
725 }
726 }
727 ast.PostfixExpr {
728 node.expr = t.expr(mut node.expr)
729 }
730 ast.PrefixExpr {
731 node.right = t.expr(mut node.right)
732 node.or_block = t.expr(mut node.or_block) as ast.OrExpr
733 }
734 ast.RangeExpr {
735 node.low = t.expr(mut node.low)
736 node.high = t.expr(mut node.high)
737 }
738 ast.SelectExpr {
739 for mut branch in node.branches {
740 branch.stmt = t.stmt(mut branch.stmt)
741 for mut stmt in branch.stmts {
742 stmt = t.stmt(mut stmt)
743 }
744 }
745 }
746 ast.SelectorExpr {
747 node.expr = t.expr(mut node.expr)
748 if mut node.expr is ast.StringLiteral && node.field_name == 'len' {
749 if !node.expr.val.contains('\\') || node.expr.is_raw {
750 return ast.IntegerLiteral{
751 val: node.expr.val.len.str()
752 pos: node.pos
753 }
754 }
755 }
756 }
757 ast.SizeOf {
758 node.expr = t.expr(mut node.expr)
759 }
760 ast.SqlExpr {
761 return t.sql_expr(mut node)
762 }
763 ast.SqlQueryDataExpr {
764 node.items = t.sql_query_data_items(node.items)
765 }
766 ast.StringInterLiteral {
767 for mut expr in node.exprs {
768 expr = t.expr(mut expr)
769 }
770 for mut expr in node.fwidth_exprs {
771 if expr !is ast.EmptyExpr {
772 expr = t.expr(mut expr)
773 }
774 }
775 for mut expr in node.precision_exprs {
776 if expr !is ast.EmptyExpr {
777 expr = t.expr(mut expr)
778 }
779 }
780 }
781 ast.StructInit {
782 node.update_expr = t.expr(mut node.update_expr)
783 for mut init_field in node.init_fields {
784 init_field.expr = t.expr(mut init_field.expr)
785 }
786 }
787 ast.UnsafeExpr {
788 node.expr = t.expr(mut node.expr)
789 }
790 ast.AtExpr {
791 // TODO
792 }
793 else {}
794 }
795
796 return node
797}
798
799fn (mut t Transformer) sql_query_data_items(items []ast.SqlQueryDataItem) []ast.SqlQueryDataItem {
800 mut new_items := []ast.SqlQueryDataItem{cap: items.len}
801 for item in items {
802 mut item_copy := item
803 new_items << t.sql_query_data_item(mut item_copy)
804 }
805 return new_items
806}
807
808fn (mut t Transformer) sql_query_data_item(mut item ast.SqlQueryDataItem) ast.SqlQueryDataItem {
809 match mut item {
810 ast.SqlQueryDataLeaf {
811 // The left side is an ORM field, but the right side can be a V variable
812 // with the same name, so `field == field` must not fold to `true`.
813 old_inside_sql := t.inside_sql
814 t.inside_sql = true
815 item.expr = t.expr(mut item.expr)
816 t.inside_sql = old_inside_sql
817 }
818 ast.SqlQueryDataIf {
819 for mut branch in item.branches {
820 branch.cond = t.expr(mut branch.cond)
821 branch.items = t.sql_query_data_items(branch.items)
822 }
823 }
824 }
825
826 return item
827}
828
829pub fn (mut t Transformer) call_expr(mut node ast.CallExpr) {
830 for mut arg in node.args {
831 arg.expr = t.expr(mut arg.expr)
832 }
833}
834
835fn (mut t Transformer) trans_const_value_to_literal(mut expr ast.Expr) {
836 mut expr_ := expr
837 if mut expr_ is ast.Ident {
838 // if there is a local variable or a fn parameter, that has the same name as the constant, do not do the substitution:
839 if _ := expr_.scope.find_var(expr_.name) {
840 return
841 }
842 if mut obj := t.table.global_scope.find_const(expr_.full_name()) {
843 if mut obj.expr is ast.BoolLiteral {
844 expr = obj.expr
845 } else if mut obj.expr is ast.IntegerLiteral {
846 expr = obj.expr
847 } else if mut obj.expr is ast.FloatLiteral {
848 expr = obj.expr
849 } else if mut obj.expr is ast.StringLiteral {
850 expr = obj.expr
851 } else if mut obj.expr is ast.InfixExpr {
852 folded_expr := t.infix_expr(mut obj.expr)
853 if folded_expr is ast.BoolLiteral {
854 expr = folded_expr
855 } else if folded_expr is ast.IntegerLiteral {
856 expr = folded_expr
857 } else if folded_expr is ast.FloatLiteral {
858 expr = folded_expr
859 } else if folded_expr is ast.StringLiteral {
860 expr = folded_expr
861 }
862 }
863 }
864 }
865}
866
867pub fn (mut t Transformer) infix_expr(mut node ast.InfixExpr) ast.Expr {
868 if node.op == .not_in || node.op == .key_in {
869 tmp_inside_in := t.inside_in
870 t.inside_in = true
871 node.left = t.expr(mut node.left)
872 node.right = t.expr(mut node.right)
873 t.inside_in = tmp_inside_in
874 } else {
875 node.left = t.expr(mut node.left)
876 node.right = t.expr(mut node.right)
877 }
878 if !t.pref.translated {
879 t.trans_const_value_to_literal(mut node.left)
880 t.trans_const_value_to_literal(mut node.right)
881 }
882
883 mut pos := node.left.pos()
884 pos.extend(node.pos)
885 pos.extend(node.right.pos())
886
887 if t.pref.is_debug || t.is_assert { // never optimize assert statements
888 return node
889 } else {
890 match mut node.left {
891 ast.BoolLiteral {
892 match mut node.right {
893 ast.BoolLiteral {
894 match node.op {
895 .eq {
896 return ast.BoolLiteral{
897 val: node.left.val == node.right.val
898 }
899 }
900 .ne {
901 return ast.BoolLiteral{
902 val: node.left.val != node.right.val
903 }
904 }
905 .and {
906 return ast.BoolLiteral{
907 val: node.left.val && node.right.val
908 }
909 }
910 .logical_or {
911 return ast.BoolLiteral{
912 val: node.left.val || node.right.val
913 }
914 }
915 else {}
916 }
917 }
918 else {}
919 }
920 }
921 ast.StringLiteral {
922 match mut node.right {
923 ast.StringLiteral {
924 match node.op {
925 .eq {
926 return ast.BoolLiteral{
927 val: node.left.val == node.right.val
928 }
929 }
930 .ne {
931 return ast.BoolLiteral{
932 val: node.left.val != node.right.val
933 }
934 }
935 .plus {
936 return if t.pref.backend == .c { ast.Expr(ast.StringLiteral{
937 val: util.smart_quote(node.left.val, node.left.is_raw) + util.smart_quote(node.right.val, node.right.is_raw)
938 pos: pos
939 }) } else { ast.Expr(node) }
940 }
941 else {}
942 }
943 }
944 else {}
945 }
946 }
947 ast.IntegerLiteral {
948 match mut node.right {
949 ast.IntegerLiteral {
950 left_val := node.left.val.i64()
951 right_val := node.right.val.i64()
952
953 match node.op {
954 .eq {
955 return ast.BoolLiteral{
956 val: left_val == right_val
957 }
958 }
959 .ne {
960 return ast.BoolLiteral{
961 val: left_val != right_val
962 }
963 }
964 .gt {
965 return ast.BoolLiteral{
966 val: left_val > right_val
967 }
968 }
969 .ge {
970 return ast.BoolLiteral{
971 val: left_val >= right_val
972 }
973 }
974 .lt {
975 return ast.BoolLiteral{
976 val: left_val < right_val
977 }
978 }
979 .le {
980 return ast.BoolLiteral{
981 val: left_val <= right_val
982 }
983 }
984 .plus {
985 return ast.IntegerLiteral{
986 val: (left_val + right_val).str()
987 pos: pos
988 }
989 }
990 .mul {
991 return ast.IntegerLiteral{
992 val: (left_val * right_val).str()
993 pos: pos
994 }
995 }
996 .power {
997 return ast.IntegerLiteral{
998 val: folded_power_i64(left_val, right_val).str()
999 pos: pos
1000 }
1001 }
1002 .minus {
1003 // HACK: prevent folding of `min_i64` values in `math` module
1004 if left_val == -9223372036854775807 && right_val == 1 {
1005 return node
1006 }
1007
1008 return ast.IntegerLiteral{
1009 val: (left_val - right_val).str()
1010 pos: pos
1011 }
1012 }
1013 .div {
1014 return ast.IntegerLiteral{
1015 val: (left_val / right_val).str()
1016 pos: pos
1017 }
1018 }
1019 .mod {
1020 return ast.IntegerLiteral{
1021 val: (left_val % right_val).str()
1022 pos: pos
1023 }
1024 }
1025 .xor {
1026 return ast.IntegerLiteral{
1027 val: (left_val ^ right_val).str()
1028 pos: pos
1029 }
1030 }
1031 .pipe {
1032 return ast.IntegerLiteral{
1033 val: (left_val | right_val).str()
1034 pos: pos
1035 }
1036 }
1037 .amp {
1038 return ast.IntegerLiteral{
1039 val: (left_val & right_val).str()
1040 pos: pos
1041 }
1042 }
1043 .left_shift {
1044 return ast.IntegerLiteral{
1045 val: (unsafe { left_val << right_val }).str()
1046 pos: pos
1047 }
1048 }
1049 .right_shift {
1050 return ast.IntegerLiteral{
1051 val: (left_val >> right_val).str()
1052 pos: pos
1053 }
1054 }
1055 .unsigned_right_shift {
1056 return ast.IntegerLiteral{
1057 val: (u64(left_val) >>> right_val).str()
1058 pos: pos
1059 }
1060 }
1061 else {}
1062 }
1063 }
1064 else {}
1065 }
1066 }
1067 ast.FloatLiteral {
1068 match mut node.right {
1069 ast.FloatLiteral {
1070 left_val := node.left.val.f64()
1071 right_val := node.right.val.f64()
1072 match node.op {
1073 .eq {
1074 return ast.BoolLiteral{
1075 val: left_val == right_val
1076 }
1077 }
1078 .ne {
1079 return ast.BoolLiteral{
1080 val: left_val != right_val
1081 }
1082 }
1083 .gt {
1084 return ast.BoolLiteral{
1085 val: left_val > right_val
1086 }
1087 }
1088 .ge {
1089 return ast.BoolLiteral{
1090 val: left_val >= right_val
1091 }
1092 }
1093 .lt {
1094 return ast.BoolLiteral{
1095 val: left_val < right_val
1096 }
1097 }
1098 .le {
1099 return ast.BoolLiteral{
1100 val: left_val <= right_val
1101 }
1102 }
1103 .plus {
1104 return folded_float_literal(left_val + right_val, pos)
1105 }
1106 .mul {
1107 return folded_float_literal(left_val * right_val, pos)
1108 }
1109 .power {
1110 return ast.FloatLiteral{
1111 val: folded_power_f64(left_val, right_val).str()
1112 pos: pos
1113 }
1114 }
1115 .minus {
1116 return folded_float_literal(left_val - right_val, pos)
1117 }
1118 .div {
1119 return folded_float_literal(left_val / right_val, pos)
1120 }
1121 else {}
1122 }
1123 }
1124 else {}
1125 }
1126 }
1127 ast.CharLiteral {
1128 match mut node.right {
1129 ast.CharLiteral {
1130 left_val := node.left.val.runes()[0]
1131 right_val := node.right.val.runes()[0]
1132
1133 match node.op {
1134 .eq {
1135 return ast.BoolLiteral{
1136 val: left_val == right_val
1137 }
1138 }
1139 .ne {
1140 return ast.BoolLiteral{
1141 val: left_val != right_val
1142 }
1143 }
1144 .gt {
1145 return ast.BoolLiteral{
1146 val: left_val > right_val
1147 }
1148 }
1149 .ge {
1150 return ast.BoolLiteral{
1151 val: left_val >= right_val
1152 }
1153 }
1154 .lt {
1155 return ast.BoolLiteral{
1156 val: left_val < right_val
1157 }
1158 }
1159 .le {
1160 return ast.BoolLiteral{
1161 val: left_val <= right_val
1162 }
1163 }
1164 .plus {
1165 return ast.CharLiteral{
1166 val: (left_val + right_val).str()
1167 pos: pos
1168 }
1169 }
1170 .mul {
1171 return ast.CharLiteral{
1172 val: (left_val * right_val).str()
1173 pos: pos
1174 }
1175 }
1176 .minus {
1177 return ast.CharLiteral{
1178 val: (left_val - right_val).str()
1179 pos: pos
1180 }
1181 }
1182 .div {
1183 return ast.CharLiteral{
1184 val: (left_val / right_val).str()
1185 pos: pos
1186 }
1187 }
1188 .mod {
1189 return ast.CharLiteral{
1190 val: (left_val % right_val).str()
1191 pos: pos
1192 }
1193 }
1194 .xor {
1195 return ast.CharLiteral{
1196 val: (left_val ^ right_val).str()
1197 pos: pos
1198 }
1199 }
1200 .pipe {
1201 return ast.CharLiteral{
1202 val: (left_val | right_val).str()
1203 pos: pos
1204 }
1205 }
1206 .amp {
1207 return ast.CharLiteral{
1208 val: (left_val & right_val).str()
1209 pos: pos
1210 }
1211 }
1212 .left_shift {
1213 return ast.CharLiteral{
1214 val: (unsafe { left_val << right_val }).str()
1215 pos: pos
1216 }
1217 }
1218 .right_shift {
1219 return ast.CharLiteral{
1220 val: (left_val >> right_val).str()
1221 pos: pos
1222 }
1223 }
1224 .unsigned_right_shift {
1225 return ast.CharLiteral{
1226 val: (u64(left_val) >>> right_val).str()
1227 pos: pos
1228 }
1229 }
1230 else {}
1231 }
1232 }
1233 else {}
1234 }
1235 }
1236 else {
1237 // for `a == a`, `a != a`, `struct.f != struct.f`
1238 // Note: can't compare `f32` or `f64` here, as `NaN != NaN` will return true in IEEE 754
1239 // Note: skip this optimization in SQL WHERE clauses, where `field == field` means
1240 // comparing a table field with a variable of the same name, not self-comparison.
1241 if !t.inside_sql && node.left.type_name() == node.right.type_name()
1242 && node.left_type !in [ast.f32_type, ast.f64_type] && node.op in [.eq, .ne]
1243 && node.left !is ast.StructInit && node.right !is ast.StructInit {
1244 left_name := '${node.left}'
1245 right_name := '${node.right}'
1246 if left_name == right_name {
1247 return ast.BoolLiteral{
1248 val: if node.op == .eq { true } else { false }
1249 }
1250 }
1251 }
1252 }
1253 }
1254
1255 return node
1256 }
1257}
1258
1259pub fn (mut t Transformer) if_expr(mut node ast.IfExpr) ast.Expr {
1260 for mut branch in node.branches {
1261 branch.cond = t.expr(mut branch.cond)
1262
1263 t.index.indent(false)
1264 for i, mut stmt in branch.stmts {
1265 stmt = t.stmt(mut stmt)
1266
1267 if i == branch.stmts.len - 1 {
1268 if mut stmt is ast.ExprStmt {
1269 expr := stmt.expr
1270
1271 match expr {
1272 ast.IfExpr {
1273 if expr.branches.len == 1 {
1274 branch.stmts.delete(branch.stmts.len - 1)
1275 branch.stmts << expr.branches[0].stmts
1276 break
1277 }
1278 }
1279 ast.MatchExpr {
1280 if expr.branches.len == 1 {
1281 branch.stmts.delete(branch.stmts.len - 1)
1282 branch.stmts << expr.branches[0].stmts
1283 break
1284 }
1285 }
1286 else {}
1287 }
1288 }
1289 }
1290 }
1291 t.index.unindent()
1292 }
1293 // where we place the result of the if when a := if ...
1294 node.left = t.expr(mut node.left)
1295 return node
1296}
1297
1298pub fn (mut t Transformer) match_expr(mut node ast.MatchExpr) ast.Expr {
1299 node.cond = t.expr(mut node.cond)
1300 for mut branch in node.branches {
1301 for mut expr in branch.exprs {
1302 expr = t.expr(mut expr)
1303 }
1304 t.index.indent(false)
1305 for i, mut stmt in branch.stmts {
1306 stmt = t.stmt(mut stmt)
1307
1308 if i == branch.stmts.len - 1 {
1309 if mut stmt is ast.ExprStmt {
1310 expr := stmt.expr
1311
1312 match expr {
1313 ast.IfExpr {
1314 if expr.branches.len == 1 {
1315 branch.stmts.delete(branch.stmts.len - 1)
1316 branch.stmts << expr.branches[0].stmts
1317 break
1318 }
1319 }
1320 ast.MatchExpr {
1321 if expr.branches.len == 1 {
1322 branch.stmts.delete(branch.stmts.len - 1)
1323 branch.stmts << expr.branches[0].stmts
1324 break
1325 }
1326 }
1327 else {}
1328 }
1329 }
1330 }
1331 }
1332 t.index.unindent()
1333 }
1334 return node
1335}
1336
1337pub fn (mut t Transformer) sql_expr(mut node ast.SqlExpr) ast.Expr {
1338 node.db_expr = t.expr(mut node.db_expr)
1339 if node.has_where {
1340 // Don't optimize `x == x` to `true` in SQL WHERE clauses,
1341 // because the left `x` refers to a table field while the right `x`
1342 // may refer to a variable (they just happen to have the same name).
1343 old_inside_sql := t.inside_sql
1344 t.inside_sql = true
1345 node.where_expr = t.expr(mut node.where_expr)
1346 t.inside_sql = old_inside_sql
1347 }
1348 if node.has_order {
1349 node.order_expr = t.expr(mut node.order_expr)
1350 }
1351 if node.has_limit {
1352 node.limit_expr = t.expr(mut node.limit_expr)
1353 }
1354 if node.has_offset {
1355 node.offset_expr = t.expr(mut node.offset_expr)
1356 }
1357 for mut field in node.fields {
1358 field.default_expr = t.expr(mut field.default_expr)
1359 }
1360 for _, mut sub_struct in node.sub_structs {
1361 sub_struct = t.expr(mut sub_struct) as ast.SqlExpr
1362 }
1363 return node
1364}
1365
1366pub fn (mut t Transformer) fn_decl(mut node ast.FnDecl) {
1367 if t.pref.trace_calls {
1368 t.fn_decl_trace_calls(mut node)
1369 }
1370 t.index.indent(true)
1371 for mut stmt in node.stmts {
1372 stmt = t.stmt(mut stmt)
1373 }
1374 t.index.unindent()
1375}
1376
1377pub fn (mut t Transformer) fn_decl_trace_calls(mut node ast.FnDecl) {
1378 // Prepend ast Nodes for `eprintln(...)` to the `FnDecl`'s stmts list,
1379 // to let the gen backend generate the target specific code for the print.
1380 if node.no_body {
1381 // Skip `C.fn()` calls
1382 return
1383 }
1384 if node.name.starts_with('v.trace_calls.') {
1385 // do not instrument the tracing functions, to avoid infinite regress
1386 return
1387 }
1388 fname := if node.is_method {
1389 receiver_name := global_table.type_to_str(node.receiver.typ)
1390 '${node.mod} ${receiver_name}.${node.name}/${node.params.len}'
1391 } else {
1392 '${node.mod} ${node.name}/${node.params.len}'
1393 }
1394 if !t.pref.trace_fns.any(fname.match_glob(it)) {
1395 return
1396 }
1397 expr_stmt := ast.ExprStmt{
1398 expr: ast.CallExpr{
1399 mod: node.mod
1400 pos: node.pos
1401 language: .v
1402 scope: node.scope
1403 name: 'v.trace_calls.on_call'
1404 args: [
1405 ast.CallArg{
1406 expr: ast.StringLiteral{
1407 val: fname
1408 }
1409 typ: ast.string_type_idx
1410 },
1411 ]
1412 }
1413 }
1414 node.stmts.prepend(expr_stmt)
1415}
1416
1417pub fn (mut t Transformer) simplify_nested_interpolation_in_sb(mut onode ast.Stmt, mut nexpr ast.CallExpr, ntype ast.Type) bool {
1418 if t.pref.autofree {
1419 return false
1420 }
1421 if nexpr.args[0].expr !is ast.StringInterLiteral {
1422 return false
1423 }
1424 original := nexpr.args[0].expr as ast.StringInterLiteral
1425 if original.exprs.len != original.expr_types.len {
1426 // This should be a generic type, e.g., `${it}` where `it` is type of T
1427 // first time, `T` maybe `int`, but second time, `T` maybe `string`
1428 return false
1429 }
1430 // only very simple string interpolations, without any formatting, like the following examples
1431 // can be optimised to a list of simpler string builder calls, instead of using str_intp:
1432 // >> sb.write_string('abc ${num}')
1433 // >> sb.write_string('abc ${num} ${some_string} ${another_string} end')
1434 for idx, w in original.fwidths {
1435 if w != 0
1436 || (idx < original.fwidth_exprs.len && original.fwidth_exprs[idx] !is ast.EmptyExpr) {
1437 return false
1438 }
1439 if original.precisions[idx] != 987698 || (idx < original.precision_exprs.len
1440 && original.precision_exprs[idx] !is ast.EmptyExpr) {
1441 return false
1442 }
1443 if original.need_fmts[idx] {
1444 return false
1445 }
1446 // good ... no complex formatting found; now check the types (only strings and non float numbers are supported)
1447 if original.expr_types[idx] == ast.string_type {
1448 continue
1449 }
1450 if !original.expr_types[idx].is_int() {
1451 return false
1452 }
1453 }
1454
1455 // first, insert all the statements, for writing the static strings, that were parts of the original string interpolation:
1456 mut calls := []ast.Stmt{}
1457 for val in original.vals {
1458 if val == '' {
1459 // there is no point in appending empty strings
1460 // so instead, just emit an empty statement, to be ignored by the backend
1461 calls << ast.EmptyStmt{
1462 pos: nexpr.pos
1463 }
1464 continue
1465 }
1466 mut ncall := ast.ExprStmt{
1467 expr: ast.Expr(ast.CallExpr{
1468 ...nexpr
1469 args: [
1470 ast.CallArg{
1471 ...nexpr.args[0]
1472 expr: ast.StringLiteral{
1473 val: val
1474 }
1475 },
1476 ]
1477 })
1478 typ: ntype
1479 pos: nexpr.pos
1480 }
1481 calls << ncall
1482 }
1483 // now, insert the statements for writing the variable expressions between the static strings:
1484 for idx, expr in original.exprs {
1485 mut ncall := ast.ExprStmt{
1486 typ: ntype
1487 expr: ast.Expr(ast.CallExpr{
1488 ...nexpr
1489 args: [
1490 ast.CallArg{
1491 ...nexpr.args[0]
1492 expr: expr
1493 },
1494 ]
1495 })
1496 pos: nexpr.pos
1497 }
1498 etype := original.expr_types[idx]
1499 if etype.is_int() {
1500 if mut ncall.expr is ast.CallExpr {
1501 ncall.expr.name = 'write_decimal'
1502 }
1503 }
1504 calls.insert(1 + 2 * idx, ncall) // the new statements should be between the existing ones for static strings
1505 }
1506 // calls << ast.node
1507 unsafe {
1508 *onode = ast.Stmt(ast.Block{
1509 scope: ast.empty_scope
1510 stmts: calls
1511 pos: nexpr.pos
1512 })
1513 }
1514 return true
1515}
1516