vxx2 / vlib / v3 / transform / map.v
499 lines · 471 sloc · 18.93 KB · 2b81918e62db9ab38787518603a7f93bee80868c
Raw
1module transform
2
3import v3.flat
4
5// MapIndexInfo stores map index info metadata used by transform.
6struct MapIndexInfo {
7 base_id flat.NodeId
8 key_id flat.NodeId
9 base_type string
10 key_type string
11 value_type string
12}
13
14// map_type_parts supports map type parts handling for Transformer.
15fn (mut t Transformer) map_type_parts(map_type string) (string, string) {
16 clean := t.clean_map_type(map_type)
17 if !clean.starts_with('map[') {
18 return '', ''
19 }
20 return t.map_key_type(clean), t.map_value_type(clean)
21}
22
23// make_new_map_call builds make new map call data for transform.
24fn (mut t Transformer) make_new_map_call(map_type string) flat.NodeId {
25 key_type, value_type := t.map_type_parts(map_type)
26 hash_fn, eq_fn, clone_fn, free_fn := map_callback_names(key_type)
27 mut args := []flat.NodeId{}
28 args << t.make_sizeof_type(key_type)
29 args << t.make_sizeof_type(value_type)
30 args << t.make_ident(hash_fn)
31 args << t.make_ident(eq_fn)
32 args << t.make_ident(clone_fn)
33 args << t.make_ident(free_fn)
34 return t.make_call_typed('new_map', args, map_type)
35}
36
37fn (t &Transformer) new_map_call_type(node flat.Node) string {
38 if node.kind != .call || node.children_count < 3 {
39 return ''
40 }
41 callee := t.a.child_node(&node, 0)
42 if callee.kind != .ident || callee.value != 'new_map' {
43 return ''
44 }
45 key_size := t.a.child_node(&node, 1)
46 value_size := t.a.child_node(&node, 2)
47 if key_size.kind != .sizeof_expr || value_size.kind != .sizeof_expr || key_size.value.len == 0
48 || value_size.value.len == 0 {
49 return ''
50 }
51 return 'map[${key_size.value}]${value_size.value}'
52}
53
54// map_callback_names supports map callback names handling for transform.
55fn map_callback_names(key_type string) (string, string, string, string) {
56 if key_type == 'string' {
57 return 'map_hash_string', 'map_eq_string', 'map_clone_string', 'map_free_string'
58 }
59 size_suffix := match key_type {
60 'u8', 'i8', 'bool', 'char' { '1' }
61 'u16', 'i16' { '2' }
62 'i64', 'u64', 'isize', 'usize', 'voidptr' { '8' }
63 else { '4' }
64 }
65
66 return 'map_hash_int_${size_suffix}', 'map_eq_int_${size_suffix}', 'map_clone_int_${size_suffix}', 'map_free_nop'
67}
68
69// map_index_info supports map index info handling for Transformer.
70fn (mut t Transformer) map_index_info(index_id flat.NodeId) ?MapIndexInfo {
71 if int(index_id) < 0 {
72 return none
73 }
74 lhs := t.a.nodes[int(index_id)]
75 if lhs.kind != .index || lhs.children_count < 2 || lhs.value == 'range' {
76 return none
77 }
78 base_id := t.a.child(&lhs, 0)
79 key_id := t.a.child(&lhs, 1)
80 base_type := t.node_type(base_id)
81 map_type := t.clean_map_type(base_type)
82 if !map_type.starts_with('map[') {
83 return none
84 }
85 key_type, raw_value_type := t.map_type_parts(map_type)
86 if key_type.len == 0 || raw_value_type.len == 0 {
87 return none
88 }
89 // Qualify a bare local sum type (`Value` -> `eval.Value`). A bare name can clash
90 // with an imported type of the same name and resolve to the wrong sum type when the
91 // value is later wrapped into the map element type.
92 mut value_type := raw_value_type
93 if !value_type.contains('.') && !value_type.contains('[') && !value_type.starts_with('&')
94 && t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
95 qualified := '${t.cur_module}.${value_type}'
96 if qualified in t.sum_types || (!isnil(t.tc) && qualified in t.tc.sum_types) {
97 value_type = qualified
98 }
99 }
100 return MapIndexInfo{
101 base_id: base_id
102 key_id: key_id
103 base_type: base_type
104 key_type: key_type
105 value_type: value_type
106 }
107}
108
109// make_map_get_expr builds make map get expr data for transform.
110fn (mut t Transformer) make_map_get_expr(map_expr flat.NodeId, base_type string, key_name string, zero_name string, value_type string) flat.NodeId {
111 clean_value_type := if t.is_fixed_array_type(value_type) {
112 fixed_array_canonical_type(value_type)
113 } else {
114 value_type
115 }
116 call := t.make_call_typed('map__get', arr3(t.runtime_addr(map_expr, base_type), t.make_prefix(.amp,
117 t.make_ident(key_name)), t.make_prefix(.amp, t.make_ident(zero_name))), 'voidptr')
118 cast := t.make_cast('&${clean_value_type}', call, '&${clean_value_type}')
119 return t.make_prefix(.mul, cast)
120}
121
122// make_map_get_check_expr builds make map get check expr data for transform.
123fn (mut t Transformer) make_map_get_check_expr(map_expr flat.NodeId, base_type string, key_name string) flat.NodeId {
124 return t.make_call_typed('map__get_check', arr2(t.runtime_addr(map_expr, base_type), t.make_prefix(.amp,
125 t.make_ident(key_name))), 'voidptr')
126}
127
128// make_map_exists_expr builds make map exists expr data for transform.
129fn (mut t Transformer) make_map_exists_expr(map_expr flat.NodeId, base_type string, key_name string) flat.NodeId {
130 return t.make_call_typed('map__exists', arr2(t.runtime_addr(map_expr, base_type), t.make_prefix(.amp,
131 t.make_ident(key_name))), 'bool')
132}
133
134// make_map_set_stmt builds make map set stmt data for transform.
135fn (mut t Transformer) make_map_set_stmt(map_expr flat.NodeId, base_type string, key_name string, value_name string) flat.NodeId {
136 call := t.make_call_typed('map__set', arr3(t.runtime_addr(map_expr, base_type), t.make_prefix(.amp,
137 t.make_ident(key_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void')
138 return t.make_expr_stmt(call)
139}
140
141// const_expr_for_ident supports const expr for ident handling for Transformer.
142fn (t &Transformer) const_expr_for_ident(id flat.NodeId) ?flat.NodeId {
143 if int(id) < 0 || isnil(t.tc) {
144 return none
145 }
146 node := t.a.nodes[int(id)]
147 if node.kind != .ident || node.value.len == 0 {
148 return none
149 }
150 if t.var_type(node.value).len > 0 {
151 return none
152 }
153 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
154 qname := '${t.cur_module}.${node.value}'
155 if expr_id := t.tc.const_exprs[qname] {
156 return expr_id
157 }
158 }
159 if expr_id := t.tc.const_exprs[node.value] {
160 return expr_id
161 }
162 return none
163}
164
165// lower_map_membership_expr builds lower map membership expr data for transform.
166fn (mut t Transformer) lower_map_membership_expr(map_id flat.NodeId, key_id flat.NodeId, map_type string) ?flat.NodeId {
167 clean_type := t.clean_map_type(map_type)
168 mut key_type := ''
169 if clean_type.starts_with('map[') {
170 key_type = t.map_key_type(clean_type)
171 }
172 if key_type.len == 0 {
173 key_type = t.node_type(key_id)
174 }
175 if key_type.len == 0 {
176 key := t.a.nodes[int(key_id)]
177 if key.kind == .selector {
178 key_type = t.selector_field_type(key)
179 }
180 }
181 if key_type.len == 0 {
182 return none
183 }
184 map_source_id := t.const_expr_for_ident(map_id) or { map_id }
185 map_expr := t.stable_expr_for_reuse(map_source_id)
186 key_name := t.new_temp('map_key')
187 t.pending_stmts << t.make_decl_assign_typed(key_name, t.transform_expr(key_id), key_type)
188 return t.make_map_exists_expr(map_expr, map_type, key_name)
189}
190
191// try_lower_map_index_expr supports try lower map index expr handling for Transformer.
192fn (mut t Transformer) try_lower_map_index_expr(_id flat.NodeId, node flat.Node) ?flat.NodeId {
193 if node.kind != .index || node.children_count < 2 || node.value == 'range' {
194 return none
195 }
196 base_id := t.a.child(&node, 0)
197 key_id := t.a.child(&node, 1)
198 base_type := t.node_type(base_id)
199 map_type := t.clean_map_type(base_type)
200 if !map_type.starts_with('map[') {
201 return none
202 }
203 key_type, value_type := t.map_type_parts(map_type)
204 if key_type.len == 0 || value_type.len == 0 {
205 return none
206 }
207 map_source_id := t.const_expr_for_ident(base_id) or { base_id }
208 map_expr := t.stable_expr_for_reuse(map_source_id)
209 key_name := t.new_temp('map_key')
210 zero_name := t.new_temp('map_zero')
211 t.pending_stmts << t.make_decl_assign_typed(key_name, t.transform_expr(key_id), key_type)
212 t.pending_stmts << t.make_decl_assign_typed(zero_name, t.zero_value_for_type(value_type),
213 value_type)
214 return t.make_map_get_expr(map_expr, base_type, key_name, zero_name, value_type)
215}
216
217// is_map_index_or_expr reports whether is map index or expr applies in transform.
218fn (mut t Transformer) is_map_index_or_expr(node flat.Node) bool {
219 if node.kind != .or_expr || node.children_count < 2 {
220 return false
221 }
222 expr := t.a.child_node(&node, 0)
223 if expr.kind != .index || expr.children_count < 2 || expr.value == 'range' {
224 return false
225 }
226 base_id := t.a.child(expr, 0)
227 base_type := t.node_type(base_id)
228 return t.clean_map_type(base_type).starts_with('map[')
229}
230
231// transform_map_index_or_expr transforms transform map index or expr data for transform.
232fn (mut t Transformer) transform_map_index_or_expr(id flat.NodeId, node flat.Node) flat.NodeId {
233 if node.children_count < 2 {
234 return id
235 }
236 expr_id := t.a.child(&node, 0)
237 body_id := t.a.child(&node, 1)
238 info := t.map_index_info(expr_id) or { return id }
239 map_expr := t.stable_expr_for_reuse(info.base_id)
240 key_name := t.new_temp('map_key')
241 ptr_name := t.new_temp('map_ptr')
242 val_name := t.new_temp('map_val')
243 outer_pending := t.pending_stmts.clone()
244 t.pending_stmts.clear()
245 key_expr := t.transform_expr(info.key_id)
246 mut prelude := []flat.NodeId{}
247 t.drain_pending(mut prelude)
248 prelude << t.make_decl_assign_typed(key_name, key_expr, info.key_type)
249 prelude << t.make_decl_assign_typed(ptr_name, t.make_map_get_check_expr(map_expr,
250 info.base_type, key_name), 'voidptr')
251 prelude << t.make_decl_assign_typed(val_name, t.zero_value_for_type(info.value_type),
252 info.value_type)
253
254 ptr_ident := t.make_ident(ptr_name)
255 found_cond := t.make_infix(.ne, ptr_ident, t.a.add(.nil_literal))
256 ptr_value := t.make_prefix(.mul, t.make_cast('&${info.value_type}', t.make_ident(ptr_name),
257 '&${info.value_type}'))
258 then_block := t.make_block(arr1(t.make_assign(t.make_ident(val_name), ptr_value)))
259 else_block := t.make_block(t.lower_map_or_body_to_stmts(body_id, val_name, info.value_type,
260 node.value))
261 t.pending_stmts = outer_pending
262 for stmt in prelude {
263 t.pending_stmts << stmt
264 }
265 t.pending_stmts << t.make_if(found_cond, then_block, else_block)
266 return t.make_ident(val_name)
267}
268
269// lower_map_or_body_to_stmts converts lower map or body to stmts data for transform.
270fn (mut t Transformer) lower_map_or_body_to_stmts(body_id flat.NodeId, target_name string, target_type string, mode string) []flat.NodeId {
271 if mode == '!' || mode == '?' {
272 if t.is_optional_type_name(t.cur_fn_ret_type) {
273 return arr1(t.make_none_return_stmt())
274 }
275 return arr1(t.make_panic_stmt('option/result propagation failed'))
276 }
277 if int(body_id) < 0 {
278 return []flat.NodeId{}
279 }
280 body := t.a.nodes[int(body_id)]
281 if body.kind != .block {
282 if body.kind == .none_expr && !t.is_optional_type_name(target_type)
283 && t.is_optional_type_name(t.cur_fn_ret_type) {
284 return arr1(t.make_none_return_stmt())
285 }
286 return arr1(t.make_assign(t.make_ident(target_name), t.transform_expr(body_id)))
287 }
288 mut result := []flat.NodeId{}
289 for i in 0 .. body.children_count {
290 child_id := t.a.child(&body, i)
291 child := t.a.nodes[int(child_id)]
292 is_last := i == body.children_count - 1
293 if is_last && child.kind == .expr_stmt && child.children_count > 0 {
294 inner_id := t.a.child(&child, 0)
295 inner := t.a.nodes[int(inner_id)]
296 if inner.kind == .none_expr && !t.is_optional_type_name(target_type)
297 && t.is_optional_type_name(t.cur_fn_ret_type) {
298 result << t.make_none_return_stmt()
299 continue
300 }
301 if t.node_type(inner_id) == 'void' {
302 expanded := t.transform_stmt(child_id)
303 t.drain_pending(mut result)
304 for eid in expanded {
305 result << eid
306 }
307 } else {
308 value := t.transform_expr(inner_id)
309 t.drain_pending(mut result)
310 result << t.make_assign(t.make_ident(target_name), value)
311 }
312 } else {
313 expanded := t.transform_stmt(child_id)
314 t.drain_pending(mut result)
315 for eid in expanded {
316 result << eid
317 }
318 }
319 }
320 _ = target_type
321 return result
322}
323
324// try_lower_map_index_assign supports try lower map index assign handling for Transformer.
325fn (mut t Transformer) try_lower_map_index_assign(node flat.Node) ?[]flat.NodeId {
326 if node.kind !in [.assign, .index_assign] || node.children_count < 2 {
327 return none
328 }
329 info := t.map_index_info(t.a.child(&node, 0)) or { return none }
330 map_expr := t.stable_expr_for_reuse(info.base_id)
331 key_name := t.new_temp('map_key')
332 mut result := []flat.NodeId{}
333 t.drain_pending(mut result)
334 result << t.make_decl_assign_typed(key_name, t.transform_expr(info.key_id), info.key_type)
335 rhs_id := t.a.child(&node, 1)
336 if node.op == .assign {
337 value_name := t.new_temp('map_val')
338 value := if info.value_type.starts_with('&') && t.is_sum_type_name(info.value_type[1..]) {
339 t.transform_expr_for_type(rhs_id, info.value_type)
340 } else if info.value_type in t.sum_types
341 || t.resolve_sum_name(info.value_type) in t.sum_types {
342 t.wrap_sum_value(rhs_id, info.value_type)
343 } else {
344 t.transform_expr(rhs_id)
345 }
346 result << t.make_decl_assign_typed(value_name, value, info.value_type)
347 result << t.make_map_set_stmt(map_expr, info.base_type, key_name, value_name)
348 return result
349 }
350 if node.op == .left_shift_assign && info.value_type.starts_with('[]') {
351 t.lower_map_index_append_with_info(info, map_expr, key_name, rhs_id, mut result)
352 return result
353 }
354 op := map_compound_to_infix_op(node.op) or { return none }
355 t.lower_map_index_compound_with_info(info, map_expr, key_name, op, rhs_id, mut result)
356 return result
357}
358
359// map_compound_to_infix_op converts map compound to infix op data for transform.
360fn map_compound_to_infix_op(op flat.Op) ?flat.Op {
361 match op {
362 .plus_assign { return flat.Op.plus }
363 .minus_assign { return flat.Op.minus }
364 .mul_assign { return flat.Op.mul }
365 .div_assign { return flat.Op.div }
366 .mod_assign { return flat.Op.mod }
367 .amp_assign { return flat.Op.amp }
368 .pipe_assign { return flat.Op.pipe }
369 .xor_assign { return flat.Op.xor }
370 .left_shift_assign { return flat.Op.left_shift }
371 .right_shift_assign { return flat.Op.right_shift }
372 else { return none }
373 }
374}
375
376// load_map_index_current reads load map index current input for transform.
377fn (mut t Transformer) load_map_index_current(info MapIndexInfo, map_expr flat.NodeId, key_name string, mut result []flat.NodeId) string {
378 zero_name := t.new_temp('map_zero')
379 current_name := t.new_temp('map_val')
380 result << t.make_decl_assign_typed(zero_name, t.zero_value_for_type(info.value_type),
381 info.value_type)
382 get_expr := t.make_map_get_expr(map_expr, info.base_type, key_name, zero_name, info.value_type)
383 result << t.make_decl_assign_typed(current_name, get_expr, info.value_type)
384 return current_name
385}
386
387// lower_map_index_compound_with_info builds lower map index compound with info data for transform.
388fn (mut t Transformer) lower_map_index_compound_with_info(info MapIndexInfo, map_expr flat.NodeId, key_name string, op flat.Op, rhs_id flat.NodeId, mut result []flat.NodeId) {
389 current_name := t.load_map_index_current(info, map_expr, key_name, mut result)
390 rhs := t.transform_expr(rhs_id)
391 new_value := if info.value_type == 'string' && op == .plus {
392 t.make_call_typed('string__plus', arr2(t.make_ident(current_name), rhs), 'string')
393 } else {
394 t.make_infix(op, t.make_ident(current_name), rhs)
395 }
396 result << t.make_assign(t.make_ident(current_name), new_value)
397 result << t.make_map_set_stmt(map_expr, info.base_type, key_name, current_name)
398}
399
400// lower_map_index_postfix_with_info builds lower map index postfix with info data for transform.
401fn (mut t Transformer) lower_map_index_postfix_with_info(info MapIndexInfo, map_expr flat.NodeId, key_name string, op flat.Op, mut result []flat.NodeId) {
402 current_name := t.load_map_index_current(info, map_expr, key_name, mut result)
403 infix_op := if op == .dec { flat.Op.minus } else { flat.Op.plus }
404 new_value := t.make_infix(infix_op, t.make_ident(current_name), t.make_int_literal(1))
405 result << t.make_assign(t.make_ident(current_name), new_value)
406 result << t.make_map_set_stmt(map_expr, info.base_type, key_name, current_name)
407}
408
409// lower_map_index_append_with_info builds lower map index append with info data for transform.
410fn (mut t Transformer) lower_map_index_append_with_info(info MapIndexInfo, map_expr flat.NodeId, key_name string, rhs_id flat.NodeId, mut result []flat.NodeId) {
411 current_name := t.load_map_index_current(info, map_expr, key_name, mut result)
412 append := t.make_infix(.left_shift, t.make_ident(current_name), t.transform_expr(rhs_id))
413 t.annotate_left_shift(append)
414 result << t.make_expr_stmt(append)
415 result << t.make_map_set_stmt(map_expr, info.base_type, key_name, current_name)
416}
417
418// try_lower_map_index_postfix_stmt
419// supports helper handling in transform.
420fn (mut t Transformer) try_lower_map_index_postfix_stmt(id flat.NodeId) ?[]flat.NodeId {
421 if int(id) < 0 {
422 return none
423 }
424 node := t.a.nodes[int(id)]
425 if node.kind != .postfix || node.children_count == 0 || node.op !in [.inc, .dec] {
426 return none
427 }
428 info := t.map_index_info(t.a.child(&node, 0)) or { return none }
429 map_expr := t.stable_expr_for_reuse(info.base_id)
430 key_name := t.new_temp('map_key')
431 mut result := []flat.NodeId{}
432 t.drain_pending(mut result)
433 result << t.make_decl_assign_typed(key_name, t.transform_expr(info.key_id), info.key_type)
434 t.lower_map_index_postfix_with_info(info, map_expr, key_name, node.op, mut result)
435 return result
436}
437
438// try_lower_map_index_append_stmt
439// supports helper handling in transform.
440fn (mut t Transformer) try_lower_map_index_append_stmt(id flat.NodeId) ?[]flat.NodeId {
441 if int(id) < 0 {
442 return none
443 }
444 node := t.a.nodes[int(id)]
445 if node.kind != .infix || node.op != .left_shift || node.children_count < 2 {
446 return none
447 }
448 info := t.map_index_info(t.a.child(&node, 0)) or { return none }
449 if !info.value_type.starts_with('[]') {
450 return none
451 }
452 map_expr := t.stable_expr_for_reuse(info.base_id)
453 key_name := t.new_temp('map_key')
454 mut result := []flat.NodeId{}
455 t.drain_pending(mut result)
456 result << t.make_decl_assign_typed(key_name, t.transform_expr(info.key_id), info.key_type)
457 t.lower_map_index_append_with_info(info, map_expr, key_name, t.a.child(&node, 1), mut result)
458 return result
459}
460
461// lower_map_init_to_runtime converts lower map init to runtime data for transform.
462fn (mut t Transformer) lower_map_init_to_runtime(id flat.NodeId, node flat.Node) flat.NodeId {
463 map_type := if node.value.len > 0 {
464 node.value
465 } else if node.typ.len > 0 {
466 node.typ
467 } else {
468 t.node_type(id)
469 }
470 if !map_type.starts_with('map[') {
471 return id
472 }
473 init_call := t.make_new_map_call(map_type)
474 if node.children_count == 0 {
475 return init_call
476 }
477 tmp_name := t.new_temp('map_lit')
478 t.pending_stmts << t.make_decl_assign_typed(tmp_name, init_call, map_type)
479 key_type, value_type := t.map_type_parts(map_type)
480 for i := 0; i + 1 < node.children_count; i += 2 {
481 key_name := t.new_temp('map_key')
482 value_name := t.new_temp('map_val')
483 t.pending_stmts << t.make_decl_assign_typed(key_name, t.transform_expr_for_type(t.a.child(&node, i),
484 key_type), key_type)
485 value_id := t.a.child(&node, i + 1)
486 value := if value_type.starts_with('&') && t.is_sum_type_name(value_type[1..]) {
487 t.transform_expr_for_type(value_id, value_type)
488 } else if value_type in t.sum_types || t.resolve_sum_name(value_type) in t.sum_types {
489 t.wrap_sum_value(value_id, value_type)
490 } else {
491 t.transform_expr_for_type(value_id, value_type)
492 }
493 t.pending_stmts << t.make_decl_assign_typed(value_name, value, value_type)
494 call := t.make_call_typed('map__set', arr3(t.make_prefix(.amp, t.make_ident(tmp_name)), t.make_prefix(.amp,
495 t.make_ident(key_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void')
496 t.pending_stmts << t.make_expr_stmt(call)
497 }
498 return t.make_ident(tmp_name)
499}
500