v4 / vlib / v3 / transform / expr.v
1555 lines · 1475 sloc · 47.97 KB · 351709f1031b64c6175612e9e459d8216eeca7f5
Raw
1module transform
2
3import v3.flat
4import v3.types
5
6// transform_infix_string_ops transforms transform infix string ops data for transform.
7fn (mut t Transformer) transform_infix_string_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
8 if node.children_count < 2 {
9 return none
10 }
11 match node.op {
12 .plus, .eq, .ne, .lt, .gt, .le, .ge {}
13 else {
14 return none
15 }
16 }
17
18 lhs_id := t.a.children[node.children_start]
19 rhs_id := t.a.children[node.children_start + 1]
20
21 is_string := t.is_string_type(lhs_id) || t.is_string_type(rhs_id)
22
23 if !is_string {
24 return none
25 }
26
27 new_lhs := t.transform_expr(lhs_id)
28 new_rhs := t.transform_expr(rhs_id)
29
30 match node.op {
31 .plus {
32 lhs := t.a.nodes[int(new_lhs)]
33 rhs := t.a.nodes[int(new_rhs)]
34 if lhs.kind == .string_literal && rhs.kind == .string_literal {
35 return t.make_string_literal(lhs.value + rhs.value)
36 }
37 return t.make_call('string__plus', arr2(new_lhs, new_rhs))
38 }
39 .eq {
40 return t.make_call('string__eq', arr2(new_lhs, new_rhs))
41 }
42 .ne {
43 eq_call := t.make_call('string__eq', arr2(new_lhs, new_rhs))
44 start := t.a.children.len
45 t.a.children << eq_call
46 return t.a.add_node(flat.Node{
47 kind: .prefix
48 op: .not
49 children_start: start
50 children_count: 1
51 })
52 }
53 .lt {
54 return t.make_call('string__lt', arr2(new_lhs, new_rhs))
55 }
56 .gt {
57 // a > b -> string__lt(b, a)
58 return t.make_call('string__lt', arr2(new_rhs, new_lhs))
59 }
60 .le {
61 // a <= b -> !(b < a) -> !string__lt(rhs, lhs)
62 lt_call := t.make_call('string__lt', arr2(new_rhs, new_lhs))
63 start := t.a.children.len
64 t.a.children << lt_call
65 return t.a.add_node(flat.Node{
66 kind: .prefix
67 op: .not
68 children_start: start
69 children_count: 1
70 })
71 }
72 .ge {
73 // a >= b -> !(a < b) -> !string__lt(lhs, rhs)
74 lt_call := t.make_call('string__lt', arr2(new_lhs, new_rhs))
75 start := t.a.children.len
76 t.a.children << lt_call
77 return t.a.add_node(flat.Node{
78 kind: .prefix
79 op: .not
80 children_start: start
81 children_count: 1
82 })
83 }
84 else {
85 return none
86 }
87 }
88}
89
90// transform_infix_array_ops transforms transform infix array ops data for transform.
91fn (mut t Transformer) transform_infix_array_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
92 if node.children_count < 2 || node.op !in [.eq, .ne] {
93 return none
94 }
95 lhs_id := t.a.children[node.children_start]
96 rhs_id := t.a.children[node.children_start + 1]
97 lhs_type := t.membership_container_type(t.node_type(lhs_id))
98 rhs_type := t.membership_container_type(t.node_type(rhs_id))
99 if !(lhs_type.starts_with('[]') || lhs_type == 'array') || !(rhs_type.starts_with('[]')
100 || rhs_type == 'array') {
101 return none
102 }
103 mut elem_type := ''
104 if lhs_type.starts_with('[]') {
105 elem_type = lhs_type[2..]
106 } else if rhs_type.starts_with('[]') {
107 elem_type = rhs_type[2..]
108 }
109 if elem_type.len == 0 {
110 elem_type = 'int'
111 }
112 new_lhs := t.transform_expr(lhs_id)
113 new_rhs := t.transform_expr(rhs_id)
114 eq_call := if elem_type == 'string' {
115 t.make_call_typed('array_eq_string', arr2(new_lhs, new_rhs), 'bool')
116 } else {
117 t.make_call_typed('array_eq_raw', arr3(new_lhs, new_rhs, t.make_sizeof_type(elem_type)),
118 'bool')
119 }
120 if node.op == .ne {
121 return t.make_prefix(.not, eq_call)
122 }
123 return eq_call
124}
125
126// transform_infix_map_ops transforms transform infix map ops data for transform.
127fn (mut t Transformer) transform_infix_map_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
128 if node.children_count < 2 || node.op !in [.eq, .ne] {
129 return none
130 }
131 lhs_id := t.a.children[node.children_start]
132 rhs_id := t.a.children[node.children_start + 1]
133 lhs_type := t.node_type(lhs_id)
134 rhs_type := t.node_type(rhs_id)
135 lhs_map_type := t.clean_map_type(lhs_type)
136 rhs_map_type := t.clean_map_type(rhs_type)
137 if !lhs_map_type.starts_with('map[') || !rhs_map_type.starts_with('map[') {
138 return none
139 }
140 mut new_lhs := t.transform_expr(lhs_id)
141 mut new_rhs := t.transform_expr(rhs_id)
142 if lhs_type.starts_with('&') {
143 new_lhs = t.make_prefix(.mul, new_lhs)
144 }
145 if rhs_type.starts_with('&') {
146 new_rhs = t.make_prefix(.mul, new_rhs)
147 }
148 eq_call := t.make_call_typed('map_map_eq', arr2(new_lhs, new_rhs), 'bool')
149 if node.op == .ne {
150 return t.make_prefix(.not, eq_call)
151 }
152 return eq_call
153}
154
155// transform_infix_struct_ops transforms transform infix struct ops data for transform.
156fn (mut t Transformer) transform_infix_struct_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
157 if node.children_count < 2 {
158 return none
159 }
160 lhs_id := t.a.children[node.children_start]
161 mut lhs_type := t.node_type(lhs_id)
162 checker_lhs_type := t.checker_node_type(lhs_id)
163 lhs_key := t.expr_key(lhs_id)
164 mut has_smartcast := false
165 if lhs_key.len > 0 {
166 if sc := t.find_smartcast(lhs_key) {
167 has_smartcast = true
168 lhs_type = t.smartcast_target_type(sc)
169 }
170 }
171 if lhs_type.len == 0 {
172 lhs_type = checker_lhs_type
173 }
174 if lhs_type.starts_with('&') {
175 return none
176 }
177 mut struct_type := t.struct_lookup_name(lhs_type)
178 if struct_type.len == 0 {
179 // Generic-struct instance operand (e.g. `Vec4[f32]`): keep the instance type
180 // so the operator lowers to the monomorphized method (`vec__Vec4_f32__plus`).
181 struct_type = t.generic_struct_instance_name(lhs_type)
182 }
183 if struct_type.len == 0 {
184 return none
185 }
186 // Skip the checker/transformer agreement guard for generic-struct instances:
187 // they resolve reliably, and an alias name (`SimdFloat4`) vs the resolved form
188 // (`vec.Vec4[f32]`) would otherwise spuriously fail the comparison.
189 if checker_lhs_type.len > 0 && !has_smartcast && !struct_type.contains('[') {
190 checker_struct_type := t.struct_lookup_name(checker_lhs_type)
191 if checker_struct_type.len > 0 && checker_struct_type != struct_type {
192 return none
193 }
194 if checker_struct_type.len == 0 && checker_lhs_type != lhs_type {
195 return none
196 }
197 }
198 if call_info := t.struct_operator_call_info(struct_type, node.op) {
199 if t.is_disabled_fn_name(call_info.name) {
200 ret_type := t.struct_operator_return_type(call_info.name)
201 if ret_type.len == 0 || ret_type == 'void' {
202 return t.make_empty()
203 }
204 return t.zero_value_for_type(ret_type)
205 }
206 lhs := t.transform_expr(lhs_id)
207 rhs := t.transform_expr(t.a.children[node.children_start + 1])
208 mut call_lhs := lhs
209 mut call_rhs := rhs
210 if call_info.reverse {
211 call_lhs = t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'op_lhs')
212 call_rhs = t.stable_transformed_expr_for_reuse(rhs, t.node_type(t.a.children[
213 node.children_start + 1]), 'op_rhs')
214 }
215 args := if call_info.reverse {
216 arr2(call_rhs, call_lhs)
217 } else {
218 arr2(call_lhs, call_rhs)
219 }
220 call := t.make_call_typed(call_info.name, args, node.typ)
221 if call_info.negate {
222 return t.make_prefix(.not, call)
223 }
224 return call
225 }
226 if node.op != .eq && node.op != .ne {
227 return none
228 }
229 if !t.has_struct_operator_fn(struct_type, '==') {
230 lhs := t.stable_expr_for_reuse(lhs_id)
231 rhs := t.stable_expr_for_reuse(t.a.children[node.children_start + 1])
232 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),
233 t.make_sizeof_type(struct_type)), 'int')
234 return t.make_infix(node.op, cmp, t.make_int_literal(0))
235 }
236 if eq_fn := t.struct_operator_fn_name(struct_type, '==') {
237 if t.is_disabled_fn_name(eq_fn) {
238 return t.make_bool_literal(node.op == .ne)
239 }
240 lhs := t.transform_expr(lhs_id)
241 rhs := t.transform_expr(t.a.children[node.children_start + 1])
242 eq_call := t.make_call_typed(eq_fn, arr2(lhs, rhs), 'bool')
243 if node.op == .ne {
244 return t.make_prefix(.not, eq_call)
245 }
246 return eq_call
247 }
248 return none
249}
250
251fn (mut t Transformer) transform_transformed_struct_eq(node flat.Node, lhs flat.NodeId, rhs flat.NodeId) ?flat.NodeId {
252 mut lhs_type := t.node_type(lhs)
253 if lhs_type.starts_with('&') {
254 lhs_type = lhs_type[1..]
255 }
256 mut rhs_type := t.node_type(rhs)
257 if rhs_type.starts_with('&') {
258 rhs_type = rhs_type[1..]
259 }
260 lhs_struct_type := t.struct_lookup_name(lhs_type)
261 rhs_struct_type := t.struct_lookup_name(rhs_type)
262 if lhs_struct_type.len == 0 || rhs_struct_type.len == 0 {
263 return none
264 }
265 struct_type := lhs_struct_type
266 if lhs_struct_type != rhs_struct_type
267 && lhs_struct_type.all_after_last('.') != rhs_struct_type.all_after_last('.') {
268 return none
269 }
270 if call_info := t.struct_operator_call_info(struct_type, node.op) {
271 if t.is_disabled_fn_name(call_info.name) {
272 ret_type := t.struct_operator_return_type(call_info.name)
273 if ret_type.len == 0 || ret_type == 'void' {
274 return t.make_empty()
275 }
276 return t.zero_value_for_type(ret_type)
277 }
278 call_lhs := t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'op_lhs')
279 call_rhs := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'op_rhs')
280 args := if call_info.reverse {
281 arr2(call_rhs, call_lhs)
282 } else {
283 arr2(call_lhs, call_rhs)
284 }
285 call := t.make_call_typed(call_info.name, args, node.typ)
286 if call_info.negate {
287 return t.make_prefix(.not, call)
288 }
289 return call
290 }
291 if node.op != .eq && node.op != .ne {
292 return none
293 }
294 if eq_fn := t.struct_operator_fn_name(struct_type, '==') {
295 if t.is_disabled_fn_name(eq_fn) {
296 return t.make_bool_literal(node.op == .ne)
297 }
298 call_lhs := t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'eq_lhs')
299 call_rhs := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'eq_rhs')
300 eq_call := t.make_call_typed(eq_fn, arr2(call_lhs, call_rhs), 'bool')
301 if node.op == .ne {
302 return t.make_prefix(.not, eq_call)
303 }
304 return eq_call
305 }
306 cmp_lhs := t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'eq_lhs')
307 cmp_rhs := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'eq_rhs')
308 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, cmp_lhs), t.make_prefix(.amp,
309 cmp_rhs), t.make_sizeof_type(struct_type)), 'int')
310 return t.make_infix(node.op, cmp, t.make_int_literal(0))
311}
312
313fn (t &Transformer) checker_node_type(id flat.NodeId) string {
314 if isnil(t.tc) || int(id) < 0 {
315 return ''
316 }
317 typ := t.tc.expr_type(id) or { t.tc.resolve_type(id) }
318 name := typ.name()
319 if name.len == 0 || name == 'void' {
320 return ''
321 }
322 return t.normalize_type_alias(name)
323}
324
325// StructOperatorCallInfo stores struct operator call info metadata used by transform.
326struct StructOperatorCallInfo {
327 name string
328 reverse bool
329 negate bool
330}
331
332// struct_operator_call_info supports struct operator call info handling for Transformer.
333fn (t &Transformer) struct_operator_call_info(struct_type string, op flat.Op) ?StructOperatorCallInfo {
334 if op_name := struct_operator_symbol(op) {
335 if method_name := t.struct_operator_fn_name(struct_type, op_name) {
336 return StructOperatorCallInfo{
337 name: method_name
338 }
339 }
340 }
341 match op {
342 .gt {
343 if method_name := t.struct_operator_fn_name(struct_type, '<') {
344 return StructOperatorCallInfo{
345 name: method_name
346 reverse: true
347 }
348 }
349 }
350 .ge {
351 if method_name := t.struct_operator_fn_name(struct_type, '<') {
352 return StructOperatorCallInfo{
353 name: method_name
354 negate: true
355 }
356 }
357 }
358 .le {
359 if method_name := t.struct_operator_fn_name(struct_type, '<') {
360 return StructOperatorCallInfo{
361 name: method_name
362 reverse: true
363 negate: true
364 }
365 }
366 }
367 .ne {
368 if method_name := t.struct_operator_fn_name(struct_type, '==') {
369 return StructOperatorCallInfo{
370 name: method_name
371 negate: true
372 }
373 }
374 }
375 else {}
376 }
377
378 return none
379}
380
381// struct_operator_symbol supports struct operator symbol handling for transform.
382fn struct_operator_symbol(op flat.Op) ?string {
383 match op {
384 .plus { return '+' }
385 .minus { return '-' }
386 .mul { return '*' }
387 .div { return '/' }
388 .mod { return '%' }
389 .eq { return '==' }
390 .ne { return '!=' }
391 .lt { return '<' }
392 .gt { return '>' }
393 .le { return '<=' }
394 .ge { return '>=' }
395 else {}
396 }
397
398 return none
399}
400
401// struct_operator_fn_name supports struct operator fn name handling for Transformer.
402fn (t &Transformer) struct_operator_fn_name(struct_type string, op_name string) ?string {
403 method_name := '${struct_type}.${op_name}'
404 require_used := op_name in ['==', '!=']
405 if t.is_known_operator_fn_name(method_name, require_used) {
406 return method_name
407 }
408 cmethod_name := c_name(method_name)
409 if t.is_known_operator_fn_name(cmethod_name, require_used) {
410 return cmethod_name
411 }
412 if name := t.generic_struct_operator_fn_name(struct_type, op_name) {
413 return name
414 }
415 return none
416}
417
418// generic_struct_instance_name returns the resolved generic-struct instance type
419// for `type_name` (its base is a known generic struct), else ''. A type alias to a
420// generic instance (`SimdFloat4` -> `vec.Vec4[f32]`) is normalized first, so an
421// operand typed by its alias still lowers to the monomorphized operator.
422fn (t &Transformer) generic_struct_instance_name(type_name string) string {
423 if isnil(t.tc) {
424 return ''
425 }
426 normalized := t.normalize_type_alias(type_name)
427 base, _, ok := generic_app_parts(normalized)
428 if !ok {
429 return ''
430 }
431 if base in t.tc.struct_generic_params {
432 return normalized
433 }
434 return ''
435}
436
437// generic_struct_operator_fn_name handles operator overloads on a generic-struct
438// instance (e.g. `Vec4[f32] + Vec4[f32]`). The operator is declared on the generic
439// form (`Vec4[T].+`) and specialized to `vec__Vec4_f32__plus` by the monomorphizer
440// (which runs after this lowering). When the generic operator exists, anticipate
441// the specialized C name so the infix lowers to that call.
442fn (t &Transformer) generic_struct_operator_fn_name(struct_type string, op_name string) ?string {
443 if isnil(t.tc) {
444 return none
445 }
446 base, _, ok := generic_app_parts(struct_type)
447 if !ok {
448 return none
449 }
450 params := t.tc.struct_generic_params[base] or { return none }
451 if params.len == 0 {
452 return none
453 }
454 generic_key := '${base}[${params.join(', ')}].${op_name}'
455 if generic_key in t.tc.fn_ret_types || generic_key in t.tc.fn_param_types {
456 return c_name('${struct_type}.${op_name}')
457 }
458 return none
459}
460
461fn (t &Transformer) is_known_operator_fn_name(name string, require_used bool) bool {
462 if !t.is_known_fn_name(name) {
463 return false
464 }
465 if !require_used || t.used_fns.len == 0 {
466 return true
467 }
468 return name in t.used_fns || c_name(name) in t.used_fns
469}
470
471// has_struct_operator_fn reports whether has struct operator fn applies in transform.
472fn (t &Transformer) has_struct_operator_fn(struct_type string, op_name string) bool {
473 if _ := t.struct_operator_fn_name(struct_type, op_name) {
474 return true
475 }
476 return false
477}
478
479// struct_operator_return_type supports struct operator return type handling for Transformer.
480fn (t &Transformer) struct_operator_return_type(fn_name string) string {
481 if ret := t.fn_ret_types[fn_name] {
482 return t.normalize_type_alias(ret)
483 }
484 if !isnil(t.tc) {
485 if ret := t.tc.fn_ret_types[fn_name] {
486 return t.normalize_type_alias(ret.name())
487 }
488 }
489 return ''
490}
491
492// infix_struct_operator_result_type
493// supports helper handling in transform.
494fn (t &Transformer) infix_struct_operator_result_type(node flat.Node, lhs_type_in string) string {
495 if node.children_count < 2 {
496 return ''
497 }
498 // `lhs_type_in` is the already-resolved type of the left operand; the caller passes
499 // it so we don't re-resolve the operand (operator-overload checks run on every infix).
500 lhs_type := if lhs_type_in.starts_with('&') { lhs_type_in[1..] } else { lhs_type_in }
501 struct_type := t.struct_lookup_name(lhs_type)
502 if struct_type.len == 0 {
503 // Generic-struct instance (`Vec4[f32]`): the specialized operator method is
504 // not registered until monomorphization, so derive the result from the
505 // generic operator's (substituted) return type.
506 if rt := t.generic_struct_operator_return_type(t.generic_struct_instance_name(lhs_type),
507 node.op)
508 {
509 return rt
510 }
511 return ''
512 }
513 if call_info := t.struct_operator_call_info(struct_type, node.op) {
514 ret_type := t.struct_operator_return_type(call_info.name)
515 if ret_type.len > 0 {
516 return ret_type
517 }
518 }
519 return ''
520}
521
522// generic_struct_operator_return_type returns the result type of an operator on a
523// generic-struct instance (`Vec4[f32]`), derived from the generic operator's
524// declared return type (`Vec4[T].- -> Vec4[T]`) with the instance's type arguments
525// substituted (`-> Vec4[f32]`), qualified with the struct's module so the outer
526// expression resolves to the monomorphized operator.
527fn (t &Transformer) generic_struct_operator_return_type(struct_type string, op flat.Op) ?string {
528 if struct_type.len == 0 || isnil(t.tc) {
529 return none
530 }
531 op_name := struct_operator_symbol(op) or { return none }
532 full_base, args, ok := generic_app_parts(struct_type)
533 if !ok {
534 return none
535 }
536 params := t.tc.struct_generic_params[full_base] or { return none }
537 generic_key := '${full_base}[${params.join(', ')}].${op_name}'
538 mut ret := ''
539 if r := t.fn_ret_types[generic_key] {
540 ret = r
541 } else if r := t.tc.fn_ret_types[generic_key] {
542 ret = r.name()
543 } else {
544 return none
545 }
546 substituted := substitute_generic_type_text_with_params(ret, args, params)
547 // Qualify a returned instance of the same struct family with the struct's module.
548 rbase, _, rok := generic_app_parts(substituted)
549 if rok && full_base.contains('.') && !rbase.contains('.')
550 && rbase == full_base.all_after_last('.') {
551 return '${full_base.all_before_last('.')}.${substituted}'
552 }
553 return substituted
554}
555
556// transform_infix_sum_ops transforms transform infix sum ops data for transform.
557fn (mut t Transformer) transform_infix_sum_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
558 if node.op !in [.eq, .ne] || node.children_count < 2 {
559 return none
560 }
561 lhs_id := t.a.children[node.children_start]
562 rhs_id := t.a.children[node.children_start + 1]
563 lhs_raw_type := t.node_type(lhs_id)
564 rhs_raw_type := t.node_type(rhs_id)
565 mut lhs_type := lhs_raw_type
566 mut rhs_type := rhs_raw_type
567 lhs_is_ptr := lhs_type.starts_with('&')
568 rhs_is_ptr := rhs_type.starts_with('&')
569 if lhs_is_ptr {
570 lhs_type = lhs_type[1..]
571 }
572 if rhs_is_ptr {
573 rhs_type = rhs_type[1..]
574 }
575 if !t.is_sum_type_name(lhs_type) || !t.is_sum_type_name(rhs_type) {
576 return none
577 }
578 sum_type := if t.is_sum_type_name(lhs_type) {
579 t.resolve_sum_name(lhs_type)
580 } else if t.is_sum_type_name(rhs_type) {
581 t.resolve_sum_name(rhs_type)
582 } else {
583 ''
584 }
585 if sum_type.len == 0 {
586 return none
587 }
588 mut lhs := t.stable_expr_for_reuse(lhs_id)
589 mut rhs := t.stable_expr_for_reuse(rhs_id)
590 if lhs_is_ptr {
591 lhs = t.make_prefix(.mul, lhs)
592 t.a.nodes[int(lhs)].typ = lhs_type
593 }
594 if rhs_is_ptr {
595 rhs = t.make_prefix(.mul, rhs)
596 t.a.nodes[int(rhs)].typ = rhs_type
597 }
598 tag_eq := t.make_infix(.eq, t.make_selector(lhs, 'typ', 'int'), t.make_selector(rhs, 'typ',
599 'int'))
600 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),
601 t.make_sizeof_type(sum_type)), 'int')
602 value_eq := t.make_infix(.eq, cmp, t.make_int_literal(0))
603 eq := t.make_infix(.logical_and, tag_eq, value_eq)
604 if node.op == .ne {
605 return t.make_prefix(.not, t.make_paren(eq))
606 }
607 return eq
608}
609
610// transform_infix_optional_none_ops supports transform_infix_optional_none_ops handling.
611fn (mut t Transformer) transform_infix_optional_none_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
612 if node.op !in [.eq, .ne] || node.children_count < 2 {
613 return none
614 }
615 lhs_id := t.a.children[node.children_start]
616 rhs_id := t.a.children[node.children_start + 1]
617 lhs := t.a.nodes[int(lhs_id)]
618 rhs := t.a.nodes[int(rhs_id)]
619 mut opt_id := flat.empty_node
620 if lhs.kind == .none_expr {
621 opt_id = rhs_id
622 } else if rhs.kind == .none_expr {
623 opt_id = lhs_id
624 } else {
625 return none
626 }
627 opt_type := t.node_type(opt_id)
628 if !t.is_optional_type_name(opt_type) {
629 return none
630 }
631 ok := t.make_selector(t.transform_expr(opt_id), 'ok', 'bool')
632 if node.op == .eq {
633 return t.make_prefix(.not, ok)
634 }
635 return ok
636}
637
638// struct_lookup_name supports struct lookup name handling for Transformer.
639fn (t &Transformer) struct_lookup_name(type_name string) string {
640 if type_name.len == 0 {
641 return ''
642 }
643 // Primitives, arrays and maps are never struct names. Bail before the qualified-name
644 // concatenation below — this runs for every infix operand, so the saved allocation
645 // matters. (Behaviour is unchanged: these always resolved to '' anyway.)
646 first := type_name[0]
647 if first == `[`
648 || (first >= `a` && first <= `z` && types.is_builtin_type_name(type_name))
649 || type_name.starts_with('map[') {
650 return ''
651 }
652 if type_name.contains('.') {
653 if type_name in t.structs {
654 return type_name
655 }
656 short_type := type_name.all_after_last('.')
657 type_mod := type_name.all_before_last('.')
658 if info := t.structs[short_type] {
659 if info.module == type_mod {
660 return short_type
661 }
662 }
663 if info := t.structs[type_name] {
664 if info.module == type_mod {
665 return type_name
666 }
667 }
668 return ''
669 }
670 if type_name in t.structs {
671 if info := t.structs[type_name] {
672 if info.module.len == 0 || info.module == t.cur_module {
673 return type_name
674 }
675 }
676 }
677 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
678 qtype := '${t.cur_module}.${type_name}'
679 if qtype in t.structs {
680 return qtype
681 }
682 }
683 return ''
684}
685
686// transform_in_expr transforms transform in expr data for transform.
687fn (mut t Transformer) transform_in_expr(id flat.NodeId, node flat.Node) flat.NodeId {
688 if node.children_count < 2 {
689 return id
690 }
691 lhs_id := t.a.children[node.children_start]
692 rhs_id := t.a.children[node.children_start + 1]
693 rhs := t.a.nodes[int(rhs_id)]
694
695 is_not_in := node.value == '!in'
696
697 mut result := id
698 if rhs.kind == .range {
699 // x in low..high -> x >= low && x < high
700 if rhs.children_count >= 2 {
701 new_lhs := t.stable_expr_for_reuse(lhs_id)
702 low_id := t.a.children[rhs.children_start]
703 high_id := t.a.children[rhs.children_start + 1]
704 new_low := t.transform_expr(low_id)
705 new_high := t.transform_expr(high_id)
706
707 ge_cmp := t.make_infix(.ge, new_lhs, new_low)
708 lt_cmp := t.make_infix(.lt, new_lhs, new_high)
709 result = t.make_infix(.logical_and, ge_cmp, lt_cmp)
710 }
711 } else if rhs.kind == .array_literal {
712 if type_checks := t.lower_type_pattern_membership(lhs_id, rhs, is_not_in) {
713 return type_checks
714 }
715 // x in [a, b, c] -> x == a || x == b || x == c
716 if rhs.children_count == 0 {
717 result = t.make_bool_literal(false)
718 } else {
719 new_lhs := t.stable_expr_for_reuse(lhs_id)
720 is_str := t.is_string_type(lhs_id)
721 mut or_chain := flat.empty_node
722 for i in 0 .. rhs.children_count {
723 elem_id := t.a.children[rhs.children_start + i]
724 new_elem := t.transform_expr(elem_id)
725 eq_cmp := if is_str {
726 t.make_call('string__eq', arr2(new_lhs, new_elem))
727 } else {
728 t.make_infix(.eq, new_lhs, new_elem)
729 }
730 if int(or_chain) < 0 {
731 or_chain = eq_cmp
732 } else {
733 or_chain = t.make_infix(.logical_or, or_chain, eq_cmp)
734 }
735 }
736 result = or_chain
737 }
738 } else {
739 rhs_type := t.node_type(rhs_id)
740 clean_rhs_type := t.membership_container_type(rhs_type)
741 rhs_is_ptr_array := t.membership_container_is_pointer_array(rhs_type)
742 if clean_rhs_type.starts_with('[]') || clean_rhs_type == 'array' {
743 if lowered := t.lower_array_membership_expr(rhs_id, lhs_id, rhs_type, false, node) {
744 result = lowered
745 } else {
746 // dynamic array membership -> array_contains_int/string(arr, val)
747 new_lhs := t.transform_expr(lhs_id)
748 mut new_rhs := t.transform_expr(rhs_id)
749 if rhs_is_ptr_array {
750 new_rhs = t.make_prefix(.mul, new_rhs)
751 }
752 mut elem := if clean_rhs_type.starts_with('[]') { clean_rhs_type[2..] } else { '' }
753 if elem.len == 0 {
754 elem = t.node_type(lhs_id)
755 }
756 fn_name := array_contains_fn_name(elem)
757 result = t.make_call_typed(fn_name, arr2(new_rhs, new_lhs), 'bool')
758 }
759 } else if rhs.kind in [.ident, .selector] && (rhs_type.len == 0 || rhs_type == 'unknown') {
760 new_lhs := t.transform_expr(lhs_id)
761 new_rhs := t.transform_expr(rhs_id)
762 mut elem := t.node_type(lhs_id)
763 lhs := t.a.nodes[int(lhs_id)]
764 if elem.len == 0 && lhs.kind == .selector {
765 elem = t.selector_field_type(lhs)
766 }
767 if elem.len == 0 && rhs.kind == .selector {
768 elem = t.selector_array_elem_type(rhs)
769 }
770 if elem.len > 0 {
771 fn_name := array_contains_fn_name(elem)
772 result = t.make_call_typed(fn_name, arr2(new_rhs, new_lhs), 'bool')
773 }
774 } else if t.is_fixed_array_type(clean_rhs_type) {
775 // fixed array membership -> fixed_array_contains_int/string(arr, len, val)
776 new_lhs := t.transform_expr(lhs_id)
777 new_rhs := t.transform_expr(rhs_id)
778 elem := fixed_array_elem_type(clean_rhs_type)
779 fn_name := fixed_array_contains_fn_name(elem)
780 len_expr := t.make_fixed_array_len_expr(clean_rhs_type)
781 result = t.make_call_typed(fn_name, arr3(new_rhs, len_expr, new_lhs), 'bool')
782 } else if clean_rhs_type == 'string' {
783 new_lhs := t.transform_expr(lhs_id)
784 new_rhs := t.transform_expr(rhs_id)
785 fn_name := if t.node_type(lhs_id) in ['u8', 'byte'] {
786 'string__contains_u8'
787 } else {
788 'string__contains'
789 }
790 result = t.make_call_typed(fn_name, arr2(new_rhs, new_lhs), 'bool')
791 } else if clean_rhs_type.starts_with('map[') || clean_rhs_type == 'map' {
792 if lowered := t.lower_map_membership_expr(rhs_id, lhs_id, rhs_type) {
793 result = lowered
794 }
795 } else {
796 // Unknown containment is kept as in_expr so the backend can reject or
797 // handle genuinely unresolved cases.
798 new_lhs := t.transform_expr(lhs_id)
799 new_rhs := t.transform_expr(rhs_id)
800 in_start := t.a.children.len
801 t.a.children << new_lhs
802 t.a.children << new_rhs
803 result = t.a.add_node(flat.Node{
804 kind: .in_expr
805 op: node.op
806 children_start: in_start
807 children_count: 2
808 pos: node.pos
809 value: 'in'
810 typ: node.typ
811 })
812 }
813 }
814
815 if is_not_in && result != id {
816 start := t.a.children.len
817 t.a.children << t.make_paren(result)
818 return t.a.add_node(flat.Node{
819 kind: .prefix
820 op: .not
821 children_start: start
822 children_count: 1
823 })
824 }
825 return result
826}
827
828// lower_type_pattern_membership builds lower type pattern membership data for transform.
829fn (mut t Transformer) lower_type_pattern_membership(lhs_id flat.NodeId, rhs flat.Node, is_not_in bool) ?flat.NodeId {
830 if rhs.children_count == 0 {
831 return none
832 }
833 mut patterns := []string{cap: int(rhs.children_count)}
834 mut sum_name := t.trim_pointer_type(t.original_expr_type(lhs_id))
835 if !t.is_sum_type_name(sum_name) {
836 sum_name = t.trim_pointer_type(t.node_type(lhs_id))
837 }
838 for i in 0 .. rhs.children_count {
839 elem_id := t.a.child(&rhs, i)
840 pattern := t.type_pattern_name(elem_id)
841 if pattern.len == 0 {
842 return none
843 }
844 if !t.is_sum_type_name(sum_name) {
845 pattern_sum := t.find_sum_type_for_variant(pattern)
846 if pattern_sum.len == 0 {
847 return none
848 }
849 sum_name = pattern_sum
850 } else if t.sum_variant_path(sum_name, pattern).len == 0 {
851 return none
852 }
853 patterns << pattern
854 }
855 if !t.is_sum_type_name(sum_name) {
856 return none
857 }
858 lhs_type := t.original_expr_type(lhs_id)
859 base := t.stable_expr_for_reuse(lhs_id)
860 mut chain := flat.empty_node
861 for pattern in patterns {
862 cmp := t.make_sum_type_pattern_check(base, lhs_type, sum_name, pattern) or { return none }
863 if int(chain) < 0 {
864 chain = cmp
865 } else {
866 chain = t.make_infix(.logical_or, chain, cmp)
867 }
868 }
869 if is_not_in {
870 return t.make_prefix(.not, t.make_paren(chain))
871 }
872 return chain
873}
874
875// type_pattern_name returns type pattern name data for Transformer.
876fn (t &Transformer) type_pattern_name(id flat.NodeId) string {
877 if int(id) < 0 {
878 return ''
879 }
880 node := t.a.nodes[int(id)]
881 match node.kind {
882 .ident {
883 return node.value
884 }
885 .selector {
886 if node.children_count == 0 {
887 return node.value
888 }
889 base := t.type_pattern_name(t.a.child(&node, 0))
890 if base.len == 0 {
891 return node.value
892 }
893 return '${base}.${node.value}'
894 }
895 .array_init {
896 if node.value.len == 0 {
897 return ''
898 }
899 return '[]${node.value}'
900 }
901 .array_literal {
902 if node.value.len == 0 {
903 return ''
904 }
905 return '[${node.children_count}]${node.value}'
906 }
907 else {
908 return ''
909 }
910 }
911}
912
913// lower_array_membership_expr builds lower array membership expr data for transform.
914fn (mut t Transformer) lower_array_membership_expr(base_id flat.NodeId, needle_id flat.NodeId, base_type string, receiver_first bool, src flat.Node) ?flat.NodeId {
915 clean_base_type := t.membership_container_type(base_type)
916 if !clean_base_type.starts_with('[]') && clean_base_type != 'array' {
917 return none
918 }
919 mut elem_type := if clean_base_type.starts_with('[]') { clean_base_type[2..] } else { '' }
920 if elem_type.len == 0 {
921 elem_type = t.membership_container_type(t.node_type(needle_id))
922 }
923 if elem_type.len == 0 {
924 return none
925 }
926 mut base := flat.empty_node
927 mut needle := flat.empty_node
928 if receiver_first {
929 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
930 needle = t.stable_expr_for_reuse(needle_id)
931 } else {
932 needle = t.stable_expr_for_reuse(needle_id)
933 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
934 }
935 mut prefix := []flat.NodeId{}
936 t.drain_pending(mut prefix)
937 result_name := t.new_temp('contains')
938 idx_name := t.new_temp('contains_idx')
939 prefix << t.make_decl_assign_typed(result_name, t.make_bool_literal(false), 'bool')
940 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
941 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
942 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
943 elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
944 eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)
945 assign_true := t.make_assign(t.make_ident(result_name), t.make_bool_literal(true))
946 then_block := t.make_block(arr1(assign_true))
947 loop_body := arr1(t.make_if(eq_expr, then_block, t.make_empty()))
948 prefix << t.make_for_stmt(init, cond, post, loop_body, src)
949 for stmt in prefix {
950 t.pending_stmts << stmt
951 }
952 return t.make_ident(result_name)
953}
954
955// lower_array_index_expr builds lower array index expr data for transform.
956fn (mut t Transformer) lower_array_index_expr(base_id flat.NodeId, needle_id flat.NodeId, base_type string, receiver_first bool, src flat.Node) ?flat.NodeId {
957 clean_base_type := t.membership_container_type(base_type)
958 if !clean_base_type.starts_with('[]') && clean_base_type != 'array' {
959 return none
960 }
961 mut elem_type := if clean_base_type.starts_with('[]') { clean_base_type[2..] } else { '' }
962 if elem_type.len == 0 {
963 elem_type = t.membership_container_type(t.node_type(needle_id))
964 }
965 if elem_type.len == 0 {
966 return none
967 }
968 mut base := flat.empty_node
969 mut needle := flat.empty_node
970 if receiver_first {
971 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
972 needle = t.stable_expr_for_reuse(needle_id)
973 } else {
974 needle = t.stable_expr_for_reuse(needle_id)
975 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
976 }
977 mut prefix := []flat.NodeId{}
978 t.drain_pending(mut prefix)
979 result_name := t.new_temp('index')
980 idx_name := t.new_temp('index_idx')
981 prefix << t.make_decl_assign_typed(result_name, t.make_int_literal(-1), 'int')
982 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
983 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
984 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
985 elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
986 eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)
987 not_found := t.make_infix(.lt, t.make_ident(result_name), t.make_int_literal(0))
988 found_cond := t.make_infix(.logical_and, not_found, eq_expr)
989 assign_idx := t.make_assign(t.make_ident(result_name), t.make_ident(idx_name))
990 loop_body := arr1(t.make_if(found_cond, t.make_block(arr1(assign_idx)), t.make_empty()))
991 prefix << t.make_for_stmt(init, cond, post, loop_body, src)
992 for stmt in prefix {
993 t.pending_stmts << stmt
994 }
995 return t.make_ident(result_name)
996}
997
998// stable_array_expr_for_membership
999// supports helper handling in transform.
1000fn (mut t Transformer) stable_array_expr_for_membership(id flat.NodeId, raw_type string, clean_type string) flat.NodeId {
1001 mut expr := t.transform_expr(id)
1002 if t.membership_container_is_pointer_array(raw_type) {
1003 expr = t.make_prefix(.mul, expr)
1004 }
1005 return t.stable_transformed_expr_for_reuse(expr, clean_type, 'in_arr')
1006}
1007
1008// make_membership_eq_expr builds make membership eq expr data for transform.
1009fn (mut t Transformer) make_membership_eq_expr(lhs flat.NodeId, rhs flat.NodeId, elem_type string) flat.NodeId {
1010 mut clean := t.membership_container_type(elem_type)
1011 if clean == 'string' {
1012 return t.make_call_typed('string__eq', arr2(lhs, rhs), 'bool')
1013 }
1014 if clean.starts_with('[]') {
1015 inner := clean[2..]
1016 if inner == 'string' {
1017 return t.make_call_typed('array_eq_string', arr2(lhs, rhs), 'bool')
1018 }
1019 return t.make_call_typed('array_eq_raw', arr3(lhs, rhs, t.make_sizeof_type(inner)), 'bool')
1020 }
1021 struct_type := t.struct_lookup_name(clean)
1022 if struct_type.len > 0 {
1023 method_name := '${struct_type}.=='
1024 if t.is_known_fn_name(method_name) {
1025 return t.make_call(method_name, arr2(lhs, rhs))
1026 }
1027 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),
1028 t.make_sizeof_type(struct_type)), 'int')
1029 return t.make_infix(.eq, cmp, t.make_int_literal(0))
1030 }
1031 return t.make_infix(.eq, lhs, rhs)
1032}
1033
1034// selector_field_type supports selector field type handling for Transformer.
1035fn (t &Transformer) selector_field_type(node flat.Node) string {
1036 if node.value.len == 0 {
1037 return ''
1038 }
1039 for _, info in t.structs {
1040 for field in info.fields {
1041 if field.name == node.value {
1042 return t.normalize_type_alias(field.typ)
1043 }
1044 }
1045 }
1046 return ''
1047}
1048
1049// selector_array_elem_type supports selector array elem type handling for Transformer.
1050fn (t &Transformer) selector_array_elem_type(node flat.Node) string {
1051 if node.value.len == 0 {
1052 return ''
1053 }
1054 for _, info in t.structs {
1055 for field in info.fields {
1056 clean_typ := t.membership_container_type(field.typ)
1057 if field.name == node.value && clean_typ.starts_with('[]') {
1058 return t.normalize_type_alias(clean_typ[2..])
1059 }
1060 }
1061 }
1062 return ''
1063}
1064
1065// membership_container_type supports membership container type handling for Transformer.
1066fn (t &Transformer) membership_container_type(typ string) string {
1067 mut clean := t.normalize_type_alias(typ).trim_space()
1068 for {
1069 if clean.starts_with('shared ') {
1070 clean = clean[7..].trim_space()
1071 continue
1072 }
1073 if clean.starts_with('&') {
1074 clean = clean[1..].trim_space()
1075 continue
1076 }
1077 if clean.starts_with('...') {
1078 clean = '[]' + clean[3..].trim_space()
1079 continue
1080 }
1081 break
1082 }
1083 return t.normalize_type_alias(clean)
1084}
1085
1086// membership_container_is_pointer_array supports membership_container_is_pointer_array handling.
1087fn (t &Transformer) membership_container_is_pointer_array(typ string) bool {
1088 mut clean := t.normalize_type_alias(typ).trim_space()
1089 for clean.starts_with('shared ') {
1090 clean = clean[7..].trim_space()
1091 }
1092 if !clean.starts_with('&') {
1093 return false
1094 }
1095 clean = t.membership_container_type(clean)
1096 return clean.starts_with('[]') || clean == 'array'
1097}
1098
1099// array_contains_fn_name reports whether array contains fn name applies in transform.
1100fn array_contains_fn_name(elem string) string {
1101 return match elem {
1102 'string' { 'array_contains_string' }
1103 'u8', 'byte' { 'array_contains_u8' }
1104 else { 'array_contains_int' }
1105 }
1106}
1107
1108// fixed_array_contains_fn_name reports whether fixed array contains fn name applies in transform.
1109fn fixed_array_contains_fn_name(elem string) string {
1110 return match elem {
1111 'string' { 'fixed_array_contains_string' }
1112 'u8', 'byte' { 'fixed_array_contains_u8' }
1113 else { 'fixed_array_contains_int' }
1114 }
1115}
1116
1117// stable_expr_for_reuse supports stable expr for reuse handling for Transformer.
1118fn (mut t Transformer) stable_expr_for_reuse(id flat.NodeId) flat.NodeId {
1119 expr := t.transform_expr(id)
1120 if t.is_stable_expr_for_reuse(expr) {
1121 return expr
1122 }
1123 tmp_name := t.new_temp('in_lhs')
1124 tmp_typ := t.node_type(id)
1125 decl := t.make_decl_assign(tmp_name, expr)
1126 if tmp_typ.len > 0 {
1127 t.a.nodes[int(decl)].typ = tmp_typ
1128 t.set_var_type(tmp_name, tmp_typ)
1129 }
1130 t.pending_stmts << decl
1131 return t.make_ident(tmp_name)
1132}
1133
1134// stable_transformed_expr_for_reuse
1135// supports helper handling in transform.
1136fn (mut t Transformer) stable_transformed_expr_for_reuse(expr flat.NodeId, typ string, prefix string) flat.NodeId {
1137 if t.is_stable_expr_for_reuse(expr) {
1138 return expr
1139 }
1140 tmp_name := t.new_temp(prefix)
1141 t.pending_stmts << t.make_decl_assign_typed(tmp_name, expr, typ)
1142 return t.make_ident(tmp_name)
1143}
1144
1145// is_stable_expr_for_reuse reports whether is stable expr for reuse applies in transform.
1146fn (t &Transformer) is_stable_expr_for_reuse(id flat.NodeId) bool {
1147 if int(id) < 0 {
1148 return true
1149 }
1150 node := t.a.nodes[int(id)]
1151 return match node.kind {
1152 .ident, .int_literal, .float_literal, .bool_literal, .char_literal, .string_literal,
1153 .nil_literal, .none_expr, .enum_val, .sizeof_expr, .typeof_expr {
1154 true
1155 }
1156 .selector {
1157 node.children_count > 0 && t.is_stable_expr_for_reuse(t.a.children[node.children_start])
1158 }
1159 .field_init {
1160 node.children_count == 0
1161 || t.is_stable_expr_for_reuse(t.a.children[node.children_start])
1162 }
1163 .struct_init {
1164 mut stable := true
1165 for i in 0 .. node.children_count {
1166 if !t.is_stable_expr_for_reuse(t.a.child(&node, i)) {
1167 stable = false
1168 break
1169 }
1170 }
1171 stable
1172 }
1173 .cast_expr {
1174 node.children_count == 0
1175 || t.is_stable_expr_for_reuse(t.a.children[node.children_start])
1176 }
1177 .index {
1178 node.children_count >= 2
1179 && t.is_stable_expr_for_reuse(t.a.children[node.children_start])
1180 && t.is_stable_expr_for_reuse(t.a.children[node.children_start + 1])
1181 }
1182 else {
1183 false
1184 }
1185 }
1186}
1187
1188// transform_fixed_array_len transforms transform fixed array len data for transform.
1189fn (mut t Transformer) transform_fixed_array_len(_id flat.NodeId, node flat.Node) ?flat.NodeId {
1190 if node.value != 'len' || node.children_count == 0 {
1191 return none
1192 }
1193 base_id := t.a.children[node.children_start]
1194 base_type := t.node_type(base_id)
1195 if !t.is_fixed_array_type(base_type) {
1196 return none
1197 }
1198 return t.make_fixed_array_len_expr(base_type)
1199}
1200
1201// clean_map_type transforms clean map type data for transform.
1202fn (t &Transformer) clean_map_type(typ string) string {
1203 mut clean := t.normalize_type_alias(typ).trim_space()
1204 for {
1205 if clean.starts_with('shared ') {
1206 clean = clean[7..].trim_space()
1207 continue
1208 }
1209 if clean.starts_with('&') {
1210 clean = clean[1..].trim_space()
1211 continue
1212 }
1213 break
1214 }
1215 return t.normalize_type_alias(clean)
1216}
1217
1218// runtime_addr supports runtime addr handling for Transformer.
1219fn (mut t Transformer) runtime_addr(expr flat.NodeId, typ string) flat.NodeId {
1220 if typ.starts_with('&') {
1221 return expr
1222 }
1223 if !t.expr_can_take_address(expr) {
1224 stable := t.stable_transformed_expr_for_reuse(expr, typ, 'addr')
1225 return t.make_prefix(.amp, stable)
1226 }
1227 return t.make_prefix(.amp, expr)
1228}
1229
1230// transform_enum_shorthand transforms transform enum shorthand data for transform.
1231fn (mut t Transformer) transform_enum_shorthand(id flat.NodeId, node flat.Node, expected_enum string) flat.NodeId {
1232 resolved_enum := t.enum_type_name_for_expected(expected_enum, '')
1233 if resolved_enum.len == 0 {
1234 return id
1235 }
1236 short_name := node.value.trim_left('.')
1237 if fields := t.enum_types[resolved_enum] {
1238 for f in fields {
1239 if f == short_name {
1240 return t.a.add_node(flat.Node{
1241 kind: .enum_val
1242 value: '${resolved_enum}.${short_name}'
1243 typ: resolved_enum
1244 })
1245 }
1246 }
1247 }
1248 return id
1249}
1250
1251// enum_type_name_for_expected supports enum type name for expected handling for Transformer.
1252fn (t &Transformer) enum_type_name_for_expected(expected_enum string, owner_mod string) string {
1253 if expected_enum.len == 0 {
1254 return ''
1255 }
1256 mut clean := expected_enum
1257 if clean.starts_with('&') {
1258 clean = clean[1..]
1259 }
1260 if clean in t.enum_types {
1261 return clean
1262 }
1263 if clean.contains('.') {
1264 return ''
1265 }
1266 for mod_name in [owner_mod, t.cur_module] {
1267 if mod_name.len == 0 || mod_name == 'main' || mod_name == 'builtin' {
1268 continue
1269 }
1270 qname := '${mod_name}.${clean}'
1271 if qname in t.enum_types {
1272 return qname
1273 }
1274 }
1275 mut found := ''
1276 for enum_name, _ in t.enum_types {
1277 if enum_name.all_after_last('.') != clean {
1278 continue
1279 }
1280 if found.len > 0 && found != enum_name {
1281 return ''
1282 }
1283 found = enum_name
1284 }
1285 return found
1286}
1287
1288// make_call builds make call data for transform.
1289pub fn (mut t Transformer) make_call(fn_name string, args []flat.NodeId) flat.NodeId {
1290 return t.make_call_typed(fn_name, args, '')
1291}
1292
1293// make_call_typed builds make call typed data for transform.
1294pub fn (mut t Transformer) make_call_typed(fn_name string, args []flat.NodeId, typ string) flat.NodeId {
1295 fn_ident := t.make_ident(fn_name)
1296 return t.make_call_expr_typed(fn_ident, args, typ)
1297}
1298
1299// make_call_expr_typed builds make call expr typed data for transform.
1300pub fn (mut t Transformer) make_call_expr_typed(fn_expr flat.NodeId, args []flat.NodeId, typ string) flat.NodeId {
1301 start := t.a.children.len
1302 t.a.children << fn_expr
1303 for arg in args {
1304 t.a.children << arg
1305 }
1306 return t.a.add_node(flat.Node{
1307 kind: .call
1308 children_start: start
1309 children_count: flat.child_count(1 + args.len)
1310 typ: typ
1311 })
1312}
1313
1314// make_empty builds make empty data for transform.
1315pub fn (mut t Transformer) make_empty() flat.NodeId {
1316 return t.a.add(.empty)
1317}
1318
1319// make_method_call builds make method call data for transform.
1320pub fn (mut t Transformer) make_method_call(receiver flat.NodeId, method_name string, args []flat.NodeId) flat.NodeId {
1321 // Build selector: receiver.method_name
1322 sel_start := t.a.children.len
1323 t.a.children << receiver
1324 selector := t.a.add_node(flat.Node{
1325 kind: .selector
1326 value: method_name
1327 children_start: sel_start
1328 children_count: 1
1329 })
1330
1331 start := t.a.children.len
1332 t.a.children << selector
1333 for arg in args {
1334 t.a.children << arg
1335 }
1336 return t.a.add_node(flat.Node{
1337 kind: .call
1338 children_start: start
1339 children_count: flat.child_count(1 + args.len)
1340 })
1341}
1342
1343// make_selector builds make selector data for transform.
1344pub fn (mut t Transformer) make_selector(base flat.NodeId, field string, typ string) flat.NodeId {
1345 return t.make_selector_op(base, field, typ, .dot)
1346}
1347
1348// make_selector_op builds make selector op data for transform.
1349pub fn (mut t Transformer) make_selector_op(base flat.NodeId, field string, typ string, op flat.Op) flat.NodeId {
1350 start := t.a.children.len
1351 t.a.children << base
1352 return t.a.add_node(flat.Node{
1353 kind: .selector
1354 op: op
1355 children_start: start
1356 children_count: 1
1357 value: field
1358 typ: typ
1359 })
1360}
1361
1362// make_index builds make index data for transform.
1363pub fn (mut t Transformer) make_index(base flat.NodeId, index flat.NodeId, typ string) flat.NodeId {
1364 start := t.a.children.len
1365 t.a.children << base
1366 t.a.children << index
1367 return t.a.add_node(flat.Node{
1368 kind: .index
1369 children_start: start
1370 children_count: 2
1371 typ: typ
1372 })
1373}
1374
1375// make_cast builds make cast data for transform.
1376pub fn (mut t Transformer) make_cast(target_type string, expr flat.NodeId, typ string) flat.NodeId {
1377 start := t.a.children.len
1378 t.a.children << expr
1379 return t.a.add_node(flat.Node{
1380 kind: .cast_expr
1381 children_start: start
1382 children_count: 1
1383 value: target_type
1384 typ: typ
1385 })
1386}
1387
1388// make_postfix builds make postfix data for transform.
1389pub fn (mut t Transformer) make_postfix(expr flat.NodeId, op flat.Op) flat.NodeId {
1390 start := t.a.children.len
1391 t.a.children << expr
1392 return t.a.add_node(flat.Node{
1393 kind: .postfix
1394 op: op
1395 children_start: start
1396 children_count: 1
1397 })
1398}
1399
1400// make_struct_init builds make struct init data for transform.
1401pub fn (mut t Transformer) make_struct_init(name string) flat.NodeId {
1402 return t.a.add_node(flat.Node{
1403 kind: .struct_init
1404 value: name
1405 typ: name
1406 })
1407}
1408
1409// make_array_init builds make array init data for transform.
1410pub fn (mut t Transformer) make_array_init(elem_type string) flat.NodeId {
1411 return t.a.add_node(flat.Node{
1412 kind: .array_init
1413 value: elem_type
1414 typ: '[]${elem_type}'
1415 })
1416}
1417
1418// make_map_init builds make map init data for transform.
1419pub fn (mut t Transformer) make_map_init(map_type string) flat.NodeId {
1420 return t.a.add_node(flat.Node{
1421 kind: .map_init
1422 value: map_type
1423 typ: map_type
1424 })
1425}
1426
1427// make_string_literal builds make string literal data for transform.
1428pub fn (mut t Transformer) make_string_literal(value string) flat.NodeId {
1429 return t.a.add_val(.string_literal, value)
1430}
1431
1432// make_int_literal builds make int literal data for transform.
1433pub fn (mut t Transformer) make_int_literal(value int) flat.NodeId {
1434 return t.a.add_val(.int_literal, '${value}')
1435}
1436
1437// make_float_literal builds make float literal data for transform.
1438pub fn (mut t Transformer) make_float_literal(value string) flat.NodeId {
1439 return t.a.add_val(.float_literal, value)
1440}
1441
1442// make_bool_literal builds make bool literal data for transform.
1443pub fn (mut t Transformer) make_bool_literal(value bool) flat.NodeId {
1444 return t.a.add_val(.bool_literal, if value { 'true' } else { 'false' })
1445}
1446
1447// make_sizeof_type builds make sizeof type data for transform.
1448pub fn (mut t Transformer) make_sizeof_type(type_name string) flat.NodeId {
1449 return t.a.add_node(flat.Node{
1450 kind: .sizeof_expr
1451 value: type_name
1452 typ: 'usize'
1453 })
1454}
1455
1456// is_fixed_array_type reports whether a v-type string denotes a fixed array
1457// like `int[5]` (as opposed to a dynamic array `[]int` or a map `map[...]...`).
1458fn (t &Transformer) is_fixed_array_type(s string) bool {
1459 if s.starts_with('[]') || s.starts_with('map[') {
1460 return false
1461 }
1462 if s.starts_with('[') {
1463 return s.contains(']')
1464 }
1465 if !s.contains('[') || !s.ends_with(']') {
1466 return false
1467 }
1468 len_text := fixed_array_len_text(s)
1469 if is_decimal_text(len_text) {
1470 return true
1471 }
1472 // A postfix fixed-array name (`ArrayFixed.name()`) can carry a non-decimal length — a const
1473 // (`int[seg_count]`) or an expression (`int[segs + 1]`) — once the checker round-trips
1474 // `[n]int` to `int[n]`. Accept it when the bracket text resolves to an integer constant,
1475 // which distinguishes it from a generic instance (`vec.Vec4[f32]`, whose bracket is a type).
1476 if len_text.contains(',') || isnil(t.tc) {
1477 return false
1478 }
1479 return t.tc.const_int_value(len_text, []string{}) != none
1480}
1481
1482// fixed_array_len supports fixed array len handling for transform.
1483fn fixed_array_len(s string) int {
1484 return fixed_array_len_text(s).int()
1485}
1486
1487// fixed_array_len_text supports fixed array len text handling for transform.
1488fn fixed_array_len_text(s string) string {
1489 return s.all_after('[').all_before(']').trim_space()
1490}
1491
1492// fixed_array_elem_type supports fixed array elem type handling for transform.
1493fn fixed_array_elem_type(s string) string {
1494 if s.starts_with('[') {
1495 return s.all_after(']')
1496 }
1497 return s.all_before('[')
1498}
1499
1500// is_decimal_text reports whether is decimal text applies in transform.
1501fn is_decimal_text(s string) bool {
1502 if s.len == 0 {
1503 return false
1504 }
1505 for ch in s {
1506 if ch < `0` || ch > `9` {
1507 return false
1508 }
1509 }
1510 return true
1511}
1512
1513// make_fixed_array_len_expr builds make fixed array len expr data for transform.
1514fn (mut t Transformer) make_fixed_array_len_expr(s string) flat.NodeId {
1515 len_text := fixed_array_len_text(s)
1516 if is_decimal_text(len_text) {
1517 return t.make_int_literal(len_text.int())
1518 }
1519 // Fold a const length — a bare const (`SEGS`) or an expression (`segs + 1`) — to
1520 // its integer value. A fixed array's `.len` is a compile-time constant; emitting
1521 // the raw expression as an ident would c_name-mangle it into an undeclared
1522 // identifier such as `segs_+_1`.
1523 if !isnil(t.tc) {
1524 if v := t.tc.const_int_value(len_text, []string{}) {
1525 return t.make_int_literal(v)
1526 }
1527 }
1528 if len_text.contains('.') {
1529 base := len_text.all_before_last('.')
1530 field := len_text.all_after_last('.')
1531 return t.make_selector(t.make_ident(base), field, 'int')
1532 }
1533 return t.make_ident(len_text)
1534}
1535
1536const c_reserved_words = ['auto', 'break', 'case', 'char', 'const', 'continue', 'copy', 'default',
1537 'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
1538 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
1539 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while']
1540
1541// c_name converts c name data for transform.
1542fn c_name(name string) string {
1543 if name.starts_with('C.') {
1544 return name[2..]
1545 }
1546 n := name.replace('[]', 'Array_').replace('.-', '__minus').replace('.+', '__plus').replace('.==',
1547 '__eq').replace('.!=', '__ne').replace('.<=', '__le').replace('.>=', '__ge').replace('.<',
1548 '__lt').replace('.>', '__gt').replace('.*', '__mul').replace('./', '__div').replace('.%',
1549 '__mod').replace('.&', '__and').replace('.|', '__or').replace('.^', '__xor').replace('.<<',
1550 '__left_shift').replace('.>>', '__right_shift').replace('&', 'ptr').replace('[', '_').replace(']', '').replace(',', '_').replace(' ', '_').replace('.', '__')
1551 if n in c_reserved_words {
1552 return 'v_${n}'
1553 }
1554 return n
1555}
1556