| 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 | 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. |
| 137 | fn (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. |
| 168 | fn (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. |
| 253 | 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 { |
| 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. |
| 275 | 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 { |
| 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. |
| 360 | fn (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. |
| 379 | fn (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 | |
| 395 | fn (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 | |
| 428 | fn 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. |
| 455 | fn (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 | |
| 487 | fn (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 | |
| 505 | fn 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 | |
| 517 | fn 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 | |