v4 / vlib / v3 / transform / for.v
531 lines · 515 sloc · 16.06 KB · 288feee4702b35beadb4037d02b96b5c8674156b
Raw
1module transform
2
3import v3.flat
4
5// transform_for_body transforms transform for body data for transform.
6fn (mut t Transformer) transform_for_body(id flat.NodeId, node flat.Node) []flat.NodeId {
7 if node.children_count < 3 {
8 return arr1(id)
9 }
10 // child 0: init statement
11 init_id := t.a.child(&node, 0)
12 mut new_init := init_id
13 if int(init_id) >= 0 {
14 expanded := t.transform_stmt(init_id)
15 if expanded.len > 0 {
16 new_init = expanded[0]
17 }
18 }
19 // child 1: condition expression
20 cond_id := t.a.child(&node, 1)
21 cond_smartcasts := t.extract_all_is_exprs(cond_id)
22 new_cond := t.transform_and_chain_smartcasts(cond_id)
23 mut cond_prefix := []flat.NodeId{}
24 t.drain_pending(mut cond_prefix)
25 // child 2: post statement
26 post_id := t.a.child(&node, 2)
27 mut new_post := post_id
28 if int(post_id) >= 0 {
29 expanded := t.transform_stmt(post_id)
30 if expanded.len > 0 {
31 new_post = expanded[0]
32 }
33 }
34 // children 3..n: body statements
35 mut body_ids := []flat.NodeId{}
36 for i in 3 .. node.children_count {
37 body_ids << t.a.child(&node, i)
38 }
39 for info in cond_smartcasts {
40 t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)
41 }
42 new_body := t.transform_stmts(body_ids)
43 for _ in cond_smartcasts {
44 t.pop_smartcast()
45 }
46 mut loop_cond := new_cond
47 mut loop_body := new_body.clone()
48 if cond_prefix.len > 0 {
49 mut guarded_body := []flat.NodeId{cap: cond_prefix.len + new_body.len + 1}
50 for stmt in cond_prefix {
51 guarded_body << stmt
52 }
53 not_cond := t.make_prefix(.not, t.make_paren(new_cond))
54 break_block := t.make_block(arr1(t.a.add(.break_stmt)))
55 guarded_body << t.make_if(not_cond, break_block, t.make_block([]flat.NodeId{}))
56 for stmt in new_body {
57 guarded_body << stmt
58 }
59 loop_cond = t.make_bool_literal(true)
60 loop_body = guarded_body.clone()
61 }
62 // Rebuild the for_stmt with transformed children
63 start := t.a.children.len
64 t.a.children << new_init
65 t.a.children << loop_cond
66 t.a.children << new_post
67 for bid in loop_body {
68 t.a.children << bid
69 }
70 count := t.a.children.len - start
71 new_id := t.a.add_node(flat.Node{
72 kind: .for_stmt
73 op: node.op
74 children_start: start
75 children_count: flat.child_count(count)
76 pos: node.pos
77 value: node.value
78 typ: node.typ
79 })
80 return arr1(new_id)
81}
82
83// transform_for_in_body transforms transform for in body data for transform.
84fn (mut t Transformer) transform_for_in_body(id flat.NodeId, node flat.Node) []flat.NodeId {
85 header_count := node.value.int()
86 if header_count < 3 || node.children_count < 3 {
87 return arr1(id)
88 }
89 key_id := t.a.child(&node, 0) // loop var ident — pass through (do not transform a binding)
90 val_id := t.a.child(&node, 1) // may be flat.empty_node (-1)
91 container_id := t.a.child(&node, 2)
92 iter_type := t.detect_for_in_type(node)
93 has_index := int(val_id) >= 0
94 container_is_range := if int(container_id) >= 0 {
95 t.a.nodes[int(container_id)].kind == .range
96 } else {
97 false
98 }
99 if header_count == 4 {
100 range_end_id := t.a.child(&node, 3)
101 body_ids := t.a.children_of(&node)[header_count..].clone()
102 return t.lower_range_for_in(id, node, key_id, container_id, range_end_id, body_ids)
103 }
104 if container_is_range {
105 range_node := t.a.nodes[int(container_id)]
106 if range_node.children_count >= 2 {
107 body_ids := t.a.children_of(&node)[header_count..].clone()
108 return t.lower_range_for_in(id, node, key_id, t.a.child(&range_node, 0), t.a.child(&range_node,
109 1), body_ids)
110 }
111 }
112 if iter_type.starts_with('map[') {
113 return t.rebuild_for_in_stmt(id, node)
114 }
115 if pool_iter := t.pool_get_results_iter_type(container_id) {
116 body_ids := t.a.children_of(&node)[header_count..].clone()
117 return t.lower_indexed_for_in(id, node, key_id, val_id, container_id, pool_iter, has_index,
118 body_ids)
119 }
120 effective_iter := if iter_type.starts_with('...') {
121 '[]' + iter_type[3..]
122 } else if iter_type.starts_with('&[]') {
123 iter_type[1..]
124 } else {
125 iter_type
126 }
127 if effective_iter.starts_with('[]') || effective_iter == 'string'
128 || t.is_fixed_array_type(effective_iter) {
129 body_ids := t.a.children_of(&node)[header_count..].clone()
130 return t.lower_indexed_for_in(id, node, key_id, val_id, container_id, effective_iter,
131 has_index, body_ids)
132 }
133 return t.rebuild_for_in_stmt(id, node)
134}
135
136// pool_get_results_iter_type supports pool get results iter type handling for Transformer.
137fn (t &Transformer) pool_get_results_iter_type(container_id flat.NodeId) ?string {
138 if int(container_id) < 0 {
139 return none
140 }
141 node := t.a.nodes[int(container_id)]
142 if node.kind != .call || node.children_count == 0 {
143 return none
144 }
145 callee_id := t.a.child(&node, 0)
146 if int(callee_id) < 0 {
147 return none
148 }
149 callee := t.a.nodes[int(callee_id)]
150 if callee.kind != .index || callee.children_count < 2 {
151 return none
152 }
153 base := t.a.child_node(&callee, 0)
154 if base.kind != .selector || base.value !in ['get_results', 'get_results_ref'] {
155 return none
156 }
157 elem_type := t.generic_call_type_arg_name(t.a.child(&callee, 1))
158 if elem_type.len == 0 {
159 return none
160 }
161 if base.value == 'get_results_ref' {
162 return '[]&${elem_type}'
163 }
164 return '[]${elem_type}'
165}
166
167// rebuild_for_in_stmt supports rebuild for in stmt handling for Transformer.
168fn (mut t Transformer) rebuild_for_in_stmt(_id flat.NodeId, node flat.Node) []flat.NodeId {
169 header_count := node.value.int()
170 key_id := t.a.child(&node, 0)
171 val_id := t.a.child(&node, 1)
172 container_id := t.a.child(&node, 2)
173 new_container := t.transform_expr(container_id)
174 iter_type := t.detect_for_in_type(node)
175 has_index := int(val_id) >= 0
176 container_is_range := if int(container_id) >= 0 {
177 t.a.nodes[int(container_id)].kind == .range
178 } else {
179 false
180 }
181 if header_count == 4 || container_is_range {
182 // range `for i in 0 .. n`: single loop var (child0) is an int
183 if int(key_id) >= 0 {
184 key_name := t.a.nodes[int(key_id)].value
185 if key_name.len > 0 {
186 t.set_var_type(key_name, 'int')
187 }
188 }
189 } else if has_index {
190 // two loop vars: child0 = key/index, child1 = value/element
191 key_name := if int(key_id) >= 0 { t.a.nodes[int(key_id)].value } else { '' }
192 val_name := if int(val_id) >= 0 { t.a.nodes[int(val_id)].value } else { '' }
193 if iter_type.starts_with('map[') {
194 // map[K]V: child0 (key) -> key type, child1 (val) -> value type
195 bracket_end := iter_type.index(']') or { 0 }
196 if key_name.len > 0 && bracket_end > 4 {
197 t.set_var_type(key_name, iter_type[4..bracket_end])
198 }
199 } else if iter_type.starts_with('[]') || iter_type == 'string' {
200 // []E: child0 (index) -> 'int'
201 if key_name.len > 0 {
202 t.set_var_type(key_name, 'int')
203 }
204 }
205 if val_name.len > 0 {
206 elem_type := t.infer_for_in_elem_type(iter_type, node)
207 if elem_type.len > 0 {
208 t.set_var_type(val_name, elem_type)
209 }
210 }
211 } else {
212 // single var, child0 is the element
213 if int(key_id) >= 0 {
214 key_name := t.a.nodes[int(key_id)].value
215 if key_name.len > 0 {
216 elem_type := t.infer_for_in_elem_type(iter_type, node)
217 if elem_type.len > 0 {
218 t.set_var_type(key_name, elem_type)
219 }
220 }
221 }
222 }
223
224 mut ids := []flat.NodeId{}
225 ids << key_id
226 ids << val_id
227 ids << new_container
228 if header_count == 4 {
229 range_end_id := t.a.child(&node, 3)
230 ids << t.transform_expr(range_end_id)
231 }
232 body_ids := t.a.children_of(&node)[header_count..].clone()
233 new_body := t.transform_stmts(body_ids)
234 for bid in new_body {
235 ids << bid
236 }
237 start := t.a.children.len
238 for cid in ids {
239 t.a.children << cid
240 }
241 return arr1(t.a.add_node(flat.Node{
242 kind: .for_in_stmt
243 op: node.op
244 children_start: start
245 children_count: flat.child_count(ids.len)
246 pos: node.pos
247 value: node.value
248 typ: node.typ
249 }))
250}
251
252// lower_range_for_in builds lower range for in data for transform.
253fn (mut t Transformer) lower_range_for_in(id flat.NodeId, node flat.Node, key_id flat.NodeId, low_id flat.NodeId, high_id flat.NodeId, body_ids []flat.NodeId) []flat.NodeId {
254 if int(key_id) < 0 {
255 return arr1(id)
256 }
257 key := t.a.nodes[int(key_id)]
258 if key.kind != .ident || key.value.len == 0 {
259 return arr1(id)
260 }
261 t.set_var_type(key.value, 'int')
262 low := t.transform_expr(low_id)
263 high := t.stable_expr_for_reuse(high_id)
264 mut prefix := []flat.NodeId{}
265 t.drain_pending(mut prefix)
266 init := t.make_decl_assign_typed(key.value, low, 'int')
267 cond := t.make_infix(.lt, t.make_ident(key.value), high)
268 post := t.make_expr_stmt(t.make_postfix(t.make_ident(key.value), .inc))
269 new_body := t.transform_stmts(body_ids)
270 prefix << t.make_for_stmt(init, cond, post, new_body, node)
271 return prefix
272}
273
274// lower_indexed_for_in builds lower indexed for in data for transform.
275fn (mut t Transformer) lower_indexed_for_in(id flat.NodeId, node flat.Node, key_id flat.NodeId, val_id flat.NodeId, container_id flat.NodeId, iter_type string, has_index bool, body_ids []flat.NodeId) []flat.NodeId {
276 if int(key_id) < 0 {
277 return arr1(id)
278 }
279 key := t.a.nodes[int(key_id)]
280 if key.kind != .ident || key.value.len == 0 {
281 return arr1(id)
282 }
283 mut container := t.stable_expr_for_reuse(container_id)
284 mut prefix := []flat.NodeId{}
285 t.drain_pending(mut prefix)
286 mut actual_iter_type := iter_type
287 container_type := t.node_type(container)
288 if container_type.len > 0 && !for_iter_type_has_generic_placeholder(actual_iter_type)
289 && !(t.is_fixed_array_type(actual_iter_type) && !t.is_fixed_array_type(container_type)) {
290 actual_iter_type = container_type
291 }
292 if actual_iter_type.starts_with('&[]') {
293 container = t.make_prefix(.mul, container)
294 t.a.nodes[int(container)].typ = actual_iter_type[1..]
295 actual_iter_type = actual_iter_type[1..]
296 }
297 elem_type := t.infer_for_in_elem_type(actual_iter_type, node)
298 if elem_type.len == 0 {
299 return arr1(id)
300 }
301 mut idx_name := key.value
302 if !has_index {
303 idx_name = t.new_temp('for_idx')
304 }
305 mut elem_name := key.value
306 if has_index {
307 if int(val_id) < 0 {
308 return arr1(id)
309 }
310 val := t.a.nodes[int(val_id)]
311 if val.kind != .ident || val.value.len == 0 {
312 return arr1(id)
313 }
314 elem_name = val.value
315 }
316 t.set_var_type(idx_name, 'int')
317 elem_is_mut := node.op == .amp && actual_iter_type != 'string'
318 elem_needs_ref := elem_is_mut && !elem_type.starts_with('&')
319 elem_var_type := if elem_needs_ref { '&${elem_type}' } else { elem_type }
320 t.set_var_type(elem_name, elem_var_type)
321 len_expr := if t.is_fixed_array_type(actual_iter_type) {
322 t.make_for_in_fixed_array_len_expr(actual_iter_type)
323 } else {
324 t.make_selector(container, 'len', 'int')
325 }
326 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
327 cond := t.make_infix(.lt, t.make_ident(idx_name), len_expr)
328 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
329 elem_expr := if elem_needs_ref && actual_iter_type.starts_with('[]') {
330 t.array_get_ptr(container, t.make_ident(idx_name), elem_type)
331 } else if elem_needs_ref {
332 t.make_prefix(.amp, t.make_index(container, t.make_ident(idx_name), elem_type))
333 } else if actual_iter_type.starts_with('[]') {
334 t.array_get_value(container, t.make_ident(idx_name), elem_type)
335 } else {
336 t.make_index(container, t.make_ident(idx_name), elem_type)
337 }
338 elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_var_type)
339 mut transformed_body := []flat.NodeId{}
340 if elem_needs_ref {
341 had_pointer_value_lvalue := t.pointer_value_lvalues[elem_name] or { false }
342 t.pointer_value_lvalues[elem_name] = true
343 transformed_body = t.transform_stmts(body_ids)
344 if had_pointer_value_lvalue {
345 t.pointer_value_lvalues[elem_name] = true
346 } else {
347 t.pointer_value_lvalues.delete(elem_name)
348 }
349 } else {
350 transformed_body = t.transform_stmts(body_ids)
351 }
352 mut new_body := []flat.NodeId{}
353 new_body << elem_decl
354 new_body << transformed_body
355 prefix << t.make_for_stmt(init, cond, post, new_body, node)
356 return prefix
357}
358
359// make_for_stmt builds make for stmt data for transform.
360fn (mut t Transformer) make_for_stmt(init flat.NodeId, cond flat.NodeId, post flat.NodeId, body []flat.NodeId, src flat.Node) flat.NodeId {
361 start := t.a.children.len
362 t.a.children << init
363 t.a.children << cond
364 t.a.children << post
365 for id in body {
366 t.a.children << id
367 }
368 return t.a.add_node(flat.Node{
369 kind: .for_stmt
370 op: src.op
371 children_start: start
372 children_count: flat.child_count(3 + body.len)
373 pos: src.pos
374 typ: src.typ
375 })
376}
377
378// detect_for_in_type resolves detect for in type information for transform.
379fn (mut t Transformer) detect_for_in_type(node flat.Node) string {
380 if node.typ.len > 0 {
381 return node.typ
382 }
383 header_count := node.value.int()
384 container_idx := if header_count >= 3 { header_count - 1 } else { 2 }
385 if node.children_count > container_idx {
386 iter_id := t.a.child(&node, container_idx)
387 if fixed_array_type := t.detect_for_in_global_fixed_array_type(iter_id) {
388 return fixed_array_type
389 }
390 return t.node_type(iter_id)
391 }
392 return ''
393}
394
395fn (t &Transformer) detect_for_in_global_fixed_array_type(id flat.NodeId) ?string {
396 if int(id) < 0 {
397 return none
398 }
399 node := t.a.nodes[int(id)]
400 if node.kind != .ident || node.value.len == 0 {
401 return none
402 }
403 if t.var_type(node.value).len > 0 {
404 return none
405 }
406 mut candidates := []string{}
407 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
408 candidates << '${t.cur_module}.${node.value}'
409 }
410 candidates << node.value
411 for candidate in candidates {
412 if typ := t.globals[candidate] {
413 normalized := t.normalize_type_alias(typ)
414 if t.is_fixed_array_type(normalized) {
415 return normalized
416 }
417 }
418 }
419 if !isnil(t.tc) {
420 checker_type := t.normalize_type_alias(t.tc.resolve_type(id).name())
421 if t.is_fixed_array_type(checker_type) {
422 return checker_type
423 }
424 }
425 return none
426}
427
428fn for_iter_type_has_generic_placeholder(iter_type string) bool {
429 clean := iter_type.trim_space()
430 if clean.len == 0 {
431 return false
432 }
433 if is_generic_placeholder_type_name(clean) {
434 return true
435 }
436 if clean.starts_with('&') {
437 return for_iter_type_has_generic_placeholder(clean[1..])
438 }
439 if clean.starts_with('[]') {
440 return for_iter_type_has_generic_placeholder(clean[2..])
441 }
442 if clean.starts_with('map[') {
443 bracket_end := clean.index(']') or { return false }
444 return for_iter_type_has_generic_placeholder(clean[4..bracket_end])
445 || for_iter_type_has_generic_placeholder(clean[bracket_end + 1..])
446 }
447 if clean.starts_with('[') {
448 bracket_end := clean.index(']') or { return false }
449 return for_iter_type_has_generic_placeholder(clean[bracket_end + 1..])
450 }
451 return false
452}
453
454// Infer the element type for the loop variable from the iterable type.
455fn (t &Transformer) infer_for_in_elem_type(iter_type string, node flat.Node) string {
456 if iter_type.starts_with('&[]') {
457 return iter_type[3..]
458 }
459 if iter_type.starts_with('[]') {
460 return iter_type[2..]
461 }
462 if iter_type.starts_with('map[') {
463 // map[K]V -> value type is everything after the closing ']'
464 bracket_end := iter_type.index(']') or { return '' }
465 if bracket_end + 1 < iter_type.len {
466 return iter_type[bracket_end + 1..]
467 }
468 return ''
469 }
470 if iter_type == 'string' {
471 return 'u8'
472 }
473 if t.is_fixed_array_type(iter_type) {
474 return for_in_fixed_array_elem_type(iter_type)
475 }
476 // Check if the iterable is a range expression
477 if node.children_count > 0 {
478 iter_id := t.a.child(&node, 0)
479 iter_node := t.a.nodes[int(iter_id)]
480 if iter_node.kind == .range {
481 return 'int'
482 }
483 }
484 return ''
485}
486
487fn (mut t Transformer) make_for_in_fixed_array_len_expr(s string) flat.NodeId {
488 len_text := for_in_fixed_array_len_text(s)
489 if is_decimal_text(len_text) {
490 return t.make_int_literal(len_text.int())
491 }
492 if !isnil(t.tc) {
493 if v := t.tc.const_int_value(len_text, []string{}) {
494 return t.make_int_literal(v)
495 }
496 }
497 if len_text.contains('.') {
498 base := len_text.all_before_last('.')
499 field := len_text.all_after_last('.')
500 return t.make_selector(t.make_ident(base), field, 'int')
501 }
502 return t.make_ident(len_text)
503}
504
505fn for_in_fixed_array_elem_type(s string) string {
506 clean := s.trim_space()
507 if clean.starts_with('[') {
508 return clean.all_after(']')
509 }
510 open := clean.last_index_u8(`[`)
511 if open < 0 {
512 return ''
513 }
514 return clean[..open]
515}
516
517fn for_in_fixed_array_len_text(s string) string {
518 clean := s.trim_space()
519 if clean.starts_with('[') {
520 return clean.all_after('[').all_before(']').trim_space()
521 }
522 open := clean.last_index_u8(`[`)
523 if open < 0 {
524 return ''
525 }
526 close_rel := clean[open + 1..].index_u8(`]`)
527 if close_rel < 0 {
528 return ''
529 }
530 return clean[open + 1..open + 1 + close_rel].trim_space()
531}
532