| 1 | module transform |
| 2 | |
| 3 | import v3.flat |
| 4 | |
| 5 | // transform_for_body transforms transform for body data for transform. |
| 6 | fn (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. |
| 84 | fn (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. |
| 145 | fn (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. |
| 176 | fn (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 | |
| 266 | fn (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. |
| 278 | fn (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 | |
| 300 | fn (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 | |
| 312 | fn (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. |
| 366 | fn (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. |
| 453 | fn (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. |
| 472 | fn (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 | |
| 493 | fn 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 | |
| 500 | fn (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 | |
| 533 | fn 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. |
| 560 | fn (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 | |
| 592 | fn (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 | |
| 610 | fn 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 | |
| 622 | fn 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 | |