v / vlib / v2 / gen / cleanc / for.v
324 lines · 313 sloc · 9.23 KB · 3dd96de45339a469cb83ffacc0e50b017468e032
Raw
1// Copyright (c) 2026 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
5module cleanc
6
7import strings
8import v2.ast
9
10fn (mut g Gen) gen_for_stmt(node ast.ForStmt) {
11 if node.init is ast.ForInStmt {
12 if g.gen_map_for_in_stmt(node, node.init) {
13 return
14 }
15 if g.gen_array_for_in_stmt(node, node.init) {
16 return
17 }
18 }
19 if g.gen_transformed_untyped_map_for_in_stmt(node) {
20 return
21 }
22 g.write_indent()
23 has_init := !is_empty_stmt(node.init)
24 has_cond := !is_empty_expr(node.cond)
25 has_post := !is_empty_stmt(node.post)
26
27 if has_init || has_post {
28 // C-style for loop: for (init; cond; post)
29 g.sb.write_string('for (')
30 if has_init {
31 g.gen_stmt_inline(node.init)
32 }
33 g.sb.write_string('; ')
34 if has_cond {
35 g.expr(node.cond)
36 }
37 g.sb.write_string('; ')
38 if has_post {
39 g.gen_stmt_inline(node.post)
40 }
41 g.sb.writeln(') {')
42 } else if has_cond {
43 // while-style: for cond {
44 g.sb.write_string('while (')
45 g.expr(node.cond)
46 g.sb.writeln(') {')
47 } else {
48 // Infinite loop: for {
49 g.sb.writeln('for (;;) {')
50 }
51
52 g.indent++
53 // Save runtime_local_types: variables declared in the loop body
54 // (e.g. _filter_it in nested map/filter) can shadow outer variables.
55 // Without save/restore, the inner type overwrites the outer in the flat map.
56 saved_local_types := g.runtime_local_types.clone()
57 saved_decl_types := g.runtime_decl_types.clone()
58 g.gen_stmts(node.stmts)
59 g.runtime_local_types = saved_local_types.clone()
60 g.runtime_decl_types = saved_decl_types.clone()
61 g.not_local_var_cache.clear()
62 g.indent--
63 g.write_indent()
64 g.sb.writeln('}')
65}
66
67fn (mut g Gen) gen_map_for_in_stmt(node ast.ForStmt, for_in ast.ForInStmt) bool {
68 mut map_type := g.get_expr_type(for_in.expr).trim_space().trim_right('*')
69 if (map_type == '' || map_type == 'int') && for_in.expr is ast.Ident {
70 map_type = (g.get_local_var_c_type(for_in.expr.name) or { '' }).trim_space().trim_right('*')
71 }
72 if !map_type.starts_with('Map_') {
73 return false
74 }
75 key_type, value_type := g.parse_map_kv_types(map_type['Map_'.len..])
76 if key_type == '' || value_type == '' {
77 return false
78 }
79 id := g.tmp_counter
80 g.tmp_counter++
81 map_tmp := '_map_iter_${id}'
82 len_tmp := '_map_len_${id}'
83 idx_tmp := '_map_idx_${id}'
84 delta_tmp := '_map_delta_${id}'
85 mut expr_sb := strings.new_builder(64)
86 saved_sb := g.sb
87 g.sb = expr_sb
88 g.expr(for_in.expr)
89 map_expr := g.sb.str()
90 g.sb = saved_sb
91 g.write_indent()
92 g.sb.writeln('${map_type} ${map_tmp} = ${map_expr};')
93 g.write_indent()
94 g.sb.writeln('int ${len_tmp} = ${map_tmp}.key_values.len;')
95 g.write_indent()
96 g.sb.writeln('for (int ${idx_tmp} = 0; (${idx_tmp} < ${len_tmp}); ${idx_tmp} = (${idx_tmp} + 1)) {')
97 g.indent++
98 g.write_indent()
99 g.sb.writeln('int ${delta_tmp} = (${map_tmp}.key_values.len - ${len_tmp});')
100 g.write_indent()
101 g.sb.writeln('${len_tmp} = ${map_tmp}.key_values.len;')
102 g.write_indent()
103 g.sb.writeln('if ((${delta_tmp} < 0)) {')
104 g.indent++
105 g.write_indent()
106 g.sb.writeln('${idx_tmp} = -1;')
107 g.write_indent()
108 g.sb.writeln('continue;')
109 g.indent--
110 g.write_indent()
111 g.sb.writeln('}')
112 g.write_indent()
113 g.sb.writeln('if (!DenseArray__has_index(&${map_tmp}.key_values, ${idx_tmp})) {')
114 g.indent++
115 g.write_indent()
116 g.sb.writeln('continue;')
117 g.indent--
118 g.write_indent()
119 g.sb.writeln('}')
120 key_name := if for_in.key is ast.Ident { for_in.key.name } else { '' }
121 value_name := if for_in.value is ast.Ident { for_in.value.name } else { '' }
122 saved_local_types := g.runtime_local_types.clone()
123 saved_decl_types := g.runtime_decl_types.clone()
124 if key_name != '' && key_name != '_' {
125 g.write_indent()
126 g.sb.writeln('${key_type} ${key_name} = *((${key_type}*)DenseArray__key(&${map_tmp}.key_values, ${idx_tmp}));')
127 if key_type == 'string' {
128 g.write_indent()
129 g.sb.writeln('${key_name} = string__clone(${key_name});')
130 }
131 g.remember_runtime_local_type(key_name, key_type)
132 }
133 if value_name != '' && value_name != '_' {
134 g.write_indent()
135 g.sb.writeln('${value_type} ${value_name} = *((${value_type}*)DenseArray__value(&${map_tmp}.key_values, ${idx_tmp}));')
136 if value_type == 'string' {
137 g.write_indent()
138 g.sb.writeln('${value_name} = string__clone(${value_name});')
139 }
140 g.remember_runtime_local_type(value_name, value_type)
141 }
142 g.gen_stmts(node.stmts)
143 g.runtime_local_types = saved_local_types.clone()
144 g.runtime_decl_types = saved_decl_types.clone()
145 g.not_local_var_cache.clear()
146 g.indent--
147 g.write_indent()
148 g.sb.writeln('}')
149 return true
150}
151
152fn cleanc_for_in_ident_name(expr ast.Expr) (string, bool) {
153 if expr is ast.Ident {
154 return expr.name, false
155 }
156 if expr is ast.ModifierExpr {
157 if expr.expr is ast.Ident {
158 return expr.expr.name, expr.kind == .key_mut
159 }
160 }
161 return '', false
162}
163
164fn (mut g Gen) gen_array_for_in_stmt(node ast.ForStmt, for_in ast.ForInStmt) bool {
165 mut array_type := g.get_expr_type(for_in.expr).trim_space().trim_right('*')
166 if (array_type == '' || array_type == 'int') && for_in.expr is ast.Ident {
167 array_type =
168 (g.get_local_var_c_type(for_in.expr.name) or { '' }).trim_space().trim_right('*')
169 }
170 if array_type == '' {
171 return false
172 }
173 mut array_decl_type := array_type
174 if !c_type_is_array_value(array_decl_type) {
175 alias_base := g.array_alias_base_type(array_decl_type)
176 if alias_base != '' {
177 array_decl_type = alias_base
178 }
179 }
180 if !c_type_is_array_value(array_decl_type) {
181 return false
182 }
183 elem_type := g.array_alias_elem_type_from_c_type(array_decl_type)
184 if elem_type == '' {
185 return false
186 }
187 id := g.tmp_counter
188 g.tmp_counter++
189 array_tmp := '_arr_iter_${id}'
190 idx_tmp := '_arr_idx_${id}'
191 mut expr_sb := strings.new_builder(64)
192 saved_sb := g.sb
193 g.sb = expr_sb
194 g.expr(for_in.expr)
195 array_expr := g.sb.str()
196 g.sb = saved_sb
197 g.write_indent()
198 g.sb.writeln('${array_decl_type} ${array_tmp} = ${array_expr};')
199 g.write_indent()
200 g.sb.writeln('for (int ${idx_tmp} = 0; (${idx_tmp} < ${array_tmp}.len); ${idx_tmp} = (${idx_tmp} + 1)) {')
201 g.indent++
202 key_name, _ := cleanc_for_in_ident_name(for_in.key)
203 value_name, value_is_mut := cleanc_for_in_ident_name(for_in.value)
204 saved_local_types := g.runtime_local_types.clone()
205 saved_decl_types := g.runtime_decl_types.clone()
206 if key_name != '' && key_name != '_' {
207 g.write_indent()
208 g.sb.writeln('int ${key_name} = ${idx_tmp};')
209 g.remember_runtime_local_type(key_name, 'int')
210 }
211 if value_name != '' && value_name != '_' {
212 g.write_indent()
213 if value_is_mut {
214 g.sb.writeln('${elem_type}* ${value_name} = &(((${elem_type}*)${array_tmp}.data)[${idx_tmp}]);')
215 g.remember_runtime_local_type(value_name, '${elem_type}*')
216 } else {
217 g.sb.writeln('${elem_type} ${value_name} = ((${elem_type}*)${array_tmp}.data)[${idx_tmp}];')
218 if elem_type == 'string' {
219 g.write_indent()
220 g.sb.writeln('${value_name} = string__clone(${value_name});')
221 }
222 g.remember_runtime_local_type(value_name, elem_type)
223 }
224 }
225 g.gen_stmts(node.stmts)
226 g.runtime_local_types = saved_local_types.clone()
227 g.runtime_decl_types = saved_decl_types.clone()
228 g.not_local_var_cache.clear()
229 g.indent--
230 g.write_indent()
231 g.sb.writeln('}')
232 return true
233}
234
235fn (mut g Gen) gen_transformed_untyped_map_for_in_stmt(node ast.ForStmt) bool {
236 if node.init !is ast.AssignStmt || node.stmts.len == 0 {
237 return false
238 }
239 init := node.init as ast.AssignStmt
240 if init.lhs.len == 0 || init.lhs[0] !is ast.Ident {
241 return false
242 }
243 key_name := (init.lhs[0] as ast.Ident).name
244 first_stmt := node.stmts[0]
245 if first_stmt !is ast.AssignStmt {
246 return false
247 }
248 value_assign := first_stmt as ast.AssignStmt
249 if value_assign.lhs.len == 0 || value_assign.rhs.len == 0 || value_assign.lhs[0] !is ast.Ident
250 || value_assign.rhs[0] !is ast.IndexExpr {
251 return false
252 }
253 value_name := (value_assign.lhs[0] as ast.Ident).name
254 index_expr := value_assign.rhs[0] as ast.IndexExpr
255 mut map_type := g.get_expr_type(index_expr.lhs).trim_space().trim_right('*')
256 if (map_type == '' || map_type == 'int') && index_expr.lhs is ast.Ident {
257 map_type =
258 (g.get_local_var_c_type(index_expr.lhs.name) or { '' }).trim_space().trim_right('*')
259 }
260 if !map_type.starts_with('Map_') {
261 return false
262 }
263 map_node := ast.ForStmt{
264 stmts: node.stmts[1..]
265 }
266 return g.gen_map_for_in_stmt(map_node, ast.ForInStmt{
267 key: ast.Expr(ast.Ident{
268 name: key_name
269 })
270 value: ast.Expr(ast.Ident{
271 name: value_name
272 })
273 expr: index_expr.lhs
274 })
275}
276
277fn (mut g Gen) gen_stmt_inline(node ast.Stmt) {
278 if !stmt_has_valid_data(node) {
279 return
280 }
281 match node {
282 ast.AssignStmt {
283 lhs := node.lhs[0]
284 rhs := node.rhs[0]
285 if node.op == .decl_assign {
286 mut name := ''
287 if lhs is ast.Ident {
288 name = lhs.name
289 }
290 typ := g.get_expr_type(rhs)
291 g.sb.write_string('${typ} ${name} = ')
292 g.expr(rhs)
293 // Register the for-loop init variable so assignments inside
294 // the loop body don't re-declare it.
295 if name != '' && typ != '' {
296 g.remember_runtime_local_type(name, typ)
297 }
298 } else {
299 g.expr(lhs)
300 op_str := match node.op {
301 .assign { '=' }
302 .plus_assign { '+=' }
303 .minus_assign { '-=' }
304 .mul_assign { '*=' }
305 .div_assign { '/=' }
306 .mod_assign { '%=' }
307 .and_assign { '&=' }
308 .or_assign { '|=' }
309 .xor_assign { '^=' }
310 .left_shift_assign { '<<=' }
311 .right_shift_assign { '>>=' }
312 else { '=' }
313 }
314
315 g.sb.write_string(' ${op_str} ')
316 g.expr(rhs)
317 }
318 }
319 ast.ExprStmt {
320 g.expr(node.expr)
321 }
322 else {}
323 }
324}
325