vxx2 / vlib / v3 / transform / for.v
636 lines · 616 sloc · 19.53 KB · 2b81918e62db9ab38787518603a7f93bee80868c
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 if t.cur_fn_is_generic {
90 return arr1(id)
91 }
92 key_id := t.a.child(&node, 0) // loop var ident — pass through (do not transform a binding)
93 val_id := t.a.child(&node, 1) // may be flat.empty_node (-1)
94 container_id := t.a.child(&node, 2)
95 iter_type := t.detect_for_in_type(node)
96 has_index := int(val_id) >= 0
97 container_is_range := if int(container_id) >= 0 {
98 t.a.nodes[int(container_id)].kind == .range
99 } else {
100 false
101 }
102 if header_count == 4 {
103 range_end_id := t.a.child(&node, 3)
104 body_ids := t.a.children_of(&node)[header_count..].clone()
105 return t.lower_range_for_in(id, node, key_id, container_id, range_end_id, body_ids)
106 }
107 if container_is_range {
108 range_node := t.a.nodes[int(container_id)]
109 if range_node.children_count >= 2 {
110 body_ids := t.a.children_of(&node)[header_count..].clone()
111 return t.lower_range_for_in(id, node, key_id, t.a.child(&range_node, 0), t.a.child(&range_node,
112 1), body_ids)
113 }
114 }
115 if iter_type.starts_with('map[') {
116 return t.rebuild_for_in_stmt(id, node)
117 }
118 if pool_iter := t.pool_get_results_iter_type(container_id) {
119 body_ids := t.a.children_of(&node)[header_count..].clone()
120 return t.lower_indexed_for_in(id, node, key_id, val_id, container_id, pool_iter, has_index,
121 body_ids)
122 }
123 if iter_elem_type := t.iterator_for_in_elem_type(iter_type) {
124 body_ids := t.a.children_of(&node)[header_count..].clone()
125 return t.lower_iterator_for_in(id, node, key_id, val_id, container_id, iter_type,
126 iter_elem_type, has_index, body_ids)
127 }
128 effective_iter := if iter_type.starts_with('...') {
129 '[]' + iter_type[3..]
130 } else if iter_type.starts_with('&[]') {
131 iter_type[1..]
132 } else {
133 iter_type
134 }
135 if effective_iter.starts_with('[]') || effective_iter == 'string'
136 || t.is_fixed_array_type(effective_iter) {
137 body_ids := t.a.children_of(&node)[header_count..].clone()
138 return t.lower_indexed_for_in(id, node, key_id, val_id, container_id, effective_iter,
139 has_index, body_ids)
140 }
141 return t.rebuild_for_in_stmt(id, node)
142}
143
144// pool_get_results_iter_type supports pool get results iter type handling for Transformer.
145fn (t &Transformer) pool_get_results_iter_type(container_id flat.NodeId) ?string {
146 if int(container_id) < 0 {
147 return none
148 }
149 node := t.a.nodes[int(container_id)]
150 if node.kind != .call || node.children_count == 0 {
151 return none
152 }
153 callee_id := t.a.child(&node, 0)
154 if int(callee_id) < 0 {
155 return none
156 }
157 callee := t.a.nodes[int(callee_id)]
158 if callee.kind != .index || callee.children_count < 2 {
159 return none
160 }
161 base := t.a.child_node(&callee, 0)
162 if base.kind != .selector || base.value !in ['get_results', 'get_results_ref'] {
163 return none
164 }
165 elem_type := t.generic_call_type_arg_name(t.a.child(&callee, 1))
166 if elem_type.len == 0 {
167 return none
168 }
169 if base.value == 'get_results_ref' {
170 return '[]&${elem_type}'
171 }
172 return '[]${elem_type}'
173}
174
175// rebuild_for_in_stmt supports rebuild for in stmt handling for Transformer.
176fn (mut t Transformer) rebuild_for_in_stmt(_id flat.NodeId, node flat.Node) []flat.NodeId {
177 header_count := node.value.int()
178 key_id := t.a.child(&node, 0)
179 val_id := t.a.child(&node, 1)
180 container_id := t.a.child(&node, 2)
181 new_container := t.transform_expr(container_id)
182 iter_type := t.detect_for_in_type(node)
183 has_index := int(val_id) >= 0
184 container_is_range := if int(container_id) >= 0 {
185 t.a.nodes[int(container_id)].kind == .range
186 } else {
187 false
188 }
189 if header_count == 4 || container_is_range {
190 // range `for i in 0 .. n`: single loop var (child0) follows the lower bound
191 if int(key_id) >= 0 {
192 key_name := t.a.nodes[int(key_id)].value
193 if key_name.len > 0 {
194 low_id := if container_is_range {
195 range_node := t.a.nodes[int(container_id)]
196 t.a.child(&range_node, 0)
197 } else {
198 container_id
199 }
200 t.set_var_type(key_name, t.range_loop_var_type_name(low_id))
201 }
202 }
203 } else if has_index {
204 // two loop vars: child0 = key/index, child1 = value/element
205 key_name := if int(key_id) >= 0 { t.a.nodes[int(key_id)].value } else { '' }
206 val_name := if int(val_id) >= 0 { t.a.nodes[int(val_id)].value } else { '' }
207 if iter_type.starts_with('map[') {
208 // map[K]V: child0 (key) -> key type, child1 (val) -> value type
209 bracket_end := iter_type.index(']') or { 0 }
210 if key_name.len > 0 && bracket_end > 4 {
211 t.set_var_type(key_name, iter_type[4..bracket_end])
212 }
213 } else if iter_type.starts_with('[]') || iter_type == 'string' {
214 // []E: child0 (index) -> 'int'
215 if key_name.len > 0 {
216 t.set_var_type(key_name, 'int')
217 }
218 }
219 if val_name.len > 0 {
220 elem_type := t.infer_for_in_elem_type(iter_type, node)
221 if elem_type.len > 0 {
222 t.set_var_type(val_name, elem_type)
223 }
224 }
225 } else {
226 // single var, child0 is the element
227 if int(key_id) >= 0 {
228 key_name := t.a.nodes[int(key_id)].value
229 if key_name.len > 0 {
230 elem_type := t.infer_for_in_elem_type(iter_type, node)
231 if elem_type.len > 0 {
232 t.set_var_type(key_name, elem_type)
233 }
234 }
235 }
236 }
237
238 mut ids := []flat.NodeId{}
239 ids << key_id
240 ids << val_id
241 ids << new_container
242 if header_count == 4 {
243 range_end_id := t.a.child(&node, 3)
244 ids << t.transform_expr(range_end_id)
245 }
246 body_ids := t.a.children_of(&node)[header_count..].clone()
247 new_body := t.transform_stmts(body_ids)
248 for bid in new_body {
249 ids << bid
250 }
251 start := t.a.children.len
252 for cid in ids {
253 t.a.children << cid
254 }
255 return arr1(t.a.add_node(flat.Node{
256 kind: .for_in_stmt
257 op: node.op
258 children_start: start
259 children_count: flat.child_count(ids.len)
260 pos: node.pos
261 value: node.value
262 typ: node.typ
263 }))
264}
265
266fn (t &Transformer) iterator_for_in_elem_type(iter_type string) ?string {
267 mut clean := iter_type.trim_space()
268 if clean.starts_with('&') {
269 clean = clean[1..]
270 }
271 if clean == 'RunesIterator' || clean == 'builtin.RunesIterator' {
272 return 'rune'
273 }
274 return none
275}
276
277// lower_range_for_in builds lower range for in data for transform.
278fn (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 {
279 if int(key_id) < 0 {
280 return arr1(id)
281 }
282 key := t.a.nodes[int(key_id)]
283 if key.kind != .ident || key.value.len == 0 {
284 return arr1(id)
285 }
286 range_type := t.range_loop_var_type_name(low_id)
287 t.set_var_type(key.value, range_type)
288 low := t.transform_expr(low_id)
289 high := t.stable_expr_for_reuse(high_id)
290 mut prefix := []flat.NodeId{}
291 t.drain_pending(mut prefix)
292 init := t.make_decl_assign_typed(key.value, low, range_type)
293 cond := t.make_infix(.lt, t.make_ident(key.value), high)
294 post := t.make_expr_stmt(t.make_postfix(t.make_ident(key.value), .inc))
295 new_body := t.transform_stmts(body_ids)
296 prefix << t.make_for_stmt(init, cond, post, new_body, node)
297 return prefix
298}
299
300fn (t &Transformer) range_loop_var_type_name(low_id flat.NodeId) string {
301 typ := t.node_type(low_id)
302 if typ.len == 0 {
303 return 'int'
304 }
305 clean := t.normalize_type_alias(typ)
306 if t.is_integer_type_name(clean) {
307 return typ
308 }
309 return 'int'
310}
311
312fn (mut t Transformer) lower_iterator_for_in(id flat.NodeId, node flat.Node, key_id flat.NodeId, val_id flat.NodeId, container_id flat.NodeId, iter_type string, elem_type string, has_index bool, body_ids []flat.NodeId) []flat.NodeId {
313 if int(key_id) < 0 {
314 return arr1(id)
315 }
316 key := t.a.nodes[int(key_id)]
317 if key.kind != .ident || key.value.len == 0 {
318 return arr1(id)
319 }
320 mut elem_name := key.value
321 if has_index {
322 if int(val_id) < 0 {
323 return arr1(id)
324 }
325 val := t.a.nodes[int(val_id)]
326 if val.kind != .ident || val.value.len == 0 {
327 return arr1(id)
328 }
329 elem_name = val.value
330 }
331 iter_name := t.new_temp('iter')
332 next_name := t.new_temp('iter_next')
333 iter_expr := t.transform_expr(container_id)
334 mut prefix := []flat.NodeId{}
335 t.drain_pending(mut prefix)
336 prefix << t.make_decl_assign_typed(iter_name, iter_expr, iter_type)
337 init := if has_index {
338 t.set_var_type(key.value, 'int')
339 t.make_decl_assign_typed(key.value, t.make_int_literal(0), 'int')
340 } else {
341 t.make_empty()
342 }
343 t.set_var_type(elem_name, elem_type)
344 next_call := t.make_call_typed('RunesIterator.next', arr1(t.make_prefix(.amp,
345 t.make_ident(iter_name))), '?${elem_type}')
346 next_decl := t.make_decl_assign_typed(next_name, next_call, '?${elem_type}')
347 no_value := t.make_prefix(.not, t.make_selector(t.make_ident(next_name), 'ok', 'bool'))
348 break_if_done := t.make_if(no_value, t.make_block(arr1(t.a.add(.break_stmt))), t.make_empty())
349 elem_decl := t.make_decl_assign_typed(elem_name, t.make_selector(t.make_ident(next_name),
350 'value', elem_type), elem_type)
351 mut loop_body := []flat.NodeId{}
352 loop_body << next_decl
353 loop_body << break_if_done
354 loop_body << elem_decl
355 loop_body << t.transform_stmts(body_ids)
356 post := if has_index {
357 t.make_expr_stmt(t.make_postfix(t.make_ident(key.value), .inc))
358 } else {
359 t.make_empty()
360 }
361 prefix << t.make_for_stmt(init, t.make_bool_literal(true), post, loop_body, node)
362 return prefix
363}
364
365// lower_indexed_for_in builds lower indexed for in data for transform.
366fn (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 {
367 if int(key_id) < 0 {
368 return arr1(id)
369 }
370 key := t.a.nodes[int(key_id)]
371 if key.kind != .ident || key.value.len == 0 {
372 return arr1(id)
373 }
374 mut container := t.stable_expr_for_reuse(container_id)
375 mut prefix := []flat.NodeId{}
376 t.drain_pending(mut prefix)
377 mut actual_iter_type := iter_type
378 container_type := t.node_type(container)
379 if container_type.len > 0 && container_type !in ['array', 'map', 'unknown']
380 && for_iter_type_is_container(container_type)
381 && !for_iter_type_has_generic_placeholder(actual_iter_type)
382 && !(t.is_fixed_array_type(actual_iter_type) && !t.is_fixed_array_type(container_type)) {
383 actual_iter_type = container_type
384 }
385 if actual_iter_type.starts_with('&[]') {
386 container = t.make_prefix(.mul, container)
387 t.a.nodes[int(container)].typ = actual_iter_type[1..]
388 actual_iter_type = actual_iter_type[1..]
389 }
390 elem_type := t.infer_for_in_elem_type(actual_iter_type, node)
391 if elem_type.len == 0 {
392 return arr1(id)
393 }
394 mut idx_name := key.value
395 if !has_index {
396 idx_name = t.new_temp('for_idx')
397 }
398 mut elem_name := key.value
399 if has_index {
400 if int(val_id) < 0 {
401 return arr1(id)
402 }
403 val := t.a.nodes[int(val_id)]
404 if val.kind != .ident || val.value.len == 0 {
405 return arr1(id)
406 }
407 elem_name = val.value
408 }
409 t.set_var_type(idx_name, 'int')
410 elem_is_mut := node.op == .amp && actual_iter_type != 'string'
411 elem_needs_ref := elem_is_mut && !elem_type.starts_with('&')
412 elem_var_type := if elem_needs_ref { '&${elem_type}' } else { elem_type }
413 t.set_var_type(elem_name, elem_var_type)
414 len_expr := if t.is_fixed_array_type(actual_iter_type) {
415 t.make_for_in_fixed_array_len_expr(actual_iter_type)
416 } else {
417 t.make_selector(container, 'len', 'int')
418 }
419 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
420 cond := t.make_infix(.lt, t.make_ident(idx_name), len_expr)
421 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
422 elem_expr := if elem_needs_ref && actual_iter_type.starts_with('[]') {
423 t.array_get_ptr(container, t.make_ident(idx_name), elem_type)
424 } else if elem_needs_ref {
425 t.make_prefix(.amp, t.make_index(container, t.make_ident(idx_name), elem_type))
426 } else if actual_iter_type.starts_with('[]') {
427 t.array_get_value(container, t.make_ident(idx_name), elem_type)
428 } else {
429 t.make_index(container, t.make_ident(idx_name), elem_type)
430 }
431 elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_var_type)
432 mut transformed_body := []flat.NodeId{}
433 if elem_needs_ref {
434 had_pointer_value_lvalue := t.pointer_value_lvalues[elem_name] or { false }
435 t.pointer_value_lvalues[elem_name] = true
436 transformed_body = t.transform_stmts(body_ids)
437 if had_pointer_value_lvalue {
438 t.pointer_value_lvalues[elem_name] = true
439 } else {
440 t.pointer_value_lvalues.delete(elem_name)
441 }
442 } else {
443 transformed_body = t.transform_stmts(body_ids)
444 }
445 mut new_body := []flat.NodeId{}
446 new_body << elem_decl
447 new_body << transformed_body
448 prefix << t.make_for_stmt(init, cond, post, new_body, node)
449 return prefix
450}
451
452// make_for_stmt builds make for stmt data for transform.
453fn (mut t Transformer) make_for_stmt(init flat.NodeId, cond flat.NodeId, post flat.NodeId, body []flat.NodeId, src flat.Node) flat.NodeId {
454 start := t.a.children.len
455 t.a.children << init
456 t.a.children << cond
457 t.a.children << post
458 for id in body {
459 t.a.children << id
460 }
461 return t.a.add_node(flat.Node{
462 kind: .for_stmt
463 op: src.op
464 children_start: start
465 children_count: flat.child_count(3 + body.len)
466 pos: src.pos
467 typ: src.typ
468 })
469}
470
471// detect_for_in_type resolves detect for in type information for transform.
472fn (mut t Transformer) detect_for_in_type(node flat.Node) string {
473 header_count := node.value.int()
474 container_idx := if header_count >= 3 { header_count - 1 } else { 2 }
475 if node.children_count > container_idx {
476 iter_id := t.a.child(&node, container_idx)
477 if fixed_array_type := t.detect_for_in_global_fixed_array_type(iter_id) {
478 return fixed_array_type
479 }
480 iter_type := t.node_type(iter_id)
481 if iter_type.len > 0
482 && (node.typ.len == 0 || for_iter_type_has_generic_placeholder(node.typ)
483 || for_iter_type_is_container(iter_type)) {
484 return iter_type
485 }
486 }
487 if node.typ.len > 0 {
488 return node.typ
489 }
490 return ''
491}
492
493fn for_iter_type_is_container(iter_type string) bool {
494 clean := iter_type.trim_space()
495 return clean == 'string' || clean.starts_with('[]') || clean.starts_with('&[]')
496 || clean.starts_with('...') || clean.starts_with('map[') || clean.starts_with('&map[')
497 || clean.starts_with('[')
498}
499
500fn (t &Transformer) detect_for_in_global_fixed_array_type(id flat.NodeId) ?string {
501 if int(id) < 0 {
502 return none
503 }
504 node := t.a.nodes[int(id)]
505 if node.kind != .ident || node.value.len == 0 {
506 return none
507 }
508 if t.var_type(node.value).len > 0 {
509 return none
510 }
511 mut candidates := []string{}
512 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
513 candidates << '${t.cur_module}.${node.value}'
514 }
515 candidates << node.value
516 for candidate in candidates {
517 if typ := t.globals[candidate] {
518 normalized := t.normalize_type_alias(typ)
519 if t.is_fixed_array_type(normalized) {
520 return normalized
521 }
522 }
523 }
524 if !isnil(t.tc) {
525 checker_type := t.normalize_type_alias(t.tc.resolve_type(id).name())
526 if t.is_fixed_array_type(checker_type) {
527 return checker_type
528 }
529 }
530 return none
531}
532
533fn for_iter_type_has_generic_placeholder(iter_type string) bool {
534 clean := iter_type.trim_space()
535 if clean.len == 0 {
536 return false
537 }
538 if is_generic_placeholder_type_name(clean) {
539 return true
540 }
541 if clean.starts_with('&') {
542 return for_iter_type_has_generic_placeholder(clean[1..])
543 }
544 if clean.starts_with('[]') {
545 return for_iter_type_has_generic_placeholder(clean[2..])
546 }
547 if clean.starts_with('map[') {
548 bracket_end := clean.index(']') or { return false }
549 return for_iter_type_has_generic_placeholder(clean[4..bracket_end])
550 || for_iter_type_has_generic_placeholder(clean[bracket_end + 1..])
551 }
552 if clean.starts_with('[') {
553 bracket_end := clean.index(']') or { return false }
554 return for_iter_type_has_generic_placeholder(clean[bracket_end + 1..])
555 }
556 return false
557}
558
559// Infer the element type for the loop variable from the iterable type.
560fn (t &Transformer) infer_for_in_elem_type(iter_type string, node flat.Node) string {
561 if iter_type.starts_with('&[]') {
562 return iter_type[3..]
563 }
564 if iter_type.starts_with('[]') {
565 return iter_type[2..]
566 }
567 if iter_type.starts_with('map[') {
568 // map[K]V -> value type is everything after the closing ']'
569 bracket_end := iter_type.index(']') or { return '' }
570 if bracket_end + 1 < iter_type.len {
571 return iter_type[bracket_end + 1..]
572 }
573 return ''
574 }
575 if iter_type == 'string' {
576 return 'u8'
577 }
578 if t.is_fixed_array_type(iter_type) {
579 return for_in_fixed_array_elem_type(iter_type)
580 }
581 // Check if the iterable is a range expression
582 if node.children_count > 0 {
583 iter_id := t.a.child(&node, 0)
584 iter_node := t.a.nodes[int(iter_id)]
585 if iter_node.kind == .range {
586 return 'int'
587 }
588 }
589 return ''
590}
591
592fn (mut t Transformer) make_for_in_fixed_array_len_expr(s string) flat.NodeId {
593 len_text := for_in_fixed_array_len_text(s)
594 if is_decimal_text(len_text) {
595 return t.make_int_literal(len_text.int())
596 }
597 if !isnil(t.tc) {
598 if v := t.tc.const_int_value(len_text, []string{}) {
599 return t.make_int_literal(v)
600 }
601 }
602 if len_text.contains('.') {
603 base := len_text.all_before_last('.')
604 field := len_text.all_after_last('.')
605 return t.make_selector(t.make_ident(base), field, 'int')
606 }
607 return t.make_ident(len_text)
608}
609
610fn for_in_fixed_array_elem_type(s string) string {
611 clean := s.trim_space()
612 if clean.starts_with('[') {
613 return clean.all_after(']')
614 }
615 open := clean.last_index_u8(`[`)
616 if open < 0 {
617 return ''
618 }
619 return clean[..open]
620}
621
622fn for_in_fixed_array_len_text(s string) string {
623 clean := s.trim_space()
624 if clean.starts_with('[') {
625 return clean.all_after('[').all_before(']').trim_space()
626 }
627 open := clean.last_index_u8(`[`)
628 if open < 0 {
629 return ''
630 }
631 close_rel := clean[open + 1..].index_u8(`]`)
632 if close_rel < 0 {
633 return ''
634 }
635 return clean[open + 1..open + 1 + close_rel].trim_space()
636}
637