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